From 69495e64eb19cbe210884cce55a80a6176eed1cd Mon Sep 17 00:00:00 2001 From: kzoeps Date: Tue, 6 Oct 2026 22:48:21 +0600 Subject: [PATCH 1/2] docs: synchronize API reference with manifest and enforce freshness --- CONTRIBUTING.md | 14 +- README.md | 21 +- api/README.md | 9 +- docs/README.md | 12 + docs/coverage.json | 547 +- docs/openapi.json | 5754 ++++++++++++----- docs/scripts/check-sources.mjs | 60 + docs/scripts/generate-openapi.mjs | 96 +- docs/scripts/openapi-artifacts.mjs | 83 + docs/scripts/refresh-sources.mjs | 320 +- docs/sources/index.json | 1662 ++++- .../lexicons/app.bsky.richtext.facet.json | 89 + .../app.certified.actor.getOrganization.json | 63 +- .../app.certified.actor.getOrganizations.json | 50 +- .../app.certified.actor.getProfile.json | 42 +- .../app.certified.actor.getProfiles.json | 60 +- ...app.certified.actor.listOrganizations.json | 43 +- .../app.certified.actor.listProfiles.json | 52 +- .../app.certified.actor.organization.json | 96 + .../lexicons/app.certified.actor.profile.json | 68 + ...p.certified.actor.searchOrganizations.json | 56 +- .../app.certified.actor.searchProfiles.json | 71 +- .../lexicons/app.certified.badge.award.json | 55 + .../app.certified.badge.definition.json | 75 + .../app.certified.badge.getBadgeAward.json | 193 + ...pp.certified.badge.getBadgeDefinition.json | 70 +- .../app.certified.badge.getBadgeResponse.json | 97 + .../app.certified.badge.listBadgeAwards.json | 119 + ....certified.badge.listBadgeDefinitions.json | 40 +- ...pp.certified.badge.listBadgeResponses.json | 76 + .../app.certified.badge.response.json | 49 + ...ertified.badge.searchBadgeDefinitions.json | 137 + docs/sources/lexicons/app.certified.defs.json | 36 + .../app.certified.graph.entityFollow.json | 42 + .../lexicons/app.certified.graph.follow.json | 40 + .../app.certified.graph.getEntityFollow.json | 85 +- .../app.certified.graph.getFollow.json | 105 +- ...pp.certified.graph.listActorFollowers.json | 45 +- ...pp.certified.graph.listActorFollowing.json | 45 +- ...p.certified.graph.listEntityFollowers.json | 97 +- ...p.certified.graph.listEntityFollowing.json | 86 +- ...app.certified.graph.listRecentFollows.json | 73 +- .../lexicons/app.certified.link.evm.json | 108 + .../app.certified.link.getEvmLink.json | 94 + .../app.certified.link.listEvmLinks.json | 86 + .../app.certified.location.getLocation.json | 81 +- .../lexicons/app.certified.location.json | 96 + .../app.certified.location.listLocations.json | 86 +- .../app.certified.signature.defs.json | 37 + .../lexicons/com.atproto.repo.strongRef.json | 24 + .../lexicons/org.hypercerts.api.defs.json | 147 + .../org.hypercerts.claim.activity.json | 182 + .../org.hypercerts.claim.contribution.json | 50 + ...percerts.claim.contributorInformation.json | 47 + .../org.hypercerts.claim.getActivity.json | 185 +- .../org.hypercerts.claim.getContribution.json | 97 + ...certs.claim.getContributorInformation.json | 97 + .../org.hypercerts.claim.getRights.json | 93 + .../org.hypercerts.claim.listActivities.json | 72 +- ...rg.hypercerts.claim.listContributions.json | 80 + ...erts.claim.listContributorInformation.json | 78 + .../org.hypercerts.claim.listRights.json | 74 + .../lexicons/org.hypercerts.claim.rights.json | 56 + ...org.hypercerts.claim.searchActivities.json | 78 +- ...g.hypercerts.collection.getCollection.json | 312 +- .../lexicons/org.hypercerts.collection.json | 128 + ...rcerts.collection.listCollectionItems.json | 158 +- ...hypercerts.collection.listCollections.json | 110 +- ...percerts.collection.searchCollections.json | 119 +- ...rg.hypercerts.context.acknowledgement.json | 54 + .../org.hypercerts.context.attachment.json | 96 + .../org.hypercerts.context.evaluation.json | 108 + ...hypercerts.context.getAcknowledgement.json | 56 + .../org.hypercerts.context.getAttachment.json | 82 +- .../org.hypercerts.context.getEvaluation.json | 132 +- ...org.hypercerts.context.getMeasurement.json | 93 + ...percerts.context.listAcknowledgements.json | 88 + ...rg.hypercerts.context.listAttachments.json | 69 +- ...rg.hypercerts.context.listEvaluations.json | 53 +- ...g.hypercerts.context.listMeasurements.json | 87 + .../org.hypercerts.context.measurement.json | 117 + .../sources/lexicons/org.hypercerts.defs.json | 136 + .../lexicons/org.hypercerts.entity.defs.json | 48 + .../org.hypercerts.entity.feature.json | 81 + .../org.hypercerts.entity.getFeature.json | 52 + .../org.hypercerts.entity.listFeatures.json | 91 + .../org.hypercerts.funding.getReceipt.json | 82 +- .../org.hypercerts.funding.listReceipts.json | 81 +- .../org.hypercerts.funding.receipt.json | 104 + .../org.hypercerts.vocab.getVocabTag.json | 97 + .../org.hypercerts.vocab.listVocabTags.json | 80 + .../lexicons/org.hypercerts.vocab.tag.json | 116 + .../org.hypercerts.workscope.cel.json | 46 + ....hypercerts.workscope.getWorkscopeTag.json | 97 + ...ypercerts.workscope.listWorkscopeTags.json | 82 + .../org.hypercerts.workscope.tag.json | 105 + .../pub.leaflet.blocks.blockquote.json | 24 + .../lexicons/pub.leaflet.blocks.bskyPost.json | 21 + .../lexicons/pub.leaflet.blocks.button.json | 22 + .../lexicons/pub.leaflet.blocks.code.json | 23 + .../lexicons/pub.leaflet.blocks.header.json | 29 + .../pub.leaflet.blocks.horizontalRule.json | 11 + .../lexicons/pub.leaflet.blocks.html.json | 42 + .../lexicons/pub.leaflet.blocks.iframe.json | 43 + .../lexicons/pub.leaflet.blocks.image.json | 53 + .../pub.leaflet.blocks.imageGallery.json | 76 + .../lexicons/pub.leaflet.blocks.math.json | 17 + ...b.leaflet.blocks.membersOnlyDelimiter.json | 12 + .../pub.leaflet.blocks.orderedList.json | 58 + .../lexicons/pub.leaflet.blocks.page.json | 17 + .../lexicons/pub.leaflet.blocks.poll.json | 18 + .../pub.leaflet.blocks.postsList.json | 33 + .../lexicons/pub.leaflet.blocks.signup.json | 12 + .../pub.leaflet.blocks.standardSitePost.json | 32 + ...eaflet.blocks.standardSitePublication.json | 24 + .../lexicons/pub.leaflet.blocks.text.json | 32 + .../pub.leaflet.blocks.unorderedList.json | 54 + .../lexicons/pub.leaflet.blocks.website.json | 31 + .../pub.leaflet.pages.linearDocument.json | 115 + .../lexicons/pub.leaflet.richtext.facet.json | 176 + .../lexicons/pub.leaflet.theme.color.json | 62 + docs/test/documentation-freshness.test.mjs | 147 + docs/test/generator.test.mjs | 4 +- docs/test/manifest-sync.test.mjs | 197 + docs/test/presentation.test.mjs | 18 +- docs/test/refresh-sources.test.mjs | 10 +- docs/web/src/presentation.mjs | 6 - package.json | 1 + 128 files changed, 14373 insertions(+), 2988 deletions(-) create mode 100644 docs/scripts/check-sources.mjs create mode 100644 docs/scripts/openapi-artifacts.mjs create mode 100644 docs/sources/lexicons/app.bsky.richtext.facet.json create mode 100644 docs/sources/lexicons/app.certified.actor.organization.json create mode 100644 docs/sources/lexicons/app.certified.actor.profile.json create mode 100644 docs/sources/lexicons/app.certified.badge.award.json create mode 100644 docs/sources/lexicons/app.certified.badge.definition.json create mode 100644 docs/sources/lexicons/app.certified.badge.getBadgeAward.json create mode 100644 docs/sources/lexicons/app.certified.badge.getBadgeResponse.json create mode 100644 docs/sources/lexicons/app.certified.badge.listBadgeAwards.json create mode 100644 docs/sources/lexicons/app.certified.badge.listBadgeResponses.json create mode 100644 docs/sources/lexicons/app.certified.badge.response.json create mode 100644 docs/sources/lexicons/app.certified.badge.searchBadgeDefinitions.json create mode 100644 docs/sources/lexicons/app.certified.defs.json create mode 100644 docs/sources/lexicons/app.certified.graph.entityFollow.json create mode 100644 docs/sources/lexicons/app.certified.graph.follow.json create mode 100644 docs/sources/lexicons/app.certified.link.evm.json create mode 100644 docs/sources/lexicons/app.certified.link.getEvmLink.json create mode 100644 docs/sources/lexicons/app.certified.link.listEvmLinks.json create mode 100644 docs/sources/lexicons/app.certified.location.json create mode 100644 docs/sources/lexicons/app.certified.signature.defs.json create mode 100644 docs/sources/lexicons/com.atproto.repo.strongRef.json create mode 100644 docs/sources/lexicons/org.hypercerts.api.defs.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.activity.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.contribution.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.contributorInformation.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.getContribution.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.getContributorInformation.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.getRights.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.listContributions.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.listContributorInformation.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.listRights.json create mode 100644 docs/sources/lexicons/org.hypercerts.claim.rights.json create mode 100644 docs/sources/lexicons/org.hypercerts.collection.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.acknowledgement.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.attachment.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.evaluation.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.getAcknowledgement.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.getMeasurement.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.listAcknowledgements.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.listMeasurements.json create mode 100644 docs/sources/lexicons/org.hypercerts.context.measurement.json create mode 100644 docs/sources/lexicons/org.hypercerts.defs.json create mode 100644 docs/sources/lexicons/org.hypercerts.entity.defs.json create mode 100644 docs/sources/lexicons/org.hypercerts.entity.feature.json create mode 100644 docs/sources/lexicons/org.hypercerts.entity.getFeature.json create mode 100644 docs/sources/lexicons/org.hypercerts.entity.listFeatures.json create mode 100644 docs/sources/lexicons/org.hypercerts.funding.receipt.json create mode 100644 docs/sources/lexicons/org.hypercerts.vocab.getVocabTag.json create mode 100644 docs/sources/lexicons/org.hypercerts.vocab.listVocabTags.json create mode 100644 docs/sources/lexicons/org.hypercerts.vocab.tag.json create mode 100644 docs/sources/lexicons/org.hypercerts.workscope.cel.json create mode 100644 docs/sources/lexicons/org.hypercerts.workscope.getWorkscopeTag.json create mode 100644 docs/sources/lexicons/org.hypercerts.workscope.listWorkscopeTags.json create mode 100644 docs/sources/lexicons/org.hypercerts.workscope.tag.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.blockquote.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.bskyPost.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.button.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.code.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.header.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.horizontalRule.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.html.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.iframe.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.image.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.imageGallery.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.math.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.membersOnlyDelimiter.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.orderedList.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.page.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.poll.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.postsList.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.signup.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.standardSitePost.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.standardSitePublication.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.text.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.unorderedList.json create mode 100644 docs/sources/lexicons/pub.leaflet.blocks.website.json create mode 100644 docs/sources/lexicons/pub.leaflet.pages.linearDocument.json create mode 100644 docs/sources/lexicons/pub.leaflet.richtext.facet.json create mode 100644 docs/sources/lexicons/pub.leaflet.theme.color.json create mode 100644 docs/test/documentation-freshness.test.mjs create mode 100644 docs/test/manifest-sync.test.mjs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2441a0c..9730e76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,16 @@ pnpm build `pnpm test:unit` runs the discovered tests under `api/tests/unit`; unit tests use local fixtures and fake process/network adapters, and do not seed a database or contact HappyView. `pnpm check` also checks generated-source freshness, JavaScript and Lua lint, types, and endpoint tests. `pnpm build` emits Lua handlers declared by the root and module manifests. Shared Lua files are bundled into capability handlers rather than installed independently. +## API reference snapshots + +When a change affects registered query Lexicons or their schemas, refresh the committed explorer artifacts from the current API manifest: + +```sh +pnpm docs:sync +``` + +This updates `docs/sources/index.json`, the committed Lexicon snapshots, `docs/openapi.json`, and `docs/coverage.json`. It uses the repository-pinned `@hypercerts-org/lexicon` dependency and makes no HappyView requests. Coverage reports manifest inclusion only; it does not validate runtime behavior or deployment. See [docs/README.md](docs/README.md) for the source and snapshot policy. + ## HTTP runtime tests `pnpm test:http` runs the suites in `api/tests/http` against the activity, badge-definition, badge-query, EVM-link, acknowledgement, collection, context-measurement, context attachment and evaluation, contributor-information, funding, location, profile, organization, feature, work-scope-tag, rights, vocabulary-tag, graph, and contribution query XRPC endpoints installed from this checkout. These tests exercise real HTTP behavior against PostgreSQL, not only Lua handlers with a fake database. @@ -56,4 +66,6 @@ For the pinned HappyView release, ordinary Lua `error()` exceptions return HTTP ## Operations with external effects -`pnpm install:api` sends admin requests to a HappyView instance and uploads declared assets. By default, conflicting declared assets stop the install before asset writes; `pnpm install:api --override` explicitly replaces only those conflicts. Override does not bypass source/dependency validation, authentication, or profile resolver-setting requirements, and writes are not rolled back if a later asset fails. Run the installer only for an explicitly approved target with an approved token. Review the target and release notes before installing a released bundle; see [api/README.md](api/README.md). +For pull requests that change the installable API bundle or operator-visible behavior, add a release note following the [Changesets guidance](.changeset/README.md). Documentation-only or endpoint-explorer-only changes do not need a Changeset. + +`pnpm install:api` sends admin requests to a HappyView instance and uploads declared assets. By default, conflicting declared assets stop the install before asset writes; `pnpm install:api --override` explicitly replaces only those conflicts. Override does not bypass source/dependency validation, authentication, or profile resolver-setting requirements, and writes are not rolled back if a later asset fails. Run the installer only for an explicitly approved target with an approved token. Review the target and release notes before installing a released bundle; see [api/README.md](api/README.md) for release installation and `HYPERCERTS_HANDLE_RESOLVER_URL` setup, permission requirements, and retained-setting behavior. diff --git a/README.md b/README.md index 19a5656..f63b46c 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,8 @@ # Hypercerts API workspace -This repository combines the shared HappyView installer, pinned Lexicon dependencies, reusable Lua projections, fixtures, offline checks, and independently owned capability modules. This composed checkout includes public query modules for actor profiles and organizations, activity, badge definitions and queries, collections, context measurements, attachments and evaluations, actor and entity follows, recent follows, funding receipts, Certified EVM links, locations, features, work-scope tags, contribution records, contributor information, vocabulary tags, and acknowledgements. The `org.hypercerts.vocab.getVocabTag` and `org.hypercerts.vocab.listVocabTags` handlers are bundled and registered through `api/modules/vocab/manifest.json`. +This repository combines the shared HappyView installer, pinned Lexicon dependencies, reusable Lua projections, fixtures, offline checks, and independently owned capability modules. `api/manifest.json` is authoritative for the modules included in this checkout. The endpoint explorer and its generated OpenAPI and coverage artifacts derive their operations from the query and procedure Lexicons registered by those modules; referenced schemas come from local API Lexicons and the pinned `@hypercerts-org/lexicon` package. -This checkout is one of the additive local sibling branches used to compose the API: - -Included query endpoints: - -- Rights: `org.hypercerts.claim.getRights`, `org.hypercerts.claim.listRights` - -- Badge definitions: `app.certified.badge.getBadgeDefinition`, `app.certified.badge.listBadgeDefinitions` -- Badge queries: `app.certified.badge.searchBadgeDefinitions`, `app.certified.badge.getBadgeAward`, `app.certified.badge.listBadgeAwards`, `app.certified.badge.getBadgeResponse`, `app.certified.badge.listBadgeResponses` -- Certified EVM links: `app.certified.link.getEvmLink`, `app.certified.link.listEvmLinks` -- Funding receipts: `org.hypercerts.funding.getReceipt`, `org.hypercerts.funding.listReceipts` -- Acknowledgements: `org.hypercerts.context.getAcknowledgement`, `org.hypercerts.context.listAcknowledgements` -- Contributions: `org.hypercerts.claim.getContribution`, `org.hypercerts.claim.listContributions` -- Contributor information: `org.hypercerts.claim.getContributorInformation`, `org.hypercerts.claim.listContributorInformation` -- Vocabulary tags: `org.hypercerts.vocab.getVocabTag`, `org.hypercerts.vocab.listVocabTags` - -`api/manifest.json` is authoritative for the modules and validation Lexicons included in this checkout. The `docs/` workspace contains the endpoint explorer and full schema snapshots. +The explorer runs from committed Lexicon snapshots and does not require an API checkout or HappyView service. See [docs/README.md](docs/README.md) for local use and the `pnpm docs:sync` command. The generated [coverage report](docs/coverage.json) lists the exact manifest-registered operation IDs and distinguishes source inclusion from runtime or deployment validation. ## Checks @@ -27,7 +12,7 @@ pnpm check pnpm build ``` -`pnpm check` validates generated handlers, lint, types, and unit tests. `pnpm build` refreshes declared Lua handler bundles. These checks do not deploy or contact a HappyView instance. `pnpm install:api` sends admin requests and requires an explicitly approved target and token. See [CONTRIBUTING.md](CONTRIBUTING.md) for local development and HTTP test guidance, and [api/README.md](api/README.md) for API bundle, badge-query, and release installation details. +`pnpm check` validates generated handlers, lint, types, API unit tests, and endpoint-explorer tests, including read-only freshness checks for the committed docs artifacts. `pnpm build` refreshes declared Lua handler bundles. These checks do not deploy or contact a HappyView instance. `pnpm install:api` sends admin requests and requires an explicitly approved target and token. See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, HTTP test guidance, and release-note requirements, and [api/README.md](api/README.md) for bundle installation and operator guidance. ## Installed HTTP endpoint inventory diff --git a/api/README.md b/api/README.md index 1ff0a29..1790864 100644 --- a/api/README.md +++ b/api/README.md @@ -1,6 +1,6 @@ # HappyView API toolkit and query modules -This package owns the shared API installer and build tooling, pinned upstream Lexicons, common view definitions, reusable Lua projections, offline fixture/test utilities, and the EVM-link, feature, contribution, rights, badge-query, vocabulary-tag, and acknowledgement modules. The `modules/shared/manifest.json` contains record schemas and shared query/view Lexicons; it contains no Lua endpoint scripts. A foundation-only install does not implement those queries. Capability modules listed in the root manifest register their endpoint Lexicons and Lua handlers separately; the `badge-queries` module installs five public badge query Lexicons and their Lua handlers. +This package owns the shared API installer and build tooling, pinned upstream Lexicons, common view definitions, reusable Lua projections, offline fixture/test utilities, and independently registered query modules. The root `api/manifest.json` selects the modules included in this checkout; each module manifest declares its assets and dependencies. The `modules/shared/manifest.json` contains record schemas and shared query/view Lexicons; it contains no Lua endpoint scripts. A foundation-only install does not implement those queries. The exact explorer operation inventory is generated in [the docs coverage report](../docs/coverage.json); its inclusion labels describe manifest registration, not runtime or deployment validation. ## EVM-link queries @@ -140,6 +140,13 @@ HAPPYVIEW_BASE_URL='https://your-happyview.example' HAPPYVIEW_ADMIN_TOKEN='. (for example zone-role.site), as defined by the governing vocabulary profile; Lexicon validation cannot bind a record key to body fields, so writers and indexers enforce that agreement. A tag reference pins the specific version (CID) of the term that the referencing record classified against; consumers resolve the term's current status and supersession by its URI." + }, + "org.hypercerts.collection.getCollection.vocabTagRecordView": { "type": "object", - "description": "The follower actor and the raw entity-follow record representing its relationship.", + "description": "Raw vocabulary-tag record view with metadata and no author hydration, used for exact-version collection reference projections.", "required": [ + "uri", + "cid", + "indexedAt", "did", - "profile", - "organization", - "follow" + "record" ], "properties": { - "did": { + "uri": { "type": "string", - "format": "did" + "format": "at-uri" }, - "profile": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.api.defs.profileView" - }, - { - "type": "null" - } - ] + "cid": { + "type": "string", + "format": "cid" }, - "organization": { + "indexedAt": { "anyOf": [ { - "$ref": "#/components/schemas/org.hypercerts.api.defs.organizationView" + "type": "string", + "format": "datetime" }, { "type": "null" } ] }, - "follow": { - "$ref": "#/components/schemas/app.certified.graph.getEntityFollow.entityFollowRecordView" + "did": { + "type": "string", + "format": "did" + }, + "record": { + "$ref": "#/components/schemas/org.hypercerts.vocab.tag.main" } } }, - "app.certified.graph.listEntityFollowers.output": { + "org.hypercerts.collection.getCollection.collectionTagView": { "type": "object", + "description": "Original strong-reference URI and CID plus the exact referenced vocabulary-tag record view, or null when that version is unavailable.", "required": [ - "followers" + "uri", + "cid", + "record" ], "properties": { - "followers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/app.certified.graph.listEntityFollowers.entityFollowerView" - } + "uri": { + "type": "string", + "format": "at-uri" }, - "cursor": { + "cid": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there are no more relationships." - } - } - }, - "org.hypercerts.claim.activity.main": { - "description": "Schema reference org.hypercerts.claim.activity is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.claim.activity" - }, - "org.hypercerts.claim.activity.contributorIdentity": { - "description": "Schema reference org.hypercerts.claim.activity#contributorIdentity is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.claim.activity#contributorIdentity" - }, - "com.atproto.repo.strongRef.main": { - "description": "Schema reference com.atproto.repo.strongRef is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "com.atproto.repo.strongRef" - }, - "org.hypercerts.claim.activity.contributorRole": { - "description": "Schema reference org.hypercerts.claim.activity#contributorRole is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.claim.activity#contributorRole" - }, - "org.hypercerts.claim.contributorInformation.main": { - "description": "Schema reference org.hypercerts.claim.contributorInformation is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.claim.contributorInformation" + "format": "cid" + }, + "record": { + "anyOf": [ + { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.vocabTagRecordView" + }, + { + "type": "null" + } + ] + } + } }, - "org.hypercerts.claim.getActivity.contributorInformationView": { + "org.hypercerts.collection.getCollection.collectionView": { "type": "object", - "description": "The exact indexed contributor-information version identified by the source strong reference.", + "description": "Collection record with hydrated author and exact-version location and tag projections. Missing author sidecars are null; unavailable referenced versions retain their source URI and CID with a null record. The original record is unchanged.", "required": [ "uri", "cid", "indexedAt", "did", + "author", "record" ], "properties": { @@ -3497,114 +6073,156 @@ "type": "string", "format": "did" }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionActorView" + }, "record": { - "$ref": "#/components/schemas/org.hypercerts.claim.contributorInformation.main" + "$ref": "#/components/schemas/org.hypercerts.collection.main" + }, + "location": { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionLocationView" + }, + "tags": { + "type": "array", + "description": "Projection of record.tags in source order, preserving duplicates. Present as an empty array when record.tags is empty; omitted when it is absent.", + "maxItems": 20, + "items": { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionTagView" + } } } }, - "org.hypercerts.claim.getActivity.contributorActorView": { + "org.hypercerts.entity.feature.main": { + "type": "record", + "description": "A record for a non-agent subject that other records (measurements, evaluations, etc.) can be about, for example a land zone or an ecological stratum. It carries the subject's identity, classification, and optional published spatial representations. Geometry lives in app.certified.location records; grouping lives in org.hypercerts.collection records." + }, + "org.hypercerts.collection.listCollectionItems.featureView": { "type": "object", - "description": "Contributor DID with nullable current Certified profile.", + "description": "Feature record view with hydrated author actor; feature location, tag, and sameAs references remain unexpanded.", "required": [ + "uri", + "cid", + "indexedAt", "did", - "profile" + "author", + "record" ], "properties": { - "did": { + "uri": { "type": "string", - "format": "did" + "format": "at-uri" }, - "profile": { + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { "anyOf": [ { - "$ref": "#/components/schemas/org.hypercerts.api.defs.profileView" + "type": "string", + "format": "datetime" }, { "type": "null" } ] + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionActorView" + }, + "record": { + "$ref": "#/components/schemas/org.hypercerts.entity.feature.main" } } }, - "org.hypercerts.claim.getActivity.activityContributorView": { + "app.certified.graph.listEntityFollowing.entityFollowingItem": { "type": "object", - "description": "Original activity contributor fields with exact-version contributor information and nullable actor hydration.", + "description": "An entity-follow target and its relationship record. Resolve the latest indexed target by URI; retain the URI and follow when the target is missing or unsupported.", "required": [ - "contributorIdentity", - "contributorInformation", - "actor" + "uri", + "entity", + "follow" ], "properties": { - "contributorIdentity": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.claim.activity.contributorIdentity" - }, - { - "$ref": "#/components/schemas/com.atproto.repo.strongRef.main" - } - ] - }, - "contributionWeight": { + "uri": { "type": "string", - "maxLength": 100 - }, - "contributionDetails": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.claim.activity.contributorRole" - }, - { - "$ref": "#/components/schemas/com.atproto.repo.strongRef.main" - } - ] + "description": "AT-URI of the followed entity, retained even when the target cannot be resolved.", + "format": "at-uri" }, - "contributorInformation": { + "entity": { "anyOf": [ { - "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.contributorInformationView" + "anyOf": [ + { + "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" + }, + { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" + }, + { + "$ref": "#/components/schemas/org.hypercerts.collection.listCollectionItems.featureView" + } + ], + "description": "Resolved ActivityView, CollectionView, or author-hydrated FeatureView, or null when missing or unsupported." }, { "type": "null" } ] }, - "actor": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.contributorActorView" - }, - { - "type": "null" - } - ] + "follow": { + "$ref": "#/components/schemas/app.certified.graph.getEntityFollow.entityFollowRecordView" } } }, - "org.hypercerts.claim.getActivity.activityView": { + "app.certified.graph.listEntityFollowing.output": { + "type": "object", + "required": [ + "entities" + ], + "properties": { + "entities": { + "type": "array", + "items": { + "$ref": "#/components/schemas/app.certified.graph.listEntityFollowing.entityFollowingItem" + } + }, + "cursor": { + "type": "string", + "description": "Next-page cursor; omitted when no more results are available." + } + } + }, + "app.certified.graph.listRecentFollows.recentFollowView": { "type": "object", - "description": "Activity record with metadata, unchanged source payload, hydrated author, and optional contributor projection.", + "description": "Raw indexed account- or entity-follow record with its publisher DID.", "required": [ "uri", "cid", "indexedAt", "did", - "author", "record" ], "properties": { "uri": { "type": "string", + "description": "AT-URI of the follow record.", "format": "at-uri" }, "cid": { "type": "string", + "description": "CID of this indexed record version.", "format": "cid" }, "indexedAt": { "anyOf": [ { "type": "string", + "description": "Indexing time, or null if unavailable; ordering uses createdAt.", "format": "datetime" }, { @@ -3614,35 +6232,54 @@ }, "did": { "type": "string", + "description": "DID of the repository that published the follow.", "format": "did" }, - "author": { - "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" - }, "record": { - "$ref": "#/components/schemas/org.hypercerts.claim.activity.main" - }, - "contributors": { + "anyOf": [ + { + "$ref": "#/components/schemas/app.certified.graph.follow.main" + }, + { + "$ref": "#/components/schemas/app.certified.graph.entityFollow.main" + } + ], + "description": "Unmodified follow record; $type identifies its collection." + } + } + }, + "app.certified.graph.listRecentFollows.output": { + "type": "object", + "required": [ + "follows" + ], + "properties": { + "follows": { "type": "array", - "description": "Hydrated contributor projection in the same order and with the same multiplicity as record.contributors; omitted only when the source field is absent.", + "description": "Raw records ordered by createdAt descending, then URI descending; duplicates are included.", "items": { - "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityContributorView" + "$ref": "#/components/schemas/app.certified.graph.listRecentFollows.recentFollowView" } + }, + "cursor": { + "type": "string", + "description": "Next-page cursor; omitted when no more records are available." } } }, - "app.certified.actor.profile.main": { - "description": "Schema reference app.certified.actor.profile is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "app.certified.actor.profile" + "app.certified.link.evm.main": { + "type": "record", + "description": "A verifiable link between an ATProto DID and an EVM wallet address, proven via a cryptographic signature. Currently supports EOA wallets via EIP-712 typed data signatures; the proof field is an open union to allow future signature methods." }, - "org.hypercerts.collection.getCollection.collectionProfileView": { + "app.certified.link.getEvmLink.evmLinkView": { "type": "object", - "description": "Profile record view used in collection author projections; indexedAt is required and may be null when the index has no timestamp.", + "description": "Indexed EVM-link record with its original address and proof, and the linked actor's hydrated Certified records.", "required": [ "uri", "cid", "indexedAt", "did", + "actor", "record" ], "properties": { @@ -3658,6 +6295,7 @@ "anyOf": [ { "type": "string", + "description": "Index timestamp, or null when the indexed row has no timestamp.", "format": "datetime" }, { @@ -3669,23 +6307,52 @@ "type": "string", "format": "did" }, + "actor": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" + }, "record": { - "$ref": "#/components/schemas/app.certified.actor.profile.main" + "$ref": "#/components/schemas/app.certified.link.evm.main" } } }, - "app.certified.actor.organization.main": { - "description": "Schema reference app.certified.actor.organization is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "app.certified.actor.organization" + "app.certified.link.getEvmLink.output": { + "type": "object", + "required": [ + "evmLink" + ], + "properties": { + "evmLink": { + "$ref": "#/components/schemas/app.certified.link.getEvmLink.evmLinkView" + } + } }, - "org.hypercerts.collection.getCollection.collectionOrganizationView": { + "app.certified.link.listEvmLinks.output": { "type": "object", - "description": "Organization sidecar view used in collection author projections; indexedAt is required and may be null when the index has no timestamp.", + "required": [ + "evmLinks" + ], + "properties": { + "evmLinks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/app.certified.link.getEvmLink.evmLinkView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } + }, + "app.certified.location.getLocation.locationView": { + "type": "object", + "description": "Indexed location with its original record and hydrated author.", "required": [ "uri", "cid", "indexedAt", "did", + "author", "record" ], "properties": { @@ -3698,76 +6365,75 @@ "format": "cid" }, "indexedAt": { - "anyOf": [ - { - "type": "string", - "format": "datetime" - }, - { - "type": "null" - } - ] + "type": "string", + "format": "datetime" }, "did": { "type": "string", "format": "did" }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" + }, "record": { - "$ref": "#/components/schemas/app.certified.actor.organization.main" + "$ref": "#/components/schemas/app.certified.location.main" } } }, - "org.hypercerts.collection.getCollection.collectionActorView": { + "app.certified.location.getLocation.output": { "type": "object", - "description": "Collection author DID with nullable profile and organization sidecar views whose indexedAt values may be null.", "required": [ - "did", - "profile", - "organization" + "location" ], "properties": { - "did": { - "type": "string", - "format": "did" - }, - "profile": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionProfileView" - }, - { - "type": "null" - } - ] - }, - "organization": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionOrganizationView" - }, - { - "type": "null" - } - ] + "location": { + "$ref": "#/components/schemas/app.certified.location.getLocation.locationView" + } + } + }, + "app.certified.location.listLocations.output": { + "type": "object", + "required": [ + "locations" + ], + "properties": { + "locations": { + "type": "array", + "description": "Matching location records in page order.", + "items": { + "$ref": "#/components/schemas/app.certified.location.getLocation.locationView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." } } }, - "org.hypercerts.collection.main": { - "description": "Schema reference org.hypercerts.collection is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.collection" + "org.hypercerts.claim.getActivity.output": { + "type": "object", + "required": [ + "activity" + ], + "properties": { + "activity": { + "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" + } + } }, - "app.certified.location.main": { - "description": "Schema reference app.certified.location is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "app.certified.location" + "org.hypercerts.claim.contribution.main": { + "type": "record", + "description": "Details about a specific contribution including role, description, and timeframe." }, - "org.hypercerts.collection.getCollection.locationRecordView": { + "org.hypercerts.claim.getContribution.contributionView": { "type": "object", - "description": "Raw location record view with metadata and no author hydration, used for exact-version collection reference projections.", + "description": "Contribution record view with a hydrated publisher and unchanged source record; the publisher is not necessarily the contributor.", "required": [ "uri", "cid", "indexedAt", "did", + "author", "record" ], "properties": { @@ -3794,52 +6460,34 @@ "type": "string", "format": "did" }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" + }, "record": { - "$ref": "#/components/schemas/app.certified.location.main" + "$ref": "#/components/schemas/org.hypercerts.claim.contribution.main" } } }, - "org.hypercerts.collection.getCollection.collectionLocationView": { + "org.hypercerts.claim.getContribution.output": { "type": "object", - "description": "Original strong-reference URI and CID plus the exact referenced location record view, or null when that version is unavailable.", "required": [ - "uri", - "cid", - "record" + "contribution" ], "properties": { - "uri": { - "type": "string", - "format": "at-uri" - }, - "cid": { - "type": "string", - "format": "cid" - }, - "record": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.locationRecordView" - }, - { - "type": "null" - } - ] + "contribution": { + "$ref": "#/components/schemas/org.hypercerts.claim.getContribution.contributionView" } } }, - "org.hypercerts.vocab.tag.main": { - "description": "Schema reference org.hypercerts.vocab.tag is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.vocab.tag" - }, - "org.hypercerts.collection.getCollection.vocabTagRecordView": { + "org.hypercerts.claim.getContributorInformation.contributorInformationView": { "type": "object", - "description": "Raw vocabulary-tag record view with metadata and no author hydration, used for exact-version collection reference projections.", + "description": "Contributor-information record with its full source payload and hydrated publishing actor.", "required": [ "uri", "cid", "indexedAt", "did", + "author", "record" ], "properties": { @@ -3866,43 +6514,32 @@ "type": "string", "format": "did" }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" + }, "record": { - "$ref": "#/components/schemas/org.hypercerts.vocab.tag.main" + "$ref": "#/components/schemas/org.hypercerts.claim.contributorInformation.main" } } }, - "org.hypercerts.collection.getCollection.collectionTagView": { + "org.hypercerts.claim.getContributorInformation.output": { "type": "object", - "description": "Original strong-reference URI and CID plus the exact referenced vocabulary-tag record view, or null when that version is unavailable.", "required": [ - "uri", - "cid", - "record" + "contributorInformation" ], "properties": { - "uri": { - "type": "string", - "format": "at-uri" - }, - "cid": { - "type": "string", - "format": "cid" - }, - "record": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.vocabTagRecordView" - }, - { - "type": "null" - } - ] + "contributorInformation": { + "$ref": "#/components/schemas/org.hypercerts.claim.getContributorInformation.contributorInformationView" } } }, - "org.hypercerts.collection.getCollection.collectionView": { + "org.hypercerts.claim.rights.main": { + "type": "record", + "description": "Describes the rights that a contributor and/or an owner has, such as whether the hypercert can be sold, transferred, and under what conditions." + }, + "org.hypercerts.claim.getRights.rightsView": { "type": "object", - "description": "Collection record view with hydrated author and optional sibling projections for location and tags. The original record is unchanged; exact strong-reference versions are used and unresolved projections retain their source URI/CID with record null.", + "description": "Rights record with its publisher actor; the full record is preserved and attachments are not expanded.", "required": [ "uri", "cid", @@ -3936,38 +6573,133 @@ "format": "did" }, "author": { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionActorView" + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" }, "record": { - "$ref": "#/components/schemas/org.hypercerts.collection.main" + "$ref": "#/components/schemas/org.hypercerts.claim.rights.main" + } + } + }, + "org.hypercerts.claim.getRights.output": { + "type": "object", + "required": [ + "rights" + ], + "properties": { + "rights": { + "$ref": "#/components/schemas/org.hypercerts.claim.getRights.rightsView" + } + } + }, + "org.hypercerts.claim.listActivities.output": { + "type": "object", + "required": [ + "activities" + ], + "properties": { + "activities": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" + } }, - "location": { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionLocationView" + "cursor": { + "type": "string", + "description": "Next-page cursor; absent when pagination is complete." + } + } + }, + "org.hypercerts.claim.listContributions.output": { + "type": "object", + "required": [ + "contributions" + ], + "properties": { + "contributions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.claim.getContribution.contributionView" + } }, - "tags": { + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted after the last page." + } + } + }, + "org.hypercerts.claim.listContributorInformation.output": { + "type": "object", + "required": [ + "contributorInformation" + ], + "properties": { + "contributorInformation": { "type": "array", - "description": "Projection of record.tags preserving order, length, and duplicates, including an empty array; omitted only when record.tags is absent.", - "maxItems": 20, "items": { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionTagView" + "$ref": "#/components/schemas/org.hypercerts.claim.getContributorInformation.contributorInformationView" } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." } } }, - "org.hypercerts.entity.feature.main": { - "description": "Schema reference org.hypercerts.entity.feature is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.entity.feature" + "org.hypercerts.claim.listRights.output": { + "type": "object", + "required": [ + "rights" + ], + "properties": { + "rights": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.claim.getRights.rightsView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } }, - "org.hypercerts.collection.listCollectionItems.featureView": { + "org.hypercerts.claim.searchActivities.output": { "type": "object", - "description": "Feature record view with hydrated author actor; feature location, tag, and sameAs references remain unexpanded.", + "required": [ + "activities" + ], + "properties": { + "activities": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" + } + }, + "cursor": { + "type": "string", + "description": "Next-page cursor; absent when pagination is complete." + } + } + }, + "org.hypercerts.collection.getCollection.output": { + "type": "object", + "required": [ + "collection" + ], + "properties": { + "collection": { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" + } + } + }, + "org.hypercerts.collection.listCollectionItems.collectionSummaryView": { + "type": "object", + "description": "Non-recursive summary of a collection item target.", "required": [ "uri", "cid", - "indexedAt", "did", - "author", - "record" + "title" ], "properties": { "uri": { @@ -3978,44 +6710,42 @@ "type": "string", "format": "cid" }, - "indexedAt": { - "anyOf": [ - { - "type": "string", - "format": "datetime" - }, - { - "type": "null" - } - ] - }, "did": { "type": "string", "format": "did" }, - "author": { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionActorView" + "type": { + "type": "string", + "maxLength": 64 }, - "record": { - "$ref": "#/components/schemas/org.hypercerts.entity.feature.main" + "title": { + "type": "string", + "maxLength": 800, + "x-lexicon-maxGraphemes": 80 + }, + "shortDescription": { + "type": "string", + "maxLength": 3000, + "x-lexicon-maxGraphemes": 300 } } }, - "app.certified.graph.listEntityFollowing.entityFollowingItem": { + "org.hypercerts.collection.listCollectionItems.collectionItemView": { "type": "object", - "description": "An entity-follow target and its relationship record. Resolve the latest indexed target by URI; retain the URI and follow when the target is missing or unsupported.", + "description": "One embedded collection item in source order, retaining its original strong reference and optional weight. Resolve only an exact URI+CID target one level deep; nested collections use a summary, and unavailable versions or unsupported kinds produce record null.", "required": [ - "uri", - "entity", - "follow" + "itemIdentifier", + "record" ], "properties": { - "uri": { + "itemIdentifier": { + "$ref": "#/components/schemas/com.atproto.repo.strongRef.main" + }, + "itemWeight": { "type": "string", - "description": "AT-URI of the followed entity, retained even when the target cannot be resolved.", - "format": "at-uri" + "maxLength": 100 }, - "entity": { + "record": { "anyOf": [ { "anyOf": [ @@ -4023,68 +6753,103 @@ "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" }, { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" + "$ref": "#/components/schemas/org.hypercerts.collection.listCollectionItems.collectionSummaryView" }, { "$ref": "#/components/schemas/org.hypercerts.collection.listCollectionItems.featureView" } ], - "description": "Resolved ActivityView, CollectionView, or author-hydrated FeatureView, or null when missing or unsupported." + "description": "Resolved ActivityView, CollectionSummaryView, or FeatureView using the item strong reference's exact URI+CID; otherwise null." }, { "type": "null" } ] + } + } + }, + "org.hypercerts.collection.listCollectionItems.output": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.collection.listCollectionItems.collectionItemView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } + }, + "org.hypercerts.collection.listCollections.output": { + "type": "object", + "required": [ + "collections" + ], + "properties": { + "collections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" + } }, - "follow": { - "$ref": "#/components/schemas/app.certified.graph.getEntityFollow.entityFollowRecordView" + "cursor": { + "type": "string", + "description": "Cursor for the next page; omitted when there is no next page." } } }, - "app.certified.graph.listEntityFollowing.output": { + "org.hypercerts.collection.searchCollections.output": { "type": "object", "required": [ - "entities" + "collections" ], "properties": { - "entities": { + "collections": { "type": "array", "items": { - "$ref": "#/components/schemas/app.certified.graph.listEntityFollowing.entityFollowingItem" + "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" } }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there are no more relationships." + "description": "Cursor for the next page; omitted when there is no next page." } } }, - "app.certified.graph.listRecentFollows.recentFollowView": { + "org.hypercerts.context.acknowledgement.main": { + "type": "record", + "description": "Acknowledges a record (subject) or its relationship in a context. Created in the acknowledging actor's repo to form a bidirectional link. Examples: a contributor acknowledging inclusion in an activity, an activity owner acknowledging inclusion in a collection, or a record owner acknowledging an evaluation." + }, + "org.hypercerts.api.defs.acknowledgementView": { "type": "object", - "description": "Raw indexed account- or entity-follow record with its publishing repository identity. The unchanged record's $type distinguishes the follow collection.", + "description": "Acknowledgement record with its publisher actor.", "required": [ "uri", "cid", "indexedAt", "did", + "author", "record" ], "properties": { "uri": { "type": "string", - "description": "AT-URI of this follow record.", "format": "at-uri" }, "cid": { "type": "string", - "description": "CID of the indexed follow record version.", "format": "cid" }, "indexedAt": { "anyOf": [ { "type": "string", - "description": "Time this follow record was indexed, not the ordering field.", "format": "datetime" }, { @@ -4094,44 +6859,34 @@ }, "did": { "type": "string", - "description": "DID of the repository that published this follow record.", "format": "did" }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" + }, "record": { - "anyOf": [ - { - "$ref": "#/components/schemas/app.certified.graph.follow.main" - }, - { - "$ref": "#/components/schemas/app.certified.graph.entityFollow.main" - } - ], - "description": "Unmodified account- or entity-follow record; its $type identifies the collection." + "$ref": "#/components/schemas/org.hypercerts.context.acknowledgement.main" } } }, - "app.certified.graph.listRecentFollows.output": { + "org.hypercerts.context.getAcknowledgement.output": { "type": "object", "required": [ - "follows" + "acknowledgement" ], "properties": { - "follows": { - "type": "array", - "description": "Raw follow records from both collections, sorted by createdAt descending then URI descending, including duplicates.", - "items": { - "$ref": "#/components/schemas/app.certified.graph.listRecentFollows.recentFollowView" - } - }, - "cursor": { - "type": "string", - "description": "Opaque cursor for the next page; omitted when no further page exists." + "acknowledgement": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.acknowledgementView" } } }, - "app.certified.location.getLocation.locationView": { + "org.hypercerts.context.attachment.main": { + "type": "record", + "description": "An attachment providing commentary, context, evidence, or documentary material related to a hypercert record (e.g. an activity, project, claim, or evaluation)." + }, + "org.hypercerts.context.getAttachment.attachmentView": { "type": "object", - "description": "Location record with full indexed metadata, original payload, and hydrated author.", + "description": "Attachment with its hydrated publisher and unchanged source record; subject, content, and location references are not expanded, and external files are not fetched. Missing publisher sidecars are null; query or hydration failures fail the request.", "required": [ "uri", "cid", @@ -4150,8 +6905,15 @@ "format": "cid" }, "indexedAt": { - "type": "string", - "format": "datetime" + "anyOf": [ + { + "type": "string", + "format": "datetime" + }, + { + "type": "null" + } + ] }, "did": { "type": "string", @@ -4161,104 +6923,94 @@ "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" }, "record": { - "$ref": "#/components/schemas/app.certified.location.main" - } - } - }, - "app.certified.location.getLocation.output": { - "type": "object", - "required": [ - "location" - ], - "properties": { - "location": { - "$ref": "#/components/schemas/app.certified.location.getLocation.locationView" + "$ref": "#/components/schemas/org.hypercerts.context.attachment.main" } } }, - "app.certified.location.listLocations.output": { + "org.hypercerts.context.getAttachment.output": { "type": "object", "required": [ - "locations" + "attachment" ], "properties": { - "locations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/app.certified.location.getLocation.locationView" - } - }, - "cursor": { - "type": "string" + "attachment": { + "$ref": "#/components/schemas/org.hypercerts.context.getAttachment.attachmentView" } } }, - "org.hypercerts.claim.getActivity.output": { - "type": "object", - "required": [ - "activity" - ], - "properties": { - "activity": { - "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" - } - } + "org.hypercerts.context.evaluation.main": { + "type": "record", + "description": "An evaluation of a hypercert record (e.g. an activity and its impact)." }, - "org.hypercerts.claim.listActivities.output": { + "org.hypercerts.context.getEvaluation.hydratedEvaluationEvaluatorView": { "type": "object", + "description": "An original evaluator entry with its profile and organization sidecars looked up.", "required": [ - "activities" + "did", + "hydrationStatus", + "profile", + "organization" ], "properties": { - "activities": { - "type": "array", - "items": { - "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" - } + "did": { + "type": "string", + "format": "did" }, - "cursor": { + "hydrationStatus": { "type": "string", - "description": "Opaque cursor for the next page; omitted when pagination is complete." + "const": "hydrated" + }, + "profile": { + "anyOf": [ + { + "$ref": "#/components/schemas/org.hypercerts.api.defs.profileView" + }, + { + "type": "null" + } + ] + }, + "organization": { + "anyOf": [ + { + "$ref": "#/components/schemas/org.hypercerts.api.defs.organizationView" + }, + { + "type": "null" + } + ] } } }, - "org.hypercerts.claim.searchActivities.output": { + "org.hypercerts.context.getEvaluation.omittedEvaluationEvaluatorView": { "type": "object", + "description": "An original evaluator entry not hydrated.", "required": [ - "activities" + "did", + "hydrationStatus" ], "properties": { - "activities": { - "type": "array", - "items": { - "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" - } + "did": { + "type": "string", + "format": "did" }, - "cursor": { + "hydrationStatus": { "type": "string", - "description": "Opaque cursor for the next page; omitted when pagination is complete." - } - } - }, - "org.hypercerts.collection.getCollection.output": { - "type": "object", - "required": [ - "collection" - ], - "properties": { - "collection": { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" + "const": "omitted" } } }, - "org.hypercerts.collection.listCollectionItems.collectionSummaryView": { + "org.hypercerts.context.getEvaluation.evaluationView": { "type": "object", - "description": "Non-recursive summary of a collection item target.", + "description": "Evaluation with the complete original record, hydrated publisher, and evaluator views. For hydrated actors, missing profile or organization sidecars are null; query or hydration failures fail the request.", "required": [ "uri", "cid", + "indexedAt", "did", - "title" + "author", + "record", + "evaluators" ], "properties": { "uri": { @@ -4269,92 +7021,134 @@ "type": "string", "format": "cid" }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, "did": { "type": "string", "format": "did" }, - "type": { - "type": "string", - "maxLength": 64 + "author": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" }, - "title": { - "type": "string", - "maxLength": 800, - "x-lexicon-maxGraphemes": 80 + "record": { + "$ref": "#/components/schemas/org.hypercerts.context.evaluation.main" }, - "shortDescription": { - "type": "string", - "maxLength": 3000, - "x-lexicon-maxGraphemes": 300 + "evaluators": { + "type": "array", + "description": "One-for-one projection of record.evaluators, preserving source order, duplicates, and entry fields. Entries after position 100 remain present with hydrationStatus \"omitted\" and no profile or organization.", + "maxItems": 1000, + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.hydratedEvaluationEvaluatorView" + }, + { + "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.omittedEvaluationEvaluatorView" + } + ] + } } } }, - "org.hypercerts.collection.listCollectionItems.collectionItemView": { + "org.hypercerts.context.getEvaluation.output": { "type": "object", - "description": "One embedded collection item in source order, retaining its original strong reference and optional weight. Resolve only an exact URI+CID target one level deep; nested collections use a summary, and unavailable versions or unsupported kinds produce record null.", "required": [ - "itemIdentifier", + "evaluation" + ], + "properties": { + "evaluation": { + "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.evaluationView" + } + } + }, + "org.hypercerts.context.measurement.main": { + "type": "record", + "description": "Measurement data related to one or more records (e.g. activities, projects, etc.)." + }, + "org.hypercerts.context.getMeasurement.measurementView": { + "type": "object", + "description": "Indexed measurement with its publisher actor and original record.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", "record" ], "properties": { - "itemIdentifier": { - "$ref": "#/components/schemas/com.atproto.repo.strongRef.main" + "uri": { + "type": "string", + "format": "at-uri" }, - "itemWeight": { + "cid": { "type": "string", - "maxLength": 100 + "format": "cid" }, - "record": { + "indexedAt": { "anyOf": [ - { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.claim.getActivity.activityView" - }, - { - "$ref": "#/components/schemas/org.hypercerts.collection.listCollectionItems.collectionSummaryView" - }, - { - "$ref": "#/components/schemas/org.hypercerts.collection.listCollectionItems.featureView" - } - ], - "description": "Resolved ActivityView, CollectionSummaryView, or FeatureView using the item strong reference's exact URI+CID; otherwise null." + { + "type": "string", + "format": "datetime" }, { "type": "null" } ] + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" + }, + "record": { + "$ref": "#/components/schemas/org.hypercerts.context.measurement.main" } } }, - "org.hypercerts.collection.listCollectionItems.output": { + "org.hypercerts.context.getMeasurement.output": { "type": "object", "required": [ - "items" + "measurement" ], "properties": { - "items": { + "measurement": { + "$ref": "#/components/schemas/org.hypercerts.context.getMeasurement.measurementView" + } + } + }, + "org.hypercerts.context.listAcknowledgements.output": { + "type": "object", + "required": [ + "acknowledgements" + ], + "properties": { + "acknowledgements": { "type": "array", "items": { - "$ref": "#/components/schemas/org.hypercerts.collection.listCollectionItems.collectionItemView" + "$ref": "#/components/schemas/org.hypercerts.api.defs.acknowledgementView" } }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there is no next page." + "description": "Omitted when there is no next page." } } }, - "org.hypercerts.collection.listCollections.output": { + "org.hypercerts.context.listAttachments.output": { "type": "object", "required": [ - "collections" + "attachments" ], "properties": { - "collections": { + "attachments": { "type": "array", "items": { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" + "$ref": "#/components/schemas/org.hypercerts.context.getAttachment.attachmentView" } }, "cursor": { @@ -4363,16 +7157,16 @@ } } }, - "org.hypercerts.collection.searchCollections.output": { + "org.hypercerts.context.listEvaluations.output": { "type": "object", "required": [ - "collections" + "evaluations" ], "properties": { - "collections": { + "evaluations": { "type": "array", "items": { - "$ref": "#/components/schemas/org.hypercerts.collection.getCollection.collectionView" + "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.evaluationView" } }, "cursor": { @@ -4381,13 +7175,28 @@ } } }, - "org.hypercerts.context.attachment.main": { - "description": "Schema reference org.hypercerts.context.attachment is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.context.attachment" + "org.hypercerts.context.listMeasurements.output": { + "type": "object", + "required": [ + "measurements" + ], + "properties": { + "measurements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.context.getMeasurement.measurementView" + } + }, + "cursor": { + "type": "string", + "description": "Next-page cursor; omitted when no page follows.", + "maxLength": 8192 + } + } }, - "org.hypercerts.context.getAttachment.attachmentView": { + "org.hypercerts.entity.defs.featureView": { "type": "object", - "description": "Attachment record view with hydrated publisher actor and the full indexed record unchanged; subject, content, and location references remain unexpanded.", + "description": "Indexed feature record with its hydrated author actor.", "required": [ "uri", "cid", @@ -4424,94 +7233,125 @@ "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" }, "record": { - "$ref": "#/components/schemas/org.hypercerts.context.attachment.main" + "$ref": "#/components/schemas/org.hypercerts.entity.feature.main" } } }, - "org.hypercerts.context.getAttachment.output": { + "org.hypercerts.entity.getFeature.output": { "type": "object", "required": [ - "attachment" + "feature" ], "properties": { - "attachment": { - "$ref": "#/components/schemas/org.hypercerts.context.getAttachment.attachmentView" + "feature": { + "$ref": "#/components/schemas/org.hypercerts.entity.defs.featureView" } } }, - "org.hypercerts.context.evaluation.main": { - "description": "Schema reference org.hypercerts.context.evaluation is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.context.evaluation" + "org.hypercerts.entity.listFeatures.output": { + "type": "object", + "required": [ + "features" + ], + "properties": { + "features": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.entity.defs.featureView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } }, - "org.hypercerts.context.getEvaluation.hydratedEvaluationEvaluatorView": { + "org.hypercerts.funding.receipt.main": { + "type": "record", + "description": "Records a funding receipt for a payment from one user to another user. It may be recorded by the recipient, by the sender, or by a third party. The sender may remain anonymous." + }, + "org.hypercerts.funding.getReceipt.receiptView": { "type": "object", - "description": "An original evaluator entry whose Certified profile and organization sidecar were looked up, including when either is missing.", + "description": "Funding receipt with its publisher actor hydrated; the indexed record is unchanged, including its decimal-string amount, and party and funded-target references are not expanded. Missing publisher profile or organization sidecars are null; indexedAt is null when the index has no timestamp. A receipt is not proof of payment.", "required": [ + "uri", + "cid", + "indexedAt", "did", - "hydrationStatus", - "profile", - "organization" + "author", + "record" ], "properties": { - "did": { + "uri": { "type": "string", - "format": "did" + "format": "at-uri" }, - "hydrationStatus": { + "cid": { "type": "string", - "const": "hydrated" + "format": "cid" }, - "profile": { + "indexedAt": { "anyOf": [ { - "$ref": "#/components/schemas/org.hypercerts.api.defs.profileView" + "type": "string", + "format": "datetime" }, { "type": "null" } ] }, - "organization": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.api.defs.organizationView" - }, - { - "type": "null" - } - ] + "did": { + "type": "string", + "format": "did" + }, + "author": { + "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" + }, + "record": { + "$ref": "#/components/schemas/org.hypercerts.funding.receipt.main" } } }, - "org.hypercerts.context.getEvaluation.omittedEvaluationEvaluatorView": { + "org.hypercerts.funding.getReceipt.output": { "type": "object", - "description": "An original evaluator entry beyond the 100-position hydration cap; profile and organization are deliberately omitted.", "required": [ - "did", - "hydrationStatus" + "receipt" ], "properties": { - "did": { - "type": "string", - "format": "did" + "receipt": { + "$ref": "#/components/schemas/org.hypercerts.funding.getReceipt.receiptView" + } + } + }, + "org.hypercerts.funding.listReceipts.output": { + "type": "object", + "required": [ + "receipts" + ], + "properties": { + "receipts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/org.hypercerts.funding.getReceipt.receiptView" + } }, - "hydrationStatus": { + "cursor": { "type": "string", - "const": "omitted" + "description": "Opaque cursor for the next page; omitted when there is no next page." } } }, - "org.hypercerts.context.getEvaluation.evaluationView": { + "org.hypercerts.vocab.getVocabTag.vocabTagView": { "type": "object", - "description": "Evaluation record view with hydrated publisher and a one-for-one evaluator projection. Preserve the original record and evaluator order, length, duplicates, and entry fields; hydrate only the first 100 source positions.", + "description": "Vocabulary-tag record with its unchanged source and hydrated publisher actor.", "required": [ "uri", "cid", "indexedAt", "did", "author", - "record", - "evaluators" + "record" ], "properties": { "uri": { @@ -4523,8 +7363,15 @@ "format": "cid" }, "indexedAt": { - "type": "string", - "format": "datetime" + "anyOf": [ + { + "type": "string", + "format": "datetime" + }, + { + "type": "null" + } + ] }, "did": { "type": "string", @@ -4534,79 +7381,46 @@ "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" }, "record": { - "$ref": "#/components/schemas/org.hypercerts.context.evaluation.main" - }, - "evaluators": { - "type": "array", - "description": "One-for-one sibling projection of record.evaluators in source order, retaining duplicates and source entry fields. Entries beyond the hydration cap remain present with hydrationStatus omitted.", - "maxItems": 1000, - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.hydratedEvaluationEvaluatorView" - }, - { - "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.omittedEvaluationEvaluatorView" - } - ] - } - } - } - }, - "org.hypercerts.context.getEvaluation.output": { - "type": "object", - "required": [ - "evaluation" - ], - "properties": { - "evaluation": { - "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.evaluationView" + "$ref": "#/components/schemas/org.hypercerts.vocab.tag.main" } } }, - "org.hypercerts.context.listAttachments.output": { + "org.hypercerts.vocab.getVocabTag.output": { "type": "object", "required": [ - "attachments" + "vocabTag" ], "properties": { - "attachments": { - "type": "array", - "items": { - "$ref": "#/components/schemas/org.hypercerts.context.getAttachment.attachmentView" - } - }, - "cursor": { - "type": "string", - "description": "Opaque cursor for the next page; omitted when there is no next page." + "vocabTag": { + "$ref": "#/components/schemas/org.hypercerts.vocab.getVocabTag.vocabTagView" } } }, - "org.hypercerts.context.listEvaluations.output": { + "org.hypercerts.vocab.listVocabTags.output": { "type": "object", "required": [ - "evaluations" + "vocabTags" ], "properties": { - "evaluations": { + "vocabTags": { "type": "array", "items": { - "$ref": "#/components/schemas/org.hypercerts.context.getEvaluation.evaluationView" + "$ref": "#/components/schemas/org.hypercerts.vocab.getVocabTag.vocabTagView" } }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there is no next page." + "description": "Opaque cursor for the next page; omitted when pagination is complete." } } }, - "org.hypercerts.funding.receipt.main": { - "description": "Schema reference org.hypercerts.funding.receipt is outside the committed local Lexicon snapshot.", - "x-lexicon-ref": "org.hypercerts.funding.receipt" + "org.hypercerts.workscope.tag.main": { + "type": "record", + "description": "A reusable scope atom for work scope logic expressions. Scopes can represent topics, languages, domains, deliverables, methods, regions, tags, or other categorical labels. Tags are composed into structured expressions via CEL (Common Expression Language) on activity records." }, - "org.hypercerts.funding.getReceipt.receiptView": { + "org.hypercerts.workscope.getWorkscopeTag.workscopeTagView": { "type": "object", - "description": "Funding-receipt record view with hydrated publisher actor and the full indexed record unchanged; party and funded-target references remain unexpanded. A receipt is not proof that payment occurred.", + "description": "Work-scope tag view with its hydrated publisher, unchanged record, and nullable index timestamp.", "required": [ "uri", "cid", @@ -4643,122 +7457,42 @@ "$ref": "#/components/schemas/org.hypercerts.api.defs.actorView" }, "record": { - "$ref": "#/components/schemas/org.hypercerts.funding.receipt.main" + "$ref": "#/components/schemas/org.hypercerts.workscope.tag.main" } } }, - "org.hypercerts.funding.getReceipt.output": { + "org.hypercerts.workscope.getWorkscopeTag.output": { "type": "object", "required": [ - "receipt" + "workscopeTag" ], "properties": { - "receipt": { - "$ref": "#/components/schemas/org.hypercerts.funding.getReceipt.receiptView" + "workscopeTag": { + "$ref": "#/components/schemas/org.hypercerts.workscope.getWorkscopeTag.workscopeTagView" } } }, - "org.hypercerts.funding.listReceipts.output": { + "org.hypercerts.workscope.listWorkscopeTags.output": { "type": "object", "required": [ - "receipts" + "workscopeTags" ], "properties": { - "receipts": { + "workscopeTags": { "type": "array", + "maxItems": 100, "items": { - "$ref": "#/components/schemas/org.hypercerts.funding.getReceipt.receiptView" + "$ref": "#/components/schemas/org.hypercerts.workscope.getWorkscopeTag.workscopeTagView" } }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there is no next page." + "description": "Opaque cursor for the next page; omitted when no next page exists." } } } } }, - "x-hypercerts-source": "api/profile-queries@134b24d020a01876e51f857e32726ccdf670200d", - "x-hypercerts-unresolved-references": [ - { - "component": "app.certified.actor.organization.main", - "reference": "app.certified.actor.organization" - }, - { - "component": "app.certified.actor.profile.main", - "reference": "app.certified.actor.profile" - }, - { - "component": "app.certified.badge.definition.main", - "reference": "app.certified.badge.definition" - }, - { - "component": "app.certified.graph.entityFollow.main", - "reference": "app.certified.graph.entityFollow" - }, - { - "component": "app.certified.graph.follow.main", - "reference": "app.certified.graph.follow" - }, - { - "component": "app.certified.location.main", - "reference": "app.certified.location" - }, - { - "component": "com.atproto.repo.strongRef.main", - "reference": "com.atproto.repo.strongRef" - }, - { - "component": "org.hypercerts.api.defs.actorView", - "reference": "org.hypercerts.api.defs#actorView" - }, - { - "component": "org.hypercerts.api.defs.organizationView", - "reference": "org.hypercerts.api.defs#organizationView" - }, - { - "component": "org.hypercerts.api.defs.profileView", - "reference": "org.hypercerts.api.defs#profileView" - }, - { - "component": "org.hypercerts.claim.activity.main", - "reference": "org.hypercerts.claim.activity" - }, - { - "component": "org.hypercerts.claim.activity.contributorIdentity", - "reference": "org.hypercerts.claim.activity#contributorIdentity" - }, - { - "component": "org.hypercerts.claim.activity.contributorRole", - "reference": "org.hypercerts.claim.activity#contributorRole" - }, - { - "component": "org.hypercerts.claim.contributorInformation.main", - "reference": "org.hypercerts.claim.contributorInformation" - }, - { - "component": "org.hypercerts.collection.main", - "reference": "org.hypercerts.collection" - }, - { - "component": "org.hypercerts.context.attachment.main", - "reference": "org.hypercerts.context.attachment" - }, - { - "component": "org.hypercerts.context.evaluation.main", - "reference": "org.hypercerts.context.evaluation" - }, - { - "component": "org.hypercerts.entity.feature.main", - "reference": "org.hypercerts.entity.feature" - }, - { - "component": "org.hypercerts.funding.receipt.main", - "reference": "org.hypercerts.funding.receipt" - }, - { - "component": "org.hypercerts.vocab.tag.main", - "reference": "org.hypercerts.vocab.tag" - } - ] + "x-hypercerts-source": "api/manifest.json; @hypercerts-org/lexicon@1.4.0", + "x-hypercerts-unresolved-references": [] } diff --git a/docs/scripts/check-sources.mjs b/docs/scripts/check-sources.mjs new file mode 100644 index 0000000..f8419f5 --- /dev/null +++ b/docs/scripts/check-sources.mjs @@ -0,0 +1,60 @@ +import { readFile } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; +import path from 'node:path'; +import { buildDocumentationArtifacts } from './openapi-artifacts.mjs'; +import { collectDocumentationSources } from './refresh-sources.mjs'; + +const docsRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +function jsonText(value) { + return `${JSON.stringify(value, null, 2)}\n`; +} + +/** + * Read the canonical API sources and reject any stale committed docs artifact. + * This check never writes files. + * @param {{ apiRoot?: string; docsRoot?: string; lexiconPackageRoot?: string }} options + */ +export async function assertDocumentationFresh(options = {}) { + const currentDocsRoot = path.resolve(options.docsRoot ?? docsRoot); + const { index, snapshots } = await collectDocumentationSources(options); + const lexicons = snapshots.map(({ content }) => JSON.parse(content)); + const artifacts = buildDocumentationArtifacts(index, lexicons); + const staleFiles = []; + + async function compare(relativePath, expected) { + try { + const actual = await readFile(path.join(currentDocsRoot, relativePath), 'utf8'); + if (actual !== expected) staleFiles.push(relativePath); + } catch (error) { + if (error?.code === 'ENOENT') staleFiles.push(relativePath); + else throw error; + } + } + + await compare('sources/index.json', jsonText(index)); + for (const [indexInList, snapshot] of snapshots.entries()) { + await compare(index.lexicons[indexInList].file, snapshot.content); + } + await compare('openapi.json', jsonText(artifacts.openapi)); + await compare('coverage.json', jsonText(artifacts.coverage)); + + if (staleFiles.length > 0) { + const paths = [...new Set(staleFiles)].sort().map((file) => ` - ${file}`).join('\n'); + throw new Error(`Documentation artifacts are stale:\n${paths}\nRun pnpm docs:sync to refresh them.`); + } + + return { endpoints: index.endpoints.length, snapshots: index.lexicons.length }; +} + +async function main() { + const result = await assertDocumentationFresh(); + console.log(`Documentation is fresh: ${result.endpoints} endpoints, ${result.snapshots} Lexicon snapshots.`); +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main().catch((error) => { + console.error(error.message); + process.exitCode = 1; + }); +} diff --git a/docs/scripts/generate-openapi.mjs b/docs/scripts/generate-openapi.mjs index cda8502..4bbfc40 100644 --- a/docs/scripts/generate-openapi.mjs +++ b/docs/scripts/generate-openapi.mjs @@ -1,71 +1,45 @@ import { readFile, writeFile } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; -import { buildOpenApi, DEFAULT_SERVER_URL } from './openapi.mjs'; +import { buildDocumentationArtifacts } from './openapi-artifacts.mjs'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); -const sourceIndex = JSON.parse(await readFile(path.join(root, 'sources/index.json'), 'utf8')); -const lexicons = []; -const coverage = {}; -const sources = {}; -for (const endpoint of sourceIndex.endpoints) { - const file = path.resolve(root, endpoint.file); - if (!file.startsWith(`${root}${path.sep}`)) { - throw new Error(`Source path escapes repository: ${endpoint.file}`); - } - const lexicon = JSON.parse(await readFile(file, 'utf8')); - if (lexicon.id !== endpoint.id || !['query', 'procedure'].includes(lexicon.defs?.main?.type)) { - throw new Error(`Source metadata does not match query/procedure Lexicon ${endpoint.id}`); +async function readJson(file) { + return JSON.parse(await readFile(file, 'utf8')); +} + +async function loadLexicons(sourceIndex) { + const lexicons = []; + const ids = new Set(); + for (const entry of sourceIndex.lexicons ?? sourceIndex.endpoints) { + if (ids.has(entry.id)) throw new Error(`Duplicate source Lexicon in docs/sources/index.json: ${entry.id}`); + ids.add(entry.id); + const file = path.resolve(root, entry.file); + if (file !== root && !file.startsWith(`${root}${path.sep}`)) { + throw new Error(`Source path escapes repository: ${entry.file}`); + } + const lexicon = await readJson(file); + if (lexicon.id !== entry.id) { + throw new Error(`Source metadata does not match Lexicon ${entry.id}`); + } + lexicons.push(lexicon); } - lexicons.push(lexicon); - coverage[endpoint.id] = endpoint.coverage; - sources[endpoint.id] = { - worktree: endpoint.source.worktree, - branch: endpoint.source.branch, - commit: endpoint.source.commit, - path: endpoint.source.path, - activeModule: endpoint.source.activeModule, - }; + return lexicons; } -const document = buildOpenApi(lexicons, { - coverage, - sources, - serverUrl: DEFAULT_SERVER_URL, - version: `snapshot-${sourceIndex.primaryCommit.slice(0, 12)}`, - source: `${sourceIndex.primaryBranch}@${sourceIndex.primaryCommit}`, -}); -const methods = lexicons.reduce((result, lexicon) => { - const method = lexicon.defs.main.type === 'query' ? 'GET' : 'POST'; - result[method] = (result[method] ?? 0) + 1; - return result; -}, {}); -const summary = Object.fromEntries( - ['primary', 'branch-only', 'unmanifested-unsupported'].map((status) => [ - status, - sourceIndex.endpoints.filter((endpoint) => endpoint.coverage === status).length, - ]), -); -const coverageReport = { - primarySource: { - worktree: sourceIndex.primaryWorktree, - branch: sourceIndex.primaryBranch, - commit: sourceIndex.primaryCommit, - manifestPath: 'hypercerts-api/manifest.json', - }, - endpointCount: lexicons.length, - methods, - coverage: summary, - unresolvedReferences: document['x-hypercerts-unresolved-references'], - endpoints: sourceIndex.endpoints.map((endpoint) => ({ - id: endpoint.id, - method: endpoint.type === 'query' ? 'GET' : 'POST', - coverage: endpoint.coverage, - source: endpoint.source, - })), -}; +async function main() { + const sourceIndex = await readJson(path.join(root, 'sources/index.json')); + const lexicons = await loadLexicons(sourceIndex); + const artifacts = buildDocumentationArtifacts(sourceIndex, lexicons); + await writeFile(path.join(root, 'openapi.json'), `${JSON.stringify(artifacts.openapi, null, 2)}\n`); + await writeFile(path.join(root, 'coverage.json'), `${JSON.stringify(artifacts.coverage, null, 2)}\n`); + console.log(`Generated ${sourceIndex.endpoints.length} manifest-registered endpoints; no unresolved schema references remain.`); +} -await writeFile(path.join(root, 'openapi.json'), `${JSON.stringify(document, null, 2)}\n`); -await writeFile(path.join(root, 'coverage.json'), `${JSON.stringify(coverageReport, null, 2)}\n`); -console.log(`Generated ${lexicons.length} endpoints; ${coverageReport.unresolvedReferences.length} external schema references remain explicit.`); +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main().catch((error) => { + console.error(error.message); + process.exitCode = 1; + }); +} diff --git a/docs/scripts/openapi-artifacts.mjs b/docs/scripts/openapi-artifacts.mjs new file mode 100644 index 0000000..4d6f3e8 --- /dev/null +++ b/docs/scripts/openapi-artifacts.mjs @@ -0,0 +1,83 @@ +import { buildOpenApi, DEFAULT_SERVER_URL } from './openapi.mjs'; + +export function buildDocumentationArtifacts(sourceIndex, lexicons) { + const lexiconsById = new Map(); + for (const lexicon of lexicons) { + if (lexiconsById.has(lexicon.id)) { + throw new Error(`Duplicate source Lexicon in docs/sources/index.json: ${lexicon.id}`); + } + lexiconsById.set(lexicon.id, lexicon); + } + + const listedLexicons = sourceIndex.lexicons ?? sourceIndex.endpoints; + const listedIds = listedLexicons.map(({ id }) => id).sort(); + const sourceIds = [...lexiconsById.keys()].sort(); + if (JSON.stringify(sourceIds) !== JSON.stringify(listedIds)) { + throw new Error('Loaded Lexicon snapshots do not exactly match docs/sources/index.json.'); + } + + const coverage = {}; + const sources = {}; + for (const endpoint of sourceIndex.endpoints) { + const lexicon = lexiconsById.get(endpoint.id); + if (!lexicon || !['query', 'procedure'].includes(lexicon.defs?.main?.type)) { + throw new Error(`Manifest endpoint ${endpoint.id} is not a query/procedure Lexicon snapshot.`); + } + if (lexicon.defs.main.type !== endpoint.type) { + throw new Error(`Endpoint type drift for ${endpoint.id}: index says ${endpoint.type}, snapshot says ${lexicon.defs.main.type}`); + } + coverage[endpoint.id] = 'manifest-registered'; + sources[endpoint.id] = { module: endpoint.module }; + } + + const expectedEndpointIds = sourceIndex.endpoints.map(({ id }) => id).sort(); + const operationIds = lexicons + .filter((lexicon) => ['query', 'procedure'].includes(lexicon.defs?.main?.type)) + .map(({ id }) => id) + .sort(); + if (JSON.stringify(operationIds) !== JSON.stringify(expectedEndpointIds)) { + throw new Error('Operation snapshots do not exactly match the manifest-registered endpoints.'); + } + + const document = buildOpenApi(lexicons, { + coverage, + sources, + serverUrl: DEFAULT_SERVER_URL, + version: 'manifest-snapshot', + source: `${sourceIndex.apiManifest}; @hypercerts-org/lexicon@${sourceIndex.pinnedLexiconPackage}`, + }); + if (document['x-hypercerts-unresolved-references'].length > 0) { + const references = document['x-hypercerts-unresolved-references'] + .map(({ component, reference }) => `${component} -> ${reference}`) + .join('\n'); + throw new Error(`OpenAPI generation found unresolved Lexicon references:\n${references}`); + } + + const methods = lexicons + .filter(({ id }) => Object.hasOwn(coverage, id)) + .reduce((result, lexicon) => { + const method = lexicon.defs.main.type === 'query' ? 'GET' : 'POST'; + result[method] = (result[method] ?? 0) + 1; + return result; + }, {}); + const coverageReport = { + source: { + apiManifest: sourceIndex.apiManifest, + pinnedLexiconPackage: sourceIndex.pinnedLexiconPackage, + }, + runtimeValidation: sourceIndex.runtimeValidation, + deploymentValidation: sourceIndex.deploymentValidation, + endpointCount: sourceIndex.endpoints.length, + methods, + inclusion: { 'manifest-registered': sourceIndex.endpoints.length }, + unresolvedReferences: document['x-hypercerts-unresolved-references'], + endpoints: sourceIndex.endpoints.map((endpoint) => ({ + id: endpoint.id, + method: endpoint.type === 'query' ? 'GET' : 'POST', + inclusion: 'manifest-registered', + module: endpoint.module, + })), + }; + + return { openapi: document, coverage: coverageReport }; +} diff --git a/docs/scripts/refresh-sources.mjs b/docs/scripts/refresh-sources.mjs index dc27173..e78d5bc 100644 --- a/docs/scripts/refresh-sources.mjs +++ b/docs/scripts/refresh-sources.mjs @@ -1,116 +1,272 @@ -import { readFile, writeFile, stat } from 'node:fs/promises'; +import { createRequire } from 'node:module'; +import { readFile, writeFile } from 'node:fs/promises'; import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; -const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); -const usage = 'Usage: node scripts/refresh-sources.mjs '; +const docsRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const repositoryRoot = path.resolve(docsRoot, '..'); +const apiRoot = path.join(repositoryRoot, 'api'); +const usage = 'Usage: pnpm docs:sync'; function isInside(parent, candidate) { return candidate === parent || candidate.startsWith(`${parent}${path.sep}`); } -function gitValue(worktree, ...args) { - return execFileSync('git', ['-C', worktree, ...args], { encoding: 'utf8' }).trim(); +function resolveInside(parent, relativePath, description) { + if (typeof relativePath !== 'string' || relativePath.length === 0) { + throw new Error(`${description} must be a non-empty relative path.`); + } + const resolved = path.resolve(parent, relativePath); + if (!isInside(parent, resolved)) { + throw new Error(`${description} escapes ${parent}: ${relativePath}`); + } + return resolved; +} + +function resolveFromInside(boundary, baseDirectory, relativePath, description) { + if (typeof relativePath !== 'string' || relativePath.length === 0) { + throw new Error(`${description} must be a non-empty relative path.`); + } + const resolved = path.resolve(baseDirectory, relativePath); + if (!isInside(boundary, resolved)) { + throw new Error(`${description} escapes ${boundary}: ${relativePath}`); + } + return resolved; } async function readJson(file) { return JSON.parse(await readFile(file, 'utf8')); } -async function activeApiLexicons(apiRoot, rootManifest) { - const active = new Map(); - for (const moduleRef of rootManifest.modules ?? []) { - const moduleFile = path.resolve(apiRoot, moduleRef); - if (!isInside(apiRoot, moduleFile)) throw new Error(`Module manifest escapes hypercerts-api: ${moduleRef}`); +function pinnedLexiconPackageRoot(apiRoot, packageName) { + try { + const requireFromApi = createRequire(path.join(apiRoot, 'package.json')); + return path.dirname(requireFromApi.resolve(`${packageName}/package.json`)); + } catch (error) { + throw new Error( + `Cannot resolve ${packageName} from api/package.json. Install the repository-pinned dependencies with ` + + '`pnpm install --frozen-lockfile`, then rerun `pnpm check` or `pnpm docs:sync`.', + { cause: error }, + ); + } +} + +function referenceParts(reference, currentLexiconId) { + if (typeof reference !== 'string' || reference.length === 0) { + throw new Error(`Invalid Lexicon reference in ${currentLexiconId}: expected a non-empty string.`); + } + if (reference.startsWith('#')) { + return { lexiconId: currentLexiconId, definitionName: reference.slice(1) || 'main' }; + } + + const [lexiconId, fragment] = reference.split('#', 2); + return { lexiconId, definitionName: fragment || 'main' }; +} + +function snapshotFile(id) { + if (!/^[a-z0-9]+(?:\.[A-Za-z0-9-]+)*$/.test(id)) { + throw new Error(`Cannot use invalid Lexicon ID as a snapshot filename: ${id}`); + } + return `sources/lexicons/${id}.json`; +} + +/** + * Read active query Lexicons from the aggregate manifest and the reachable + * schema Lexicons from its declared validation sources. No remote refs are fetched. + * @param {{ apiRoot?: string; docsRoot?: string; lexiconPackageRoot?: string }} options + */ +export async function collectDocumentationSources(options = {}) { + const currentApiRoot = path.resolve(options.apiRoot ?? apiRoot); + const currentDocsRoot = path.resolve(options.docsRoot ?? docsRoot); + const rootManifestFile = path.join(currentApiRoot, 'manifest.json'); + const manifest = await readJson(rootManifestFile); + const apiPackage = await readJson(path.join(currentApiRoot, 'package.json')); + const packageName = '@hypercerts-org/lexicon'; + const packageVersion = apiPackage.dependencies?.[packageName]; + const validationSources = new Map(); + + for (const source of manifest.validationLexicons ?? []) { + if (!source.id || validationSources.has(source.id)) { + throw new Error(`api/manifest.json has a missing or duplicate validation Lexicon ID: ${source.id ?? '(missing)'}`); + } + if (!source.path && !source.packagePath) { + throw new Error(`api/manifest.json has no source path for validation Lexicon ${source.id}`); + } + validationSources.set(source.id, source); + } + + let packageRoot; + async function getPackageRoot() { + if (packageRoot) return packageRoot; + packageRoot = options.lexiconPackageRoot + ? path.resolve(options.lexiconPackageRoot) + : pinnedLexiconPackageRoot(currentApiRoot, packageName); + const resolvedPackage = await readJson(path.join(packageRoot, 'package.json')); + if (resolvedPackage.name !== packageName || resolvedPackage.version !== packageVersion) { + throw new Error(`Expected ${packageName}@${packageVersion}, resolved ${resolvedPackage.name ?? '(unnamed)'}@${resolvedPackage.version ?? '(unknown)'}.`); + } + return packageRoot; + } + + async function readSource(source, baseDirectory, label) { + if (source.path) { + const file = resolveFromInside(currentApiRoot, baseDirectory, source.path, `${label} path`); + return readJson(file); + } + if (source.packagePath) { + if (!packageVersion) { + throw new Error(`api/package.json does not pin ${packageName}, required by ${label}.`); + } + const resolvedPackageRoot = await getPackageRoot(); + const file = resolveFromInside(resolvedPackageRoot, resolvedPackageRoot, source.packagePath, `${label} packagePath`); + return readJson(file); + } + throw new Error(`${label} has neither path nor packagePath.`); + } + + const activeEndpoints = new Map(); + for (const moduleRef of manifest.modules ?? []) { + const moduleFile = resolveInside(currentApiRoot, moduleRef, 'Module manifest path'); const moduleManifest = await readJson(moduleFile); for (const asset of moduleManifest.assets ?? []) { - if (asset.kind !== 'lexicon' || !asset.path) continue; - const lexiconFile = path.resolve(path.dirname(moduleFile), asset.path); - if (!isInside(apiRoot, lexiconFile)) throw new Error(`Lexicon path escapes hypercerts-api: ${asset.path}`); - const lexicon = await readJson(lexiconFile); + if (asset.kind !== 'lexicon') continue; + const lexicon = await readSource(asset, path.dirname(moduleFile), `Lexicon asset ${asset.id ?? '(missing ID)'}`); + if (!asset.id || lexicon.id !== asset.id) { + throw new Error(`Lexicon asset ID mismatch in ${moduleRef}: expected ${asset.id ?? '(missing ID)'}, got ${lexicon.id ?? '(missing ID)'}`); + } const type = lexicon.defs?.main?.type; - if (['query', 'procedure'].includes(type)) active.set(lexicon.id, moduleRef); + if (!['query', 'procedure'].includes(type)) continue; + if (activeEndpoints.has(lexicon.id)) { + throw new Error(`Duplicate registered query/procedure Lexicon: ${lexicon.id}`); + } + activeEndpoints.set(lexicon.id, { + id: lexicon.id, + type, + module: moduleRef, + sourcePath: asset.path ?? asset.packagePath, + lexicon, + }); } } - return active; -} -async function refreshEndpoint(endpoint, sourceRoot, primaryWorktree) { - const worktree = path.resolve(sourceRoot, endpoint.source.worktree); - if (!isInside(sourceRoot, worktree)) throw new Error(`Worktree path escapes source root: ${endpoint.source.worktree}`); - const sourceFile = path.resolve(worktree, endpoint.source.path); - if (!isInside(worktree, sourceFile)) throw new Error(`Source file escapes worktree: ${endpoint.source.path}`); - const bytes = await readFile(sourceFile); - const lexicon = JSON.parse(bytes.toString('utf8')); - const type = lexicon.defs?.main?.type; - if (lexicon.id !== endpoint.id || !['query', 'procedure'].includes(type)) { - throw new Error(`Expected ${endpoint.id} to be a query/procedure Lexicon at ${sourceFile}`); - } - const apiRoot = path.join(worktree, 'hypercerts-api'); - const rootManifest = await readJson(path.join(apiRoot, 'manifest.json')); - const active = await activeApiLexicons(apiRoot, rootManifest); - const activeModule = active.get(endpoint.id) ?? null; - endpoint.file = `sources/lexicons/${endpoint.id}.json`; - endpoint.type = type; - if (!activeModule) { - endpoint.coverage = 'unmanifested-unsupported'; - } else if (endpoint.source.worktree === primaryWorktree) { - endpoint.coverage = 'primary'; - } else { - endpoint.coverage = 'branch-only'; - } - endpoint.source.branch = gitValue(worktree, 'branch', '--show-current'); - endpoint.source.commit = gitValue(worktree, 'rev-parse', 'HEAD'); - endpoint.source.activeModule = activeModule; - - return { - snapshot: { file: path.join(root, endpoint.file), bytes }, - sourceRoot: { - branch: endpoint.source.branch, - commit: endpoint.source.commit, - manifestPath: 'hypercerts-api/manifest.json', - targetHappyViewRevision: rootManifest.targetHappyViewRevision, - }, + const sourceDocuments = new Map(); + for (const [id, endpoint] of activeEndpoints) { + sourceDocuments.set(id, { lexicon: endpoint.lexicon, source: { module: endpoint.module, path: endpoint.sourcePath } }); + } + const visitedDefinitions = new Set(); + + async function loadLexicon(id, reference) { + const existing = sourceDocuments.get(id); + if (existing) return existing.lexicon; + const source = validationSources.get(id); + if (!source) { + throw new Error(`Cannot resolve Lexicon reference ${JSON.stringify(reference)}: ${id} is not declared in api/manifest.json validationLexicons.`); + } + const lexicon = await readSource(source, currentApiRoot, `Validation Lexicon ${id}`); + if (lexicon.id !== id) { + throw new Error(`Validation Lexicon ID mismatch: manifest declares ${id}, source contains ${lexicon.id ?? '(missing ID)'}`); + } + const record = { + lexicon, + source: source.path ? { path: source.path } : { packagePath: source.packagePath }, + }; + sourceDocuments.set(id, record); + return lexicon; + } + + async function visitReference(reference, currentLexiconId) { + const { lexiconId, definitionName } = referenceParts(reference, currentLexiconId); + const key = `${lexiconId}#${definitionName}`; + if (visitedDefinitions.has(key)) return; + visitedDefinitions.add(key); + const targetLexicon = await loadLexicon(lexiconId, reference); + const targetDefinition = targetLexicon.defs?.[definitionName]; + if (!targetDefinition) { + throw new Error(`Cannot resolve Lexicon reference ${JSON.stringify(reference)}: ${lexiconId} has no definition ${definitionName}.`); + } + await visitReferences(targetDefinition, lexiconId); + } + + async function visitReferences(value, currentLexiconId) { + if (Array.isArray(value)) { + for (const item of value) await visitReferences(item, currentLexiconId); + return; + } + if (!value || typeof value !== 'object') return; + if (value.type === 'ref') { + await visitReference(value.ref, currentLexiconId); + return; + } + if (value.type === 'union' && Array.isArray(value.refs)) { + for (const reference of value.refs) await visitReference(reference, currentLexiconId); + return; + } + for (const child of Object.values(value)) await visitReferences(child, currentLexiconId); + } + + for (const endpoint of activeEndpoints.values()) { + await visitReferences(endpoint.lexicon.defs.main, endpoint.id); + } + + const lexicons = [...sourceDocuments] + .sort(([left], [right]) => left.localeCompare(right)) + .map(([id, entry]) => ({ + id, + file: snapshotFile(id), + kind: activeEndpoints.has(id) ? 'endpoint' : 'schema', + source: entry.source, + })); + const endpoints = [...activeEndpoints.values()] + .sort((left, right) => left.id.localeCompare(right.id)) + .map(({ id, type, module, sourcePath }) => ({ + id, + type, + module, + file: snapshotFile(id), + source: { path: sourcePath }, + })); + const index = { + apiManifest: 'api/manifest.json', + pinnedLexiconPackage: packageVersion ?? null, + sourcePolicy: 'Explorer operations are the query/procedure Lexicons declared by modules in api/manifest.json. Referenced schemas are resolved from that manifest and its pinned Lexicon package; no remote refs are fetched.', + runtimeValidation: 'not-assessed', + deploymentValidation: 'not-assessed', + endpoints, + lexicons, }; + const snapshots = lexicons.map(({ id, file }) => ({ + file: resolveInside(currentDocsRoot, file, `Snapshot for ${id}`), + content: `${JSON.stringify(sourceDocuments.get(id).lexicon, null, 2)}\n`, + })); + + return { index, snapshots }; } async function main() { - const sourceRootArg = process.argv[2]; - if (sourceRootArg === '--help' || sourceRootArg === '-h') { - console.log(`${usage}\nReads the recorded worktree paths and refreshes only this repository's source snapshots and generated artifacts.`); + if (process.argv.includes('--help') || process.argv.includes('-h')) { + console.log(`${usage}\nRefreshes docs/sources/index.json, committed Lexicon snapshots, OpenAPI, and coverage from api/manifest.json.`); return; } - if (!sourceRootArg) { + if (process.argv.length > 2) { console.error(usage); process.exitCode = 2; return; } - const sourceRoot = path.resolve(sourceRootArg); - if (!(await stat(sourceRoot)).isDirectory()) throw new Error(`Source root is not a directory: ${sourceRoot}`); - const indexFile = path.join(root, 'sources/index.json'); - const index = await readJson(indexFile); - const refreshed = []; - - for (const endpoint of index.endpoints) { - const result = await refreshEndpoint(endpoint, sourceRoot, index.primaryWorktree); - refreshed.push(result.snapshot); - index.sourceRoots[endpoint.source.worktree] = result.sourceRoot; - } - - const primaryApi = path.join(sourceRoot, index.primaryWorktree, 'hypercerts-api'); - const packageManifest = await readJson(path.join(primaryApi, 'package.json')); - index.primaryBranch = index.sourceRoots[index.primaryWorktree].branch; - index.primaryCommit = index.sourceRoots[index.primaryWorktree].commit; - index.pinnedLexiconPackage = packageManifest.dependencies?.['@hypercerts-org/lexicon'] ?? null; - for (const snapshot of refreshed) await writeFile(snapshot.file, snapshot.bytes); - await writeFile(indexFile, `${JSON.stringify(index, null, 2)}\n`); - execFileSync(process.execPath, [path.join(root, 'scripts/generate-openapi.mjs')], { cwd: root, stdio: 'inherit' }); - console.log(`Refreshed ${refreshed.length} Lexicon snapshots from ${sourceRoot}.`); + const { index, snapshots } = await collectDocumentationSources(); + for (const snapshot of snapshots) await writeFile(snapshot.file, snapshot.content); + await writeFile(path.join(docsRoot, 'sources/index.json'), `${JSON.stringify(index, null, 2)}\n`); + execFileSync(process.execPath, [path.join(docsRoot, 'scripts/generate-openapi.mjs')], { + cwd: docsRoot, + stdio: 'inherit', + }); + console.log(`Refreshed ${index.endpoints.length} manifest-registered endpoints and ${index.lexicons.length - index.endpoints.length} referenced schema snapshots.`); } -main().catch((error) => { - console.error(`${error.message}\n${usage}`); - process.exitCode = 1; -}); +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main().catch((error) => { + console.error(`${error.message}\n${usage}`); + process.exitCode = 1; + }); +} diff --git a/docs/sources/index.json b/docs/sources/index.json index bea6dee..65ca6a2 100644 --- a/docs/sources/index.json +++ b/docs/sources/index.json @@ -1,487 +1,1441 @@ { + "apiManifest": "api/manifest.json", + "pinnedLexiconPackage": "1.4.0", + "sourcePolicy": "Explorer operations are the query/procedure Lexicons declared by modules in api/manifest.json. Referenced schemas are resolved from that manifest and its pinned Lexicon package; no remote refs are fetched.", + "runtimeValidation": "not-assessed", + "deploymentValidation": "not-assessed", "endpoints": [ { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.actor.getOrganization.json", "id": "app.certified.actor.getOrganization", + "type": "query", + "module": "modules/organization/manifest.json", + "file": "sources/lexicons/app.certified.actor.getOrganization.json", "source": { - "activeModule": "modules/organization/manifest.json", - "branch": "api/organization-queries", - "commit": "918a72afabd987110ab9eaaa06e1e8eb51a0b916", - "path": "hypercerts-api/lexicons/app.certified.actor.getOrganization.json", - "worktree": "happyview-organization-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.getOrganization.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.actor.getOrganizations.json", "id": "app.certified.actor.getOrganizations", + "type": "query", + "module": "modules/organization/manifest.json", + "file": "sources/lexicons/app.certified.actor.getOrganizations.json", "source": { - "activeModule": "modules/organization/manifest.json", - "branch": "api/organization-queries", - "commit": "918a72afabd987110ab9eaaa06e1e8eb51a0b916", - "path": "hypercerts-api/lexicons/app.certified.actor.getOrganizations.json", - "worktree": "happyview-organization-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.getOrganizations.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.actor.getProfile.json", "id": "app.certified.actor.getProfile", + "type": "query", + "module": "modules/profile/manifest.json", + "file": "sources/lexicons/app.certified.actor.getProfile.json", "source": { - "activeModule": "modules/profile/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.actor.getProfile.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.getProfile.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.actor.getProfiles.json", "id": "app.certified.actor.getProfiles", + "type": "query", + "module": "modules/profile/manifest.json", + "file": "sources/lexicons/app.certified.actor.getProfiles.json", "source": { - "activeModule": "modules/profile/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.actor.getProfiles.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.getProfiles.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.actor.listOrganizations.json", "id": "app.certified.actor.listOrganizations", + "type": "query", + "module": "modules/organization/manifest.json", + "file": "sources/lexicons/app.certified.actor.listOrganizations.json", "source": { - "activeModule": "modules/organization/manifest.json", - "branch": "api/organization-queries", - "commit": "918a72afabd987110ab9eaaa06e1e8eb51a0b916", - "path": "hypercerts-api/lexicons/app.certified.actor.listOrganizations.json", - "worktree": "happyview-organization-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.listOrganizations.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.actor.listProfiles.json", "id": "app.certified.actor.listProfiles", + "type": "query", + "module": "modules/profile/manifest.json", + "file": "sources/lexicons/app.certified.actor.listProfiles.json", "source": { - "activeModule": "modules/profile/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.actor.listProfiles.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.listProfiles.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.actor.searchOrganizations.json", "id": "app.certified.actor.searchOrganizations", + "type": "query", + "module": "modules/organization/manifest.json", + "file": "sources/lexicons/app.certified.actor.searchOrganizations.json", "source": { - "activeModule": "modules/organization/manifest.json", - "branch": "api/organization-queries", - "commit": "918a72afabd987110ab9eaaa06e1e8eb51a0b916", - "path": "hypercerts-api/lexicons/app.certified.actor.searchOrganizations.json", - "worktree": "happyview-organization-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.searchOrganizations.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.actor.searchProfiles.json", "id": "app.certified.actor.searchProfiles", + "type": "query", + "module": "modules/profile/manifest.json", + "file": "sources/lexicons/app.certified.actor.searchProfiles.json", "source": { - "activeModule": "modules/profile/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.actor.searchProfiles.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.actor.searchProfiles.json" + } + }, + { + "id": "app.certified.badge.getBadgeAward", + "type": "query", + "module": "modules/badge-queries/manifest.json", + "file": "sources/lexicons/app.certified.badge.getBadgeAward.json", + "source": { + "path": "../../lexicons/app.certified.badge.getBadgeAward.json" + } }, { - "coverage": "unmanifested-unsupported", - "file": "sources/lexicons/app.certified.badge.getBadgeDefinition.json", "id": "app.certified.badge.getBadgeDefinition", + "type": "query", + "module": "modules/badge-definitions/manifest.json", + "file": "sources/lexicons/app.certified.badge.getBadgeDefinition.json", "source": { - "activeModule": null, - "branch": "api/badge-definitions", - "commit": "a0455e92881077b3da33c7753fdb4486712d8f5b", - "path": "hypercerts-api/lexicons/app.certified.badge.getBadgeDefinition.json", - "worktree": "happyview-badge-definitions-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.badge.getBadgeDefinition.json" + } + }, + { + "id": "app.certified.badge.getBadgeResponse", + "type": "query", + "module": "modules/badge-queries/manifest.json", + "file": "sources/lexicons/app.certified.badge.getBadgeResponse.json", + "source": { + "path": "../../lexicons/app.certified.badge.getBadgeResponse.json" + } + }, + { + "id": "app.certified.badge.listBadgeAwards", + "type": "query", + "module": "modules/badge-queries/manifest.json", + "file": "sources/lexicons/app.certified.badge.listBadgeAwards.json", + "source": { + "path": "../../lexicons/app.certified.badge.listBadgeAwards.json" + } }, { - "coverage": "unmanifested-unsupported", - "file": "sources/lexicons/app.certified.badge.listBadgeDefinitions.json", "id": "app.certified.badge.listBadgeDefinitions", + "type": "query", + "module": "modules/badge-definitions/manifest.json", + "file": "sources/lexicons/app.certified.badge.listBadgeDefinitions.json", "source": { - "activeModule": null, - "branch": "api/badge-definitions", - "commit": "a0455e92881077b3da33c7753fdb4486712d8f5b", - "path": "hypercerts-api/lexicons/app.certified.badge.listBadgeDefinitions.json", - "worktree": "happyview-badge-definitions-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.badge.listBadgeDefinitions.json" + } + }, + { + "id": "app.certified.badge.listBadgeResponses", + "type": "query", + "module": "modules/badge-queries/manifest.json", + "file": "sources/lexicons/app.certified.badge.listBadgeResponses.json", + "source": { + "path": "../../lexicons/app.certified.badge.listBadgeResponses.json" + } + }, + { + "id": "app.certified.badge.searchBadgeDefinitions", + "type": "query", + "module": "modules/badge-queries/manifest.json", + "file": "sources/lexicons/app.certified.badge.searchBadgeDefinitions.json", + "source": { + "path": "../../lexicons/app.certified.badge.searchBadgeDefinitions.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.graph.getEntityFollow.json", "id": "app.certified.graph.getEntityFollow", + "type": "query", + "module": "modules/entity-follow/manifest.json", + "file": "sources/lexicons/app.certified.graph.getEntityFollow.json", "source": { - "activeModule": "modules/entity-follow/manifest.json", - "branch": "api/entity-follow-queries", - "commit": "81bab9cfc1fdf7ee52bc892274b2d016a519c0bc", - "path": "hypercerts-api/lexicons/app.certified.graph.getEntityFollow.json", - "worktree": "happyview-entity-follow-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.graph.getEntityFollow.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.graph.getFollow.json", "id": "app.certified.graph.getFollow", + "type": "query", + "module": "modules/actor-follow/manifest.json", + "file": "sources/lexicons/app.certified.graph.getFollow.json", "source": { - "activeModule": "modules/actor-follow/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.graph.getFollow.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.graph.getFollow.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.graph.listActorFollowers.json", "id": "app.certified.graph.listActorFollowers", + "type": "query", + "module": "modules/actor-follow/manifest.json", + "file": "sources/lexicons/app.certified.graph.listActorFollowers.json", "source": { - "activeModule": "modules/actor-follow/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.graph.listActorFollowers.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.graph.listActorFollowers.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.graph.listActorFollowing.json", "id": "app.certified.graph.listActorFollowing", + "type": "query", + "module": "modules/actor-follow/manifest.json", + "file": "sources/lexicons/app.certified.graph.listActorFollowing.json", "source": { - "activeModule": "modules/actor-follow/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.graph.listActorFollowing.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.graph.listActorFollowing.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.graph.listEntityFollowers.json", "id": "app.certified.graph.listEntityFollowers", + "type": "query", + "module": "modules/entity-follow/manifest.json", + "file": "sources/lexicons/app.certified.graph.listEntityFollowers.json", "source": { - "activeModule": "modules/entity-follow/manifest.json", - "branch": "api/entity-follow-queries", - "commit": "81bab9cfc1fdf7ee52bc892274b2d016a519c0bc", - "path": "hypercerts-api/lexicons/app.certified.graph.listEntityFollowers.json", - "worktree": "happyview-entity-follow-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.graph.listEntityFollowers.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.graph.listEntityFollowing.json", "id": "app.certified.graph.listEntityFollowing", + "type": "query", + "module": "modules/entity-follow/manifest.json", + "file": "sources/lexicons/app.certified.graph.listEntityFollowing.json", "source": { - "activeModule": "modules/entity-follow/manifest.json", - "branch": "api/entity-follow-queries", - "commit": "81bab9cfc1fdf7ee52bc892274b2d016a519c0bc", - "path": "hypercerts-api/lexicons/app.certified.graph.listEntityFollowing.json", - "worktree": "happyview-entity-follow-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.graph.listEntityFollowing.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/app.certified.graph.listRecentFollows.json", "id": "app.certified.graph.listRecentFollows", + "type": "query", + "module": "modules/recent-follows/manifest.json", + "file": "sources/lexicons/app.certified.graph.listRecentFollows.json", "source": { - "activeModule": "modules/recent-follows/manifest.json", - "branch": "api/recent-follows", - "commit": "0503116f5d1b63d493ccdd2a7aa721da67caf875", - "path": "hypercerts-api/lexicons/app.certified.graph.listRecentFollows.json", - "worktree": "happyview-recent-follows-queries" - }, - "type": "query" + "path": "../../lexicons/app.certified.graph.listRecentFollows.json" + } + }, + { + "id": "app.certified.link.getEvmLink", + "type": "query", + "module": "modules/evm-links/manifest.json", + "file": "sources/lexicons/app.certified.link.getEvmLink.json", + "source": { + "path": "../../lexicons/app.certified.link.getEvmLink.json" + } + }, + { + "id": "app.certified.link.listEvmLinks", + "type": "query", + "module": "modules/evm-links/manifest.json", + "file": "sources/lexicons/app.certified.link.listEvmLinks.json", + "source": { + "path": "../../lexicons/app.certified.link.listEvmLinks.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.location.getLocation.json", "id": "app.certified.location.getLocation", + "type": "query", + "module": "modules/location/manifest.json", + "file": "sources/lexicons/app.certified.location.getLocation.json", "source": { - "activeModule": "modules/location/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.location.getLocation.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.location.getLocation.json" + } }, { - "coverage": "primary", - "file": "sources/lexicons/app.certified.location.listLocations.json", "id": "app.certified.location.listLocations", + "type": "query", + "module": "modules/location/manifest.json", + "file": "sources/lexicons/app.certified.location.listLocations.json", "source": { - "activeModule": "modules/location/manifest.json", - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "path": "hypercerts-api/lexicons/app.certified.location.listLocations.json", - "worktree": "happyview-profile-history-rewrite" - }, - "type": "query" + "path": "../../lexicons/app.certified.location.listLocations.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.claim.getActivity.json", "id": "org.hypercerts.claim.getActivity", + "type": "query", + "module": "modules/shared/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.getActivity.json", "source": { - "activeModule": "modules/activity/manifest.json", - "branch": "api/activity-queries", - "commit": "43ac93be85747e09b8b5f6fa4aafb27d536f9367", - "path": "hypercerts-api/lexicons/org.hypercerts.claim.getActivity.json", - "worktree": "happyview-activity-queries-worktree" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.claim.getActivity.json" + } + }, + { + "id": "org.hypercerts.claim.getContribution", + "type": "query", + "module": "modules/contribution/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.getContribution.json", + "source": { + "path": "../../lexicons/org.hypercerts.claim.getContribution.json" + } + }, + { + "id": "org.hypercerts.claim.getContributorInformation", + "type": "query", + "module": "modules/contributor-information/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.getContributorInformation.json", + "source": { + "path": "../../lexicons/org.hypercerts.claim.getContributorInformation.json" + } + }, + { + "id": "org.hypercerts.claim.getRights", + "type": "query", + "module": "modules/rights/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.getRights.json", + "source": { + "path": "../../lexicons/org.hypercerts.claim.getRights.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.claim.listActivities.json", "id": "org.hypercerts.claim.listActivities", + "type": "query", + "module": "modules/activity/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.listActivities.json", "source": { - "activeModule": "modules/activity/manifest.json", - "branch": "api/activity-queries", - "commit": "43ac93be85747e09b8b5f6fa4aafb27d536f9367", - "path": "hypercerts-api/lexicons/org.hypercerts.claim.listActivities.json", - "worktree": "happyview-activity-queries-worktree" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.claim.listActivities.json" + } + }, + { + "id": "org.hypercerts.claim.listContributions", + "type": "query", + "module": "modules/contribution/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.listContributions.json", + "source": { + "path": "../../lexicons/org.hypercerts.claim.listContributions.json" + } + }, + { + "id": "org.hypercerts.claim.listContributorInformation", + "type": "query", + "module": "modules/contributor-information/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.listContributorInformation.json", + "source": { + "path": "../../lexicons/org.hypercerts.claim.listContributorInformation.json" + } + }, + { + "id": "org.hypercerts.claim.listRights", + "type": "query", + "module": "modules/rights/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.listRights.json", + "source": { + "path": "../../lexicons/org.hypercerts.claim.listRights.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.claim.searchActivities.json", "id": "org.hypercerts.claim.searchActivities", + "type": "query", + "module": "modules/activity/manifest.json", + "file": "sources/lexicons/org.hypercerts.claim.searchActivities.json", "source": { - "activeModule": "modules/activity/manifest.json", - "branch": "api/activity-queries", - "commit": "43ac93be85747e09b8b5f6fa4aafb27d536f9367", - "path": "hypercerts-api/lexicons/org.hypercerts.claim.searchActivities.json", - "worktree": "happyview-activity-queries-worktree" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.claim.searchActivities.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.collection.getCollection.json", "id": "org.hypercerts.collection.getCollection", + "type": "query", + "module": "modules/shared/manifest.json", + "file": "sources/lexicons/org.hypercerts.collection.getCollection.json", "source": { - "activeModule": "modules/collection/manifest.json", - "branch": "api/collection-queries", - "commit": "2b3162b785299d2362af7f725eff4ca5e47ce609", - "path": "hypercerts-api/lexicons/org.hypercerts.collection.getCollection.json", - "worktree": "happyview-activity-queries-phase1" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.collection.getCollection.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.collection.listCollectionItems.json", "id": "org.hypercerts.collection.listCollectionItems", + "type": "query", + "module": "modules/shared/manifest.json", + "file": "sources/lexicons/org.hypercerts.collection.listCollectionItems.json", "source": { - "activeModule": "modules/collection/manifest.json", - "branch": "api/collection-queries", - "commit": "2b3162b785299d2362af7f725eff4ca5e47ce609", - "path": "hypercerts-api/lexicons/org.hypercerts.collection.listCollectionItems.json", - "worktree": "happyview-activity-queries-phase1" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.collection.listCollectionItems.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.collection.listCollections.json", "id": "org.hypercerts.collection.listCollections", + "type": "query", + "module": "modules/collection/manifest.json", + "file": "sources/lexicons/org.hypercerts.collection.listCollections.json", "source": { - "activeModule": "modules/collection/manifest.json", - "branch": "api/collection-queries", - "commit": "2b3162b785299d2362af7f725eff4ca5e47ce609", - "path": "hypercerts-api/lexicons/org.hypercerts.collection.listCollections.json", - "worktree": "happyview-activity-queries-phase1" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.collection.listCollections.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.collection.searchCollections.json", "id": "org.hypercerts.collection.searchCollections", + "type": "query", + "module": "modules/collection/manifest.json", + "file": "sources/lexicons/org.hypercerts.collection.searchCollections.json", "source": { - "activeModule": "modules/collection/manifest.json", - "branch": "api/collection-queries", - "commit": "2b3162b785299d2362af7f725eff4ca5e47ce609", - "path": "hypercerts-api/lexicons/org.hypercerts.collection.searchCollections.json", - "worktree": "happyview-activity-queries-phase1" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.collection.searchCollections.json" + } + }, + { + "id": "org.hypercerts.context.getAcknowledgement", + "type": "query", + "module": "modules/acknowledgements/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.getAcknowledgement.json", + "source": { + "path": "../../lexicons/org.hypercerts.context.getAcknowledgement.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.context.getAttachment.json", "id": "org.hypercerts.context.getAttachment", + "type": "query", + "module": "modules/context-attachments/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.getAttachment.json", "source": { - "activeModule": "modules/context-attachments/manifest.json", - "branch": "api/context-attachments", - "commit": "43bafb3ef58c0bcb6b627f1b8bd905aa579d4e00", - "path": "hypercerts-api/lexicons/org.hypercerts.context.getAttachment.json", - "worktree": "happyview-context-attachments-queries" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.context.getAttachment.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.context.getEvaluation.json", "id": "org.hypercerts.context.getEvaluation", + "type": "query", + "module": "modules/context-evaluation/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.getEvaluation.json", "source": { - "activeModule": "modules/context-evaluation/manifest.json", - "branch": "api/context-evaluations", - "commit": "52014876cadf9d01b06507cf06ae6649a1d42dfa", - "path": "hypercerts-api/lexicons/org.hypercerts.context.getEvaluation.json", - "worktree": "happyview-context-evaluations-queries" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.context.getEvaluation.json" + } + }, + { + "id": "org.hypercerts.context.getMeasurement", + "type": "query", + "module": "modules/context-measurements/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.getMeasurement.json", + "source": { + "path": "../../lexicons/org.hypercerts.context.getMeasurement.json" + } + }, + { + "id": "org.hypercerts.context.listAcknowledgements", + "type": "query", + "module": "modules/acknowledgements/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.listAcknowledgements.json", + "source": { + "path": "../../lexicons/org.hypercerts.context.listAcknowledgements.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.context.listAttachments.json", "id": "org.hypercerts.context.listAttachments", + "type": "query", + "module": "modules/context-attachments/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.listAttachments.json", "source": { - "activeModule": "modules/context-attachments/manifest.json", - "branch": "api/context-attachments", - "commit": "43bafb3ef58c0bcb6b627f1b8bd905aa579d4e00", - "path": "hypercerts-api/lexicons/org.hypercerts.context.listAttachments.json", - "worktree": "happyview-context-attachments-queries" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.context.listAttachments.json" + } }, { - "coverage": "branch-only", - "file": "sources/lexicons/org.hypercerts.context.listEvaluations.json", "id": "org.hypercerts.context.listEvaluations", + "type": "query", + "module": "modules/context-evaluation/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.listEvaluations.json", "source": { - "activeModule": "modules/context-evaluation/manifest.json", - "branch": "api/context-evaluations", - "commit": "52014876cadf9d01b06507cf06ae6649a1d42dfa", - "path": "hypercerts-api/lexicons/org.hypercerts.context.listEvaluations.json", - "worktree": "happyview-context-evaluations-queries" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.context.listEvaluations.json" + } + }, + { + "id": "org.hypercerts.context.listMeasurements", + "type": "query", + "module": "modules/context-measurements/manifest.json", + "file": "sources/lexicons/org.hypercerts.context.listMeasurements.json", + "source": { + "path": "../../lexicons/org.hypercerts.context.listMeasurements.json" + } + }, + { + "id": "org.hypercerts.entity.getFeature", + "type": "query", + "module": "modules/features/manifest.json", + "file": "sources/lexicons/org.hypercerts.entity.getFeature.json", + "source": { + "path": "../../lexicons/org.hypercerts.entity.getFeature.json" + } + }, + { + "id": "org.hypercerts.entity.listFeatures", + "type": "query", + "module": "modules/features/manifest.json", + "file": "sources/lexicons/org.hypercerts.entity.listFeatures.json", + "source": { + "path": "../../lexicons/org.hypercerts.entity.listFeatures.json" + } }, { - "coverage": "unmanifested-unsupported", - "file": "sources/lexicons/org.hypercerts.funding.getReceipt.json", "id": "org.hypercerts.funding.getReceipt", + "type": "query", + "module": "modules/funding/manifest.json", + "file": "sources/lexicons/org.hypercerts.funding.getReceipt.json", "source": { - "activeModule": null, - "branch": "api/funding-receipts", - "commit": "d3643a26c0bab24972b847fbf3e07794702aea04", - "path": "hypercerts-api/lexicons/org.hypercerts.funding.getReceipt.json", - "worktree": "happyview-funding-receipts-queries" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.funding.getReceipt.json" + } }, { - "coverage": "unmanifested-unsupported", - "file": "sources/lexicons/org.hypercerts.funding.listReceipts.json", "id": "org.hypercerts.funding.listReceipts", + "type": "query", + "module": "modules/funding/manifest.json", + "file": "sources/lexicons/org.hypercerts.funding.listReceipts.json", + "source": { + "path": "../../lexicons/org.hypercerts.funding.listReceipts.json" + } + }, + { + "id": "org.hypercerts.vocab.getVocabTag", + "type": "query", + "module": "modules/vocab/manifest.json", + "file": "sources/lexicons/org.hypercerts.vocab.getVocabTag.json", + "source": { + "path": "../../lexicons/org.hypercerts.vocab.getVocabTag.json" + } + }, + { + "id": "org.hypercerts.vocab.listVocabTags", + "type": "query", + "module": "modules/vocab/manifest.json", + "file": "sources/lexicons/org.hypercerts.vocab.listVocabTags.json", "source": { - "activeModule": null, - "branch": "api/funding-receipts", - "commit": "d3643a26c0bab24972b847fbf3e07794702aea04", - "path": "hypercerts-api/lexicons/org.hypercerts.funding.listReceipts.json", - "worktree": "happyview-funding-receipts-queries" - }, - "type": "query" + "path": "../../lexicons/org.hypercerts.vocab.listVocabTags.json" + } + }, + { + "id": "org.hypercerts.workscope.getWorkscopeTag", + "type": "query", + "module": "modules/workscope-tags/manifest.json", + "file": "sources/lexicons/org.hypercerts.workscope.getWorkscopeTag.json", + "source": { + "path": "../../lexicons/org.hypercerts.workscope.getWorkscopeTag.json" + } + }, + { + "id": "org.hypercerts.workscope.listWorkscopeTags", + "type": "query", + "module": "modules/workscope-tags/manifest.json", + "file": "sources/lexicons/org.hypercerts.workscope.listWorkscopeTags.json", + "source": { + "path": "../../lexicons/org.hypercerts.workscope.listWorkscopeTags.json" + } } ], - "pinnedLexiconPackage": "1.4.0", - "primaryBranch": "api/profile-queries", - "primaryCommit": "134b24d020a01876e51f857e32726ccdf670200d", - "primaryWorktree": "happyview-profile-history-rewrite", - "sourcePolicy": "Only Lexicon main query/procedure definitions; active root-manifest membership determines primary vs branch-only. Unreferenced query Lexicons are included as unmanifested-unsupported.", - "sourceRoots": { - "happyview-activity-queries-phase1": { - "branch": "api/collection-queries", - "commit": "2b3162b785299d2362af7f725eff4ca5e47ce609", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-activity-queries-worktree": { - "branch": "api/activity-queries", - "commit": "43ac93be85747e09b8b5f6fa4aafb27d536f9367", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-badge-definitions-queries": { - "branch": "api/badge-definitions", - "commit": "a0455e92881077b3da33c7753fdb4486712d8f5b", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-context-attachments-queries": { - "branch": "api/context-attachments", - "commit": "43bafb3ef58c0bcb6b627f1b8bd905aa579d4e00", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-context-evaluations-queries": { - "branch": "api/context-evaluations", - "commit": "52014876cadf9d01b06507cf06ae6649a1d42dfa", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-entity-follow-queries": { - "branch": "api/entity-follow-queries", - "commit": "81bab9cfc1fdf7ee52bc892274b2d016a519c0bc", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-funding-receipts-queries": { - "branch": "api/funding-receipts", - "commit": "d3643a26c0bab24972b847fbf3e07794702aea04", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-organization-queries": { - "branch": "api/organization-queries", - "commit": "918a72afabd987110ab9eaaa06e1e8eb51a0b916", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-recent-follows-queries": { - "branch": "api/recent-follows", - "commit": "0503116f5d1b63d493ccdd2a7aa721da67caf875", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" - }, - "happyview-profile-history-rewrite": { - "branch": "api/profile-queries", - "commit": "134b24d020a01876e51f857e32726ccdf670200d", - "manifestPath": "hypercerts-api/manifest.json", - "targetHappyViewRevision": "a2f347e605d7cd9c11669b320e43c10dbfe982e3" + "lexicons": [ + { + "id": "app.bsky.richtext.facet", + "file": "sources/lexicons/app.bsky.richtext.facet.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/bsky/richtext/facet.json" + } + }, + { + "id": "app.certified.actor.getOrganization", + "file": "sources/lexicons/app.certified.actor.getOrganization.json", + "kind": "endpoint", + "source": { + "module": "modules/organization/manifest.json", + "path": "../../lexicons/app.certified.actor.getOrganization.json" + } + }, + { + "id": "app.certified.actor.getOrganizations", + "file": "sources/lexicons/app.certified.actor.getOrganizations.json", + "kind": "endpoint", + "source": { + "module": "modules/organization/manifest.json", + "path": "../../lexicons/app.certified.actor.getOrganizations.json" + } + }, + { + "id": "app.certified.actor.getProfile", + "file": "sources/lexicons/app.certified.actor.getProfile.json", + "kind": "endpoint", + "source": { + "module": "modules/profile/manifest.json", + "path": "../../lexicons/app.certified.actor.getProfile.json" + } + }, + { + "id": "app.certified.actor.getProfiles", + "file": "sources/lexicons/app.certified.actor.getProfiles.json", + "kind": "endpoint", + "source": { + "module": "modules/profile/manifest.json", + "path": "../../lexicons/app.certified.actor.getProfiles.json" + } + }, + { + "id": "app.certified.actor.listOrganizations", + "file": "sources/lexicons/app.certified.actor.listOrganizations.json", + "kind": "endpoint", + "source": { + "module": "modules/organization/manifest.json", + "path": "../../lexicons/app.certified.actor.listOrganizations.json" + } + }, + { + "id": "app.certified.actor.listProfiles", + "file": "sources/lexicons/app.certified.actor.listProfiles.json", + "kind": "endpoint", + "source": { + "module": "modules/profile/manifest.json", + "path": "../../lexicons/app.certified.actor.listProfiles.json" + } + }, + { + "id": "app.certified.actor.organization", + "file": "sources/lexicons/app.certified.actor.organization.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/actor/organization.json" + } + }, + { + "id": "app.certified.actor.profile", + "file": "sources/lexicons/app.certified.actor.profile.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/actor/profile.json" + } + }, + { + "id": "app.certified.actor.searchOrganizations", + "file": "sources/lexicons/app.certified.actor.searchOrganizations.json", + "kind": "endpoint", + "source": { + "module": "modules/organization/manifest.json", + "path": "../../lexicons/app.certified.actor.searchOrganizations.json" + } + }, + { + "id": "app.certified.actor.searchProfiles", + "file": "sources/lexicons/app.certified.actor.searchProfiles.json", + "kind": "endpoint", + "source": { + "module": "modules/profile/manifest.json", + "path": "../../lexicons/app.certified.actor.searchProfiles.json" + } + }, + { + "id": "app.certified.badge.award", + "file": "sources/lexicons/app.certified.badge.award.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/badge/award.json" + } + }, + { + "id": "app.certified.badge.definition", + "file": "sources/lexicons/app.certified.badge.definition.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/badge/definition.json" + } + }, + { + "id": "app.certified.badge.getBadgeAward", + "file": "sources/lexicons/app.certified.badge.getBadgeAward.json", + "kind": "endpoint", + "source": { + "module": "modules/badge-queries/manifest.json", + "path": "../../lexicons/app.certified.badge.getBadgeAward.json" + } + }, + { + "id": "app.certified.badge.getBadgeDefinition", + "file": "sources/lexicons/app.certified.badge.getBadgeDefinition.json", + "kind": "endpoint", + "source": { + "module": "modules/badge-definitions/manifest.json", + "path": "../../lexicons/app.certified.badge.getBadgeDefinition.json" + } + }, + { + "id": "app.certified.badge.getBadgeResponse", + "file": "sources/lexicons/app.certified.badge.getBadgeResponse.json", + "kind": "endpoint", + "source": { + "module": "modules/badge-queries/manifest.json", + "path": "../../lexicons/app.certified.badge.getBadgeResponse.json" + } + }, + { + "id": "app.certified.badge.listBadgeAwards", + "file": "sources/lexicons/app.certified.badge.listBadgeAwards.json", + "kind": "endpoint", + "source": { + "module": "modules/badge-queries/manifest.json", + "path": "../../lexicons/app.certified.badge.listBadgeAwards.json" + } + }, + { + "id": "app.certified.badge.listBadgeDefinitions", + "file": "sources/lexicons/app.certified.badge.listBadgeDefinitions.json", + "kind": "endpoint", + "source": { + "module": "modules/badge-definitions/manifest.json", + "path": "../../lexicons/app.certified.badge.listBadgeDefinitions.json" + } + }, + { + "id": "app.certified.badge.listBadgeResponses", + "file": "sources/lexicons/app.certified.badge.listBadgeResponses.json", + "kind": "endpoint", + "source": { + "module": "modules/badge-queries/manifest.json", + "path": "../../lexicons/app.certified.badge.listBadgeResponses.json" + } + }, + { + "id": "app.certified.badge.response", + "file": "sources/lexicons/app.certified.badge.response.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/badge/response.json" + } + }, + { + "id": "app.certified.badge.searchBadgeDefinitions", + "file": "sources/lexicons/app.certified.badge.searchBadgeDefinitions.json", + "kind": "endpoint", + "source": { + "module": "modules/badge-queries/manifest.json", + "path": "../../lexicons/app.certified.badge.searchBadgeDefinitions.json" + } + }, + { + "id": "app.certified.defs", + "file": "sources/lexicons/app.certified.defs.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/defs.json" + } + }, + { + "id": "app.certified.graph.entityFollow", + "file": "sources/lexicons/app.certified.graph.entityFollow.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/graph/entityFollow.json" + } + }, + { + "id": "app.certified.graph.follow", + "file": "sources/lexicons/app.certified.graph.follow.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/graph/follow.json" + } + }, + { + "id": "app.certified.graph.getEntityFollow", + "file": "sources/lexicons/app.certified.graph.getEntityFollow.json", + "kind": "endpoint", + "source": { + "module": "modules/entity-follow/manifest.json", + "path": "../../lexicons/app.certified.graph.getEntityFollow.json" + } + }, + { + "id": "app.certified.graph.getFollow", + "file": "sources/lexicons/app.certified.graph.getFollow.json", + "kind": "endpoint", + "source": { + "module": "modules/actor-follow/manifest.json", + "path": "../../lexicons/app.certified.graph.getFollow.json" + } + }, + { + "id": "app.certified.graph.listActorFollowers", + "file": "sources/lexicons/app.certified.graph.listActorFollowers.json", + "kind": "endpoint", + "source": { + "module": "modules/actor-follow/manifest.json", + "path": "../../lexicons/app.certified.graph.listActorFollowers.json" + } + }, + { + "id": "app.certified.graph.listActorFollowing", + "file": "sources/lexicons/app.certified.graph.listActorFollowing.json", + "kind": "endpoint", + "source": { + "module": "modules/actor-follow/manifest.json", + "path": "../../lexicons/app.certified.graph.listActorFollowing.json" + } + }, + { + "id": "app.certified.graph.listEntityFollowers", + "file": "sources/lexicons/app.certified.graph.listEntityFollowers.json", + "kind": "endpoint", + "source": { + "module": "modules/entity-follow/manifest.json", + "path": "../../lexicons/app.certified.graph.listEntityFollowers.json" + } + }, + { + "id": "app.certified.graph.listEntityFollowing", + "file": "sources/lexicons/app.certified.graph.listEntityFollowing.json", + "kind": "endpoint", + "source": { + "module": "modules/entity-follow/manifest.json", + "path": "../../lexicons/app.certified.graph.listEntityFollowing.json" + } + }, + { + "id": "app.certified.graph.listRecentFollows", + "file": "sources/lexicons/app.certified.graph.listRecentFollows.json", + "kind": "endpoint", + "source": { + "module": "modules/recent-follows/manifest.json", + "path": "../../lexicons/app.certified.graph.listRecentFollows.json" + } + }, + { + "id": "app.certified.link.evm", + "file": "sources/lexicons/app.certified.link.evm.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/link/evm.json" + } + }, + { + "id": "app.certified.link.getEvmLink", + "file": "sources/lexicons/app.certified.link.getEvmLink.json", + "kind": "endpoint", + "source": { + "module": "modules/evm-links/manifest.json", + "path": "../../lexicons/app.certified.link.getEvmLink.json" + } + }, + { + "id": "app.certified.link.listEvmLinks", + "file": "sources/lexicons/app.certified.link.listEvmLinks.json", + "kind": "endpoint", + "source": { + "module": "modules/evm-links/manifest.json", + "path": "../../lexicons/app.certified.link.listEvmLinks.json" + } + }, + { + "id": "app.certified.location", + "file": "sources/lexicons/app.certified.location.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/location.json" + } + }, + { + "id": "app.certified.location.getLocation", + "file": "sources/lexicons/app.certified.location.getLocation.json", + "kind": "endpoint", + "source": { + "module": "modules/location/manifest.json", + "path": "../../lexicons/app.certified.location.getLocation.json" + } + }, + { + "id": "app.certified.location.listLocations", + "file": "sources/lexicons/app.certified.location.listLocations.json", + "kind": "endpoint", + "source": { + "module": "modules/location/manifest.json", + "path": "../../lexicons/app.certified.location.listLocations.json" + } + }, + { + "id": "app.certified.signature.defs", + "file": "sources/lexicons/app.certified.signature.defs.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/app/certified/signature/defs.json" + } + }, + { + "id": "com.atproto.repo.strongRef", + "file": "sources/lexicons/com.atproto.repo.strongRef.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/com/atproto/repo/strongRef.json" + } + }, + { + "id": "org.hypercerts.api.defs", + "file": "sources/lexicons/org.hypercerts.api.defs.json", + "kind": "schema", + "source": { + "path": "lexicons/org.hypercerts.api.defs.json" + } + }, + { + "id": "org.hypercerts.claim.activity", + "file": "sources/lexicons/org.hypercerts.claim.activity.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/claim/activity.json" + } + }, + { + "id": "org.hypercerts.claim.contribution", + "file": "sources/lexicons/org.hypercerts.claim.contribution.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/claim/contribution.json" + } + }, + { + "id": "org.hypercerts.claim.contributorInformation", + "file": "sources/lexicons/org.hypercerts.claim.contributorInformation.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/claim/contributorInformation.json" + } + }, + { + "id": "org.hypercerts.claim.getActivity", + "file": "sources/lexicons/org.hypercerts.claim.getActivity.json", + "kind": "endpoint", + "source": { + "module": "modules/shared/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.getActivity.json" + } + }, + { + "id": "org.hypercerts.claim.getContribution", + "file": "sources/lexicons/org.hypercerts.claim.getContribution.json", + "kind": "endpoint", + "source": { + "module": "modules/contribution/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.getContribution.json" + } + }, + { + "id": "org.hypercerts.claim.getContributorInformation", + "file": "sources/lexicons/org.hypercerts.claim.getContributorInformation.json", + "kind": "endpoint", + "source": { + "module": "modules/contributor-information/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.getContributorInformation.json" + } + }, + { + "id": "org.hypercerts.claim.getRights", + "file": "sources/lexicons/org.hypercerts.claim.getRights.json", + "kind": "endpoint", + "source": { + "module": "modules/rights/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.getRights.json" + } + }, + { + "id": "org.hypercerts.claim.listActivities", + "file": "sources/lexicons/org.hypercerts.claim.listActivities.json", + "kind": "endpoint", + "source": { + "module": "modules/activity/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.listActivities.json" + } + }, + { + "id": "org.hypercerts.claim.listContributions", + "file": "sources/lexicons/org.hypercerts.claim.listContributions.json", + "kind": "endpoint", + "source": { + "module": "modules/contribution/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.listContributions.json" + } + }, + { + "id": "org.hypercerts.claim.listContributorInformation", + "file": "sources/lexicons/org.hypercerts.claim.listContributorInformation.json", + "kind": "endpoint", + "source": { + "module": "modules/contributor-information/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.listContributorInformation.json" + } + }, + { + "id": "org.hypercerts.claim.listRights", + "file": "sources/lexicons/org.hypercerts.claim.listRights.json", + "kind": "endpoint", + "source": { + "module": "modules/rights/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.listRights.json" + } + }, + { + "id": "org.hypercerts.claim.rights", + "file": "sources/lexicons/org.hypercerts.claim.rights.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/claim/rights.json" + } + }, + { + "id": "org.hypercerts.claim.searchActivities", + "file": "sources/lexicons/org.hypercerts.claim.searchActivities.json", + "kind": "endpoint", + "source": { + "module": "modules/activity/manifest.json", + "path": "../../lexicons/org.hypercerts.claim.searchActivities.json" + } + }, + { + "id": "org.hypercerts.collection", + "file": "sources/lexicons/org.hypercerts.collection.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/collection.json" + } + }, + { + "id": "org.hypercerts.collection.getCollection", + "file": "sources/lexicons/org.hypercerts.collection.getCollection.json", + "kind": "endpoint", + "source": { + "module": "modules/shared/manifest.json", + "path": "../../lexicons/org.hypercerts.collection.getCollection.json" + } + }, + { + "id": "org.hypercerts.collection.listCollectionItems", + "file": "sources/lexicons/org.hypercerts.collection.listCollectionItems.json", + "kind": "endpoint", + "source": { + "module": "modules/shared/manifest.json", + "path": "../../lexicons/org.hypercerts.collection.listCollectionItems.json" + } + }, + { + "id": "org.hypercerts.collection.listCollections", + "file": "sources/lexicons/org.hypercerts.collection.listCollections.json", + "kind": "endpoint", + "source": { + "module": "modules/collection/manifest.json", + "path": "../../lexicons/org.hypercerts.collection.listCollections.json" + } + }, + { + "id": "org.hypercerts.collection.searchCollections", + "file": "sources/lexicons/org.hypercerts.collection.searchCollections.json", + "kind": "endpoint", + "source": { + "module": "modules/collection/manifest.json", + "path": "../../lexicons/org.hypercerts.collection.searchCollections.json" + } + }, + { + "id": "org.hypercerts.context.acknowledgement", + "file": "sources/lexicons/org.hypercerts.context.acknowledgement.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/context/acknowledgement.json" + } + }, + { + "id": "org.hypercerts.context.attachment", + "file": "sources/lexicons/org.hypercerts.context.attachment.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/context/attachment.json" + } + }, + { + "id": "org.hypercerts.context.evaluation", + "file": "sources/lexicons/org.hypercerts.context.evaluation.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/context/evaluation.json" + } + }, + { + "id": "org.hypercerts.context.getAcknowledgement", + "file": "sources/lexicons/org.hypercerts.context.getAcknowledgement.json", + "kind": "endpoint", + "source": { + "module": "modules/acknowledgements/manifest.json", + "path": "../../lexicons/org.hypercerts.context.getAcknowledgement.json" + } + }, + { + "id": "org.hypercerts.context.getAttachment", + "file": "sources/lexicons/org.hypercerts.context.getAttachment.json", + "kind": "endpoint", + "source": { + "module": "modules/context-attachments/manifest.json", + "path": "../../lexicons/org.hypercerts.context.getAttachment.json" + } + }, + { + "id": "org.hypercerts.context.getEvaluation", + "file": "sources/lexicons/org.hypercerts.context.getEvaluation.json", + "kind": "endpoint", + "source": { + "module": "modules/context-evaluation/manifest.json", + "path": "../../lexicons/org.hypercerts.context.getEvaluation.json" + } + }, + { + "id": "org.hypercerts.context.getMeasurement", + "file": "sources/lexicons/org.hypercerts.context.getMeasurement.json", + "kind": "endpoint", + "source": { + "module": "modules/context-measurements/manifest.json", + "path": "../../lexicons/org.hypercerts.context.getMeasurement.json" + } + }, + { + "id": "org.hypercerts.context.listAcknowledgements", + "file": "sources/lexicons/org.hypercerts.context.listAcknowledgements.json", + "kind": "endpoint", + "source": { + "module": "modules/acknowledgements/manifest.json", + "path": "../../lexicons/org.hypercerts.context.listAcknowledgements.json" + } + }, + { + "id": "org.hypercerts.context.listAttachments", + "file": "sources/lexicons/org.hypercerts.context.listAttachments.json", + "kind": "endpoint", + "source": { + "module": "modules/context-attachments/manifest.json", + "path": "../../lexicons/org.hypercerts.context.listAttachments.json" + } + }, + { + "id": "org.hypercerts.context.listEvaluations", + "file": "sources/lexicons/org.hypercerts.context.listEvaluations.json", + "kind": "endpoint", + "source": { + "module": "modules/context-evaluation/manifest.json", + "path": "../../lexicons/org.hypercerts.context.listEvaluations.json" + } + }, + { + "id": "org.hypercerts.context.listMeasurements", + "file": "sources/lexicons/org.hypercerts.context.listMeasurements.json", + "kind": "endpoint", + "source": { + "module": "modules/context-measurements/manifest.json", + "path": "../../lexicons/org.hypercerts.context.listMeasurements.json" + } + }, + { + "id": "org.hypercerts.context.measurement", + "file": "sources/lexicons/org.hypercerts.context.measurement.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/context/measurement.json" + } + }, + { + "id": "org.hypercerts.defs", + "file": "sources/lexicons/org.hypercerts.defs.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/defs.json" + } + }, + { + "id": "org.hypercerts.entity.defs", + "file": "sources/lexicons/org.hypercerts.entity.defs.json", + "kind": "schema", + "source": { + "path": "lexicons/org.hypercerts.entity.defs.json" + } + }, + { + "id": "org.hypercerts.entity.feature", + "file": "sources/lexicons/org.hypercerts.entity.feature.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/entity/feature.json" + } + }, + { + "id": "org.hypercerts.entity.getFeature", + "file": "sources/lexicons/org.hypercerts.entity.getFeature.json", + "kind": "endpoint", + "source": { + "module": "modules/features/manifest.json", + "path": "../../lexicons/org.hypercerts.entity.getFeature.json" + } + }, + { + "id": "org.hypercerts.entity.listFeatures", + "file": "sources/lexicons/org.hypercerts.entity.listFeatures.json", + "kind": "endpoint", + "source": { + "module": "modules/features/manifest.json", + "path": "../../lexicons/org.hypercerts.entity.listFeatures.json" + } + }, + { + "id": "org.hypercerts.funding.getReceipt", + "file": "sources/lexicons/org.hypercerts.funding.getReceipt.json", + "kind": "endpoint", + "source": { + "module": "modules/funding/manifest.json", + "path": "../../lexicons/org.hypercerts.funding.getReceipt.json" + } + }, + { + "id": "org.hypercerts.funding.listReceipts", + "file": "sources/lexicons/org.hypercerts.funding.listReceipts.json", + "kind": "endpoint", + "source": { + "module": "modules/funding/manifest.json", + "path": "../../lexicons/org.hypercerts.funding.listReceipts.json" + } + }, + { + "id": "org.hypercerts.funding.receipt", + "file": "sources/lexicons/org.hypercerts.funding.receipt.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/funding/receipt.json" + } + }, + { + "id": "org.hypercerts.vocab.getVocabTag", + "file": "sources/lexicons/org.hypercerts.vocab.getVocabTag.json", + "kind": "endpoint", + "source": { + "module": "modules/vocab/manifest.json", + "path": "../../lexicons/org.hypercerts.vocab.getVocabTag.json" + } + }, + { + "id": "org.hypercerts.vocab.listVocabTags", + "file": "sources/lexicons/org.hypercerts.vocab.listVocabTags.json", + "kind": "endpoint", + "source": { + "module": "modules/vocab/manifest.json", + "path": "../../lexicons/org.hypercerts.vocab.listVocabTags.json" + } + }, + { + "id": "org.hypercerts.vocab.tag", + "file": "sources/lexicons/org.hypercerts.vocab.tag.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/vocab/tag.json" + } + }, + { + "id": "org.hypercerts.workscope.cel", + "file": "sources/lexicons/org.hypercerts.workscope.cel.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/workscope/cel.json" + } + }, + { + "id": "org.hypercerts.workscope.getWorkscopeTag", + "file": "sources/lexicons/org.hypercerts.workscope.getWorkscopeTag.json", + "kind": "endpoint", + "source": { + "module": "modules/workscope-tags/manifest.json", + "path": "../../lexicons/org.hypercerts.workscope.getWorkscopeTag.json" + } + }, + { + "id": "org.hypercerts.workscope.listWorkscopeTags", + "file": "sources/lexicons/org.hypercerts.workscope.listWorkscopeTags.json", + "kind": "endpoint", + "source": { + "module": "modules/workscope-tags/manifest.json", + "path": "../../lexicons/org.hypercerts.workscope.listWorkscopeTags.json" + } + }, + { + "id": "org.hypercerts.workscope.tag", + "file": "sources/lexicons/org.hypercerts.workscope.tag.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/org/hypercerts/workscope/tag.json" + } + }, + { + "id": "pub.leaflet.blocks.blockquote", + "file": "sources/lexicons/pub.leaflet.blocks.blockquote.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/blockquote.json" + } + }, + { + "id": "pub.leaflet.blocks.bskyPost", + "file": "sources/lexicons/pub.leaflet.blocks.bskyPost.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/bskyPost.json" + } + }, + { + "id": "pub.leaflet.blocks.button", + "file": "sources/lexicons/pub.leaflet.blocks.button.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/button.json" + } + }, + { + "id": "pub.leaflet.blocks.code", + "file": "sources/lexicons/pub.leaflet.blocks.code.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/code.json" + } + }, + { + "id": "pub.leaflet.blocks.header", + "file": "sources/lexicons/pub.leaflet.blocks.header.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/header.json" + } + }, + { + "id": "pub.leaflet.blocks.horizontalRule", + "file": "sources/lexicons/pub.leaflet.blocks.horizontalRule.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/horizontalRule.json" + } + }, + { + "id": "pub.leaflet.blocks.html", + "file": "sources/lexicons/pub.leaflet.blocks.html.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/html.json" + } + }, + { + "id": "pub.leaflet.blocks.iframe", + "file": "sources/lexicons/pub.leaflet.blocks.iframe.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/iframe.json" + } + }, + { + "id": "pub.leaflet.blocks.image", + "file": "sources/lexicons/pub.leaflet.blocks.image.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/image.json" + } + }, + { + "id": "pub.leaflet.blocks.imageGallery", + "file": "sources/lexicons/pub.leaflet.blocks.imageGallery.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/imageGallery.json" + } + }, + { + "id": "pub.leaflet.blocks.math", + "file": "sources/lexicons/pub.leaflet.blocks.math.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/math.json" + } + }, + { + "id": "pub.leaflet.blocks.membersOnlyDelimiter", + "file": "sources/lexicons/pub.leaflet.blocks.membersOnlyDelimiter.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/membersOnlyDelimiter.json" + } + }, + { + "id": "pub.leaflet.blocks.orderedList", + "file": "sources/lexicons/pub.leaflet.blocks.orderedList.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/orderedList.json" + } + }, + { + "id": "pub.leaflet.blocks.page", + "file": "sources/lexicons/pub.leaflet.blocks.page.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/page.json" + } + }, + { + "id": "pub.leaflet.blocks.poll", + "file": "sources/lexicons/pub.leaflet.blocks.poll.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/poll.json" + } + }, + { + "id": "pub.leaflet.blocks.postsList", + "file": "sources/lexicons/pub.leaflet.blocks.postsList.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/postsList.json" + } + }, + { + "id": "pub.leaflet.blocks.signup", + "file": "sources/lexicons/pub.leaflet.blocks.signup.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/signup.json" + } + }, + { + "id": "pub.leaflet.blocks.standardSitePost", + "file": "sources/lexicons/pub.leaflet.blocks.standardSitePost.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/standardSitePost.json" + } + }, + { + "id": "pub.leaflet.blocks.standardSitePublication", + "file": "sources/lexicons/pub.leaflet.blocks.standardSitePublication.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/standardSitePublication.json" + } + }, + { + "id": "pub.leaflet.blocks.text", + "file": "sources/lexicons/pub.leaflet.blocks.text.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/text.json" + } + }, + { + "id": "pub.leaflet.blocks.unorderedList", + "file": "sources/lexicons/pub.leaflet.blocks.unorderedList.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/unorderedList.json" + } + }, + { + "id": "pub.leaflet.blocks.website", + "file": "sources/lexicons/pub.leaflet.blocks.website.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/blocks/website.json" + } + }, + { + "id": "pub.leaflet.pages.linearDocument", + "file": "sources/lexicons/pub.leaflet.pages.linearDocument.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/pages/linearDocument.json" + } + }, + { + "id": "pub.leaflet.richtext.facet", + "file": "sources/lexicons/pub.leaflet.richtext.facet.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/richtext/facet.json" + } + }, + { + "id": "pub.leaflet.theme.color", + "file": "sources/lexicons/pub.leaflet.theme.color.json", + "kind": "schema", + "source": { + "packagePath": "lexicons/pub/leaflet/theme/color.json" + } } - } + ] } diff --git a/docs/sources/lexicons/app.bsky.richtext.facet.json b/docs/sources/lexicons/app.bsky.richtext.facet.json new file mode 100644 index 0000000..a6534d3 --- /dev/null +++ b/docs/sources/lexicons/app.bsky.richtext.facet.json @@ -0,0 +1,89 @@ +{ + "lexicon": 1, + "id": "app.bsky.richtext.facet", + "defs": { + "main": { + "type": "object", + "description": "Annotation of a sub-string within rich text.", + "required": [ + "index", + "features" + ], + "properties": { + "index": { + "type": "ref", + "ref": "#byteSlice" + }, + "features": { + "type": "array", + "items": { + "type": "union", + "refs": [ + "#mention", + "#link", + "#tag" + ] + } + } + } + }, + "mention": { + "type": "object", + "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + } + } + }, + "link": { + "type": "object", + "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "uri" + } + } + }, + "tag": { + "type": "object", + "description": "Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags').", + "required": [ + "tag" + ], + "properties": { + "tag": { + "type": "string", + "maxLength": 640, + "maxGraphemes": 64 + } + } + }, + "byteSlice": { + "type": "object", + "description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.", + "required": [ + "byteStart", + "byteEnd" + ], + "properties": { + "byteStart": { + "type": "integer", + "minimum": 0 + }, + "byteEnd": { + "type": "integer", + "minimum": 0 + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.actor.getOrganization.json b/docs/sources/lexicons/app.certified.actor.getOrganization.json index 8ed2ce8..62b8402 100644 --- a/docs/sources/lexicons/app.certified.actor.getOrganization.json +++ b/docs/sources/lexicons/app.certified.actor.getOrganization.json @@ -4,43 +4,78 @@ "defs": { "main": { "type": "query", - "description": "Looks up an actor's indexed organization sidecar and associated profile. The organization sidecar must exist; a missing profile is represented as null. Authentication is not required.", + "description": "Gets an actor's indexed organization sidecar and associated profile by DID. Authentication is not required.", "parameters": { "type": "params", - "required": ["actor"], + "required": [ + "actor" + ], "properties": { "actor": { "type": "string", "format": "did", - "description": "DID of the actor whose organization sidecar is requested." + "description": "DID of the actor to look up." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "The actor DID is invalid or a query parameter is not accepted." }, - { "name": "RecordNotFound", "description": "No indexed organization sidecar exists for this DID." } + { + "name": "InvalidRequest", + "description": "The actor DID is invalid or an unsupported parameter was supplied." + }, + { + "name": "RecordNotFound", + "description": "No indexed organization sidecar exists for this DID." + }, + { + "name": "OrganizationQueryFailed", + "description": "The indexed organization sidecar or associated profile could not be queried." + } ] }, "organizationActorView": { "type": "object", - "description": "Actor with its required organization sidecar and nullable base profile.", - "required": ["did", "profile", "organization"], - "nullable": ["profile"], + "description": "Actor with an indexed organization sidecar and nullable profile view.", + "required": [ + "did", + "profile", + "organization" + ], + "nullable": [ + "profile" + ], "properties": { - "did": { "type": "string", "format": "did" }, - "profile": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" }, - "organization": { "type": "ref", "ref": "org.hypercerts.api.defs#organizationView" } + "did": { + "type": "string", + "format": "did" + }, + "profile": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView" + }, + "organization": { + "type": "ref", + "ref": "org.hypercerts.api.defs#organizationView" + } } }, "output": { "type": "object", - "required": ["actor"], + "required": [ + "actor" + ], "properties": { - "actor": { "type": "ref", "ref": "#organizationActorView" } + "actor": { + "type": "ref", + "ref": "#organizationActorView" + } } } } diff --git a/docs/sources/lexicons/app.certified.actor.getOrganizations.json b/docs/sources/lexicons/app.certified.actor.getOrganizations.json index 5c331cf..64b33dc 100644 --- a/docs/sources/lexicons/app.certified.actor.getOrganizations.json +++ b/docs/sources/lexicons/app.certified.actor.getOrganizations.json @@ -4,44 +4,68 @@ "defs": { "main": { "type": "query", - "description": "Returns one organization result for each supplied actor DID, preserving input order and duplicates. Authentication is not required. actors is a required nonempty array of 1..100 DIDs, counted before deduplication and encoded as repeated unbracketed query keys. Handles are not resolved; the query uses indexed records only and makes no network calls. A missing organization sidecar is returned as null, and present sidecars include the associated profile or null. This endpoint does not paginate.", + "description": "Returns an indexed organization sidecar and associated profile for each supplied actor DID. Authentication is not required.", "parameters": { "type": "params", - "required": ["actors"], + "required": [ + "actors" + ], "properties": { "actors": { "type": "array", - "description": "Actor DIDs to look up; repeated unbracketed values preserve their original order and duplicates.", + "description": "Actor DIDs to look up, supplied as repeated unbracketed query values.", "minLength": 1, "maxLength": 100, - "items": { "type": "string", "format": "did" } + "items": { + "type": "string", + "format": "did" + } } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "actors must contain 1 through 100 valid DIDs; unknown query parameters are not accepted." }, - { "name": "OrganizationQueryFailed", "description": "The indexed organization sidecars or associated profiles could not be queried." } + { + "name": "InvalidRequest", + "description": "An actor DID or query parameter is invalid." + }, + { + "name": "OrganizationQueryFailed", + "description": "The indexed organization sidecars or associated profiles could not be queried." + } ] }, "output": { "type": "object", - "required": ["organizations"], + "required": [ + "organizations" + ], "properties": { "organizations": { "type": "array", - "description": "One result per supplied actor occurrence, in input order. The organization is null when no indexed sidecar exists.", - "items": { "type": "ref", "ref": "#organizationResult" } + "description": "One result per supplied actor occurrence, in request order.", + "items": { + "type": "ref", + "ref": "#organizationResult" + } } } }, "organizationResult": { "type": "object", - "required": ["actor", "organization"], - "nullable": ["organization"], + "required": [ + "actor", + "organization" + ], + "nullable": [ + "organization" + ], "properties": { "actor": { "type": "string", @@ -51,7 +75,7 @@ "organization": { "type": "ref", "ref": "app.certified.actor.getOrganization#organizationActorView", - "description": "Hydrated actor view when this DID has an indexed organization sidecar; otherwise null." + "description": "Hydrated actor view, or null when no indexed organization sidecar exists." } } } diff --git a/docs/sources/lexicons/app.certified.actor.getProfile.json b/docs/sources/lexicons/app.certified.actor.getProfile.json index 3fd4dbd..ab9658c 100644 --- a/docs/sources/lexicons/app.certified.actor.getProfile.json +++ b/docs/sources/lexicons/app.certified.actor.getProfile.json @@ -4,10 +4,12 @@ "defs": { "main": { "type": "query", - "description": "Public lookup of one indexed Certified profile by DID or handle. Handles are resolved through the configured resolver's com.atproto.identity.resolveHandle response; the response DID is validated, but no DID document is fetched for independent verification. Operators trust the resolver configured for their HappyView instance. Authentication is not required.", + "description": "Gets an indexed Certified profile by DID or handle. Handles resolve through the configured resolver's com.atproto.identity.resolveHandle endpoint; no DID document is fetched for independent verification. Authentication is not required.", "parameters": { "type": "params", - "required": ["actor"], + "required": [ + "actor" + ], "properties": { "actor": { "type": "string", @@ -18,24 +20,44 @@ }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "The actor is neither a valid DID nor a valid handle, or an unknown parameter was supplied." }, - { "name": "HandleResolverConfigError", "description": "HYPERCERTS_HANDLE_RESOLVER_URL is missing or invalid; configure an HTTPS resolver base URL for this HappyView instance." }, - { "name": "HandleResolutionFailed", "description": "The configured resolver request failed or returned a response without a valid DID." }, - { "name": "RecordNotFound", "description": "The actor was identified, but no indexed Certified profile exists." }, - { "name": "ProfileQueryFailed", "description": "The indexed profile could not be queried." } + { + "name": "InvalidRequest", + "description": "The actor is not a valid DID or handle, or an unsupported parameter was supplied." + }, + { + "name": "HandleResolverConfigError", + "description": "HYPERCERTS_HANDLE_RESOLVER_URL is missing or invalid." + }, + { + "name": "HandleResolutionFailed", + "description": "The handle could not be resolved to a valid DID." + }, + { + "name": "RecordNotFound", + "description": "The actor was identified, but no indexed Certified profile exists." + }, + { + "name": "ProfileQueryFailed", + "description": "The indexed profile could not be queried." + } ] }, "output": { "type": "object", - "required": ["profile"], + "required": [ + "profile" + ], "properties": { "profile": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView", - "description": "The complete indexed Certified profile record with record metadata and its repository DID." + "description": "Indexed Certified profile record with its AT-URI, CID, repository DID, and indexing time." } } } diff --git a/docs/sources/lexicons/app.certified.actor.getProfiles.json b/docs/sources/lexicons/app.certified.actor.getProfiles.json index 3d949f5..1a0fde9 100644 --- a/docs/sources/lexicons/app.certified.actor.getProfiles.json +++ b/docs/sources/lexicons/app.certified.actor.getProfiles.json @@ -4,47 +4,79 @@ "defs": { "main": { "type": "query", - "description": "Returns one profile result for each supplied actor DID, preserving input order and duplicates. Authentication is not required. actors is a required nonempty array of 1..100 DIDs, counted before deduplication, encoded as repeated unbracketed query keys. Handles are not resolved. Missing indexed profiles are returned as null. This endpoint does not paginate.", + "description": "Returns an indexed Certified profile for each supplied actor DID. Authentication is not required.", "parameters": { "type": "params", - "required": ["actors"], + "required": [ + "actors" + ], "properties": { "actors": { "type": "array", - "description": "Actor DIDs to look up; repeated unbracketed values preserve their original order and duplicates.", + "description": "Actor DIDs to look up, supplied as repeated unbracketed query values.", "minLength": 1, "maxLength": 100, - "items": { "type": "string", "format": "did" } + "items": { + "type": "string", + "format": "did" + } } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "actors must contain 1 through 100 valid DIDs; unknown query parameters are not accepted." }, - { "name": "ProfileQueryFailed", "description": "The indexed profiles could not be queried." } + { + "name": "InvalidRequest", + "description": "An actor DID or query parameter is invalid." + }, + { + "name": "ProfileQueryFailed", + "description": "The indexed profiles could not be queried." + } ] }, "output": { "type": "object", - "required": ["profiles"], + "required": [ + "profiles" + ], "properties": { "profiles": { "type": "array", - "description": "One result per supplied actor occurrence, in input order. The profile is null when no indexed profile exists.", - "items": { "type": "ref", "ref": "#profileResult" } + "description": "One result per supplied actor occurrence, in request order.", + "items": { + "type": "ref", + "ref": "#profileResult" + } } } }, "profileResult": { "type": "object", - "required": ["actor", "profile"], - "nullable": ["profile"], + "required": [ + "actor", + "profile" + ], + "nullable": [ + "profile" + ], "properties": { - "actor": { "type": "string", "format": "did" }, - "profile": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" } + "actor": { + "type": "string", + "format": "did", + "description": "Actor DID from the corresponding request occurrence." + }, + "profile": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView", + "description": "Indexed profile view, or null when none is indexed." + } } } } diff --git a/docs/sources/lexicons/app.certified.actor.listOrganizations.json b/docs/sources/lexicons/app.certified.actor.listOrganizations.json index 1065b02..3117f09 100644 --- a/docs/sources/lexicons/app.certified.actor.listOrganizations.json +++ b/docs/sources/lexicons/app.certified.actor.listOrganizations.json @@ -4,7 +4,7 @@ "defs": { "main": { "type": "query", - "description": "Lists actors with indexed organization sidecars. organizationTypes accepts at most 100 repeated unbracketed values and uses OR within that filter; distinct filters use AND. Omitted visibility imposes no restriction, including public, unlisted, and unspecified values; supplied visibility is an exact string match. Results sort by (createdAt, uri), default desc, with a direction-bound opaque cursor. Keep other parameters unchanged between pages. Authentication is not required.", + "description": "Lists actors with indexed organization sidecars matching the supplied filters. Filters combine with AND, and values within each array filter are ORed. Authentication is not required.", "parameters": { "type": "params", "properties": { @@ -12,44 +12,65 @@ "type": "array", "maxLength": 100, "description": "Exact organizationType values; OR within this filter.", - "items": { "type": "string", "maxLength": 128 } + "items": { + "type": "string", + "maxLength": 128 + } }, "visibility": { "type": "string", - "description": "Exact match against the sidecar visibility value. Omission adds no visibility restriction; missing values do not match a supplied value." + "description": "Exact visibility match; omission is unrestricted, but records with no visibility value do not match." }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort direction for (createdAt, uri); default desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sort by a valid zoned createdAt, otherwise indexedAt, falling back to row creation time; URI breaks ties. Default desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum actors in the page; default 25." + "description": "Page size; default 25." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor, bound to sortDirection." + "description": "Opaque cursor bound to sortDirection; keep other parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A filter value, page bound, cursor, repeated scalar, or query parameter is invalid." } + { + "name": "InvalidRequest", + "description": "A filter or scalar parameter is invalid or repeated, or an unsupported parameter was supplied." + }, + { + "name": "OrganizationQueryFailed", + "description": "The indexed organization sidecar or associated profile could not be queried." + } ] }, "output": { "type": "object", - "required": ["actors"], + "required": [ + "actors" + ], "properties": { "actors": { "type": "array", - "items": { "type": "ref", "ref": "app.certified.actor.getOrganization#organizationActorView" } + "items": { + "type": "ref", + "ref": "app.certified.actor.getOrganization#organizationActorView" + } }, "cursor": { "type": "string", diff --git a/docs/sources/lexicons/app.certified.actor.listProfiles.json b/docs/sources/lexicons/app.certified.actor.listProfiles.json index 86a6150..0fe9724 100644 --- a/docs/sources/lexicons/app.certified.actor.listProfiles.json +++ b/docs/sources/lexicons/app.certified.actor.listProfiles.json @@ -4,33 +4,65 @@ "defs": { "main": { "type": "query", - "description": "Public paginated discovery listing of indexed Certified profiles. Authentication is not required. This endpoint accepts pagination parameters only; use getProfiles for actor-specific batch lookup and searchProfiles for profile-text search. The handler applies limit=25 and sortDirection=desc when omitted; limit is 1..100. Results sort by a valid zoned createdAt, otherwise indexedAt (or stored row creation time when indexedAt is absent), then URI, both in the requested direction. The opaque cursor is direction-bound and provides no snapshot guarantee; keep other parameters unchanged between pages. Unknown parameters and repeated scalar keys are rejected.", + "description": "Lists indexed Certified profiles with pagination. Authentication is not required.", "parameters": { "type": "params", "properties": { - "sortDirection": { "type": "string", "description": "Sort direction; handler default is desc.", "enum": ["asc", "desc"] }, - "limit": { "type": "integer", "description": "Page size; handler default is 25.", "minimum": 1, "maximum": 100 }, - "cursor": { "type": "string", "description": "Opaque versioned keyset cursor bound to sortDirection." } + "sortDirection": { + "type": "string", + "description": "Sort by a valid zoned createdAt, otherwise indexedAt, falling back to row creation time; URI breaks ties. Default desc.", + "enum": [ + "asc", + "desc" + ] + }, + "limit": { + "type": "integer", + "description": "Page size; default 25.", + "minimum": 1, + "maximum": 100 + }, + "cursor": { + "type": "string", + "description": "Opaque cursor bound to sortDirection; keep parameters unchanged between pages. Pages are not a snapshot." + } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A parameter is invalid, repeated where scalar, or unknown." }, - { "name": "ProfileQueryFailed", "description": "The indexed profiles could not be queried." } + { + "name": "InvalidRequest", + "description": "A pagination parameter is invalid, repeated where scalar, or unsupported." + }, + { + "name": "ProfileQueryFailed", + "description": "The indexed profiles could not be queried." + } ] }, "output": { "type": "object", - "required": ["profiles"], + "required": [ + "profiles" + ], "properties": { "profiles": { "type": "array", - "items": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" } + "items": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView" + } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when pagination is complete." } + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when pagination is complete." + } } } } diff --git a/docs/sources/lexicons/app.certified.actor.organization.json b/docs/sources/lexicons/app.certified.actor.organization.json new file mode 100644 index 0000000..4053223 --- /dev/null +++ b/docs/sources/lexicons/app.certified.actor.organization.json @@ -0,0 +1,96 @@ +{ + "lexicon": 1, + "id": "app.certified.actor.organization", + "defs": { + "main": { + "type": "record", + "description": "Extended metadata for an organization actor. Complements the base actor profile with organization-specific fields like legal structure and reference links.", + "key": "literal:self", + "record": { + "type": "object", + "required": [ + "createdAt" + ], + "properties": { + "organizationType": { + "type": "array", + "description": "Legal or operational structures of the organization (e.g. 'nonprofit', 'ngo', 'government', 'social-enterprise', 'cooperative').", + "items": { + "type": "string", + "maxLength": 128, + "maxGraphemes": 100 + }, + "maxLength": 10 + }, + "urls": { + "type": "array", + "description": "Additional reference URLs (social media profiles, contact pages, donation links, etc.) with a display label for each URL.", + "items": { + "type": "ref", + "ref": "#urlItem" + } + }, + "location": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "A strong reference to the location where the organization is based. The record referenced must conform with the lexicon app.certified.location." + }, + "foundedDate": { + "type": "string", + "format": "datetime", + "description": "When the organization was established. Stored as datetime per ATProto conventions (no date-only format exists). Clients should use midnight UTC (e.g., '2005-01-01T00:00:00.000Z'); consumers should treat only the date portion as canonical." + }, + "longDescription": { + "type": "union", + "refs": [ + "org.hypercerts.defs#descriptionString", + "pub.leaflet.pages.linearDocument", + "com.atproto.repo.strongRef" + ], + "description": "Long-form description of the organization, such as its mission, history, or detailed project narrative. An inline string for plain text or markdown, a Leaflet linear document record embedded directly, or a strong reference to an existing document record." + }, + "visibility": { + "type": "string", + "knownValues": [ + "public", + "unlisted" + ], + "description": "Controls whether the organization or project is publicly discoverable on platforms that honor this setting." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + }, + "urlItem": { + "type": "object", + "description": "A labeled URL reference.", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The URL.", + "maxLength": 10000, + "maxGraphemes": 2048 + }, + "label": { + "type": "string", + "description": "Optional human-readable label for this URL (e.g. 'Support page', 'Donation page').", + "maxLength": 640, + "maxGraphemes": 64 + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.actor.profile.json b/docs/sources/lexicons/app.certified.actor.profile.json new file mode 100644 index 0000000..d234df0 --- /dev/null +++ b/docs/sources/lexicons/app.certified.actor.profile.json @@ -0,0 +1,68 @@ +{ + "lexicon": 1, + "id": "app.certified.actor.profile", + "defs": { + "main": { + "type": "record", + "description": "A declaration of a Certified account profile.", + "key": "literal:self", + "record": { + "type": "object", + "required": [ + "createdAt" + ], + "properties": { + "displayName": { + "type": "string", + "description": "Display name for the account", + "maxGraphemes": 64, + "maxLength": 640 + }, + "description": { + "type": "string", + "description": "Free-form profile description text.", + "maxGraphemes": 256, + "maxLength": 2560 + }, + "pronouns": { + "type": "string", + "description": "Free-form pronouns text.", + "maxGraphemes": 20, + "maxLength": 200 + }, + "website": { + "type": "string", + "format": "uri", + "description": "Account website URL" + }, + "avatar": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallImage" + ], + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'" + }, + "banner": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#largeImage" + ], + "description": "Larger horizontal image to display behind profile view." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.actor.searchOrganizations.json b/docs/sources/lexicons/app.certified.actor.searchOrganizations.json index d5e9834..0b8a685 100644 --- a/docs/sources/lexicons/app.certified.actor.searchOrganizations.json +++ b/docs/sources/lexicons/app.certified.actor.searchOrganizations.json @@ -4,63 +4,89 @@ "defs": { "main": { "type": "query", - "description": "Searches the associated Certified profile displayName and description using the complete trimmed search text as a case-insensitive literal substring within either field. An organization without an associated profile cannot match nonblank search text; blank-after-trimming search imposes no search restriction. Other filters combine with search using AND. Results and pagination otherwise match listOrganizations. Authentication is not required.", + "description": "Searches organizations by associated profile text and optional filters. Filters combine with AND, and values within each array filter are ORed. Authentication is not required.", "parameters": { "type": "params", - "required": ["search"], + "required": [ + "search" + ], "properties": { "search": { "type": "string", - "description": "Required search text; matching is literal, case-insensitive, and does not split terms across fields. A blank trimmed value imposes no search restriction." + "description": "Trimmed text is matched literally and case-insensitively as a substring in either displayName or description. Blank text is unrestricted; nonblank text requires an associated profile." }, "actors": { "type": "array", "maxLength": 100, - "description": "Actor DIDs whose organization sidecars are included; DIDs only, with OR within this filter.", - "items": { "type": "string", "format": "did" } + "description": "Actor DIDs to include; values are ORed and handles are not resolved.", + "items": { + "type": "string", + "format": "did" + } }, "organizationTypes": { "type": "array", "maxLength": 100, "description": "Exact organizationType values; OR within this filter.", - "items": { "type": "string", "maxLength": 128 } + "items": { + "type": "string", + "maxLength": 128 + } }, "visibility": { "type": "string", - "description": "Exact match against the sidecar visibility value. Omission adds no visibility restriction; missing values do not match a supplied value." + "description": "Exact visibility match; omission is unrestricted, but records with no visibility value do not match." }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort direction for (createdAt, uri); default desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sort by a valid zoned createdAt, otherwise indexedAt, falling back to row creation time; URI breaks ties. Default desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum actors in the page; default 25." + "description": "Page size; default 25." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor, bound to sortDirection." + "description": "Opaque cursor bound to sortDirection; keep other parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A filter value, page bound, cursor, repeated scalar, or query parameter is invalid." } + { + "name": "InvalidRequest", + "description": "A filter or scalar parameter is invalid or repeated, or an unsupported parameter was supplied." + }, + { + "name": "OrganizationQueryFailed", + "description": "The indexed organization sidecar or associated profile could not be queried." + } ] }, "output": { "type": "object", - "required": ["actors"], + "required": [ + "actors" + ], "properties": { "actors": { "type": "array", - "items": { "type": "ref", "ref": "app.certified.actor.getOrganization#organizationActorView" } + "items": { + "type": "ref", + "ref": "app.certified.actor.getOrganization#organizationActorView" + } }, "cursor": { "type": "string", diff --git a/docs/sources/lexicons/app.certified.actor.searchProfiles.json b/docs/sources/lexicons/app.certified.actor.searchProfiles.json index df63acb..1af08c8 100644 --- a/docs/sources/lexicons/app.certified.actor.searchProfiles.json +++ b/docs/sources/lexicons/app.certified.actor.searchProfiles.json @@ -4,36 +4,81 @@ "defs": { "main": { "type": "query", - "description": "Public text search over indexed Certified profiles. Authentication is not required. search is trimmed and matched as a case-insensitive literal substring in displayName or description; blank-after-trim search imposes no text restriction, though listProfiles is preferred for unrestricted listing. actors uses repeated unbracketed keys, accepts at most 100 supplied DIDs before deduplication, and ORs them together; actor and search filters combine with AND before pagination. The handler applies limit=25 and sortDirection=desc when omitted; limit is 1..100. Results sort by a valid zoned createdAt, otherwise indexedAt (or stored row creation time when indexedAt is absent), then URI, both in the requested direction. The opaque cursor is direction-bound and provides no snapshot guarantee; keep other parameters unchanged between pages. Unknown parameters and repeated scalar keys are rejected.", + "description": "Searches indexed Certified profiles by text, optionally restricted to actor DIDs. Filters combine with AND; values in the actor array are ORed. Authentication is not required.", "parameters": { "type": "params", - "required": ["search"], + "required": [ + "search" + ], "properties": { - "search": { "type": "string", "description": "Required search text; after trimming, matched case-insensitively as a literal substring in displayName or description. Blank is unrestricted." }, - "sortDirection": { "type": "string", "description": "Sort direction; handler default is desc.", "enum": ["asc", "desc"] }, - "actors": { "type": "array", "description": "DIDs whose indexed profiles may be returned. Repeated values are ORed; handles are not resolved.", "maxLength": 100, "items": { "type": "string", "format": "did" } }, - "limit": { "type": "integer", "description": "Page size; handler default is 25.", "minimum": 1, "maximum": 100 }, - "cursor": { "type": "string", "description": "Opaque versioned keyset cursor bound to sortDirection." } + "search": { + "type": "string", + "description": "Trimmed text is matched literally and case-insensitively as a substring in displayName or description. Blank text is unrestricted." + }, + "sortDirection": { + "type": "string", + "description": "Sort by a valid zoned createdAt, otherwise indexedAt, falling back to row creation time; URI breaks ties. Default desc.", + "enum": [ + "asc", + "desc" + ] + }, + "actors": { + "type": "array", + "description": "Actor DIDs to include; values are ORed and handles are not resolved.", + "maxLength": 100, + "items": { + "type": "string", + "format": "did" + } + }, + "limit": { + "type": "integer", + "description": "Page size; default 25.", + "minimum": 1, + "maximum": 100 + }, + "cursor": { + "type": "string", + "description": "Opaque cursor bound to sortDirection; keep parameters unchanged between pages. Pages are not a snapshot." + } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A parameter is invalid, repeated where scalar, or unknown." }, - { "name": "ProfileQueryFailed", "description": "The indexed profiles could not be queried." } + { + "name": "InvalidRequest", + "description": "A filter or pagination parameter is invalid, repeated where scalar, or unsupported." + }, + { + "name": "ProfileQueryFailed", + "description": "The indexed profiles could not be queried." + } ] }, "output": { "type": "object", - "required": ["profiles"], + "required": [ + "profiles" + ], "properties": { "profiles": { "type": "array", - "items": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" } + "items": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView" + } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when pagination is complete." } + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when pagination is complete." + } } } } diff --git a/docs/sources/lexicons/app.certified.badge.award.json b/docs/sources/lexicons/app.certified.badge.award.json new file mode 100644 index 0000000..ff6d0b5 --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.award.json @@ -0,0 +1,55 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.award", + "defs": { + "main": { + "type": "record", + "description": "Records a badge award to a user, project, or activity claim.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "badge", + "subject", + "createdAt" + ], + "properties": { + "badge": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Strong reference to the badge definition at the time of award. The record referenced must conform with the lexicon app.certified.badge.definition." + }, + "subject": { + "type": "union", + "description": "Entity the badge award is for (either an account DID or any specific AT Protocol record), e.g. a user, a project, or a specific activity claim.", + "refs": [ + "app.certified.defs#did", + "com.atproto.repo.strongRef" + ] + }, + "note": { + "type": "string", + "description": "Optional statement explaining the reason for this badge award.", + "maxLength": 500 + }, + "url": { + "type": "string", + "format": "uri", + "description": "Optional URL the badge award links to.", + "maxLength": 2048 + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.badge.definition.json b/docs/sources/lexicons/app.certified.badge.definition.json new file mode 100644 index 0000000..b6f1857 --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.definition.json @@ -0,0 +1,75 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.definition", + "defs": { + "main": { + "type": "record", + "description": "Defines a badge that can be awarded via badge award records to users, projects, or activity claims.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "title", + "badgeType", + "createdAt" + ], + "properties": { + "badgeType": { + "type": "string", + "description": "Category of the badge. Values beyond the known set are permitted.", + "knownValues": [ + "endorsement", + "verification", + "participation", + "certification", + "affiliation", + "recognition" + ], + "maxLength": 100 + }, + "title": { + "type": "string", + "description": "Human-readable title of the badge.", + "maxLength": 256 + }, + "icon": { + "type": "blob", + "description": "Icon representing the badge, stored as a blob for compact visual display.", + "accept": [ + "image/png", + "image/jpeg", + "image/webp", + "image/svg+xml" + ], + "maxSize": 1048576 + }, + "description": { + "type": "string", + "description": "Optional short statement describing what the badge represents.", + "maxLength": 5000, + "maxGraphemes": 500 + }, + "allowedIssuers": { + "type": "array", + "description": "Optional allowlist of DIDs allowed to issue this badge. If omitted, anyone may issue it.", + "maxLength": 100, + "items": { + "type": "ref", + "ref": "app.certified.defs#did" + } + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.badge.getBadgeAward.json b/docs/sources/lexicons/app.certified.badge.getBadgeAward.json new file mode 100644 index 0000000..9924734 --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.getBadgeAward.json @@ -0,0 +1,193 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.getBadgeAward", + "defs": { + "main": { + "type": "query", + "description": "Gets a badge award and its recipient response by AT-URI without authentication.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of the badge-award record." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a badge-award AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed badge award exists at this AT-URI." + }, + { + "name": "BadgeAwardQueryFailed", + "description": "The indexed badge award could not be queried." + } + ] + }, + "output": { + "type": "object", + "required": [ + "badgeAward" + ], + "properties": { + "badgeAward": { + "type": "ref", + "ref": "#badgeAwardView" + } + } + }, + "badgeDefinitionRecordView": { + "type": "object", + "description": "Exact indexed badge-definition version referenced by the award.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.badge.definition" + } + } + }, + "badgeResponseRecordView": { + "type": "object", + "description": "Raw recipient response record without author hydration.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.badge.response" + } + } + }, + "badgeAwardView": { + "type": "object", + "description": "Badge award with its publisher, exact definition version, and recipient response status.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record", + "badge", + "responseStatus", + "recipientResponse" + ], + "nullable": [ + "badge", + "recipientResponse", + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "app.certified.badge.award" + }, + "badge": { + "type": "ref", + "ref": "#badgeDefinitionRecordView", + "description": "The referenced definition version, or null when it is unavailable." + }, + "responseStatus": { + "type": "string", + "enum": [ + "accepted", + "rejected", + "unanswered" + ], + "description": "Latest eligible recipient response, or unanswered when none exists." + }, + "recipientResponse": { + "type": "ref", + "ref": "#badgeResponseRecordView", + "description": "Latest eligible response record, or null when unanswered." + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.badge.getBadgeDefinition.json b/docs/sources/lexicons/app.certified.badge.getBadgeDefinition.json index fc1c22d..da5b8cd 100644 --- a/docs/sources/lexicons/app.certified.badge.getBadgeDefinition.json +++ b/docs/sources/lexicons/app.certified.badge.getBadgeDefinition.json @@ -4,51 +4,89 @@ "defs": { "main": { "type": "query", - "description": "Public lookup of one indexed badge definition by exact AT-URI and DID authority. The original record is preserved and only its publisher actor is hydrated. Authentication is not required.", + "description": "Returns an indexed badge definition and its publisher actor. Authentication is not required.", "parameters": { "type": "params", - "required": ["uri"], + "required": [ + "uri" + ], "properties": { "uri": { "type": "string", "format": "at-uri", - "description": "Full AT-URI of the badge-definition record using a DID authority." + "description": "AT-URI of a badge definition with a DID authority; lookup matches the URI, not a record CID." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ { "name": "InvalidRequest", - "description": "The URI is invalid or is not a badge-definition record AT-URI with a DID authority." + "description": "The URI is invalid or does not identify a badge-definition AT-URI with a DID authority." }, { "name": "RecordNotFound", - "description": "No indexed badge definition exists at this exact AT-URI." + "description": "No indexed badge definition matches this AT-URI." } ] }, "output": { "type": "object", - "required": ["badgeDefinition"], + "required": [ + "badgeDefinition" + ], "properties": { - "badgeDefinition": { "type": "ref", "ref": "#badgeDefinitionView" } + "badgeDefinition": { + "type": "ref", + "ref": "#badgeDefinitionView" + } } }, "badgeDefinitionView": { "type": "object", - "description": "Full indexed badge-definition record view with hydrated publisher actor.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record"], + "description": "Full indexed badge-definition record with its publisher actor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "org.hypercerts.api.defs#actorView" }, - "record": { "type": "ref", "ref": "app.certified.badge.definition" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "app.certified.badge.definition" + } } } } diff --git a/docs/sources/lexicons/app.certified.badge.getBadgeResponse.json b/docs/sources/lexicons/app.certified.badge.getBadgeResponse.json new file mode 100644 index 0000000..e95a81c --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.getBadgeResponse.json @@ -0,0 +1,97 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.getBadgeResponse", + "defs": { + "main": { + "type": "query", + "description": "Gets one badge response by AT-URI without authentication.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of the badge-response record." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a badge-response AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed badge response exists at this AT-URI." + }, + { + "name": "BadgeResponseQueryFailed", + "description": "The indexed badge response could not be queried." + } + ] + }, + "output": { + "type": "object", + "required": [ + "badgeResponse" + ], + "properties": { + "badgeResponse": { + "type": "ref", + "ref": "#badgeResponseView" + } + } + }, + "badgeResponseView": { + "type": "object", + "description": "Raw badge response with its publisher actor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "app.certified.badge.response" + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.badge.listBadgeAwards.json b/docs/sources/lexicons/app.certified.badge.listBadgeAwards.json new file mode 100644 index 0000000..01137d1 --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.listBadgeAwards.json @@ -0,0 +1,119 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.listBadgeAwards", + "defs": { + "main": { + "type": "query", + "description": "Lists public badge awards with recipient response status and pagination without authentication.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "DIDs of repositories that published the awards.", + "items": { + "type": "string", + "format": "did" + } + }, + "badgeUris": { + "type": "array", + "maxLength": 100, + "description": "Badge-definition AT-URIs.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "badgeTypes": { + "type": "array", + "maxLength": 100, + "description": "Exact badgeType values from the referenced definition version.", + "items": { + "type": "string", + "maxLength": 100 + } + }, + "subjects": { + "type": "array", + "maxLength": 100, + "description": "DIDs or full record AT-URIs with valid collection NSIDs.", + "items": { + "type": "string" + } + }, + "responses": { + "type": "array", + "maxLength": 100, + "description": "Computed recipient response statuses.", + "items": { + "type": "string", + "enum": [ + "accepted", + "rejected", + "unanswered" + ] + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Sort direction for (createdAt, uri)." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum awards returned." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor from the previous page." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A filter or page parameter is invalid." + }, + { + "name": "BadgeAwardQueryFailed", + "description": "The indexed badge awards could not be queried." + } + ] + }, + "output": { + "type": "object", + "required": [ + "badgeAwards" + ], + "properties": { + "badgeAwards": { + "type": "array", + "items": { + "type": "ref", + "ref": "app.certified.badge.getBadgeAward#badgeAwardView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when pagination is complete." + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.badge.listBadgeDefinitions.json b/docs/sources/lexicons/app.certified.badge.listBadgeDefinitions.json index a27d975..a50b08b 100644 --- a/docs/sources/lexicons/app.certified.badge.listBadgeDefinitions.json +++ b/docs/sources/lexicons/app.certified.badge.listBadgeDefinitions.json @@ -4,55 +4,69 @@ "defs": { "main": { "type": "query", - "description": "Lists indexed badge definitions with author-DID and exact open-string badgeType filters. Repeated unbracketed array keys accept up to 100 values each; values within each filter use OR and distinct filters use AND. Results sort by (createdAt, uri), default desc, with an opaque cursor bound to sortDirection. Keep other parameters unchanged between pages. Authentication is not required.", + "description": "Lists indexed badge definitions. Author and badgeType filters are combined with AND; values within each filter are matched with OR. Authentication is not required.", "parameters": { "type": "params", "properties": { "authors": { "type": "array", "maxLength": 100, - "description": "DIDs of repositories that published the badge definitions; OR within this filter.", - "items": { "type": "string", "format": "did" } + "description": "Publisher DIDs to include.", + "items": { + "type": "string", + "format": "did" + } }, "badgeTypes": { "type": "array", "maxLength": 100, - "description": "Exact open-string badgeType values; OR within this filter.", - "items": { "type": "string", "maxLength": 100 } + "description": "Exact badgeType strings to include.", + "items": { + "type": "string", + "maxLength": 100 + } }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "default": "desc", - "description": "Sort direction for (createdAt, uri)." + "description": "Selects ascending or descending order by createdAt, then AT-URI." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25, - "description": "Maximum definitions in the page." + "description": "Maximum number of definitions to return in the page." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor, bound to sortDirection." + "description": "Opaque cursor from the previous page, bound to sortDirection. Keep other query parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ { "name": "InvalidRequest", - "description": "A filter value, page bound, cursor, repeated scalar, or query parameter is invalid." + "description": "A filter, query parameter, page bound, or cursor is invalid." } ] }, "output": { "type": "object", - "required": ["badgeDefinitions"], + "required": [ + "badgeDefinitions" + ], "properties": { "badgeDefinitions": { "type": "array", @@ -63,7 +77,7 @@ }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there is no next page." + "description": "Cursor for the next page; omitted when there are no more results." } } } diff --git a/docs/sources/lexicons/app.certified.badge.listBadgeResponses.json b/docs/sources/lexicons/app.certified.badge.listBadgeResponses.json new file mode 100644 index 0000000..30f9ce4 --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.listBadgeResponses.json @@ -0,0 +1,76 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.listBadgeResponses", + "defs": { + "main": { + "type": "query", + "description": "Lists raw public badge responses with optional award filtering and pagination without authentication.", + "parameters": { + "type": "params", + "properties": { + "badgeAward": { + "type": "string", + "format": "at-uri", + "description": "Badge-award AT-URI to match against response records." + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Sort direction for (createdAt, uri)." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum responses returned." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor from the previous page." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A filter or page parameter is invalid." + }, + { + "name": "BadgeResponseQueryFailed", + "description": "The indexed badge responses could not be queried." + } + ] + }, + "output": { + "type": "object", + "required": [ + "badgeResponses" + ], + "properties": { + "badgeResponses": { + "type": "array", + "items": { + "type": "ref", + "ref": "app.certified.badge.getBadgeResponse#badgeResponseView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when pagination is complete." + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.badge.response.json b/docs/sources/lexicons/app.certified.badge.response.json new file mode 100644 index 0000000..72c45ae --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.response.json @@ -0,0 +1,49 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.response", + "defs": { + "main": { + "type": "record", + "description": "Recipient response to a badge award.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "badgeAward", + "response", + "createdAt" + ], + "properties": { + "badgeAward": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Strong reference to the badge award being responded to. The record referenced must conform with the lexicon app.certified.badge.award." + }, + "response": { + "type": "string", + "knownValues": [ + "accepted", + "rejected" + ], + "description": "The recipient’s response for the badge (accepted or rejected)." + }, + "weight": { + "type": "string", + "description": "Optional relative weight for accepted badges, assigned by the recipient.", + "maxLength": 50 + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.badge.searchBadgeDefinitions.json b/docs/sources/lexicons/app.certified.badge.searchBadgeDefinitions.json new file mode 100644 index 0000000..630da89 --- /dev/null +++ b/docs/sources/lexicons/app.certified.badge.searchBadgeDefinitions.json @@ -0,0 +1,137 @@ +{ + "lexicon": 1, + "id": "app.certified.badge.searchBadgeDefinitions", + "defs": { + "main": { + "type": "query", + "description": "Searches badge definitions by title or description without authentication.", + "parameters": { + "type": "params", + "required": [ + "search" + ], + "properties": { + "search": { + "type": "string", + "description": "Text matched against definition titles and descriptions." + }, + "authors": { + "type": "array", + "maxLength": 100, + "description": "DIDs of repositories that published the definitions.", + "items": { + "type": "string", + "format": "did" + } + }, + "badgeTypes": { + "type": "array", + "maxLength": 100, + "description": "Exact badgeType values.", + "items": { + "type": "string", + "maxLength": 100 + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Sort direction for (createdAt, uri)." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum definitions returned." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor from the previous page." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A parameter is invalid, repeated where scalar, or unknown." + }, + { + "name": "BadgeDefinitionQueryFailed", + "description": "The indexed badge definitions could not be queried." + } + ] + }, + "output": { + "type": "object", + "required": [ + "badgeDefinitions" + ], + "properties": { + "badgeDefinitions": { + "type": "array", + "items": { + "type": "ref", + "ref": "#badgeDefinitionView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when pagination is complete." + } + } + }, + "badgeDefinitionView": { + "type": "object", + "description": "Indexed badge definition with its publisher actor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "app.certified.badge.definition" + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.defs.json b/docs/sources/lexicons/app.certified.defs.json new file mode 100644 index 0000000..4a64ce0 --- /dev/null +++ b/docs/sources/lexicons/app.certified.defs.json @@ -0,0 +1,36 @@ +{ + "lexicon": 1, + "id": "app.certified.defs", + "description": "Common type definitions used across certified protocols.", + "defs": { + "did": { + "type": "object", + "description": "A Decentralized Identifier (DID) string.", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did", + "description": "The DID string value.", + "maxLength": 256 + } + } + }, + "recordSubject": { + "type": "object", + "description": "A subject that is a record, referenced by AT-URI. The URI must use the DID form (not handle form) and deliberately carries no CID: it references the record's identity, not a specific version, so references made against it survive updates to the record.", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI (DID form) of the record being followed. Schema validation enforces only general at-uri syntax; writers MUST supply a full record URI (did authority, collection, and rkey — not a handle authority or a partial URI), and consumers (AppView/indexer) are expected to reject or skip subjects that do not conform." + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.graph.entityFollow.json b/docs/sources/lexicons/app.certified.graph.entityFollow.json new file mode 100644 index 0000000..45f52be --- /dev/null +++ b/docs/sources/lexicons/app.certified.graph.entityFollow.json @@ -0,0 +1,42 @@ +{ + "lexicon": 1, + "id": "app.certified.graph.entityFollow", + "defs": { + "main": { + "type": "record", + "description": "Record declaring a social 'follow' relationship with a non-account entity, such as a specific record. Account (DID) follows are out of scope and belong to app.certified.graph.follow. The subject is an open union so additional non-DID entity kinds can be added in the future. Duplicate follows will be ignored by the AppView.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "subject", + "createdAt" + ], + "properties": { + "subject": { + "type": "union", + "refs": [ + "app.certified.defs#recordSubject" + ], + "description": "The entity being followed. Must never include account DIDs; use app.certified.graph.follow for accounts." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "via": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Optional strong reference to a record that mediated this follow (e.g. a starter pack or other curated list). Mirrors the optional `via` field on app.certified.graph.follow; the referenced record may conform with any lexicon." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.graph.follow.json b/docs/sources/lexicons/app.certified.graph.follow.json new file mode 100644 index 0000000..c6116e0 --- /dev/null +++ b/docs/sources/lexicons/app.certified.graph.follow.json @@ -0,0 +1,40 @@ +{ + "lexicon": 1, + "id": "app.certified.graph.follow", + "defs": { + "main": { + "type": "record", + "description": "Record declaring a social 'follow' relationship of another account. Duplicate follows will be ignored by the AppView.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "subject", + "createdAt" + ], + "properties": { + "subject": { + "type": "string", + "format": "did", + "description": "DID of the account being followed." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "via": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Optional strong reference to a record that mediated this follow (e.g. a starter pack or other curated list). Mirrors the optional `via` field on app.bsky.graph.follow; the referenced record may conform with any lexicon." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.graph.getEntityFollow.json b/docs/sources/lexicons/app.certified.graph.getEntityFollow.json index 4894b7a..5393187 100644 --- a/docs/sources/lexicons/app.certified.graph.getEntityFollow.json +++ b/docs/sources/lexicons/app.certified.graph.getEntityFollow.json @@ -4,37 +4,90 @@ "defs": { "main": { "type": "query", - "description": "Returns the representative entity-follow record for one actor and entity URI, or explicit null when the relationship is not indexed. Duplicate relationships use the record with earliest createdAt, URI ascending as tie-breaker. Operational failures return errors, never a missing-relationship response. Authentication is not required.", + "description": "Returns the representative entity-follow record for an actor and entity AT-URI, or null if none is indexed. No authentication is required; lookup failures return errors rather than null.", "parameters": { "type": "params", - "required": ["actor", "entity"], + "required": [ + "actor", + "entity" + ], "properties": { - "actor": { "type": "string", "format": "did", "description": "DID of the actor who published the entity-follow record." }, - "entity": { "type": "string", "format": "at-uri", "description": "Full DID-authority AT-URI of the followed record identity; matching does not pin a CID or restrict the target collection." } + "actor": { + "type": "string", + "format": "did", + "description": "DID that published the entity-follow record." + }, + "entity": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of the followed record using a DID authority; any target collection is accepted, and matching is by URI, not CID." + } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, - "errors": [{ "name": "InvalidRequest", "description": "The actor DID, entity AT-URI, or query parameters are invalid." }] + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The actor DID, entity AT-URI, or another query parameter is invalid." + } + ] }, "entityFollowRecordView": { "type": "object", "description": "Raw indexed entity-follow record view, including the DID that published the relationship.", - "required": ["uri", "cid", "indexedAt", "did", "record"], - "nullable": ["indexedAt"], + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "record": { "type": "ref", "ref": "app.certified.graph.entityFollow" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.graph.entityFollow" + } } }, "output": { "type": "object", - "required": ["follow"], - "nullable": ["follow"], + "required": [ + "follow" + ], + "nullable": [ + "follow" + ], "properties": { - "follow": { "type": "ref", "ref": "#entityFollowRecordView", "description": "Raw representative entity-follow record, or null when no matching relationship is indexed." } + "follow": { + "type": "ref", + "ref": "#entityFollowRecordView", + "description": "Earliest matching record by createdAt, with URI ascending on ties; null if none is indexed." + } } } } diff --git a/docs/sources/lexicons/app.certified.graph.getFollow.json b/docs/sources/lexicons/app.certified.graph.getFollow.json index 6fc3298..d08a777 100644 --- a/docs/sources/lexicons/app.certified.graph.getFollow.json +++ b/docs/sources/lexicons/app.certified.graph.getFollow.json @@ -4,65 +4,122 @@ "defs": { "main": { "type": "query", - "description": "Returns the earliest indexed follow record for one actor-to-actor relationship, or null when no matching relationship is indexed. Authentication is not required.", + "description": "Returns the representative follow record for an actor-to-actor relationship, or null if none is indexed. No authentication is required.", "parameters": { "type": "params", - "required": ["actor", "subject"], + "required": [ + "actor", + "subject" + ], "properties": { "actor": { "type": "string", "format": "did", - "description": "DID of the actor who published the follow record. Required." + "description": "DID of the actor who published the follow record." }, "subject": { "type": "string", "format": "did", - "description": "DID of the actor named as the follow record's subject. Required." + "description": "DID named as the follow record's subject." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A required DID is invalid, repeated, or accompanied by an unknown parameter." } + { + "name": "InvalidRequest", + "description": "A DID or query parameter is invalid." + } ] }, "followRecordView": { "type": "object", - "description": "Raw indexed follow record view, including the DID that published the relationship.", - "required": ["uri", "cid", "indexedAt", "did", "record"], + "description": "Raw indexed follow record with its publisher DID. For duplicates, the earliest createdAt wins, with URI ascending on ties.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "record": { "type": "ref", "ref": "app.certified.graph.follow" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.graph.follow" + } } }, "actorFollowView": { "type": "object", - "description": "The displayed actor and the raw follow record representing its relationship.", - "required": ["did", "profile", "organization", "follow"], - "nullable": ["profile", "organization"], + "description": "Displayed actor with nullable profile and organization sidecars, paired with the representative follow record.", + "required": [ + "did", + "profile", + "organization", + "follow" + ], + "nullable": [ + "profile", + "organization" + ], "properties": { - "did": { "type": "string", "format": "did" }, - "profile": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" }, - "organization": { "type": "ref", "ref": "org.hypercerts.api.defs#organizationView" }, - "follow": { "type": "ref", "ref": "#followRecordView" } + "did": { + "type": "string", + "format": "did" + }, + "profile": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView" + }, + "organization": { + "type": "ref", + "ref": "org.hypercerts.api.defs#organizationView" + }, + "follow": { + "type": "ref", + "ref": "#followRecordView" + } } }, "output": { "type": "object", - "description": "The raw representative follow record, or explicit null when the relationship is not indexed.", - "required": ["follow"], - "nullable": ["follow"], + "description": "Representative raw follow record, or null if no relationship is indexed.", + "required": [ + "follow" + ], + "nullable": [ + "follow" + ], "properties": { "follow": { "type": "ref", "ref": "#followRecordView", - "description": "The earliest-created indexed record for the actor and subject pair, with URI and publisher metadata." + "description": "Representative record for the actor and subject pair, with URI and publisher metadata." } } } diff --git a/docs/sources/lexicons/app.certified.graph.listActorFollowers.json b/docs/sources/lexicons/app.certified.graph.listActorFollowers.json index 16516cf..d148c3e 100644 --- a/docs/sources/lexicons/app.certified.graph.listActorFollowers.json +++ b/docs/sources/lexicons/app.certified.graph.listActorFollowers.json @@ -4,58 +4,75 @@ "defs": { "main": { "type": "query", - "description": "Lists actors whose indexed follow records name the requested actor as subject. Authentication is not required. Duplicate relationships are collapsed before pagination.", + "description": "Lists actors whose follow records name the requested DID as subject. No authentication is required; duplicate relationships are collapsed before pagination, and only the returned actors are hydrated.", "parameters": { "type": "params", - "required": ["actor"], + "required": [ + "actor" + ], "properties": { "actor": { "type": "string", "format": "did", - "description": "DID of the actor whose incoming follows are listed. Required." + "description": "DID whose incoming follows are listed." }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort by each representative follow's createdAt and URI in this direction; the default is desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sorts by representative follow createdAt and URI; defaults to desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum relationships returned; the handler default is 25." + "description": "Maximum relationships per page; defaults to 25." }, "cursor": { "type": "string", - "description": "Opaque cursor from the previous page, bound to sortDirection. Keep other query parameters unchanged between pages." + "description": "Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A required DID, sort direction, limit, cursor, or query parameter is invalid." } + { + "name": "InvalidRequest", + "description": "A DID or query parameter is invalid." + } ] }, "output": { "type": "object", - "required": ["followers", "totalCount"], + "required": [ + "followers", + "totalCount" + ], "properties": { "followers": { "type": "array", - "items": { "type": "ref", "ref": "app.certified.graph.getFollow#actorFollowView" }, - "description": "Incoming relationships, with each displayed DID identifying the actor who published the follow." + "items": { + "type": "ref", + "ref": "app.certified.graph.getFollow#actorFollowView" + }, + "description": "Incoming relationships; each displayed DID identifies the actor who published the follow." }, "totalCount": { "type": "integer", "minimum": 0, - "description": "Number of matching relationships after duplicate relationships are collapsed, before cursor and limit are applied." + "description": "Count of deduplicated relationships before cursor and limit are applied." }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there are no more relationships." + "description": "Next-page cursor; omitted when no more results are available." } } } diff --git a/docs/sources/lexicons/app.certified.graph.listActorFollowing.json b/docs/sources/lexicons/app.certified.graph.listActorFollowing.json index a4cc6e8..aa61b5c 100644 --- a/docs/sources/lexicons/app.certified.graph.listActorFollowing.json +++ b/docs/sources/lexicons/app.certified.graph.listActorFollowing.json @@ -4,58 +4,75 @@ "defs": { "main": { "type": "query", - "description": "Lists actors named as subjects by the requested actor's indexed follow records. Authentication is not required. Duplicate relationships are collapsed before pagination.", + "description": "Lists actors named as subjects by the requested DID's follow records. No authentication is required; duplicate relationships are collapsed before pagination, and only the returned actors are hydrated.", "parameters": { "type": "params", - "required": ["actor"], + "required": [ + "actor" + ], "properties": { "actor": { "type": "string", "format": "did", - "description": "DID of the actor whose outgoing follows are listed. Required." + "description": "DID whose outgoing follows are listed." }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort by each representative follow's createdAt and URI in this direction; the default is desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sorts by representative follow createdAt and URI; defaults to desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum relationships returned; the handler default is 25." + "description": "Maximum relationships per page; defaults to 25." }, "cursor": { "type": "string", - "description": "Opaque cursor from the previous page, bound to sortDirection. Keep other query parameters unchanged between pages." + "description": "Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A required DID, sort direction, limit, cursor, or query parameter is invalid." } + { + "name": "InvalidRequest", + "description": "A DID or query parameter is invalid." + } ] }, "output": { "type": "object", - "required": ["following", "totalCount"], + "required": [ + "following", + "totalCount" + ], "properties": { "following": { "type": "array", - "items": { "type": "ref", "ref": "app.certified.graph.getFollow#actorFollowView" }, - "description": "Outgoing relationships, with each displayed DID identifying the actor named as the follow subject." + "items": { + "type": "ref", + "ref": "app.certified.graph.getFollow#actorFollowView" + }, + "description": "Outgoing relationships; each displayed DID identifies the actor named as the follow subject." }, "totalCount": { "type": "integer", "minimum": 0, - "description": "Number of matching relationships after duplicate relationships are collapsed, before cursor and limit are applied." + "description": "Count of deduplicated relationships before cursor and limit are applied." }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when there are no more relationships." + "description": "Next-page cursor; omitted when no more results are available." } } } diff --git a/docs/sources/lexicons/app.certified.graph.listEntityFollowers.json b/docs/sources/lexicons/app.certified.graph.listEntityFollowers.json index 7fb74f6..ada7c73 100644 --- a/docs/sources/lexicons/app.certified.graph.listEntityFollowers.json +++ b/docs/sources/lexicons/app.certified.graph.listEntityFollowers.json @@ -4,38 +4,101 @@ "defs": { "main": { "type": "query", - "description": "Lists actors following one entity URI. Duplicate (actor, entity) relationships are collapsed before pagination using the earliest-created record and URI ascending tie-breaker. Results sort by each representative follow's (createdAt, uri) in sortDirection, default desc, with an opaque direction-bound cursor. Only the returned page is hydrated; missing profiles or organization sidecars remain explicit nulls. Operational failures return errors, not partial pages. Authentication is not required.", + "description": "Lists actors following an entity AT-URI. No authentication is required; duplicate actor–entity relationships use the earliest createdAt, with URI ascending on ties. Only the returned page is hydrated: missing profile and organization sidecars are null, while lookup failures return errors rather than partial pages.", "parameters": { "type": "params", - "required": ["entity"], + "required": [ + "entity" + ], "properties": { - "entity": { "type": "string", "format": "at-uri", "description": "Full DID-authority AT-URI of the followed record identity; matching does not pin a CID or restrict the target collection." }, - "sortDirection": { "type": "string", "enum": ["asc", "desc"], "description": "Sort direction for representative follow (createdAt, uri); default desc." }, - "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Maximum relationships in the page; default 25." }, - "cursor": { "type": "string", "description": "Opaque cursor bound to sortDirection; keep all other query parameters unchanged." } + "entity": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of the followed record using a DID authority; any target collection is accepted, and matching is by URI, not CID." + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "Sorts by representative follow createdAt and URI; defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum relationships per page; defaults to 25." + }, + "cursor": { + "type": "string", + "description": "Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages." + } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, - "errors": [{ "name": "InvalidRequest", "description": "The entity AT-URI, sort direction, page bound, cursor, or query parameters are invalid." }] + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The entity AT-URI or another query parameter is invalid." + } + ] }, "entityFollowerView": { "type": "object", "description": "The follower actor and the raw entity-follow record representing its relationship.", - "required": ["did", "profile", "organization", "follow"], - "nullable": ["profile", "organization"], + "required": [ + "did", + "profile", + "organization", + "follow" + ], + "nullable": [ + "profile", + "organization" + ], "properties": { - "did": { "type": "string", "format": "did" }, - "profile": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" }, - "organization": { "type": "ref", "ref": "org.hypercerts.api.defs#organizationView" }, - "follow": { "type": "ref", "ref": "app.certified.graph.getEntityFollow#entityFollowRecordView" } + "did": { + "type": "string", + "format": "did" + }, + "profile": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView" + }, + "organization": { + "type": "ref", + "ref": "org.hypercerts.api.defs#organizationView" + }, + "follow": { + "type": "ref", + "ref": "app.certified.graph.getEntityFollow#entityFollowRecordView" + } } }, "output": { "type": "object", - "required": ["followers"], + "required": [ + "followers" + ], "properties": { - "followers": { "type": "array", "items": { "type": "ref", "ref": "#entityFollowerView" } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when there are no more relationships." } + "followers": { + "type": "array", + "items": { + "type": "ref", + "ref": "#entityFollowerView" + } + }, + "cursor": { + "type": "string", + "description": "Next-page cursor; omitted when no more results are available." + } } } } diff --git a/docs/sources/lexicons/app.certified.graph.listEntityFollowing.json b/docs/sources/lexicons/app.certified.graph.listEntityFollowing.json index a2765d7..b1b06dc 100644 --- a/docs/sources/lexicons/app.certified.graph.listEntityFollowing.json +++ b/docs/sources/lexicons/app.certified.graph.listEntityFollowing.json @@ -4,27 +4,69 @@ "defs": { "main": { "type": "query", - "description": "Lists entities followed by one actor. Duplicate (actor, entity URI) relationships are collapsed before pagination using the earliest-created record and URI ascending tie-breaker. Results sort by each representative follow's (createdAt, uri) in sortDirection, default desc, with an opaque direction-bound cursor. Resolve the latest indexed target at each URI and hydrate only this page. Preserve the target URI and follow record when the target is missing or unsupported. Operational failures return errors, not partial pages. Authentication is not required.", + "description": "Lists entities followed by an actor DID. No authentication is required; duplicate actor–entity relationships use the earliest createdAt, with URI ascending on ties. Only returned-page targets are hydrated and resolved to the latest indexed record by URI; missing or unsupported targets retain their URI and follow with entity null, while lookup failures return errors rather than partial pages.", "parameters": { "type": "params", - "required": ["actor"], + "required": [ + "actor" + ], "properties": { - "actor": { "type": "string", "format": "did", "description": "DID of the actor whose outgoing entity follows are listed." }, - "sortDirection": { "type": "string", "enum": ["asc", "desc"], "description": "Sort direction for representative follow (createdAt, uri); default desc." }, - "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Maximum relationships in the page; default 25." }, - "cursor": { "type": "string", "description": "Opaque cursor bound to sortDirection; keep all other query parameters unchanged." } + "actor": { + "type": "string", + "format": "did", + "description": "DID whose outgoing entity follows are listed." + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "Sorts by representative follow createdAt and URI; defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum relationships per page; defaults to 25." + }, + "cursor": { + "type": "string", + "description": "Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages." + } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, - "errors": [{ "name": "InvalidRequest", "description": "The actor DID, sort direction, page bound, cursor, or query parameters are invalid." }] + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The actor DID or another query parameter is invalid." + } + ] }, "entityFollowingItem": { "type": "object", "description": "An entity-follow target and its relationship record. Resolve the latest indexed target by URI; retain the URI and follow when the target is missing or unsupported.", - "required": ["uri", "entity", "follow"], - "nullable": ["entity"], + "required": [ + "uri", + "entity", + "follow" + ], + "nullable": [ + "entity" + ], "properties": { - "uri": { "type": "string", "format": "at-uri", "description": "AT-URI of the followed entity, retained even when the target cannot be resolved." }, + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the followed entity, retained even when the target cannot be resolved." + }, "entity": { "type": "union", "refs": [ @@ -34,15 +76,29 @@ ], "description": "Resolved ActivityView, CollectionView, or author-hydrated FeatureView, or null when missing or unsupported." }, - "follow": { "type": "ref", "ref": "app.certified.graph.getEntityFollow#entityFollowRecordView" } + "follow": { + "type": "ref", + "ref": "app.certified.graph.getEntityFollow#entityFollowRecordView" + } } }, "output": { "type": "object", - "required": ["entities"], + "required": [ + "entities" + ], "properties": { - "entities": { "type": "array", "items": { "type": "ref", "ref": "#entityFollowingItem" } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when there are no more relationships." } + "entities": { + "type": "array", + "items": { + "type": "ref", + "ref": "#entityFollowingItem" + } + }, + "cursor": { + "type": "string", + "description": "Next-page cursor; omitted when no more results are available." + } } } } diff --git a/docs/sources/lexicons/app.certified.graph.listRecentFollows.json b/docs/sources/lexicons/app.certified.graph.listRecentFollows.json index 963b50a..d74c526 100644 --- a/docs/sources/lexicons/app.certified.graph.listRecentFollows.json +++ b/docs/sources/lexicons/app.certified.graph.listRecentFollows.json @@ -4,67 +4,102 @@ "defs": { "main": { "type": "query", - "description": "Beta query for raw account- and entity-follow records published by anyone, ordered by descending (createdAt, uri). Duplicate records are preserved and before is an exclusive upper bound on publisher-declared createdAt; these behaviors are provisional pending consumer confirmation. This is a recent-activity feed, not a lossless ingestion or backfill API; late-indexed records and concurrent changes may be missed across pages. Authentication is not required.", + "description": "Beta query for raw account- and entity-follow records from any publisher. No authentication is required; duplicates are preserved, and before is an exclusive bound on publisher-declared createdAt. Pages are not a snapshot, so late indexing or concurrent changes may cause records to be missed.", "parameters": { "type": "params", "properties": { "before": { "type": "string", "format": "datetime", - "description": "Exclusive upper bound on the follow record's publisher-declared createdAt, not indexedAt; omit to start with the newest indexed records. Keep unchanged across cursor pages." + "description": "Exclusive upper bound on publisher-declared createdAt, not indexedAt; omit to start with the newest records." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum follow records per page; default 25." + "description": "Maximum records per page; defaults to 25." }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; keep before unchanged. Pagination has no snapshot guarantee." + "description": "Opaque next-page cursor; keep before unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ { "name": "InvalidRequest", - "description": "The before datetime, limit, cursor, or query parameter is invalid; use a valid datetime, limit of 1–100, or a cursor from this query." + "description": "A datetime, limit, cursor, or query parameter is invalid." } ] }, "recentFollowView": { "type": "object", - "description": "Raw indexed account- or entity-follow record with its publishing repository identity. The unchanged record's $type distinguishes the follow collection.", - "required": ["uri", "cid", "indexedAt", "did", "record"], - "nullable": ["indexedAt"], + "description": "Raw indexed account- or entity-follow record with its publisher DID.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri", "description": "AT-URI of this follow record." }, - "cid": { "type": "string", "format": "cid", "description": "CID of the indexed follow record version." }, - "indexedAt": { "type": "string", "format": "datetime", "description": "Time this follow record was indexed, not the ordering field." }, - "did": { "type": "string", "format": "did", "description": "DID of the repository that published this follow record." }, + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the follow record." + }, + "cid": { + "type": "string", + "format": "cid", + "description": "CID of this indexed record version." + }, + "indexedAt": { + "type": "string", + "format": "datetime", + "description": "Indexing time, or null if unavailable; ordering uses createdAt." + }, + "did": { + "type": "string", + "format": "did", + "description": "DID of the repository that published the follow." + }, "record": { "type": "union", - "refs": ["app.certified.graph.follow", "app.certified.graph.entityFollow"], - "description": "Unmodified account- or entity-follow record; its $type identifies the collection." + "refs": [ + "app.certified.graph.follow", + "app.certified.graph.entityFollow" + ], + "description": "Unmodified follow record; $type identifies its collection." } } }, "output": { "type": "object", - "required": ["follows"], + "required": [ + "follows" + ], "properties": { "follows": { "type": "array", - "items": { "type": "ref", "ref": "#recentFollowView" }, - "description": "Raw follow records from both collections, sorted by createdAt descending then URI descending, including duplicates." + "items": { + "type": "ref", + "ref": "#recentFollowView" + }, + "description": "Raw records ordered by createdAt descending, then URI descending; duplicates are included." }, "cursor": { "type": "string", - "description": "Opaque cursor for the next page; omitted when no further page exists." + "description": "Next-page cursor; omitted when no more records are available." } } } diff --git a/docs/sources/lexicons/app.certified.link.evm.json b/docs/sources/lexicons/app.certified.link.evm.json new file mode 100644 index 0000000..30c4435 --- /dev/null +++ b/docs/sources/lexicons/app.certified.link.evm.json @@ -0,0 +1,108 @@ +{ + "lexicon": 1, + "id": "app.certified.link.evm", + "defs": { + "main": { + "type": "record", + "description": "A verifiable link between an ATProto DID and an EVM wallet address, proven via a cryptographic signature. Currently supports EOA wallets via EIP-712 typed data signatures; the proof field is an open union to allow future signature methods.", + "key": "any", + "record": { + "type": "object", + "required": [ + "address", + "proof", + "createdAt" + ], + "properties": { + "address": { + "type": "string", + "description": "EVM wallet address (0x-prefixed, with EIP-55 checksum recommended).", + "minLength": 42, + "maxLength": 42 + }, + "proof": { + "type": "union", + "refs": [ + "#eip712Proof" + ], + "description": "Cryptographic proof of wallet ownership. The union is open to allow future proof methods (e.g. ERC-1271, ERC-6492). Each variant bundles its signature with the corresponding message format." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content. The EIP-712 `proof` field proves wallet consent (orthogonal trust statement); `signatures` proves record provenance (e.g. that a platform UI minted this record)." + } + } + } + }, + "eip712Proof": { + "type": "object", + "description": "EOA wallet ownership proof via EIP-712 typed data signature. Contains both the structured message that was signed and the resulting signature.", + "required": [ + "signature", + "message" + ], + "properties": { + "signature": { + "type": "string", + "description": "ECDSA signature over the EIP-712 hash (hex-encoded with 0x prefix, 64 or 65 bytes).", + "minLength": 130, + "maxLength": 132 + }, + "message": { + "type": "ref", + "ref": "#eip712Message", + "description": "The EIP-712 structured message fields that were signed." + } + } + }, + "eip712Message": { + "type": "object", + "description": "The EIP-712 typed data message that was signed by the wallet. Contains the fields binding an ATProto DID to an EVM address on a specific chain.", + "required": [ + "did", + "evmAddress", + "chainId", + "timestamp", + "nonce" + ], + "properties": { + "did": { + "type": "string", + "format": "did", + "description": "The ATProto DID being linked to the EVM address.", + "maxLength": 256 + }, + "evmAddress": { + "type": "string", + "description": "The EVM wallet address (must match the top-level address field).", + "minLength": 42, + "maxLength": 42 + }, + "chainId": { + "type": "string", + "description": "EVM chain ID as string (bigint serialized). Identifies which chain was used for signing; for EOA wallets the identity link applies across all EVM-compatible chains.", + "minLength": 1, + "maxLength": 78 + }, + "timestamp": { + "type": "string", + "description": "Unix timestamp when the attestation was created (bigint serialized).", + "minLength": 1, + "maxLength": 78 + }, + "nonce": { + "type": "string", + "description": "Replay-protection nonce (bigint serialized).", + "minLength": 1, + "maxLength": 78 + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.link.getEvmLink.json b/docs/sources/lexicons/app.certified.link.getEvmLink.json new file mode 100644 index 0000000..b8c0382 --- /dev/null +++ b/docs/sources/lexicons/app.certified.link.getEvmLink.json @@ -0,0 +1,94 @@ +{ + "lexicon": 1, + "id": "app.certified.link.getEvmLink", + "defs": { + "main": { + "type": "query", + "description": "Looks up an indexed EVM-link record by exact AT-URI and hydrates its actor. Authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of an app.certified.link.evm record with a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not an app.certified.link.evm record AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed EVM-link record exists at this AT-URI." + } + ] + }, + "output": { + "type": "object", + "required": [ + "evmLink" + ], + "properties": { + "evmLink": { + "type": "ref", + "ref": "#evmLinkView" + } + } + }, + "evmLinkView": { + "type": "object", + "description": "Indexed EVM-link record with its original address and proof, and the linked actor's hydrated Certified records.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "actor", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime", + "description": "Index timestamp, or null when the indexed row has no timestamp." + }, + "did": { + "type": "string", + "format": "did" + }, + "actor": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "app.certified.link.evm" + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.link.listEvmLinks.json b/docs/sources/lexicons/app.certified.link.listEvmLinks.json new file mode 100644 index 0000000..d7e3e0c --- /dev/null +++ b/docs/sources/lexicons/app.certified.link.listEvmLinks.json @@ -0,0 +1,86 @@ +{ + "lexicon": 1, + "id": "app.certified.link.listEvmLinks", + "defs": { + "main": { + "type": "query", + "description": "Lists indexed EVM-link records, optionally filtered by actor DID and wallet address. Authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "actors": { + "type": "array", + "maxLength": 100, + "description": "Actor DIDs that own matching records; values within this filter use OR.", + "items": { + "type": "string", + "format": "did" + } + }, + "addresses": { + "type": "array", + "maxLength": 100, + "description": "EVM wallet addresses matched case-insensitively; values within this filter use OR.", + "items": { + "type": "string", + "minLength": 42, + "maxLength": 42 + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Order by the record's createdAt timestamp and then AT-URI." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum number of links to return." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor from a previous page, bound to sortDirection." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "An actor DID, EVM address, page bound, cursor, repeated scalar, or query parameter is invalid." + } + ] + }, + "output": { + "type": "object", + "required": [ + "evmLinks" + ], + "properties": { + "evmLinks": { + "type": "array", + "items": { + "type": "ref", + "ref": "app.certified.link.getEvmLink#evmLinkView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.location.getLocation.json b/docs/sources/lexicons/app.certified.location.getLocation.json index 6655b9f..189b3ce 100644 --- a/docs/sources/lexicons/app.certified.location.getLocation.json +++ b/docs/sources/lexicons/app.certified.location.getLocation.json @@ -4,33 +4,84 @@ "defs": { "main": { "type": "query", - "description": "Public lookup of one indexed location record by full AT-URI. Authentication is not required.", + "description": "Returns one indexed location by AT-URI; authentication is not required.", "parameters": { "type": "params", - "required": ["uri"], + "required": [ + "uri" + ], "properties": { - "uri": { "type": "string", "format": "at-uri", "description": "Full record AT-URI using a DID authority." } + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full location AT-URI with a DID authority; lookup matches the URI, not a CID." + } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, - "errors": [{ "name": "RecordNotFound", "description": "No indexed location exists at this AT-URI." }] + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "RecordNotFound", + "description": "No indexed location exists at this AT-URI." + } + ] }, "output": { "type": "object", - "required": ["location"], - "properties": { "location": { "type": "ref", "ref": "#locationView" } } + "required": [ + "location" + ], + "properties": { + "location": { + "type": "ref", + "ref": "#locationView", + "description": "The matching indexed location." + } + } }, "locationView": { "type": "object", - "description": "Location record with full indexed metadata, original payload, and hydrated author.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record"], + "description": "Indexed location with its original record and hydrated author.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "org.hypercerts.api.defs#actorView" }, - "record": { "type": "ref", "ref": "app.certified.location" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView", + "description": "Author DID with indexed profile and organization views; missing records are null, while lookup failures fail the query." + }, + "record": { + "type": "ref", + "ref": "app.certified.location" + } } } } diff --git a/docs/sources/lexicons/app.certified.location.json b/docs/sources/lexicons/app.certified.location.json new file mode 100644 index 0000000..1128571 --- /dev/null +++ b/docs/sources/lexicons/app.certified.location.json @@ -0,0 +1,96 @@ +{ + "lexicon": 1, + "id": "app.certified.location", + "defs": { + "main": { + "type": "record", + "description": "A location reference", + "key": "tid", + "record": { + "type": "object", + "required": [ + "lpVersion", + "srs", + "locationType", + "location", + "createdAt" + ], + "properties": { + "lpVersion": { + "type": "string", + "description": "The version of the Location Protocol", + "maxLength": 10 + }, + "srs": { + "type": "string", + "format": "uri", + "description": "The Spatial Reference System URI (e.g., http://www.opengis.net/def/crs/OGC/1.3/CRS84) that defines the coordinate system.", + "maxLength": 100 + }, + "locationType": { + "type": "string", + "description": "An identifier for the format of the location data. Use `country-code` for uppercase ISO 3166-1 alpha-2 codes in `location.string` (e.g. `CH`). Although this lexicon requires `srs`, it has no meaning for this type and consumers must ignore it. Use `geojson-point` for a single GeoJSON Point; use `geojson` as the catch-all for any other GeoJSON geometry (Polygon, MultiPolygon, FeatureCollection, etc.) — the inner payload's own GeoJSON `type` field carries the specifics. Values beyond the known set are permitted; see the Location Protocol spec for the canonical registry: https://spec.decentralizedgeo.org/specification/location-types/#location-type-registry", + "knownValues": [ + "coordinate-decimal", + "geojson-point", + "geojson", + "h3", + "geohash", + "wkt", + "address", + "country-code", + "scaledCoordinates" + ], + "maxLength": 20 + }, + "location": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallBlob", + "#string" + ], + "description": "The location of where the work was performed as a URI, blob, or inline string." + }, + "name": { + "type": "string", + "description": "Human-readable name for this location (e.g. 'Golden Gate Park', 'San Francisco Bay Area')", + "maxLength": 1000, + "maxGraphemes": 100 + }, + "description": { + "type": "string", + "description": "Additional context about this location, such as its significance to the work or specific boundaries", + "maxLength": 2000, + "maxGraphemes": 500 + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + }, + "string": { + "type": "object", + "required": [ + "string" + ], + "description": "A location represented as a string, e.g. coordinates or a small GeoJSON string.", + "properties": { + "string": { + "type": "string", + "description": "The location string value", + "maxLength": 10000, + "maxGraphemes": 1000 + } + } + } + } +} diff --git a/docs/sources/lexicons/app.certified.location.listLocations.json b/docs/sources/lexicons/app.certified.location.listLocations.json index 25d9b23..dd7ec15 100644 --- a/docs/sources/lexicons/app.certified.location.listLocations.json +++ b/docs/sources/lexicons/app.certified.location.listLocations.json @@ -4,27 +4,89 @@ "defs": { "main": { "type": "query", - "description": "Public listing of indexed location records. Authentication is not required. Array filters use repeated unbracketed keys, maximum 100 supplied values before deduplication; omission is unrestricted. The handler applies limit=25 and sortDirection=desc when omitted; limit is 1..100. Results sort by a valid zoned createdAt, otherwise indexedAt (or the stored row creation time when indexedAt is absent), then URI, both in the requested direction; the opaque direction-bound cursor captures that database timestamp at UTC microsecond precision, with no snapshot guarantee. Keep other parameters unchanged between pages. Unknown parameters and repeated scalar keys are rejected.", + "description": "Lists indexed location records; authentication is not required. Array filters use repeated unbracketed query keys; filters combine with AND, values within each array with OR, and omitted filters are unrestricted.", "parameters": { "type": "params", "properties": { - "authors": { "type": "array", "maxLength": 100, "items": { "type": "string", "format": "did" } }, - "uris": { "type": "array", "description": "Full record AT-URIs using DID authorities.", "maxLength": 100, "items": { "type": "string", "format": "at-uri" } }, - "locationTypes": { "type": "array", "description": "Exact open-string match against locationType; empty strings are values, not sentinels.", "maxLength": 100, "items": { "type": "string", "maxLength": 20 } }, - "limit": { "type": "integer", "description": "Page size; handler default is 25.", "minimum": 1, "maximum": 100 }, - "cursor": { "type": "string", "description": "Opaque versioned keyset cursor bound to sortDirection." }, - "sortDirection": { "type": "string", "description": "Sort direction; handler default is desc.", "enum": ["asc", "desc"] } + "authors": { + "type": "array", + "description": "Filter by author DID.", + "maxLength": 100, + "items": { + "type": "string", + "format": "did" + } + }, + "uris": { + "type": "array", + "description": "Filter by full location AT-URI with a DID authority; matching uses the URI, not a CID.", + "maxLength": 100, + "items": { + "type": "string", + "format": "at-uri" + } + }, + "locationTypes": { + "type": "array", + "description": "Match exact strings against locationType; empty strings are matched literally.", + "maxLength": 100, + "items": { + "type": "string", + "maxLength": 20 + } + }, + "limit": { + "type": "integer", + "description": "Page size; defaults to 25.", + "minimum": 1, + "maximum": 100 + }, + "cursor": { + "type": "string", + "description": "Opaque keyset cursor; keep all other parameters unchanged between pages. The cursor is bound to sortDirection, and pagination is not snapshot-isolated." + }, + "sortDirection": { + "type": "string", + "description": "Sort by a valid zoned createdAt; otherwise indexedAt or stored row creation time, then URI, all in this direction. Defaults to desc.", + "enum": [ + "asc", + "desc" + ] + } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, - "errors": [{ "name": "InvalidRequest", "description": "A query parameter is invalid, repeated where scalar, or unknown." }] + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A query parameter is invalid, unknown, or repeated where only one value is allowed." + } + ] }, "output": { "type": "object", - "required": ["locations"], + "required": [ + "locations" + ], "properties": { - "locations": { "type": "array", "items": { "type": "ref", "ref": "app.certified.location.getLocation#locationView" } }, - "cursor": { "type": "string"} + "locations": { + "type": "array", + "description": "Matching location records in page order.", + "items": { + "type": "ref", + "ref": "app.certified.location.getLocation#locationView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } } } } diff --git a/docs/sources/lexicons/app.certified.signature.defs.json b/docs/sources/lexicons/app.certified.signature.defs.json new file mode 100644 index 0000000..103a5c2 --- /dev/null +++ b/docs/sources/lexicons/app.certified.signature.defs.json @@ -0,0 +1,37 @@ +{ + "lexicon": 1, + "id": "app.certified.signature.defs", + "description": "Common type definitions for cryptographic signatures attached to records, per the ATProtocol Attestation Specification.", + "defs": { + "list": { + "type": "array", + "description": "Reusable array of cryptographic signatures attesting to a record's content. Open union of inline signatures and strong references to remote attestation proof records.", + "items": { + "type": "union", + "refs": [ + "app.certified.signature.defs#inline", + "com.atproto.repo.strongRef" + ] + } + }, + "inline": { + "type": "object", + "description": "Inline attestation signature embedded directly in a record. Conforms to the ATProtocol Attestation Specification: the signed input is the 36-byte CIDv1 (dag-cbor + SHA-256) of the record with the `signatures` field removed and a temporary `$sig` metadata object (containing `$type` and the housing repository DID) inserted before canonical DAG-CBOR encoding. ECDSA with the low-S variant per BIP-0062 is required; the curve (P-256 or K-256) is determined by the multicodec prefix of the verification method's `publicKeyMultibase`.", + "required": [ + "signature", + "key" + ], + "properties": { + "signature": { + "type": "bytes", + "description": "ECDSA signature bytes (raw r,s) over the 36-byte CID of the record. Low-S variant per BIP-0062 is mandatory." + }, + "key": { + "type": "string", + "description": "Full DID verification method reference (format: did:{method}:{identifier}#{fragment}). Identifies the signer and the specific key used; the key's multicodec prefix determines the signing curve.", + "maxLength": 512 + } + } + } + } +} diff --git a/docs/sources/lexicons/com.atproto.repo.strongRef.json b/docs/sources/lexicons/com.atproto.repo.strongRef.json new file mode 100644 index 0000000..8a57a2f --- /dev/null +++ b/docs/sources/lexicons/com.atproto.repo.strongRef.json @@ -0,0 +1,24 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.strongRef", + "description": "A URI with a content-hash fingerprint.", + "defs": { + "main": { + "type": "object", + "required": [ + "uri", + "cid" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.api.defs.json b/docs/sources/lexicons/org.hypercerts.api.defs.json new file mode 100644 index 0000000..c365710 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.api.defs.json @@ -0,0 +1,147 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.api.defs", + "description": "Shared views for Hypercerts API read models.", + "defs": { + "profileView": { + "type": "object", + "description": "Profile record view preserving the full indexed record; indexedAt may be null when the index has no timestamp.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.actor.profile" + } + } + }, + "organizationView": { + "type": "object", + "description": "Raw organization sidecar record view preserving the full indexed record; indexedAt may be null when the index has no timestamp.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.actor.organization" + } + } + }, + "actorView": { + "type": "object", + "description": "Actor DID with nullable profile and raw organization sidecar views.", + "required": [ + "did", + "profile", + "organization" + ], + "nullable": [ + "profile", + "organization" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "profile": { + "type": "ref", + "ref": "#profileView" + }, + "organization": { + "type": "ref", + "ref": "#organizationView" + } + } + }, + "acknowledgementView": { + "type": "object", + "description": "Acknowledgement record with its publisher actor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.context.acknowledgement" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.activity.json b/docs/sources/lexicons/org.hypercerts.claim.activity.json new file mode 100644 index 0000000..e9879c5 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.activity.json @@ -0,0 +1,182 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.activity", + "defs": { + "main": { + "type": "record", + "description": "A hypercert record tracking impact work.", + "key": "any", + "record": { + "type": "object", + "required": [ + "title", + "shortDescription", + "createdAt" + ], + "properties": { + "title": { + "type": "string", + "description": "Display title summarizing the impact work (e.g. 'Reforestation in Amazon Basin 2024')", + "maxLength": 256 + }, + "shortDescription": { + "type": "string", + "description": "Short summary of this activity claim, suitable for previews and list views. Rich text annotations may be provided via `shortDescriptionFacets`.", + "maxLength": 3000, + "maxGraphemes": 300 + }, + "shortDescriptionFacets": { + "type": "array", + "description": "Rich text annotations for `shortDescription` (mentions, URLs, hashtags, etc).", + "items": { + "type": "ref", + "ref": "app.bsky.richtext.facet" + } + }, + "description": { + "type": "union", + "refs": [ + "org.hypercerts.defs#descriptionString", + "pub.leaflet.pages.linearDocument", + "com.atproto.repo.strongRef" + ], + "description": "Long-form description of the activity. An inline string for plain text or markdown, a Leaflet linear document for rich-text content, or a strong reference to an external description record." + }, + "image": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallImage" + ], + "description": "The hypercert visual representation as a URI or image blob." + }, + "contributors": { + "type": "array", + "description": "An array of contributor objects, each containing contributor information, weight, and contribution details.", + "items": { + "type": "ref", + "ref": "#contributor" + } + }, + "workScope": { + "type": "union", + "refs": [ + "org.hypercerts.workscope.cel", + "#workScopeString" + ], + "description": "Work scope definition. A CEL expression for structured, machine-evaluable scopes or a free-form string for simple and legacy scopes." + }, + "startDate": { + "type": "string", + "format": "datetime", + "description": "When the work began" + }, + "endDate": { + "type": "string", + "format": "datetime", + "description": "When the work ended" + }, + "locations": { + "type": "array", + "description": "An array of strong references to the location where activity was performed. The record referenced must conform with the lexicon app.certified.location.", + "maxLength": 1000, + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + } + }, + "rights": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "A strong reference to the rights that this hypercert has. The record referenced must conform with the lexicon org.hypercerts.claim.rights." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + }, + "contributor": { + "type": "object", + "description": "A contributor to the activity, with identity, weight, and contribution details.", + "required": [ + "contributorIdentity" + ], + "properties": { + "contributorIdentity": { + "type": "union", + "refs": [ + "#contributorIdentity", + "com.atproto.repo.strongRef" + ], + "description": "Inline contributor identity object with an identity string (DID or identifier) via org.hypercerts.claim.activity#contributorIdentity, or a strong reference to a contributor information record. The record referenced must conform with the lexicon org.hypercerts.claim.contributorInformation." + }, + "contributionWeight": { + "type": "string", + "description": "The relative weight/importance of this contribution (stored as a string to avoid float precision issues). Must be a positive numeric value. Weights do not need to sum to a specific total; normalization can be performed by the consuming application as needed.", + "maxLength": 100 + }, + "contributionDetails": { + "type": "union", + "refs": [ + "#contributorRole", + "com.atproto.repo.strongRef" + ], + "description": "Inline contribution role object with a role string via org.hypercerts.claim.activity#contributorRole, or a strong reference to a contribution details record. The record referenced must conform with the lexicon org.hypercerts.claim.contribution." + } + } + }, + "contributorIdentity": { + "type": "object", + "description": "Contributor information as a string (DID or identifier).", + "required": [ + "identity" + ], + "properties": { + "identity": { + "type": "string", + "description": "The contributor identity string (DID or identifier).", + "maxLength": 1000, + "maxGraphemes": 100 + } + } + }, + "contributorRole": { + "type": "object", + "description": "Contribution details as a string.", + "required": [ + "role" + ], + "properties": { + "role": { + "type": "string", + "description": "The contribution role or details.", + "maxLength": 1000, + "maxGraphemes": 100 + } + } + }, + "workScopeString": { + "type": "object", + "description": "A free-form string describing the work scope for simple or legacy scopes.", + "required": [ + "scope" + ], + "properties": { + "scope": { + "type": "string", + "description": "The work scope description string.", + "maxLength": 10000, + "maxGraphemes": 1000 + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.contribution.json b/docs/sources/lexicons/org.hypercerts.claim.contribution.json new file mode 100644 index 0000000..a1ce481 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.contribution.json @@ -0,0 +1,50 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.contribution", + "defs": { + "main": { + "type": "record", + "description": "Details about a specific contribution including role, description, and timeframe.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "createdAt" + ], + "properties": { + "role": { + "type": "string", + "description": "Role or title of the contributor.", + "maxLength": 100 + }, + "contributionDescription": { + "type": "string", + "description": "Description of what the contribution concretely involved.", + "maxLength": 10000, + "maxGraphemes": 1000 + }, + "startDate": { + "type": "string", + "format": "datetime", + "description": "When this contribution started. Should fall within the parent hypercert's timeframe." + }, + "endDate": { + "type": "string", + "format": "datetime", + "description": "When this contribution finished. Should fall within the parent hypercert's timeframe." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.contributorInformation.json b/docs/sources/lexicons/org.hypercerts.claim.contributorInformation.json new file mode 100644 index 0000000..fee0d10 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.contributorInformation.json @@ -0,0 +1,47 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.contributorInformation", + "defs": { + "main": { + "type": "record", + "description": "Contributor information including identifier, display name, and image.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "createdAt" + ], + "properties": { + "identifier": { + "type": "string", + "description": "DID (did:plc:...) or URI to a social profile of the contributor.", + "maxLength": 2048 + }, + "displayName": { + "type": "string", + "description": "Human-readable name for the contributor as it should appear in UI.", + "maxLength": 100 + }, + "image": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallImage" + ], + "description": "The contributor visual representation as a URI or image blob." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.getActivity.json b/docs/sources/lexicons/org.hypercerts.claim.getActivity.json index e61af64..d4a6659 100644 --- a/docs/sources/lexicons/org.hypercerts.claim.getActivity.json +++ b/docs/sources/lexicons/org.hypercerts.claim.getActivity.json @@ -4,94 +4,199 @@ "defs": { "main": { "type": "query", - "description": "Public lookup of one indexed activity by full AT-URI. Authentication is not required.", + "description": "Looks up one indexed activity by full AT-URI. Authentication is not required.", "parameters": { "type": "params", - "required": ["uri"], + "required": [ + "uri" + ], "properties": { "uri": { "type": "string", "format": "at-uri", - "description": "Full AT-URI of the activity record, using a DID authority." + "description": "Full AT-URI of the activity, using a DID authority." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "The URI is not a full activity AT-URI or the request contains an unsupported parameter." }, - { "name": "RecordNotFound", "description": "No indexed activity exists at this AT-URI." }, - { "name": "ActivityQueryFailed", "description": "The activity or required hydration lookup failed; retry after checking the HappyView PostgreSQL service." } + { + "name": "InvalidRequest", + "description": "The URI is invalid or the request includes an unsupported parameter." + }, + { + "name": "RecordNotFound", + "description": "No indexed activity exists at the requested URI." + }, + { + "name": "ActivityQueryFailed", + "description": "The activity lookup or required hydration failed." + } ] }, "output": { "type": "object", - "required": ["activity"], + "required": [ + "activity" + ], "properties": { - "activity": { "type": "ref", "ref": "#activityView" } + "activity": { + "type": "ref", + "ref": "#activityView" + } } }, "activityView": { "type": "object", - "description": "Activity record with metadata, unchanged source payload, hydrated author, and optional contributor projection.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record"], - "nullable": ["indexedAt"], + "description": "Activity record with its original payload, hydrated author, and contributors only when present in the source record.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "org.hypercerts.api.defs#actorView" }, - "record": { "type": "ref", "ref": "org.hypercerts.claim.activity" }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.claim.activity" + }, "contributors": { "type": "array", "description": "Hydrated contributor projection in the same order and with the same multiplicity as record.contributors; omitted only when the source field is absent.", - "items": { "type": "ref", "ref": "#activityContributorView" } + "items": { + "type": "ref", + "ref": "#activityContributorView" + } } } }, "activityContributorView": { "type": "object", - "description": "Original activity contributor fields with exact-version contributor information and nullable actor hydration.", - "required": ["contributorIdentity", "contributorInformation", "actor"], - "nullable": ["contributorInformation", "actor"], + "description": "Source contributor fields with nullable hydrated projections; unavailable referenced information or unresolved identities are null, while lookup failures fail the query.", + "required": [ + "contributorIdentity", + "contributorInformation", + "actor" + ], + "nullable": [ + "contributorInformation", + "actor" + ], "properties": { "contributorIdentity": { "type": "union", - "refs": ["org.hypercerts.claim.activity#contributorIdentity", "com.atproto.repo.strongRef"] + "refs": [ + "org.hypercerts.claim.activity#contributorIdentity", + "com.atproto.repo.strongRef" + ] + }, + "contributionWeight": { + "type": "string", + "maxLength": 100 }, - "contributionWeight": { "type": "string", "maxLength": 100 }, "contributionDetails": { "type": "union", - "refs": ["org.hypercerts.claim.activity#contributorRole", "com.atproto.repo.strongRef"] + "refs": [ + "org.hypercerts.claim.activity#contributorRole", + "com.atproto.repo.strongRef" + ] + }, + "contributorInformation": { + "type": "ref", + "ref": "#contributorInformationView" }, - "contributorInformation": { "type": "ref", "ref": "#contributorInformationView" }, - "actor": { "type": "ref", "ref": "#contributorActorView" } + "actor": { + "type": "ref", + "ref": "#contributorActorView" + } } }, "contributorInformationView": { "type": "object", - "description": "The exact indexed contributor-information version identified by the source strong reference.", - "required": ["uri", "cid", "indexedAt", "did", "record"], - "nullable": ["indexedAt"], + "description": "Contributor-information record matched by the source reference's URI and CID; newer versions are not substituted.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "record": { "type": "ref", "ref": "org.hypercerts.claim.contributorInformation" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.claim.contributorInformation" + } } }, "contributorActorView": { "type": "object", - "description": "Contributor DID with nullable current Certified profile.", - "required": ["did", "profile"], - "nullable": ["profile"], + "description": "Contributor DID with its current profile, if indexed.", + "required": [ + "did", + "profile" + ], + "nullable": [ + "profile" + ], "properties": { - "did": { "type": "string", "format": "did" }, - "profile": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" } + "did": { + "type": "string", + "format": "did" + }, + "profile": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView" + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.claim.getContribution.json b/docs/sources/lexicons/org.hypercerts.claim.getContribution.json new file mode 100644 index 0000000..d9cf709 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.getContribution.json @@ -0,0 +1,97 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.getContribution", + "defs": { + "main": { + "type": "query", + "description": "Looks up one contribution record by exact AT-URI; authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of the contribution record, using a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI must identify a contribution record with a DID authority." + }, + { + "name": "RecordNotFound", + "description": "No indexed contribution record exists at this AT-URI." + }, + { + "name": "ContributionQueryFailed", + "description": "The contribution record or publisher could not be read from PostgreSQL." + } + ] + }, + "output": { + "type": "object", + "required": [ + "contribution" + ], + "properties": { + "contribution": { + "type": "ref", + "ref": "#contributionView" + } + } + }, + "contributionView": { + "type": "object", + "description": "Contribution record view with a hydrated publisher and unchanged source record; the publisher is not necessarily the contributor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.claim.contribution" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.getContributorInformation.json b/docs/sources/lexicons/org.hypercerts.claim.getContributorInformation.json new file mode 100644 index 0000000..66d75a7 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.getContributorInformation.json @@ -0,0 +1,97 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.getContributorInformation", + "defs": { + "main": { + "type": "query", + "description": "Publicly looks up one indexed contributor-information record by exact AT-URI and includes its publisher.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full contributor-information record AT-URI with a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a contributor-information record AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed contributor-information record exists at this AT-URI." + }, + { + "name": "ContributorInformationQueryFailed", + "description": "The record or required publisher hydration lookup failed." + } + ] + }, + "output": { + "type": "object", + "required": [ + "contributorInformation" + ], + "properties": { + "contributorInformation": { + "type": "ref", + "ref": "#contributorInformationView" + } + } + }, + "contributorInformationView": { + "type": "object", + "description": "Contributor-information record with its full source payload and hydrated publishing actor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.claim.contributorInformation" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.getRights.json b/docs/sources/lexicons/org.hypercerts.claim.getRights.json new file mode 100644 index 0000000..bfbb383 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.getRights.json @@ -0,0 +1,93 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.getRights", + "defs": { + "main": { + "type": "query", + "description": "Looks up one indexed rights record by exact DID-authority AT-URI. Authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of a rights record, using a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or does not identify a rights record." + }, + { + "name": "RecordNotFound", + "description": "No indexed rights record exists at this AT-URI." + } + ] + }, + "rightsView": { + "type": "object", + "description": "Rights record with its publisher actor; the full record is preserved and attachments are not expanded.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.claim.rights" + } + } + }, + "output": { + "type": "object", + "required": [ + "rights" + ], + "properties": { + "rights": { + "type": "ref", + "ref": "#rightsView" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.listActivities.json b/docs/sources/lexicons/org.hypercerts.claim.listActivities.json index 4883c45..050f976 100644 --- a/docs/sources/lexicons/org.hypercerts.claim.listActivities.json +++ b/docs/sources/lexicons/org.hypercerts.claim.listActivities.json @@ -4,73 +4,105 @@ "defs": { "main": { "type": "query", - "description": "Lists indexed activities with AND between filters and OR within each array filter. Repeated unbracketed array values accept at most 100 supplied entries; duplicates are removed. Defaults are limit=25 and sortDirection=desc; limit is 1..100. Results sort by (createdAt, uri) and use a direction-bound opaque cursor; keep all other parameters unchanged between pages. Authentication is not required.", + "description": "Lists indexed activities. Filters combine with AND; values within each array filter combine with OR, and duplicate values are ignored. Authentication is not required.", "parameters": { "type": "params", "properties": { "authors": { "type": "array", "maxLength": 100, - "description": "Repository-owner DIDs to include; values use OR.", - "items": { "type": "string", "format": "did" } + "description": "Filter by activity repository-owner DID.", + "items": { + "type": "string", + "format": "did" + } }, "hasOrganizationRecord": { "type": "boolean", - "description": "Whether each activity author has an app.certified.actor.organization/self record. False matches authors without that record regardless of profile presence." + "description": "Filter by whether the activity author has an app.certified.actor.organization/self record, regardless of profile presence." }, "contributors": { "type": "array", "maxLength": 100, - "description": "Contributor DIDs to match against inline identities and exact referenced contributor-information versions; values use OR.", - "items": { "type": "string", "format": "did" } + "description": "Filter by contributor DID, including inline identities and the exact referenced contributor-information version.", + "items": { + "type": "string", + "format": "did" + } }, "involvedActors": { "type": "array", "maxLength": 100, - "description": "DIDs matching either the activity repository owner or a contributor; values use OR, then combine with other filters using AND.", - "items": { "type": "string", "format": "did" } + "description": "Filter by activity repository-owner or contributor DID.", + "items": { + "type": "string", + "format": "did" + } }, "uris": { "type": "array", "maxLength": 100, - "description": "Activity AT-URIs for bounded batch hydration; input order is not preserved.", - "items": { "type": "string", "format": "at-uri" } + "description": "Filter by exact activity AT-URI; results follow the selected sort order, not input order.", + "items": { + "type": "string", + "format": "at-uri" + } }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort direction for (createdAt, uri); default desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sort by activity createdAt, then URI; defaults to desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum activities in this page; default 25." + "description": "Maximum activities per page; defaults to 25." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor bound to sortDirection." + "description": "Opaque cursor for the next page, bound to sortDirection. Keep all other query parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A filter, organization-record flag, page bound, cursor, repeated scalar, or query parameter is invalid." }, - { "name": "ActivityQueryFailed", "description": "The activity query or required hydration lookup failed; retry after checking the HappyView PostgreSQL service." } + { + "name": "InvalidRequest", + "description": "A filter, sort direction, page limit, cursor, or query parameter is invalid." + }, + { + "name": "ActivityQueryFailed", + "description": "The activity query or required hydration failed." + } ] }, "output": { "type": "object", - "required": ["activities"], + "required": [ + "activities" + ], "properties": { "activities": { "type": "array", - "items": { "type": "ref", "ref": "org.hypercerts.claim.getActivity#activityView" } + "items": { + "type": "ref", + "ref": "org.hypercerts.claim.getActivity#activityView" + } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when pagination is complete." } + "cursor": { + "type": "string", + "description": "Next-page cursor; absent when pagination is complete." + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.claim.listContributions.json b/docs/sources/lexicons/org.hypercerts.claim.listContributions.json new file mode 100644 index 0000000..ab679dc --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.listContributions.json @@ -0,0 +1,80 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.listContributions", + "defs": { + "main": { + "type": "query", + "description": "Lists contribution records by publisher DID, sorted by createdAt and URI; authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Repeated unbracketed publisher DIDs are ORed; use at most 100 values.", + "items": { + "type": "string", + "format": "did" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Sort direction for createdAt and URI; defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum records in a page from 1 to 100; defaults to 25." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor bound to sortDirection; keep filters unchanged between pages." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "An author DID, sort direction, page bound, or cursor is invalid." + }, + { + "name": "ContributionQueryFailed", + "description": "The contribution records or publishers could not be read from PostgreSQL." + } + ] + }, + "output": { + "type": "object", + "required": [ + "contributions" + ], + "properties": { + "contributions": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.claim.getContribution#contributionView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted after the last page." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.listContributorInformation.json b/docs/sources/lexicons/org.hypercerts.claim.listContributorInformation.json new file mode 100644 index 0000000..92673f6 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.listContributorInformation.json @@ -0,0 +1,78 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.listContributorInformation", + "defs": { + "main": { + "type": "query", + "description": "Publicly lists indexed contributor-information records with an optional publisher-DID filter and stable createdAt ordering.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Publisher repository DIDs; values use OR.", + "items": { + "type": "string", + "format": "did" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "Direction for (createdAt, uri); defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum records in the page; defaults to 25." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor bound to sortDirection; keep other parameters unchanged between pages." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "An author DID, sort direction, page bound, cursor, repeated scalar, or query parameter is invalid." + }, + { + "name": "ContributorInformationQueryFailed", + "description": "The record query or required publisher hydration lookup failed." + } + ] + }, + "output": { + "type": "object", + "required": [ + "contributorInformation" + ], + "properties": { + "contributorInformation": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.claim.getContributorInformation#contributorInformationView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.listRights.json b/docs/sources/lexicons/org.hypercerts.claim.listRights.json new file mode 100644 index 0000000..d9b47d2 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.listRights.json @@ -0,0 +1,74 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.listRights", + "defs": { + "main": { + "type": "query", + "description": "Lists indexed rights records with an optional publisher-DID filter. Authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Publisher DIDs; repeat the unbracketed key for multiple authors. Values use OR.", + "items": { + "type": "string", + "format": "did" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "Direction for the (createdAt, uri) order; defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum records in the page; defaults to 25." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor from a previous page; keep the sort direction unchanged." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "An author, sort direction, page bound, cursor, repeated scalar, or query parameter is invalid." + } + ] + }, + "output": { + "type": "object", + "required": [ + "rights" + ], + "properties": { + "rights": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.claim.getRights#rightsView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.rights.json b/docs/sources/lexicons/org.hypercerts.claim.rights.json new file mode 100644 index 0000000..30f3c8f --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.claim.rights.json @@ -0,0 +1,56 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.claim.rights", + "defs": { + "main": { + "type": "record", + "description": "Describes the rights that a contributor and/or an owner has, such as whether the hypercert can be sold, transferred, and under what conditions.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "rightsName", + "rightsType", + "rightsDescription", + "createdAt" + ], + "properties": { + "rightsName": { + "type": "string", + "description": "Human-readable name for these rights (e.g. 'All Rights Reserved', 'CC BY-SA 4.0')", + "maxLength": 100 + }, + "rightsType": { + "type": "string", + "description": "Short identifier code for this rights type (e.g. 'ARR', 'CC-BY-SA') to facilitate filtering and search", + "maxLength": 10 + }, + "rightsDescription": { + "type": "string", + "description": "Detailed explanation of the rights holders' permissions, restrictions, and conditions", + "maxLength": 10000, + "maxGraphemes": 1000 + }, + "attachment": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallBlob" + ], + "description": "An attachment to define the rights further, e.g. a legal document." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.claim.searchActivities.json b/docs/sources/lexicons/org.hypercerts.claim.searchActivities.json index 8951e8d..214a975 100644 --- a/docs/sources/lexicons/org.hypercerts.claim.searchActivities.json +++ b/docs/sources/lexicons/org.hypercerts.claim.searchActivities.json @@ -4,78 +4,112 @@ "defs": { "main": { "type": "query", - "description": "Searches indexed activities by a case-insensitive literal substring in title or shortDescription. Surrounding whitespace is trimmed; blank search imposes no text restriction. Other filters combine with search using AND and retain their listing semantics. Repeated unbracketed array values accept at most 100 supplied entries; duplicates are removed. Defaults are limit=25 and sortDirection=desc; limit is 1..100. Results sort by (createdAt, uri) and use a direction-bound opaque cursor. Authentication is not required.", + "description": "Searches indexed activities by a case-insensitive literal substring of title or shortDescription. Search and other filters combine with AND; values within each array filter combine with OR, and duplicate values are ignored. Authentication is not required.", "parameters": { "type": "params", - "required": ["search"], + "required": [ + "search" + ], "properties": { "search": { "type": "string", - "description": "Complete text substring to find in title or shortDescription; literal %, _, and other characters are not wildcards." + "description": "Literal, case-insensitive substring of title or shortDescription; surrounding whitespace is trimmed, and blank input applies no text filter." }, "authors": { "type": "array", "maxLength": 100, - "description": "Repository-owner DIDs to include; values use OR.", - "items": { "type": "string", "format": "did" } + "description": "Filter by activity repository-owner DID.", + "items": { + "type": "string", + "format": "did" + } }, "hasOrganizationRecord": { "type": "boolean", - "description": "Whether each activity author has an app.certified.actor.organization/self record. False matches authors without that record regardless of profile presence." + "description": "Filter by whether the activity author has an app.certified.actor.organization/self record, regardless of profile presence." }, "contributors": { "type": "array", "maxLength": 100, - "description": "Contributor DIDs to match against inline identities and exact referenced contributor-information versions; values use OR.", - "items": { "type": "string", "format": "did" } + "description": "Filter by contributor DID, including inline identities and the exact referenced contributor-information version.", + "items": { + "type": "string", + "format": "did" + } }, "involvedActors": { "type": "array", "maxLength": 100, - "description": "DIDs matching either the activity repository owner or a contributor; values use OR, then combine with other filters using AND.", - "items": { "type": "string", "format": "did" } + "description": "Filter by activity repository-owner or contributor DID.", + "items": { + "type": "string", + "format": "did" + } }, "uris": { "type": "array", "maxLength": 100, - "description": "Activity AT-URIs for bounded batch hydration; input order is not preserved.", - "items": { "type": "string", "format": "at-uri" } + "description": "Filter by exact activity AT-URI; results follow the selected sort order, not input order.", + "items": { + "type": "string", + "format": "at-uri" + } }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort direction for (createdAt, uri); default desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sort by activity createdAt, then URI; defaults to desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum activities in this page; default 25." + "description": "Maximum activities per page; defaults to 25." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor bound to sortDirection." + "description": "Opaque cursor for the next page, bound to sortDirection. Keep all other query parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A filter, organization-record flag, page bound, cursor, repeated scalar, or query parameter is invalid." }, - { "name": "ActivityQueryFailed", "description": "The activity query or required hydration lookup failed; retry after checking the HappyView PostgreSQL service." } + { + "name": "InvalidRequest", + "description": "A filter, sort direction, page limit, cursor, or query parameter is invalid." + }, + { + "name": "ActivityQueryFailed", + "description": "The activity query or required hydration failed." + } ] }, "output": { "type": "object", - "required": ["activities"], + "required": [ + "activities" + ], "properties": { "activities": { "type": "array", - "items": { "type": "ref", "ref": "org.hypercerts.claim.getActivity#activityView" } + "items": { + "type": "ref", + "ref": "org.hypercerts.claim.getActivity#activityView" + } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when pagination is complete." } + "cursor": { + "type": "string", + "description": "Next-page cursor; absent when pagination is complete." + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.collection.getCollection.json b/docs/sources/lexicons/org.hypercerts.collection.getCollection.json index 85d44db..56ff188 100644 --- a/docs/sources/lexicons/org.hypercerts.collection.getCollection.json +++ b/docs/sources/lexicons/org.hypercerts.collection.getCollection.json @@ -7,7 +7,9 @@ "description": "Looks up one indexed collection by exact AT-URI. Hydrates its author and exact-version location and tag sibling projections; the original collection record remains unchanged. Missing related target versions are null without hiding the collection. Authentication is not required.", "parameters": { "type": "params", - "required": ["uri"], + "required": [ + "uri" + ], "properties": { "uri": { "type": "string", @@ -16,122 +18,310 @@ } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, "errors": [ - { "name": "InvalidRequest", "description": "The URI is invalid or is not a collection record AT-URI." }, - { "name": "RecordNotFound", "description": "No indexed collection exists at this AT-URI." } + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a collection record AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed collection exists at this AT-URI." + } ] }, "output": { "type": "object", - "required": ["collection"], + "required": [ + "collection" + ], "properties": { - "collection": { "type": "ref", "ref": "#collectionView" } + "collection": { + "type": "ref", + "ref": "#collectionView" + } } }, "collectionProfileView": { "type": "object", "description": "Profile record view used in collection author projections; indexedAt is required and may be null when the index has no timestamp.", - "required": ["uri", "cid", "indexedAt", "did", "record"], - "nullable": ["indexedAt"], + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "record": { "type": "ref", "ref": "app.certified.actor.profile" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.actor.profile" + } } }, "collectionOrganizationView": { "type": "object", "description": "Organization sidecar view used in collection author projections; indexedAt is required and may be null when the index has no timestamp.", - "required": ["uri", "cid", "indexedAt", "did", "record"], - "nullable": ["indexedAt"], + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "record": { "type": "ref", "ref": "app.certified.actor.organization" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.actor.organization" + } } }, "collectionActorView": { "type": "object", "description": "Collection author DID with nullable profile and organization sidecar views whose indexedAt values may be null.", - "required": ["did", "profile", "organization"], - "nullable": ["profile", "organization"], + "required": [ + "did", + "profile", + "organization" + ], + "nullable": [ + "profile", + "organization" + ], "properties": { - "did": { "type": "string", "format": "did" }, - "profile": { "type": "ref", "ref": "#collectionProfileView" }, - "organization": { "type": "ref", "ref": "#collectionOrganizationView" } + "did": { + "type": "string", + "format": "did" + }, + "profile": { + "type": "ref", + "ref": "#collectionProfileView" + }, + "organization": { + "type": "ref", + "ref": "#collectionOrganizationView" + } } }, "locationRecordView": { "type": "object", "description": "Raw location record view with metadata and no author hydration, used for exact-version collection reference projections.", - "required": ["uri", "cid", "indexedAt", "did", "record"], - "nullable": ["indexedAt"], + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "record": { "type": "ref", "ref": "app.certified.location" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "app.certified.location" + } } }, "vocabTagRecordView": { "type": "object", "description": "Raw vocabulary-tag record view with metadata and no author hydration, used for exact-version collection reference projections.", - "required": ["uri", "cid", "indexedAt", "did", "record"], - "nullable": ["indexedAt"], + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "record": { "type": "ref", "ref": "org.hypercerts.vocab.tag" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.vocab.tag" + } } }, "collectionLocationView": { "type": "object", "description": "Original strong-reference URI and CID plus the exact referenced location record view, or null when that version is unavailable.", - "required": ["uri", "cid", "record"], - "nullable": ["record"], + "required": [ + "uri", + "cid", + "record" + ], + "nullable": [ + "record" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "record": { "type": "ref", "ref": "#locationRecordView" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "record": { + "type": "ref", + "ref": "#locationRecordView" + } } }, "collectionTagView": { "type": "object", "description": "Original strong-reference URI and CID plus the exact referenced vocabulary-tag record view, or null when that version is unavailable.", - "required": ["uri", "cid", "record"], - "nullable": ["record"], + "required": [ + "uri", + "cid", + "record" + ], + "nullable": [ + "record" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "record": { "type": "ref", "ref": "#vocabTagRecordView" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "record": { + "type": "ref", + "ref": "#vocabTagRecordView" + } } }, "collectionView": { "type": "object", - "description": "Collection record view with hydrated author and optional sibling projections for location and tags. The original record is unchanged; exact strong-reference versions are used and unresolved projections retain their source URI/CID with record null.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record"], - "nullable": ["indexedAt"], + "description": "Collection record with hydrated author and exact-version location and tag projections. Missing author sidecars are null; unavailable referenced versions retain their source URI and CID with a null record. The original record is unchanged.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "#collectionActorView" }, - "record": { "type": "ref", "ref": "org.hypercerts.collection" }, - "location": { "type": "ref", "ref": "#collectionLocationView" }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "#collectionActorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.collection" + }, + "location": { + "type": "ref", + "ref": "#collectionLocationView" + }, "tags": { "type": "array", "maxLength": 20, - "items": { "type": "ref", "ref": "#collectionTagView" }, - "description": "Projection of record.tags preserving order, length, and duplicates, including an empty array; omitted only when record.tags is absent." + "items": { + "type": "ref", + "ref": "#collectionTagView" + }, + "description": "Projection of record.tags in source order, preserving duplicates. Present as an empty array when record.tags is empty; omitted when it is absent." } } } diff --git a/docs/sources/lexicons/org.hypercerts.collection.json b/docs/sources/lexicons/org.hypercerts.collection.json new file mode 100644 index 0000000..6b84fe7 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.collection.json @@ -0,0 +1,128 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.collection", + "defs": { + "main": { + "type": "record", + "description": "A collection/group of items (activities and/or other collections). Collections support recursive nesting.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "title", + "createdAt" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of this collection. Values beyond the known set are permitted.", + "knownValues": [ + "favorites", + "project", + "portfolio", + "program" + ], + "maxLength": 64 + }, + "title": { + "type": "string", + "description": "Display name for this collection (e.g. 'Q1 2025 Impact Projects')", + "maxLength": 800, + "maxGraphemes": 80 + }, + "shortDescription": { + "type": "string", + "maxLength": 3000, + "maxGraphemes": 300, + "description": "Short summary of this collection, suitable for previews and list views. Rich text annotations may be provided via `shortDescriptionFacets`." + }, + "shortDescriptionFacets": { + "type": "array", + "description": "Rich text annotations for `shortDescription` (mentions, URLs, hashtags, etc).", + "items": { + "type": "ref", + "ref": "app.bsky.richtext.facet" + } + }, + "description": { + "type": "union", + "refs": [ + "org.hypercerts.defs#descriptionString", + "pub.leaflet.pages.linearDocument", + "com.atproto.repo.strongRef" + ], + "description": "Long-form description of the collection. An inline string for plain text or markdown, a Leaflet linear document for rich-text content, or a strong reference to an external description record." + }, + "avatar": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallImage" + ], + "description": "The collection's avatar/profile image as a URI or image blob." + }, + "banner": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#largeImage" + ], + "description": "Larger horizontal image to display behind the collection view." + }, + "items": { + "type": "array", + "description": "Array of items in this collection with optional weights.", + "maxLength": 1000, + "items": { + "type": "ref", + "ref": "#item" + } + }, + "location": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "A strong reference to the location where this collection's activities were performed. The record referenced must conform with the lexicon app.certified.location." + }, + "tags": { + "type": "array", + "description": "References to org.hypercerts.vocab.tag records classifying this record. All listed tags apply simultaneously (logical AND); the array carries no ordering, weighting, negation, inheritance, or rule logic, and never will — any future expression logic must arrive as a new field and must never reinterpret this one.", + "maxLength": 20, + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + } + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + }, + "item": { + "type": "object", + "description": "An item in a collection, with an identifier and optional weight.", + "required": [ + "itemIdentifier" + ], + "properties": { + "itemIdentifier": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Strong reference to an item in this collection. Items can be activities (org.hypercerts.claim.activity), features (org.hypercerts.entity.feature), and/or other collections (org.hypercerts.collection)." + }, + "itemWeight": { + "type": "string", + "description": "Optional weight for this item (positive numeric value stored as string). Weights do not need to sum to a specific total; normalization can be performed by the consuming application as needed.", + "maxLength": 100 + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.collection.listCollectionItems.json b/docs/sources/lexicons/org.hypercerts.collection.listCollectionItems.json index b5dcc7a..3ec0f14 100644 --- a/docs/sources/lexicons/org.hypercerts.collection.listCollectionItems.json +++ b/docs/sources/lexicons/org.hypercerts.collection.listCollectionItems.json @@ -7,62 +7,162 @@ "description": "Resolves one collection's embedded items in source order, one level only. Each target must match the item's exact strong-reference URI+CID; nested collections are summaries, and unavailable versions or unsupported kinds return record null. Pagination follows the latest indexed collection at the same URI across pages, even after collection changes; no version-change error or restart is required. Item additions, removals, or reordering may cause skipped or repeated results. There is no sortDirection parameter. Authentication is not required.", "parameters": { "type": "params", - "required": ["collection"], + "required": [ + "collection" + ], "properties": { - "collection": { "type": "string", "format": "at-uri", "description": "Full collection record AT-URI using a DID authority." }, - "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Maximum embedded items in the page; default 25." }, - "cursor": { "type": "string", "description": "Opaque item-pagination cursor. Continue with the same collection URI; it is not bound to a collection version." } + "collection": { + "type": "string", + "format": "at-uri", + "description": "Full collection record AT-URI using a DID authority." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum embedded items in the page; default 25." + }, + "cursor": { + "type": "string", + "description": "Opaque item-pagination cursor. Continue with the same collection URI; it is not bound to a collection version." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, "errors": [ - { "name": "InvalidRequest", "description": "The collection AT-URI, page bound, cursor, or query parameters are invalid." }, - { "name": "RecordNotFound", "description": "No indexed collection exists at this AT-URI." } + { + "name": "InvalidRequest", + "description": "The collection AT-URI, page bound, cursor, or query parameters are invalid." + }, + { + "name": "RecordNotFound", + "description": "No indexed collection exists at this AT-URI." + } ] }, "output": { "type": "object", - "required": ["items"], + "required": [ + "items" + ], "properties": { - "items": { "type": "array", "items": { "type": "ref", "ref": "#collectionItemView" } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when there is no next page." } + "items": { + "type": "array", + "items": { + "type": "ref", + "ref": "#collectionItemView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } } }, "collectionSummaryView": { "type": "object", "description": "Non-recursive summary of a collection item target.", - "required": ["uri", "cid", "did", "title"], + "required": [ + "uri", + "cid", + "did", + "title" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "did": { "type": "string", "format": "did" }, - "type": { "type": "string", "maxLength": 64 }, - "title": { "type": "string", "maxLength": 800, "maxGraphemes": 80 }, - "shortDescription": { "type": "string", "maxLength": 3000, "maxGraphemes": 300 } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "did": { + "type": "string", + "format": "did" + }, + "type": { + "type": "string", + "maxLength": 64 + }, + "title": { + "type": "string", + "maxLength": 800, + "maxGraphemes": 80 + }, + "shortDescription": { + "type": "string", + "maxLength": 3000, + "maxGraphemes": 300 + } } }, "featureView": { "type": "object", "description": "Feature record view with hydrated author actor; feature location, tag, and sameAs references remain unexpanded.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record"], - "nullable": ["indexedAt"], + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "org.hypercerts.collection.getCollection#collectionActorView" }, - "record": { "type": "ref", "ref": "org.hypercerts.entity.feature" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.collection.getCollection#collectionActorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.entity.feature" + } } }, "collectionItemView": { "type": "object", "description": "One embedded collection item in source order, retaining its original strong reference and optional weight. Resolve only an exact URI+CID target one level deep; nested collections use a summary, and unavailable versions or unsupported kinds produce record null.", - "required": ["itemIdentifier", "record"], - "nullable": ["record"], + "required": [ + "itemIdentifier", + "record" + ], + "nullable": [ + "record" + ], "properties": { - "itemIdentifier": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, - "itemWeight": { "type": "string", "maxLength": 100 }, + "itemIdentifier": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "itemWeight": { + "type": "string", + "maxLength": 100 + }, "record": { "type": "union", "refs": [ diff --git a/docs/sources/lexicons/org.hypercerts.collection.listCollections.json b/docs/sources/lexicons/org.hypercerts.collection.listCollections.json index 0cc6b92..7daadb1 100644 --- a/docs/sources/lexicons/org.hypercerts.collection.listCollections.json +++ b/docs/sources/lexicons/org.hypercerts.collection.listCollections.json @@ -4,30 +4,110 @@ "defs": { "main": { "type": "query", - "description": "Lists indexed collections. Array inputs use repeated unbracketed keys and are limited to 100 values per parameter. All distinct filters combine with AND. Values within authors, types, uris, and itemUris use OR; tagUris is the exception and requires every requested URI to occur in the collection's tags. itemUris and tagUris compare reference URIs only, without CID or target-body lookup. Duplicate inputs are removed; URI input order is not preserved. Results sort by (createdAt, uri), default desc, with an opaque direction-bound cursor. Authentication is not required.", + "description": "Lists indexed collections. Pass array filters as repeated query parameters; filters combine with AND, while values in each array are ORed except tagUris, which requires every supplied URI. Authentication is not required.", "parameters": { "type": "params", "properties": { - "authors": { "type": "array", "maxLength": 100, "description": "Repository-owner DIDs; OR within this filter.", "items": { "type": "string", "format": "did" } }, - "hasOrganizationRecord": { "type": "boolean", "description": "Whether the collection author has an app.certified.actor.organization/self record; false matches authors without it regardless of profile presence." }, - "types": { "type": "array", "maxLength": 100, "description": "Exact open-string collection types; OR within this filter.", "items": { "type": "string", "maxLength": 64 } }, - "uris": { "type": "array", "maxLength": 100, "description": "Exact collection record AT-URIs for bounded batch hydration; OR within this filter.", "items": { "type": "string", "format": "at-uri" } }, - "itemUris": { "type": "array", "maxLength": 100, "description": "Returns collections where any embedded items[].itemIdentifier.uri matches a supplied AT-URI; URI-only matching, without CID comparison or item hydration.", "items": { "type": "string", "format": "at-uri" } }, - "tagUris": { "type": "array", "maxLength": 100, "description": "Conjunctive tag filter: every supplied vocabulary-tag AT-URI must occur in record.tags. Compares URI only, without CID comparison or tag-body inspection.", "items": { "type": "string", "format": "at-uri" } }, - "sortDirection": { "type": "string", "enum": ["asc", "desc"], "description": "Sort direction for (createdAt, uri); default desc." }, - "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Maximum collections in the page; default 25." }, - "cursor": { "type": "string", "description": "Opaque pagination cursor, bound to sortDirection." } + "authors": { + "type": "array", + "maxLength": 100, + "description": "Repository-owner DID to match.", + "items": { + "type": "string", + "format": "did" + } + }, + "hasOrganizationRecord": { + "type": "boolean", + "description": "Filter by the author's organization/self record; false matches absence regardless of profile presence." + }, + "types": { + "type": "array", + "maxLength": 100, + "description": "Exact collection type to match.", + "items": { + "type": "string", + "maxLength": 64 + } + }, + "uris": { + "type": "array", + "maxLength": 100, + "description": "Match by collection record AT-URI; this does not select a CID version.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "itemUris": { + "type": "array", + "maxLength": 100, + "description": "Match when an embedded item's itemIdentifier.uri equals a supplied AT-URI; matching ignores CID.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "tagUris": { + "type": "array", + "maxLength": 100, + "description": "Require every supplied vocabulary-tag AT-URI to occur in record.tags; matching ignores CID and tag records.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "Direction for (createdAt, uri) ordering; defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum collections per page; defaults to 25." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor from a previous response; use the same sortDirection." + } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, - "errors": [{ "name": "InvalidRequest", "description": "A filter value, organization-record flag, page bound, cursor, repeated scalar, or query parameter is invalid." }] + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A query parameter is invalid or unsupported." + } + ] }, "output": { "type": "object", - "required": ["collections"], + "required": [ + "collections" + ], "properties": { - "collections": { "type": "array", "items": { "type": "ref", "ref": "org.hypercerts.collection.getCollection#collectionView" } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when there is no next page." } + "collections": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.collection.getCollection#collectionView" + } + }, + "cursor": { + "type": "string", + "description": "Cursor for the next page; omitted when there is no next page." + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.collection.searchCollections.json b/docs/sources/lexicons/org.hypercerts.collection.searchCollections.json index 3024366..cef1dbe 100644 --- a/docs/sources/lexicons/org.hypercerts.collection.searchCollections.json +++ b/docs/sources/lexicons/org.hypercerts.collection.searchCollections.json @@ -4,32 +4,117 @@ "defs": { "main": { "type": "query", - "description": "Searches collection title or shortDescription using the complete trimmed search text as a case-insensitive literal substring within either field; terms are not split across fields. Blank-after-trimming search imposes no search restriction. Other filters combine with search using AND. Filters, pagination, ordering, hydration, and output otherwise match listCollections. Authentication is not required.", + "description": "Searches collection title or shortDescription for the complete trimmed text as a case-insensitive literal substring; blank text adds no search restriction. Other filters combine with search using AND, with OR within arrays except tagUris, which requires every supplied URI. Authentication is not required.", "parameters": { "type": "params", - "required": ["search"], + "required": [ + "search" + ], "properties": { - "search": { "type": "string", "description": "Required search text; literal case-insensitive substring in title or shortDescription. A blank trimmed value imposes no search restriction." }, - "authors": { "type": "array", "maxLength": 100, "description": "Repository-owner DIDs; OR within this filter.", "items": { "type": "string", "format": "did" } }, - "hasOrganizationRecord": { "type": "boolean", "description": "Whether the collection author has an app.certified.actor.organization/self record; false matches authors without it regardless of profile presence." }, - "types": { "type": "array", "maxLength": 100, "description": "Exact open-string collection types; OR within this filter.", "items": { "type": "string", "maxLength": 64 } }, - "uris": { "type": "array", "maxLength": 100, "description": "Exact collection record AT-URIs for bounded batch hydration; OR within this filter.", "items": { "type": "string", "format": "at-uri" } }, - "itemUris": { "type": "array", "maxLength": 100, "description": "Returns collections where any embedded items[].itemIdentifier.uri matches a supplied AT-URI; URI-only matching, without CID comparison or item hydration.", "items": { "type": "string", "format": "at-uri" } }, - "tagUris": { "type": "array", "maxLength": 100, "description": "Conjunctive tag filter: every supplied vocabulary-tag AT-URI must occur in record.tags. Compares URI only, without CID comparison or tag-body inspection.", "items": { "type": "string", "format": "at-uri" } }, - "sortDirection": { "type": "string", "enum": ["asc", "desc"], "description": "Sort direction for (createdAt, uri); default desc." }, - "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Maximum collections in the page; default 25." }, - "cursor": { "type": "string", "description": "Opaque pagination cursor, bound to sortDirection." } + "search": { + "type": "string", + "description": "Case-insensitive literal substring in title or shortDescription; blank trimmed text adds no search restriction." + }, + "authors": { + "type": "array", + "maxLength": 100, + "description": "Repository-owner DID to match.", + "items": { + "type": "string", + "format": "did" + } + }, + "hasOrganizationRecord": { + "type": "boolean", + "description": "Filter by the author's organization/self record; false matches absence regardless of profile presence." + }, + "types": { + "type": "array", + "maxLength": 100, + "description": "Exact collection type to match.", + "items": { + "type": "string", + "maxLength": 64 + } + }, + "uris": { + "type": "array", + "maxLength": 100, + "description": "Match by collection record AT-URI; this does not select a CID version.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "itemUris": { + "type": "array", + "maxLength": 100, + "description": "Match when an embedded item's itemIdentifier.uri equals a supplied AT-URI; matching ignores CID.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "tagUris": { + "type": "array", + "maxLength": 100, + "description": "Require every supplied vocabulary-tag AT-URI to occur in record.tags; matching ignores CID and tag records.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "Direction for (createdAt, uri) ordering; defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum collections per page; defaults to 25." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor from a previous response; use the same sortDirection." + } } }, - "output": { "encoding": "application/json", "schema": { "type": "ref", "ref": "#output" } }, - "errors": [{ "name": "InvalidRequest", "description": "Search text, a filter value, organization-record flag, page bound, cursor, repeated scalar, or query parameter is invalid." }] + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "Search text or another query parameter is invalid or unsupported." + } + ] }, "output": { "type": "object", - "required": ["collections"], + "required": [ + "collections" + ], "properties": { - "collections": { "type": "array", "items": { "type": "ref", "ref": "org.hypercerts.collection.getCollection#collectionView" } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when there is no next page." } + "collections": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.collection.getCollection#collectionView" + } + }, + "cursor": { + "type": "string", + "description": "Cursor for the next page; omitted when there is no next page." + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.context.acknowledgement.json b/docs/sources/lexicons/org.hypercerts.context.acknowledgement.json new file mode 100644 index 0000000..b99d161 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.acknowledgement.json @@ -0,0 +1,54 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.acknowledgement", + "defs": { + "main": { + "type": "record", + "description": "Acknowledges a record (subject) or its relationship in a context. Created in the acknowledging actor's repo to form a bidirectional link. Examples: a contributor acknowledging inclusion in an activity, an activity owner acknowledging inclusion in a collection, or a record owner acknowledging an evaluation.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "subject", + "acknowledged", + "createdAt" + ], + "properties": { + "subject": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "The record being acknowledged (e.g. an activity, a contributor information record, an evaluation)." + }, + "context": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "com.atproto.repo.strongRef" + ], + "description": "Context for the acknowledgement (e.g. the collection that includes an activity, or the activity that includes a contributor). A URI for a lightweight reference or a strong reference for content-hash verification." + }, + "acknowledged": { + "type": "boolean", + "description": "Whether the relationship is acknowledged (true) or rejected (false)." + }, + "comment": { + "type": "string", + "description": "Optional plain-text comment providing additional context or reasoning.", + "maxLength": 10000, + "maxGraphemes": 1000 + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.context.attachment.json b/docs/sources/lexicons/org.hypercerts.context.attachment.json new file mode 100644 index 0000000..28e0ab6 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.attachment.json @@ -0,0 +1,96 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.attachment", + "defs": { + "main": { + "type": "record", + "description": "An attachment providing commentary, context, evidence, or documentary material related to a hypercert record (e.g. an activity, project, claim, or evaluation).", + "key": "tid", + "record": { + "type": "object", + "required": [ + "title", + "createdAt" + ], + "properties": { + "subjects": { + "type": "array", + "description": "References to the subject(s) the attachment is connected to—this may be an activity claim, outcome claim, measurement, evaluation, or even another attachment. This is optional as the attachment can exist before the claim is recorded.", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100 + }, + "contentType": { + "type": "string", + "description": "The type of attachment. Values beyond the known set are permitted.", + "knownValues": [ + "report", + "audit", + "evidence", + "testimonial", + "methodology" + ], + "maxLength": 64 + }, + "content": { + "type": "array", + "description": "The files, documents, or external references included in this attachment record.", + "items": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallBlob" + ] + }, + "maxLength": 100 + }, + "title": { + "type": "string", + "maxLength": 256, + "description": "Display title for this attachment (e.g. 'Impact Assessment Report', 'Audit Findings')" + }, + "shortDescription": { + "type": "string", + "description": "Short summary of this attachment, suitable for previews and list views. Rich text annotations may be provided via `shortDescriptionFacets`.", + "maxLength": 3000, + "maxGraphemes": 300 + }, + "shortDescriptionFacets": { + "type": "array", + "description": "Rich text annotations for `shortDescription` (mentions, URLs, hashtags, etc).", + "items": { + "type": "ref", + "ref": "app.bsky.richtext.facet" + } + }, + "description": { + "type": "union", + "refs": [ + "org.hypercerts.defs#descriptionString", + "pub.leaflet.pages.linearDocument", + "com.atproto.repo.strongRef" + ], + "description": "Long-form description of the attachment. An inline string for plain text or markdown, a Leaflet linear document for rich-text content, or a strong reference to an external description record." + }, + "location": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "A strong reference to the location where this attachment's subject matter occurred. The record referenced must conform with the lexicon app.certified.location." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.context.evaluation.json b/docs/sources/lexicons/org.hypercerts.context.evaluation.json new file mode 100644 index 0000000..7adb81a --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.evaluation.json @@ -0,0 +1,108 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.evaluation", + "defs": { + "main": { + "type": "record", + "description": "An evaluation of a hypercert record (e.g. an activity and its impact).", + "key": "tid", + "record": { + "type": "object", + "required": [ + "evaluators", + "summary", + "createdAt" + ], + "properties": { + "subject": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "A strong reference to what is being evaluated (e.g. activity, measurement, contribution, etc.)" + }, + "evaluators": { + "type": "array", + "description": "DIDs of the evaluators", + "items": { + "type": "ref", + "ref": "app.certified.defs#did" + }, + "maxLength": 1000 + }, + "content": { + "type": "array", + "description": "Evaluation data (URIs or blobs) containing detailed reports or methodology", + "items": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallBlob" + ] + }, + "maxLength": 100 + }, + "measurements": { + "type": "array", + "description": "Optional references to the measurements that contributed to this evaluation. The record(s) referenced must conform with the lexicon org.hypercerts.context.measurement", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100 + }, + "summary": { + "type": "string", + "description": "Brief evaluation summary", + "maxLength": 5000, + "maxGraphemes": 1000 + }, + "score": { + "type": "ref", + "ref": "#score", + "description": "Optional overall score for this evaluation on a numeric scale." + }, + "location": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "An optional reference for georeferenced evaluations. The record referenced must conform with the lexicon app.certified.location." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + }, + "score": { + "type": "object", + "description": "Overall score for an evaluation on a numeric scale.", + "required": [ + "min", + "max", + "value" + ], + "properties": { + "min": { + "type": "string", + "description": "Minimum value of the scale as a numeric string (e.g. '0', '1').", + "maxLength": 50 + }, + "max": { + "type": "string", + "description": "Maximum value of the scale as a numeric string (e.g. '5', '10').", + "maxLength": 50 + }, + "value": { + "type": "string", + "description": "Score within the inclusive range [min, max] as a numeric string (e.g. '3.7').", + "maxLength": 50 + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.context.getAcknowledgement.json b/docs/sources/lexicons/org.hypercerts.context.getAcknowledgement.json new file mode 100644 index 0000000..bccc534 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.getAcknowledgement.json @@ -0,0 +1,56 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.getAcknowledgement", + "defs": { + "main": { + "type": "query", + "description": "Gets one public acknowledgement by exact AT-URI; authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Acknowledgement record AT-URI with a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or does not identify an acknowledgement record." + }, + { + "name": "RecordNotFound", + "description": "No acknowledgement is indexed at this AT-URI." + }, + { + "name": "AcknowledgementQueryFailed", + "description": "The acknowledgement or publisher lookup failed." + } + ] + }, + "output": { + "type": "object", + "required": [ + "acknowledgement" + ], + "properties": { + "acknowledgement": { + "type": "ref", + "ref": "org.hypercerts.api.defs#acknowledgementView" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.context.getAttachment.json b/docs/sources/lexicons/org.hypercerts.context.getAttachment.json index 5794a27..68cb64b 100644 --- a/docs/sources/lexicons/org.hypercerts.context.getAttachment.json +++ b/docs/sources/lexicons/org.hypercerts.context.getAttachment.json @@ -4,47 +4,93 @@ "defs": { "main": { "type": "query", - "description": "Looks up one indexed attachment by exact AT-URI and hydrates its publisher actor. The full record is preserved unchanged; subjects, content, and location remain unexpanded, and external files are not fetched. Missing author profile or organization sidecar is null; operational hydration failures return errors. A missing indexed attachment is RecordNotFound. Authentication is not required.", + "description": "Returns the current indexed attachment at the given AT-URI with its publisher view. Authentication is not required.", "parameters": { "type": "params", - "required": ["uri"], + "required": [ + "uri" + ], "properties": { "uri": { "type": "string", "format": "at-uri", - "description": "Full attachment record AT-URI using a DID authority." + "description": "AT-URI of the attachment record, using a DID authority; lookup is by URI only, not a specific CID/version." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "The URI is invalid or is not an attachment record AT-URI." }, - { "name": "RecordNotFound", "description": "No indexed attachment exists at this AT-URI." }, - { "name": "AttachmentQueryFailed", "description": "The attachment query or required publisher hydration failed; retry after checking the HappyView PostgreSQL service." } + { + "name": "InvalidRequest", + "description": "The URI is invalid or does not identify an attachment record." + }, + { + "name": "RecordNotFound", + "description": "No indexed attachment was found at this AT-URI." + }, + { + "name": "AttachmentQueryFailed", + "description": "The attachment lookup or required publisher hydration failed." + } ] }, "output": { "type": "object", - "required": ["attachment"], + "required": [ + "attachment" + ], "properties": { - "attachment": { "type": "ref", "ref": "#attachmentView" } + "attachment": { + "type": "ref", + "ref": "#attachmentView" + } } }, "attachmentView": { "type": "object", - "description": "Attachment record view with hydrated publisher actor and the full indexed record unchanged; subject, content, and location references remain unexpanded.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record"], - "nullable": ["indexedAt"], + "description": "Attachment with its hydrated publisher and unchanged source record; subject, content, and location references are not expanded, and external files are not fetched. Missing publisher sidecars are null; query or hydration failures fail the request.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "org.hypercerts.api.defs#actorView" }, - "record": { "type": "ref", "ref": "org.hypercerts.context.attachment" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.context.attachment" + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.context.getEvaluation.json b/docs/sources/lexicons/org.hypercerts.context.getEvaluation.json index 85c8fb6..891c481 100644 --- a/docs/sources/lexicons/org.hypercerts.context.getEvaluation.json +++ b/docs/sources/lexicons/org.hypercerts.context.getEvaluation.json @@ -4,75 +4,149 @@ "defs": { "main": { "type": "query", - "description": "Looks up one indexed evaluation by exact AT-URI and hydrates its publisher plus the first 100 original evaluator entries. Later entries remain in the projection with hydrationStatus omitted and no profile or organization. The original record and evaluator positions, order, and duplicates are preserved. Missing profiles and organization sidecars are null; operational hydration failures return errors. Authentication is not required.", + "description": "Returns the indexed evaluation at the given AT-URI with its publisher and evaluator views. Authentication is not required.", "parameters": { "type": "params", - "required": ["uri"], + "required": [ + "uri" + ], "properties": { "uri": { "type": "string", "format": "at-uri", - "description": "Full evaluation record AT-URI using a DID authority." + "description": "AT-URI of the evaluation record, using a DID authority; lookup is by URI only, not a specific CID/version." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "The URI is invalid or is not an evaluation record AT-URI." }, - { "name": "RecordNotFound", "description": "No indexed evaluation exists at this AT-URI." } + { + "name": "InvalidRequest", + "description": "The URI is invalid or does not identify an evaluation record." + }, + { + "name": "RecordNotFound", + "description": "No indexed evaluation was found at this AT-URI." + } ] }, "hydratedEvaluationEvaluatorView": { "type": "object", - "description": "An original evaluator entry whose Certified profile and organization sidecar were looked up, including when either is missing.", - "required": ["did", "hydrationStatus", "profile", "organization"], - "nullable": ["profile", "organization"], + "description": "An original evaluator entry with its profile and organization sidecars looked up.", + "required": [ + "did", + "hydrationStatus", + "profile", + "organization" + ], + "nullable": [ + "profile", + "organization" + ], "properties": { - "did": { "type": "string", "format": "did" }, - "hydrationStatus": { "type": "string", "const": "hydrated" }, - "profile": { "type": "ref", "ref": "org.hypercerts.api.defs#profileView" }, - "organization": { "type": "ref", "ref": "org.hypercerts.api.defs#organizationView" } + "did": { + "type": "string", + "format": "did" + }, + "hydrationStatus": { + "type": "string", + "const": "hydrated" + }, + "profile": { + "type": "ref", + "ref": "org.hypercerts.api.defs#profileView" + }, + "organization": { + "type": "ref", + "ref": "org.hypercerts.api.defs#organizationView" + } } }, "omittedEvaluationEvaluatorView": { "type": "object", - "description": "An original evaluator entry beyond the 100-position hydration cap; profile and organization are deliberately omitted.", - "required": ["did", "hydrationStatus"], + "description": "An original evaluator entry not hydrated.", + "required": [ + "did", + "hydrationStatus" + ], "properties": { - "did": { "type": "string", "format": "did" }, - "hydrationStatus": { "type": "string", "const": "omitted" } + "did": { + "type": "string", + "format": "did" + }, + "hydrationStatus": { + "type": "string", + "const": "omitted" + } } }, "evaluationView": { "type": "object", - "description": "Evaluation record view with hydrated publisher and a one-for-one evaluator projection. Preserve the original record and evaluator order, length, duplicates, and entry fields; hydrate only the first 100 source positions.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record", "evaluators"], + "description": "Evaluation with the complete original record, hydrated publisher, and evaluator views. For hydrated actors, missing profile or organization sidecars are null; query or hydration failures fail the request.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record", + "evaluators" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "org.hypercerts.api.defs#actorView" }, - "record": { "type": "ref", "ref": "org.hypercerts.context.evaluation" }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.context.evaluation" + }, "evaluators": { "type": "array", "maxLength": 1000, - "description": "One-for-one sibling projection of record.evaluators in source order, retaining duplicates and source entry fields. Entries beyond the hydration cap remain present with hydrationStatus omitted.", + "description": "One-for-one projection of record.evaluators, preserving source order, duplicates, and entry fields. Entries after position 100 remain present with hydrationStatus \"omitted\" and no profile or organization.", "items": { "type": "union", - "refs": ["#hydratedEvaluationEvaluatorView", "#omittedEvaluationEvaluatorView"] + "refs": [ + "#hydratedEvaluationEvaluatorView", + "#omittedEvaluationEvaluatorView" + ] } } } }, "output": { "type": "object", - "required": ["evaluation"], + "required": [ + "evaluation" + ], "properties": { - "evaluation": { "type": "ref", "ref": "#evaluationView" } + "evaluation": { + "type": "ref", + "ref": "#evaluationView" + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.context.getMeasurement.json b/docs/sources/lexicons/org.hypercerts.context.getMeasurement.json new file mode 100644 index 0000000..aef4289 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.getMeasurement.json @@ -0,0 +1,93 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.getMeasurement", + "defs": { + "main": { + "type": "query", + "description": "Gets an indexed measurement by exact AT-URI and hydrates its publisher. Authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full measurement record AT-URI using a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a measurement record AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed measurement exists at this AT-URI." + } + ] + }, + "measurementView": { + "type": "object", + "description": "Indexed measurement with its publisher actor and original record.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.context.measurement" + } + } + }, + "output": { + "type": "object", + "required": [ + "measurement" + ], + "properties": { + "measurement": { + "type": "ref", + "ref": "#measurementView" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.context.listAcknowledgements.json b/docs/sources/lexicons/org.hypercerts.context.listAcknowledgements.json new file mode 100644 index 0000000..640afce --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.listAcknowledgements.json @@ -0,0 +1,88 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.listAcknowledgements", + "defs": { + "main": { + "type": "query", + "description": "Lists public acknowledgements by publisher or subject; authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Publisher DIDs; OR within this filter.", + "items": { + "type": "string", + "format": "did" + } + }, + "subjects": { + "type": "array", + "maxLength": 100, + "description": "Subject record AT-URIs; matching ignores CID.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "description": "Sort direction for createdAt and URI; default desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum records per page." + }, + "cursor": { + "type": "string", + "description": "Opaque next-page cursor bound to sortDirection; keep other parameters unchanged." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A filter value, sort direction, page bound, cursor, repeated scalar, or query parameter is invalid." + }, + { + "name": "AcknowledgementQueryFailed", + "description": "The acknowledgement or publisher lookup failed." + } + ] + }, + "output": { + "type": "object", + "required": [ + "acknowledgements" + ], + "properties": { + "acknowledgements": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.api.defs#acknowledgementView" + } + }, + "cursor": { + "type": "string", + "description": "Omitted when there is no next page." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.context.listAttachments.json b/docs/sources/lexicons/org.hypercerts.context.listAttachments.json index 1352135..73fbdb7 100644 --- a/docs/sources/lexicons/org.hypercerts.context.listAttachments.json +++ b/docs/sources/lexicons/org.hypercerts.context.listAttachments.json @@ -4,69 +4,100 @@ "defs": { "main": { "type": "query", - "description": "Lists indexed attachments. Repeated unbracketed array keys accept at most 100 values each; values within each filter use OR, distinct filters use AND, and duplicate inputs are removed. subjects matches any record.subjects[].uri without CID comparison; contentTypes is an exact open-string match. Results sort by (createdAt, uri), default desc, with an opaque cursor bound to sortDirection; input URI order is not preserved. The full record and hydrated publisher actor are returned; related targets and file bytes are not fetched. Missing author records are null, while operational hydration failures return errors. Authentication is not required.", + "description": "Lists indexed attachments with optional publisher, record URI, subject URI, and content-type filters. Values within each array filter use OR; distinct filters use AND, and duplicate values are ignored. Authentication is not required.", "parameters": { "type": "params", "properties": { "authors": { "type": "array", "maxLength": 100, - "description": "Publisher repository DIDs; OR within this filter.", - "items": { "type": "string", "format": "did" } + "description": "Publisher repository DIDs to match.", + "items": { + "type": "string", + "format": "did" + } }, "uris": { "type": "array", "maxLength": 100, - "description": "Exact attachment AT-URIs for bounded batch hydration; OR within this filter.", - "items": { "type": "string", "format": "at-uri" } + "description": "Attachment AT-URIs with DID authorities to include; matching uses URI only, not CID/version. Results follow sort order, not input order.", + "items": { + "type": "string", + "format": "at-uri" + } }, "subjects": { "type": "array", "maxLength": 100, - "description": "Subject AT-URIs matched against any record.subjects[].uri, regardless of position and without CID comparison; OR within this filter.", - "items": { "type": "string", "format": "at-uri" } + "description": "Match any record.subjects[].uri against these AT-URIs with DID authorities; CID values are ignored.", + "items": { + "type": "string", + "format": "at-uri" + } }, "contentTypes": { "type": "array", "maxLength": 100, - "description": "Exact open-string matches against record.contentType; this is not a file MIME-type filter.", - "items": { "type": "string" } + "description": "Exact matches on record.contentType; this is not a file MIME-type filter.", + "items": { + "type": "string" + } }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort direction for (createdAt, uri); default desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sort direction by (createdAt, uri); defaults to desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum attachments in the page; default 25." + "description": "Maximum page size; defaults to 25." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor bound to sortDirection; keep other parameters unchanged between pages." + "description": "Opaque cursor tied to sortDirection; keep all other parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A filter value, sort direction, page bound, cursor, repeated scalar, or query parameter is invalid." }, - { "name": "AttachmentQueryFailed", "description": "The attachment query or required publisher hydration failed; retry after checking the HappyView PostgreSQL service." } + { + "name": "InvalidRequest", + "description": "A filter value, cursor, sort direction, page size, or query parameter is invalid." + }, + { + "name": "AttachmentQueryFailed", + "description": "The attachment query or required publisher hydration failed." + } ] }, "output": { "type": "object", - "required": ["attachments"], + "required": [ + "attachments" + ], "properties": { "attachments": { "type": "array", - "items": { "type": "ref", "ref": "org.hypercerts.context.getAttachment#attachmentView" } + "items": { + "type": "ref", + "ref": "org.hypercerts.context.getAttachment#attachmentView" + } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when there is no next page." } + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.context.listEvaluations.json b/docs/sources/lexicons/org.hypercerts.context.listEvaluations.json index dcfba48..5e70695 100644 --- a/docs/sources/lexicons/org.hypercerts.context.listEvaluations.json +++ b/docs/sources/lexicons/org.hypercerts.context.listEvaluations.json @@ -4,60 +4,83 @@ "defs": { "main": { "type": "query", - "description": "Lists indexed evaluations. Repeated unbracketed array keys accept at most 100 values each; values within authors, evaluators, and subjects use OR, while distinct filters use AND. evaluators matches every record.evaluators[].did, including entries beyond the hydration cap; subjects matches record.subject.uri without CID comparison. Results sort by (createdAt, uri), default desc, with an opaque cursor bound to sortDirection. Each result preserves its full record and evaluator order, length, duplicates, and entry fields; hydrate only the first 100 original evaluator positions, marking all later entries omitted. Missing profiles or organization sidecars are null; operational hydration failures return errors. Authentication is not required.", + "description": "Lists indexed evaluations with optional publisher, evaluator, and subject filters. Values within each array filter use OR; distinct filters use AND. Authentication is not required.", "parameters": { "type": "params", "properties": { "authors": { "type": "array", "maxLength": 100, - "description": "Publisher repository DIDs; OR within this filter.", - "items": { "type": "string", "format": "did" } + "description": "Publisher repository DIDs to match.", + "items": { + "type": "string", + "format": "did" + } }, "evaluators": { "type": "array", "maxLength": 100, - "description": "DIDs named in the original record.evaluators[].did entries; filtering includes positions beyond the hydration cap.", - "items": { "type": "string", "format": "did" } + "description": "DIDs in any record.evaluators[].did entry, including entries beyond the hydration cap.", + "items": { + "type": "string", + "format": "did" + } }, "subjects": { "type": "array", "maxLength": 100, - "description": "Subject AT-URIs matched against record.subject.uri without CID comparison; OR within this filter.", - "items": { "type": "string", "format": "at-uri" } + "description": "Match record.subject.uri by AT-URI with a DID authority; the reference CID is ignored.", + "items": { + "type": "string", + "format": "at-uri" + } }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort direction for (createdAt, uri); default desc." + "enum": [ + "asc", + "desc" + ], + "description": "Sort direction by (createdAt, uri); defaults to desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum evaluations in the page; default 25." + "description": "Maximum page size; defaults to 25." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor, bound to sortDirection; keep other parameters unchanged between pages." + "description": "Opaque cursor tied to sortDirection; keep all other parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A filter value, sort direction, page bound, cursor, repeated scalar, or query parameter is invalid." } + { + "name": "InvalidRequest", + "description": "A filter value, cursor, sort direction, page size, or query parameter is invalid." + } ] }, "output": { "type": "object", - "required": ["evaluations"], + "required": [ + "evaluations" + ], "properties": { "evaluations": { "type": "array", - "items": { "type": "ref", "ref": "org.hypercerts.context.getEvaluation#evaluationView" } + "items": { + "type": "ref", + "ref": "org.hypercerts.context.getEvaluation#evaluationView" + } }, "cursor": { "type": "string", diff --git a/docs/sources/lexicons/org.hypercerts.context.listMeasurements.json b/docs/sources/lexicons/org.hypercerts.context.listMeasurements.json new file mode 100644 index 0000000..00a4bf8 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.listMeasurements.json @@ -0,0 +1,87 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.listMeasurements", + "defs": { + "main": { + "type": "query", + "description": "Lists measurements with optional publisher and subject filters. Authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Publisher repository DIDs, not record.measurers.", + "items": { + "type": "string", + "format": "did" + } + }, + "subjects": { + "type": "array", + "maxLength": 100, + "description": "Subject AT-URIs matched against record.subjects[].uri, ignoring CID.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Sort by createdAt and URI; default desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum page size; default 25." + }, + "cursor": { + "type": "string", + "maxLength": 8192, + "description": "Opaque cursor tied to sortDirection; retain other filters between pages." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A filter value, sort direction, page bound, cursor, repeated scalar, or query parameter is invalid." + } + ] + }, + "output": { + "type": "object", + "required": [ + "measurements" + ], + "properties": { + "measurements": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.context.getMeasurement#measurementView" + } + }, + "cursor": { + "type": "string", + "maxLength": 8192, + "description": "Next-page cursor; omitted when no page follows." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.context.measurement.json b/docs/sources/lexicons/org.hypercerts.context.measurement.json new file mode 100644 index 0000000..07b2ef2 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.context.measurement.json @@ -0,0 +1,117 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.context.measurement", + "defs": { + "main": { + "type": "record", + "description": "Measurement data related to one or more records (e.g. activities, projects, etc.).", + "key": "tid", + "record": { + "type": "object", + "required": [ + "metric", + "unit", + "value", + "createdAt" + ], + "properties": { + "subjects": { + "type": "array", + "description": "Strong references to the records this measurement refers to (e.g. activities, projects, or claims).", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100 + }, + "metric": { + "type": "string", + "description": "The metric being measured, e.g. forest area restored, number of users, etc.", + "maxLength": 500 + }, + "unit": { + "type": "string", + "description": "The unit of the measured value (e.g. kg CO₂e, hectares, %, index score).", + "maxLength": 50 + }, + "value": { + "type": "string", + "description": "The measured value as a numeric string (e.g. '1234.56')", + "maxLength": 500 + }, + "startDate": { + "type": "string", + "format": "datetime", + "description": "The start date and time when the measurement began." + }, + "endDate": { + "type": "string", + "format": "datetime", + "description": "The end date and time when the measurement ended. For one-time measurements, this should equal the start date." + }, + "locations": { + "type": "array", + "description": "Optional geographic references related to where the measurement was taken. Each referenced record must conform with the app.certified.location lexicon.", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100 + }, + "methodType": { + "type": "string", + "description": "Short identifier for the measurement methodology", + "maxLength": 30 + }, + "methodURI": { + "type": "string", + "format": "uri", + "description": "URI to methodology documentation, standard protocol, or measurement procedure" + }, + "evidenceURI": { + "type": "array", + "description": "URIs to related evidence or underlying data (e.g. org.hypercerts.claim.evidence records or raw datasets)", + "items": { + "type": "string", + "format": "uri" + }, + "maxLength": 50 + }, + "measurers": { + "type": "array", + "description": "DIDs of the entities that performed this measurement", + "items": { + "type": "ref", + "ref": "app.certified.defs#did" + }, + "maxLength": 100 + }, + "comment": { + "type": "string", + "description": "Short comment of this measurement, suitable for previews and list views. Rich text annotations may be provided via `commentFacets`.", + "maxLength": 3000, + "maxGraphemes": 300 + }, + "commentFacets": { + "type": "array", + "description": "Rich text annotations for `comment` (mentions, URLs, hashtags, etc).", + "items": { + "type": "ref", + "ref": "app.bsky.richtext.facet" + } + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.defs.json b/docs/sources/lexicons/org.hypercerts.defs.json new file mode 100644 index 0000000..a4145da --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.defs.json @@ -0,0 +1,136 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.defs", + "description": "Common type definitions used across all Hypercerts protocols.", + "defs": { + "descriptionString": { + "type": "object", + "description": "An inline long-form description as plain text or markdown, with optional rich-text annotations.", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "string", + "description": "The description text (plain text or markdown).", + "maxLength": 250000, + "maxGraphemes": 25000 + }, + "facets": { + "type": "array", + "description": "Rich text annotations for the description (mentions, URLs, hashtags, etc).", + "items": { + "type": "ref", + "ref": "app.bsky.richtext.facet" + } + } + } + }, + "uri": { + "type": "object", + "required": [ + "uri" + ], + "description": "Object containing a URI to external data", + "properties": { + "uri": { + "type": "string", + "format": "uri", + "description": "URI to external data" + } + } + }, + "smallBlob": { + "type": "object", + "required": [ + "blob" + ], + "description": "Object containing a blob to external data", + "properties": { + "blob": { + "type": "blob", + "accept": [ + "*/*" + ], + "maxSize": 10485760, + "description": "Blob to external data (up to 10MB)" + } + } + }, + "largeBlob": { + "type": "object", + "required": [ + "blob" + ], + "description": "Object containing a blob to external data", + "properties": { + "blob": { + "type": "blob", + "accept": [ + "*/*" + ], + "maxSize": 104857600, + "description": "Blob to external data (up to 100MB)" + } + } + }, + "smallImage": { + "type": "object", + "required": [ + "image" + ], + "description": "Object containing a small image", + "properties": { + "image": { + "type": "blob", + "accept": [ + "image/jpeg", + "image/jpg", + "image/png", + "image/webp" + ], + "maxSize": 5242880, + "description": "Image (up to 5MB)" + } + } + }, + "smallVideo": { + "type": "object", + "required": [ + "video" + ], + "description": "Object containing a small video", + "properties": { + "video": { + "type": "blob", + "accept": [ + "video/mp4", + "video/webm" + ], + "maxSize": 20971520, + "description": "Video (up to 20MB)" + } + } + }, + "largeImage": { + "type": "object", + "required": [ + "image" + ], + "description": "Object containing a large image", + "properties": { + "image": { + "type": "blob", + "accept": [ + "image/jpeg", + "image/jpg", + "image/png", + "image/webp" + ], + "maxSize": 10485760, + "description": "Image (up to 10MB)" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.entity.defs.json b/docs/sources/lexicons/org.hypercerts.entity.defs.json new file mode 100644 index 0000000..83915f4 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.entity.defs.json @@ -0,0 +1,48 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.entity.defs", + "description": "Shared definitions for Hypercerts entity queries.", + "defs": { + "featureView": { + "type": "object", + "description": "Indexed feature record with its hydrated author actor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.entity.feature" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.entity.feature.json b/docs/sources/lexicons/org.hypercerts.entity.feature.json new file mode 100644 index 0000000..22fe9de --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.entity.feature.json @@ -0,0 +1,81 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.entity.feature", + "defs": { + "main": { + "type": "record", + "description": "A record for a non-agent subject that other records (measurements, evaluations, etc.) can be about, for example a land zone or an ecological stratum. It carries the subject's identity, classification, and optional published spatial representations. Geometry lives in app.certified.location records; grouping lives in org.hypercerts.collection records.", + "key": "any", + "record": { + "type": "object", + "required": [ + "title", + "createdAt" + ], + "properties": { + "type": { + "type": "string", + "description": "The coarse kind of subject this feature is. The initial known kinds are spatial — zone (an area a steward authors and manages) and stratum (an analytical unit derived from a model or methodology) — but the record is subject-general: spatiality comes from the presence of locations, not from type, and further kinds may be documented as adoption demonstrates need. This is not the feature's role or detailed classification — those are expressed through tags. Values beyond the known set are permitted.", + "knownValues": [ + "zone", + "stratum" + ], + "maxLength": 64 + }, + "title": { + "type": "string", + "description": "Display name of the feature.", + "maxLength": 800, + "maxGraphemes": 80 + }, + "description": { + "type": "union", + "description": "Long-form description of the feature, as an inline string, a Leaflet document, or a reference to another record.", + "refs": [ + "org.hypercerts.defs#descriptionString", + "pub.leaflet.pages.linearDocument", + "com.atproto.repo.strongRef" + ] + }, + "locations": { + "type": "array", + "description": "Optional published spatial representations of this feature. Each referenced record must conform with app.certified.location. Multiple entries are alternative representations of the same subject at different precisions or encodings — never different places. Genuinely different places are either one MultiPolygon inside a single location record, or separate features. Writers SHOULD list entries in publisher-preferred order, most preferred first. All referenced locations are public once published: for sensitive subjects, publish only a coarse representation and keep exact geometry unpublished. An empty or absent array is valid: the subject's geometry is unpublished or not applicable.", + "maxLength": 1000, + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + } + }, + "tags": { + "type": "array", + "description": "References to org.hypercerts.vocab.tag records classifying this record. All listed tags apply simultaneously (logical AND); the array carries no ordering, weighting, negation, inheritance, or rule logic, and never will — any future expression logic must arrive as a new field and must never reinterpret this one.", + "maxLength": 20, + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + } + }, + "sameAs": { + "type": "array", + "description": "URIs of external identifiers that denote the same real-world subject, for example a cadastral parcel ID or a gazetteer entry. Entity identity only; vocabulary concordance belongs on org.hypercerts.vocab.tag.", + "maxLength": 20, + "items": { + "type": "string", + "format": "uri" + } + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.entity.getFeature.json b/docs/sources/lexicons/org.hypercerts.entity.getFeature.json new file mode 100644 index 0000000..6d7d192 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.entity.getFeature.json @@ -0,0 +1,52 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.entity.getFeature", + "defs": { + "main": { + "type": "query", + "description": "Looks up one indexed feature by exact AT-URI with author hydration; authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full feature record AT-URI using a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a feature record AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed feature exists at this AT-URI." + } + ] + }, + "output": { + "type": "object", + "required": [ + "feature" + ], + "properties": { + "feature": { + "type": "ref", + "ref": "org.hypercerts.entity.defs#featureView" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.entity.listFeatures.json b/docs/sources/lexicons/org.hypercerts.entity.listFeatures.json new file mode 100644 index 0000000..1b103f5 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.entity.listFeatures.json @@ -0,0 +1,91 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.entity.listFeatures", + "defs": { + "main": { + "type": "query", + "description": "Lists indexed features with filters and direction-bound pagination; authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Repository-owner DIDs; values combine with OR.", + "items": { + "type": "string", + "format": "did" + } + }, + "hasOrganizationRecord": { + "type": "boolean", + "description": "Whether the author has an organization self record; false is independent of profile presence." + }, + "types": { + "type": "array", + "maxLength": 100, + "description": "Exact open-string feature types; values combine with OR.", + "items": { + "type": "string", + "maxLength": 64, + "description": "Exact case-sensitive feature type." + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Direction for sorting by createdAt and URI." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum number of features in the page." + }, + "cursor": { + "type": "string", + "maxLength": 32768, + "description": "Opaque cursor from the previous page, bound to sortDirection." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A filter, page bound, cursor, repeated scalar, or query parameter is invalid." + } + ] + }, + "output": { + "type": "object", + "required": [ + "features" + ], + "properties": { + "features": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.entity.defs#featureView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.funding.getReceipt.json b/docs/sources/lexicons/org.hypercerts.funding.getReceipt.json index c810d63..392b1e8 100644 --- a/docs/sources/lexicons/org.hypercerts.funding.getReceipt.json +++ b/docs/sources/lexicons/org.hypercerts.funding.getReceipt.json @@ -4,47 +4,93 @@ "defs": { "main": { "type": "query", - "description": "Looks up one indexed funding receipt by exact AT-URI and hydrates its publisher actor. The full receipt record is preserved unchanged, including amount as a decimal string; parties and funded targets remain unexpanded. A receipt is not proof that payment occurred. Missing author profile or organization sidecar is null; operational hydration failures return errors. A missing indexed receipt is RecordNotFound. Authentication is not required.", + "description": "Returns one indexed funding receipt and hydrates its publisher actor. Authentication is not required.", "parameters": { "type": "params", - "required": ["uri"], + "required": [ + "uri" + ], "properties": { "uri": { "type": "string", "format": "at-uri", - "description": "Full funding-receipt record AT-URI using a DID authority." + "description": "Funding-receipt AT-URI using a DID authority; lookup matches the URI only, not a CID." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "The URI is invalid or is not a funding-receipt record AT-URI." }, - { "name": "RecordNotFound", "description": "No indexed funding receipt exists at this AT-URI." }, - { "name": "ReceiptQueryFailed", "description": "The receipt query or publisher hydration lookup failed; retry after checking the HappyView PostgreSQL service." } + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a funding-receipt record AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed funding receipt exists at this AT-URI." + }, + { + "name": "ReceiptQueryFailed", + "description": "The receipt query or publisher hydration failed." + } ] }, "output": { "type": "object", - "required": ["receipt"], + "required": [ + "receipt" + ], "properties": { - "receipt": { "type": "ref", "ref": "#receiptView" } + "receipt": { + "type": "ref", + "ref": "#receiptView" + } } }, "receiptView": { "type": "object", - "description": "Funding-receipt record view with hydrated publisher actor and the full indexed record unchanged; party and funded-target references remain unexpanded. A receipt is not proof that payment occurred.", - "required": ["uri", "cid", "indexedAt", "did", "author", "record"], - "nullable": ["indexedAt"], + "description": "Funding receipt with its publisher actor hydrated; the indexed record is unchanged, including its decimal-string amount, and party and funded-target references are not expanded. Missing publisher profile or organization sidecars are null; indexedAt is null when the index has no timestamp. A receipt is not proof of payment.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], "properties": { - "uri": { "type": "string", "format": "at-uri" }, - "cid": { "type": "string", "format": "cid" }, - "indexedAt": { "type": "string", "format": "datetime" }, - "did": { "type": "string", "format": "did" }, - "author": { "type": "ref", "ref": "org.hypercerts.api.defs#actorView" }, - "record": { "type": "ref", "ref": "org.hypercerts.funding.receipt" } + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.funding.receipt" + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.funding.listReceipts.json b/docs/sources/lexicons/org.hypercerts.funding.listReceipts.json index 02d5ddb..3ab8d2b 100644 --- a/docs/sources/lexicons/org.hypercerts.funding.listReceipts.json +++ b/docs/sources/lexicons/org.hypercerts.funding.listReceipts.json @@ -4,81 +4,116 @@ "defs": { "main": { "type": "query", - "description": "Lists indexed funding receipt records without collapsing payments. Distinct filters combine with AND; values within each array use OR. from and to accept plain strings but the server must reject the whole request if any entry is not a DID or full record AT-URI, identifying the parameter and entry, explaining the accepted formats, and telling callers to resolve handles to DIDs. A DID matches only the party's DID variant; an AT-URI matches only its strong-reference URI, without CID comparison. These filters do not match free text or resolve wallets; omitted from adds no sender condition. forUris matches record.for.uri without CID comparison. transactionIds match exact identifiers without normalization. Repeated unbracketed array keys accept at most 100 values each; duplicates are removed. Results sort by (createdAt, uri), default desc, with an opaque cursor bound to sortDirection. The full receipt record and hydrated publisher are returned without target expansion or a duplicate parties projection. A receipt is not proof of payment; operational failures return errors, not empty pages. Authentication is not required.", + "description": "Lists indexed funding receipts matching the supplied filters: filters combine with AND, while values within an array combine with OR. Repeat array parameters to supply multiple values; duplicates are ignored. Authentication is not required.", "parameters": { "type": "params", "properties": { "authors": { "type": "array", "maxLength": 100, - "description": "Publisher repository DIDs, including facilitator, personal, and group repositories; OR within this filter.", - "items": { "type": "string", "format": "did" } + "description": "Publisher repository DIDs, including facilitator, personal, and group repositories.", + "items": { + "type": "string", + "format": "did" + } }, "uris": { "type": "array", "maxLength": 100, - "description": "Exact funding-receipt AT-URIs for bounded batch hydration; OR within this filter.", - "items": { "type": "string", "format": "at-uri" } + "description": "Funding-receipt AT-URIs with a DID authority, matched by URI only, not CID.", + "items": { + "type": "string", + "format": "at-uri" + } }, "from": { "type": "array", "maxLength": 100, - "description": "Mixed DID or full record AT-URI identifiers. DIDs match only the record's from DID variant; AT-URIs match only its strong-reference URI. Free text and wallet-to-DID expansion do not match; invalid entries reject the whole request with the parameter and entry identified.", - "items": { "type": "string" } + "description": "Sender DIDs or full record AT-URIs with a DID authority. DIDs match only the DID variant; AT-URIs match only the strong-reference URI, not CID. Resolve handles to DIDs; invalid entries reject the request with the parameter and entry identified.", + "items": { + "type": "string" + } }, "to": { "type": "array", "maxLength": 100, - "description": "Mixed DID or full record AT-URI identifiers. DIDs match only the record's to DID variant; AT-URIs match only its strong-reference URI. Free text and wallet-to-DID expansion do not match; invalid entries reject the whole request with the parameter and entry identified.", - "items": { "type": "string" } + "description": "Recipient DIDs or full record AT-URIs with a DID authority. DIDs match only the DID variant; AT-URIs match only the strong-reference URI, not CID. Resolve handles to DIDs; invalid entries reject the request with the parameter and entry identified.", + "items": { + "type": "string" + } }, "forUris": { "type": "array", "maxLength": 100, - "description": "Funded-target AT-URIs matched against record.for.uri only, without CID comparison.", - "items": { "type": "string", "format": "at-uri" } + "description": "Funded-target AT-URIs with a DID authority, matched by URI only; CID is not compared.", + "items": { + "type": "string", + "format": "at-uri" + } }, "transactionIds": { "type": "array", "maxLength": 100, - "description": "Exact transaction identifier matches; values are not normalized or generically lowercased.", - "items": { "type": "string" } + "description": "Exact transaction ID matches; values are not normalized.", + "items": { + "type": "string" + } }, "sortDirection": { "type": "string", - "enum": ["asc", "desc"], - "description": "Sort direction for (createdAt, uri); default desc." + "enum": [ + "asc", + "desc" + ], + "description": "Direction for ordering by createdAt then URI; defaults to desc." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, - "description": "Maximum receipts in the page; default 25." + "description": "Maximum receipts per page; default 25." }, "cursor": { "type": "string", - "description": "Opaque pagination cursor bound to sortDirection; keep other parameters unchanged between pages." + "description": "Opaque cursor bound to sortDirection; keep all other parameters unchanged between pages." } } }, "output": { "encoding": "application/json", - "schema": { "type": "ref", "ref": "#output" } + "schema": { + "type": "ref", + "ref": "#output" + } }, "errors": [ - { "name": "InvalidRequest", "description": "A filter or page parameter is invalid. For from/to values, identify the failing parameter and entry, require a valid DID or full record AT-URI, and tell callers to resolve handles to DIDs." }, - { "name": "ReceiptQueryFailed", "description": "The receipt query or publisher hydration lookup failed; retry after checking the HappyView PostgreSQL service." } + { + "name": "InvalidRequest", + "description": "A filter or pagination parameter is invalid." + }, + { + "name": "ReceiptQueryFailed", + "description": "The receipt query or publisher hydration failed." + } ] }, "output": { "type": "object", - "required": ["receipts"], + "required": [ + "receipts" + ], "properties": { "receipts": { "type": "array", - "items": { "type": "ref", "ref": "org.hypercerts.funding.getReceipt#receiptView" } + "items": { + "type": "ref", + "ref": "org.hypercerts.funding.getReceipt#receiptView" + } }, - "cursor": { "type": "string", "description": "Opaque cursor for the next page; omitted when there is no next page." } + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when there is no next page." + } } } } diff --git a/docs/sources/lexicons/org.hypercerts.funding.receipt.json b/docs/sources/lexicons/org.hypercerts.funding.receipt.json new file mode 100644 index 0000000..71a22a0 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.funding.receipt.json @@ -0,0 +1,104 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.funding.receipt", + "defs": { + "main": { + "type": "record", + "description": "Records a funding receipt for a payment from one user to another user. It may be recorded by the recipient, by the sender, or by a third party. The sender may remain anonymous.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "to", + "amount", + "currency", + "createdAt" + ], + "properties": { + "from": { + "type": "union", + "description": "The sender of the funds (a free-text string, an account DID, or a strong reference to a record). Optional — omit to represent anonymity.", + "refs": [ + "#text", + "app.certified.defs#did", + "com.atproto.repo.strongRef" + ] + }, + "to": { + "type": "union", + "description": "The recipient of the funds (a free-text string, an account DID, or a strong reference to a record).", + "refs": [ + "#text", + "app.certified.defs#did", + "com.atproto.repo.strongRef" + ] + }, + "amount": { + "type": "string", + "description": "Amount of funding received as a numeric string (e.g. '1000.50').", + "maxLength": 50 + }, + "currency": { + "type": "string", + "description": "Currency of the payment (e.g. EUR, USD, ETH).", + "maxLength": 10 + }, + "paymentRail": { + "type": "string", + "description": "How the funds were transferred (e.g. bank_transfer, credit_card, onchain, cash, check, payment_processor).", + "maxLength": 50 + }, + "paymentNetwork": { + "type": "string", + "description": "Optional network within the payment rail (e.g. arbitrum, ethereum, sepa, visa, paypal).", + "maxLength": 50 + }, + "transactionId": { + "type": "string", + "description": "Identifier of the underlying payment transaction (e.g. bank reference, onchain transaction hash, or processor-specific ID). Use paymentNetwork to specify the network where applicable.", + "maxLength": 256 + }, + "for": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Optional strong reference to the activity, project, or organization this funding relates to." + }, + "notes": { + "type": "string", + "description": "Optional notes or additional context for this funding receipt.", + "maxLength": 500 + }, + "occurredAt": { + "type": "string", + "format": "datetime", + "description": "Timestamp when the payment occurred." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this receipt record was created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + }, + "text": { + "type": "object", + "description": "A free-text string value (e.g. a display name, wallet address, or other identifier).", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "string", + "description": "The string value.", + "maxLength": 2048 + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.vocab.getVocabTag.json b/docs/sources/lexicons/org.hypercerts.vocab.getVocabTag.json new file mode 100644 index 0000000..645391f --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.vocab.getVocabTag.json @@ -0,0 +1,97 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.vocab.getVocabTag", + "defs": { + "main": { + "type": "query", + "description": "Looks up one published vocabulary tag by full DID-authority AT-URI and returns its unchanged record with publisher details; authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full AT-URI of the vocabulary-tag record, using a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid, targets another collection, or query parameters are unsupported." + }, + { + "name": "RecordNotFound", + "description": "No indexed vocabulary tag exists at this AT-URI." + }, + { + "name": "VocabTagQueryFailed", + "description": "The indexed tag or required publisher lookup failed; retry after checking the HappyView PostgreSQL service." + } + ] + }, + "output": { + "type": "object", + "required": [ + "vocabTag" + ], + "properties": { + "vocabTag": { + "type": "ref", + "ref": "#vocabTagView" + } + } + }, + "vocabTagView": { + "type": "object", + "description": "Vocabulary-tag record with its unchanged source and hydrated publisher actor.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.vocab.tag" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.vocab.listVocabTags.json b/docs/sources/lexicons/org.hypercerts.vocab.listVocabTags.json new file mode 100644 index 0000000..9deb202 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.vocab.listVocabTags.json @@ -0,0 +1,80 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.vocab.listVocabTags", + "defs": { + "main": { + "type": "query", + "description": "Lists published vocabulary tags by optional publisher DIDs with stable cursor pagination; authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Publisher repository DIDs; values are ORed.", + "items": { + "type": "string", + "format": "did" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Direction for (createdAt, uri); default desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Page size from 1 to 100; default 25." + }, + "cursor": { + "type": "string", + "description": "Opaque cursor; keep direction and filters unchanged between pages." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A filter value, page bound, cursor, repeated scalar, or query parameter is invalid." + }, + { + "name": "VocabTagQueryFailed", + "description": "The indexed tags or required publisher lookups could not be queried; retry after checking the HappyView PostgreSQL service." + } + ] + }, + "output": { + "type": "object", + "required": [ + "vocabTags" + ], + "properties": { + "vocabTags": { + "type": "array", + "items": { + "type": "ref", + "ref": "org.hypercerts.vocab.getVocabTag#vocabTagView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when pagination is complete." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.vocab.tag.json b/docs/sources/lexicons/org.hypercerts.vocab.tag.json new file mode 100644 index 0000000..c6ab877 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.vocab.tag.json @@ -0,0 +1,116 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.vocab.tag", + "defs": { + "main": { + "type": "record", + "description": "A reusable vocabulary term for classifying any Hypercerts record — for example a land-cover class, a zone role, or a methodology. The vocabulary is domain-general; geospatial categories are simply its first entries. The schema carries portable term data; named vocabulary profiles decide which published terms and attachment points they recognize. Recommended record key: the deterministic form . (for example zone-role.site), as defined by the governing vocabulary profile; Lexicon validation cannot bind a record key to body fields, so writers and indexers enforce that agreement. A tag reference pins the specific version (CID) of the term that the referencing record classified against; consumers resolve the term's current status and supersession by its URI.", + "key": "any", + "record": { + "type": "object", + "required": [ + "key", + "name", + "category", + "status", + "createdAt" + ], + "properties": { + "key": { + "type": "string", + "description": "Stable lowercase machine identifier for this term within its publisher and category, for example boundary or mangrove. Must not contain dots, so that the recommended . record key remains unambiguous.", + "maxLength": 120, + "format": "record-key" + }, + "name": { + "type": "string", + "description": "Human-readable display name of the term.", + "maxLength": 200 + }, + "category": { + "type": "string", + "description": "The classification axis this term belongs to, for example land-cover or zone-role. One term belongs to one category; a subject may carry tags from many categories at once. Values beyond the known set are permitted. Must not contain dots, so that the recommended . record key remains unambiguous.", + "knownValues": [ + "zone-role", + "land-cover", + "ecosystem-type", + "stratum-class", + "tenure", + "methodology", + "outcome-class", + "evidence-type" + ], + "maxLength": 50, + "format": "record-key" + }, + "description": { + "type": "string", + "description": "Definition and scope notes for the term, written so a reader outside the publishing organization can apply it consistently.", + "maxLength": 10000, + "maxGraphemes": 1000 + }, + "broader": { + "type": "array", + "description": "Optional references to directly broader terms, forming a polyhierarchy (a term may have several broader terms). Each referenced record must conform with org.hypercerts.vocab.tag.", + "maxLength": 20, + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + } + }, + "status": { + "type": "string", + "description": "Lifecycle status of the term: proposed (submitted, not yet governed), accepted (in active governed use), or deprecated (retired; see supersededBy). Values beyond the known set are permitted.", + "knownValues": [ + "proposed", + "accepted", + "deprecated" + ], + "maxLength": 20 + }, + "supersededBy": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "The replacement term for a deprecated term, letting consumers roll classifications forward without rewriting published records. The referenced record must conform with org.hypercerts.vocab.tag." + }, + "aliases": { + "type": "array", + "description": "Alternative human-readable labels and abbreviations for search and display. Aliases carry no identity: references always point at the record, not at a label.", + "maxLength": 50, + "items": { + "type": "string", + "maxLength": 200 + } + }, + "sameAs": { + "type": "array", + "description": "URIs of exactly equivalent concepts in external vocabularies, for example ENVO, IUCN GET, OSM tag conventions, or registry methodology identifiers. Exact matches only; typed broader/narrower/close mappings are a named future field.", + "maxLength": 20, + "items": { + "type": "string", + "format": "uri" + } + }, + "referenceDocument": { + "type": "union", + "description": "A document defining or motivating this term, as a URI or an attached small blob.", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallBlob" + ] + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.workscope.cel.json b/docs/sources/lexicons/org.hypercerts.workscope.cel.json new file mode 100644 index 0000000..ea69f5d --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.workscope.cel.json @@ -0,0 +1,46 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.workscope.cel", + "defs": { + "main": { + "type": "object", + "description": "A structured, machine-evaluable work scope definition using CEL (Common Expression Language). Tags referenced in the expression correspond to org.hypercerts.workscope.tag keys. See https://github.com/google/cel-spec. Note: this is intentionally type 'object' (not 'record') so it can be directly embedded inline in union types (e.g., activity.workScope) without requiring a separate collection or strongRef indirection.", + "required": [ + "expression", + "usedTags", + "version", + "createdAt" + ], + "properties": { + "expression": { + "type": "string", + "maxLength": 10000, + "maxGraphemes": 5000, + "description": "A CEL expression encoding the work scope conditions. Example: scope.hasAll(['mangrove_restoration', 'environmental_education']) && location.country == 'KE'" + }, + "usedTags": { + "type": "array", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100, + "description": "Strong references to org.hypercerts.workscope.tag records used in the expression. Enables fast indexing by AT-URI and provides referential integrity to the underlying tag records." + }, + "version": { + "type": "string", + "maxLength": 16, + "knownValues": [ + "v1" + ], + "description": "CEL context schema version." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this expression was originally created." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.workscope.getWorkscopeTag.json b/docs/sources/lexicons/org.hypercerts.workscope.getWorkscopeTag.json new file mode 100644 index 0000000..a432f5c --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.workscope.getWorkscopeTag.json @@ -0,0 +1,97 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.workscope.getWorkscopeTag", + "defs": { + "main": { + "type": "query", + "description": "Returns an indexed work-scope tag and its hydrated publisher for an exact AT-URI; authentication is not required.", + "parameters": { + "type": "params", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "Full work-scope-tag AT-URI using a DID authority." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "The URI is invalid or is not a work-scope-tag AT-URI." + }, + { + "name": "RecordNotFound", + "description": "No indexed work-scope tag exists at this AT-URI." + }, + { + "name": "WorkscopeTagQueryFailed", + "description": "The record lookup or publisher hydration failed." + } + ] + }, + "workscopeTagView": { + "type": "object", + "description": "Work-scope tag view with its hydrated publisher, unchanged record, and nullable index timestamp.", + "required": [ + "uri", + "cid", + "indexedAt", + "did", + "author", + "record" + ], + "nullable": [ + "indexedAt" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "indexedAt": { + "type": "string", + "format": "datetime" + }, + "did": { + "type": "string", + "format": "did" + }, + "author": { + "type": "ref", + "ref": "org.hypercerts.api.defs#actorView" + }, + "record": { + "type": "ref", + "ref": "org.hypercerts.workscope.tag" + } + } + }, + "output": { + "type": "object", + "required": [ + "workscopeTag" + ], + "properties": { + "workscopeTag": { + "type": "ref", + "ref": "#workscopeTagView" + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.workscope.listWorkscopeTags.json b/docs/sources/lexicons/org.hypercerts.workscope.listWorkscopeTags.json new file mode 100644 index 0000000..5ecc069 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.workscope.listWorkscopeTags.json @@ -0,0 +1,82 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.workscope.listWorkscopeTags", + "defs": { + "main": { + "type": "query", + "description": "Lists indexed work-scope tags by publisher DID with stable timestamp-and-URI ordering; authentication is not required.", + "parameters": { + "type": "params", + "properties": { + "authors": { + "type": "array", + "maxLength": 100, + "description": "Publisher repository DIDs; repeat this key for OR matching, up to 100 values.", + "items": { + "type": "string", + "format": "did" + } + }, + "sortDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc", + "description": "Sort direction for timestamp-and-URI ordering; defaults to desc." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25, + "description": "Maximum page size from 1 to 100; defaults to 25." + }, + "cursor": { + "type": "string", + "maxLength": 8192, + "description": "Opaque cursor bound to sortDirection; keep other parameters unchanged between pages." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "#output" + } + }, + "errors": [ + { + "name": "InvalidRequest", + "description": "A query parameter is invalid." + }, + { + "name": "WorkscopeTagQueryFailed", + "description": "The record query or publisher hydration failed." + } + ] + }, + "output": { + "type": "object", + "required": [ + "workscopeTags" + ], + "properties": { + "workscopeTags": { + "type": "array", + "maxLength": 100, + "items": { + "type": "ref", + "ref": "org.hypercerts.workscope.getWorkscopeTag#workscopeTagView" + } + }, + "cursor": { + "type": "string", + "description": "Opaque cursor for the next page; omitted when no next page exists." + } + } + } + } +} diff --git a/docs/sources/lexicons/org.hypercerts.workscope.tag.json b/docs/sources/lexicons/org.hypercerts.workscope.tag.json new file mode 100644 index 0000000..ad3c165 --- /dev/null +++ b/docs/sources/lexicons/org.hypercerts.workscope.tag.json @@ -0,0 +1,105 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.workscope.tag", + "defs": { + "main": { + "type": "record", + "description": "A reusable scope atom for work scope logic expressions. Scopes can represent topics, languages, domains, deliverables, methods, regions, tags, or other categorical labels. Tags are composed into structured expressions via CEL (Common Expression Language) on activity records.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "key", + "name", + "createdAt" + ], + "properties": { + "key": { + "type": "string", + "description": "Lowercase, underscore-separated machine-readable key for this scope (e.g., 'mangrove_restoration', 'biodiversity_monitoring'). Used as the canonical identifier in CEL expressions.", + "maxLength": 120 + }, + "name": { + "type": "string", + "description": "Human-readable name for this scope.", + "maxLength": 200 + }, + "category": { + "type": "string", + "description": "Category type of this scope. Values beyond the known set are permitted.", + "knownValues": [ + "topic", + "language", + "domain", + "method" + ], + "maxLength": 50 + }, + "description": { + "type": "string", + "description": "Optional longer description of this scope.", + "maxLength": 10000, + "maxGraphemes": 1000 + }, + "parent": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Optional strong reference to a parent work scope tag record for taxonomy/hierarchy support. The record referenced must conform with the lexicon org.hypercerts.workscope.tag." + }, + "status": { + "type": "string", + "description": "Lifecycle status of this tag. Communities propose tags, curators accept them, deprecated tags point to replacements via supersededBy. Values beyond the known set are permitted.", + "knownValues": [ + "proposed", + "accepted", + "deprecated" + ], + "maxLength": 20 + }, + "supersededBy": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "When status is 'deprecated', points to the replacement work scope tag record. The record referenced must conform with the lexicon org.hypercerts.workscope.tag." + }, + "aliases": { + "type": "array", + "items": { + "type": "string", + "maxLength": 200 + }, + "maxLength": 50, + "description": "Alternative human-readable names for this scope (e.g., translations, abbreviations, or common synonyms). Unlike sameAs, these are plain-text labels, not links to external ontologies." + }, + "sameAs": { + "type": "array", + "items": { + "type": "string", + "format": "uri", + "maxLength": 2048 + }, + "maxLength": 20, + "description": "URIs to semantically equivalent concepts in external ontologies or taxonomies (e.g., Wikidata QIDs, ENVO terms, SDG targets). Used for interoperability, not as documentation." + }, + "referenceDocument": { + "type": "union", + "refs": [ + "org.hypercerts.defs#uri", + "org.hypercerts.defs#smallBlob" + ], + "description": "Link to a governance or reference document where this work scope tag is defined and further explained." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created." + }, + "signatures": { + "type": "ref", + "ref": "app.certified.signature.defs#list", + "description": "Optional cryptographic signatures attesting to this record's content." + } + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.blockquote.json b/docs/sources/lexicons/pub.leaflet.blocks.blockquote.json new file mode 100644 index 0000000..ac343d2 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.blockquote.json @@ -0,0 +1,24 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.blockquote", + "defs": { + "main": { + "type": "object", + "required": [ + "plaintext" + ], + "properties": { + "plaintext": { + "type": "string" + }, + "facets": { + "type": "array", + "items": { + "type": "ref", + "ref": "pub.leaflet.richtext.facet" + } + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.bskyPost.json b/docs/sources/lexicons/pub.leaflet.blocks.bskyPost.json new file mode 100644 index 0000000..bcc895e --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.bskyPost.json @@ -0,0 +1,21 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.bskyPost", + "defs": { + "main": { + "type": "object", + "required": [ + "postRef" + ], + "properties": { + "postRef": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "clientHost": { + "type": "string" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.button.json b/docs/sources/lexicons/pub.leaflet.blocks.button.json new file mode 100644 index 0000000..471f6b6 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.button.json @@ -0,0 +1,22 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.button", + "defs": { + "main": { + "type": "object", + "required": [ + "text", + "url" + ], + "properties": { + "text": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.code.json b/docs/sources/lexicons/pub.leaflet.blocks.code.json new file mode 100644 index 0000000..80337f6 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.code.json @@ -0,0 +1,23 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.code", + "defs": { + "main": { + "type": "object", + "required": [ + "plaintext" + ], + "properties": { + "plaintext": { + "type": "string" + }, + "language": { + "type": "string" + }, + "syntaxHighlightingTheme": { + "type": "string" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.header.json b/docs/sources/lexicons/pub.leaflet.blocks.header.json new file mode 100644 index 0000000..ef741ba --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.header.json @@ -0,0 +1,29 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.header", + "defs": { + "main": { + "type": "object", + "required": [ + "plaintext" + ], + "properties": { + "level": { + "type": "integer", + "minimum": 1, + "maximum": 6 + }, + "plaintext": { + "type": "string" + }, + "facets": { + "type": "array", + "items": { + "type": "ref", + "ref": "pub.leaflet.richtext.facet" + } + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.horizontalRule.json b/docs/sources/lexicons/pub.leaflet.blocks.horizontalRule.json new file mode 100644 index 0000000..c194140 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.horizontalRule.json @@ -0,0 +1,11 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.horizontalRule", + "defs": { + "main": { + "type": "object", + "required": [], + "properties": {} + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.html.json b/docs/sources/lexicons/pub.leaflet.blocks.html.json new file mode 100644 index 0000000..2d76eb8 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.html.json @@ -0,0 +1,42 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.html", + "defs": { + "main": { + "type": "object", + "required": [ + "html" + ], + "properties": { + "html": { + "type": "string", + "description": "Inline HTML rendered via a sandboxed iframe's srcdoc attribute." + }, + "height": { + "type": "integer", + "minimum": 16, + "maximum": 1600 + }, + "aspectRatio": { + "type": "ref", + "ref": "#aspectRatio" + } + } + }, + "aspectRatio": { + "type": "object", + "required": [ + "width", + "height" + ], + "properties": { + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.iframe.json b/docs/sources/lexicons/pub.leaflet.blocks.iframe.json new file mode 100644 index 0000000..c87739c --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.iframe.json @@ -0,0 +1,43 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.iframe", + "defs": { + "main": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" + }, + "html": { + "type": "string", + "description": "DEPRECATED — use pub.leaflet.blocks.html instead. Inline HTML rendered via the iframe's srcdoc attribute. Takes precedence over url." + }, + "height": { + "type": "integer", + "minimum": 16, + "maximum": 1600 + }, + "aspectRatio": { + "type": "ref", + "ref": "#aspectRatio" + } + } + }, + "aspectRatio": { + "type": "object", + "required": [ + "width", + "height" + ], + "properties": { + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.image.json b/docs/sources/lexicons/pub.leaflet.blocks.image.json new file mode 100644 index 0000000..4536af9 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.image.json @@ -0,0 +1,53 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.image", + "defs": { + "main": { + "type": "object", + "required": [ + "image", + "aspectRatio" + ], + "properties": { + "image": { + "type": "blob", + "accept": [ + "image/*" + ], + "maxSize": 1000000 + }, + "alt": { + "type": "string", + "description": "Alt text description of the image, for accessibility." + }, + "aspectRatio": { + "type": "ref", + "ref": "#aspectRatio" + }, + "fullBleed": { + "type": "boolean", + "description": "Whether the image should extend to the full width of the container, ignoring padding." + }, + "width": { + "type": "integer", + "description": "Display width of the image in pixels, capped at the page width." + } + } + }, + "aspectRatio": { + "type": "object", + "required": [ + "width", + "height" + ], + "properties": { + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.imageGallery.json b/docs/sources/lexicons/pub.leaflet.blocks.imageGallery.json new file mode 100644 index 0000000..1dc57ef --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.imageGallery.json @@ -0,0 +1,76 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.imageGallery", + "defs": { + "main": { + "type": "object", + "required": [ + "images" + ], + "properties": { + "gap": { + "type": "integer", + "description": "Gap between images in pixels." + }, + "format": { + "type": "string", + "knownValues": [ + "grid", + "carousel", + "strip" + ] + }, + "images": { + "type": "array", + "items": { + "type": "ref", + "ref": "#image" + } + }, + "maxWidth": { + "type": "integer", + "description": "Max width per image in grid view (px); drives how many columns fit." + } + } + }, + "image": { + "type": "object", + "required": [ + "image", + "aspectRatio" + ], + "properties": { + "alt": { + "type": "string", + "description": "Alt text description of the image, for accessibility." + }, + "image": { + "type": "blob", + "accept": [ + "image/*" + ], + "maxSize": 1000000 + }, + "aspectRatio": { + "type": "ref", + "ref": "#aspectRatio" + } + } + }, + "aspectRatio": { + "type": "object", + "required": [ + "width", + "height" + ], + "properties": { + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.math.json b/docs/sources/lexicons/pub.leaflet.blocks.math.json new file mode 100644 index 0000000..9847c05 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.math.json @@ -0,0 +1,17 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.math", + "defs": { + "main": { + "type": "object", + "required": [ + "tex" + ], + "properties": { + "tex": { + "type": "string" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.membersOnlyDelimiter.json b/docs/sources/lexicons/pub.leaflet.blocks.membersOnlyDelimiter.json new file mode 100644 index 0000000..188f0fb --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.membersOnlyDelimiter.json @@ -0,0 +1,12 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.membersOnlyDelimiter", + "defs": { + "main": { + "type": "object", + "description": "Marks where members-only content begins; blocks after this delimiter are only served to readers with an active paid membership.", + "required": [], + "properties": {} + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.orderedList.json b/docs/sources/lexicons/pub.leaflet.blocks.orderedList.json new file mode 100644 index 0000000..6278b4a --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.orderedList.json @@ -0,0 +1,58 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.orderedList", + "defs": { + "main": { + "type": "object", + "required": [ + "children" + ], + "properties": { + "startIndex": { + "type": "integer", + "description": "The starting number for this ordered list. Defaults to 1 if not specified." + }, + "children": { + "type": "array", + "items": { + "type": "ref", + "ref": "#listItem" + } + } + } + }, + "listItem": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "checked": { + "type": "boolean", + "description": "If present, this item is a checklist item. true = checked, false = unchecked. If absent, this is a normal list item." + }, + "content": { + "type": "union", + "refs": [ + "pub.leaflet.blocks.text", + "pub.leaflet.blocks.header", + "pub.leaflet.blocks.image" + ] + }, + "children": { + "type": "array", + "description": "Nested ordered list items. Mutually exclusive with unorderedListChildren; if both are present, children takes precedence.", + "items": { + "type": "ref", + "ref": "#listItem" + } + }, + "unorderedListChildren": { + "type": "ref", + "description": "A nested unordered list. Mutually exclusive with children; if both are present, children takes precedence.", + "ref": "pub.leaflet.blocks.unorderedList" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.page.json b/docs/sources/lexicons/pub.leaflet.blocks.page.json new file mode 100644 index 0000000..2b20da5 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.page.json @@ -0,0 +1,17 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.page", + "defs": { + "main": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.poll.json b/docs/sources/lexicons/pub.leaflet.blocks.poll.json new file mode 100644 index 0000000..06a8bbb --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.poll.json @@ -0,0 +1,18 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.poll", + "defs": { + "main": { + "type": "object", + "required": [ + "pollRef" + ], + "properties": { + "pollRef": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.postsList.json b/docs/sources/lexicons/pub.leaflet.blocks.postsList.json new file mode 100644 index 0000000..cf55eb4 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.postsList.json @@ -0,0 +1,33 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.postsList", + "defs": { + "main": { + "type": "object", + "required": [], + "properties": { + "view": { + "type": "string", + "knownValues": [ + "small", + "medium" + ] + }, + "limit": { + "type": "integer", + "minimum": 1, + "description": "Show at most this many posts." + }, + "filterByTags": { + "type": "array", + "items": { + "type": "string" + } + }, + "highlightFirstPost": { + "type": "boolean" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.signup.json b/docs/sources/lexicons/pub.leaflet.blocks.signup.json new file mode 100644 index 0000000..d482024 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.signup.json @@ -0,0 +1,12 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.signup", + "defs": { + "main": { + "type": "object", + "description": "A subscribe/signup form for the publication. Renders the publication's subscribe form; carries no configurable data.", + "required": [], + "properties": {} + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.standardSitePost.json b/docs/sources/lexicons/pub.leaflet.blocks.standardSitePost.json new file mode 100644 index 0000000..dca0fa1 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.standardSitePost.json @@ -0,0 +1,32 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.standardSitePost", + "defs": { + "main": { + "type": "object", + "required": [ + "uri" + ], + "properties": { + "cid": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "size": { + "type": "string", + "knownValues": [ + "large", + "medium", + "small" + ] + }, + "showPublicationTheme": { + "type": "boolean" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.standardSitePublication.json b/docs/sources/lexicons/pub.leaflet.blocks.standardSitePublication.json new file mode 100644 index 0000000..8adbc38 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.standardSitePublication.json @@ -0,0 +1,24 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.standardSitePublication", + "defs": { + "main": { + "type": "object", + "required": [ + "uri" + ], + "properties": { + "cid": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "showPublicationTheme": { + "type": "boolean" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.text.json b/docs/sources/lexicons/pub.leaflet.blocks.text.json new file mode 100644 index 0000000..e8e3e97 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.text.json @@ -0,0 +1,32 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.text", + "defs": { + "main": { + "type": "object", + "required": [ + "plaintext" + ], + "properties": { + "plaintext": { + "type": "string" + }, + "textSize": { + "type": "string", + "enum": [ + "default", + "small", + "large" + ] + }, + "facets": { + "type": "array", + "items": { + "type": "ref", + "ref": "pub.leaflet.richtext.facet" + } + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.unorderedList.json b/docs/sources/lexicons/pub.leaflet.blocks.unorderedList.json new file mode 100644 index 0000000..3566fa3 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.unorderedList.json @@ -0,0 +1,54 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.unorderedList", + "defs": { + "main": { + "type": "object", + "required": [ + "children" + ], + "properties": { + "children": { + "type": "array", + "items": { + "type": "ref", + "ref": "#listItem" + } + } + } + }, + "listItem": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "checked": { + "type": "boolean", + "description": "If present, this item is a checklist item. true = checked, false = unchecked. If absent, this is a normal list item." + }, + "content": { + "type": "union", + "refs": [ + "pub.leaflet.blocks.text", + "pub.leaflet.blocks.header", + "pub.leaflet.blocks.image" + ] + }, + "children": { + "type": "array", + "description": "Nested unordered list items. Mutually exclusive with orderedListChildren; if both are present, children takes precedence.", + "items": { + "type": "ref", + "ref": "#listItem" + } + }, + "orderedListChildren": { + "type": "ref", + "description": "Nested ordered list items. Mutually exclusive with children; if both are present, children takes precedence.", + "ref": "pub.leaflet.blocks.orderedList" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.blocks.website.json b/docs/sources/lexicons/pub.leaflet.blocks.website.json new file mode 100644 index 0000000..6725845 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.blocks.website.json @@ -0,0 +1,31 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.blocks.website", + "defs": { + "main": { + "type": "object", + "required": [ + "src" + ], + "properties": { + "previewImage": { + "type": "blob", + "accept": [ + "image/*" + ], + "maxSize": 1000000 + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "src": { + "type": "string", + "format": "uri" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.pages.linearDocument.json b/docs/sources/lexicons/pub.leaflet.pages.linearDocument.json new file mode 100644 index 0000000..28b751e --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.pages.linearDocument.json @@ -0,0 +1,115 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.pages.linearDocument", + "defs": { + "main": { + "type": "object", + "required": [ + "blocks" + ], + "properties": { + "id": { + "type": "string" + }, + "blocks": { + "type": "array", + "items": { + "type": "ref", + "ref": "#block" + } + } + } + }, + "block": { + "type": "object", + "required": [ + "block" + ], + "properties": { + "block": { + "type": "union", + "refs": [ + "pub.leaflet.blocks.iframe", + "pub.leaflet.blocks.html", + "pub.leaflet.blocks.text", + "pub.leaflet.blocks.blockquote", + "pub.leaflet.blocks.header", + "pub.leaflet.blocks.image", + "pub.leaflet.blocks.imageGallery", + "pub.leaflet.blocks.unorderedList", + "pub.leaflet.blocks.orderedList", + "pub.leaflet.blocks.website", + "pub.leaflet.blocks.math", + "pub.leaflet.blocks.code", + "pub.leaflet.blocks.horizontalRule", + "pub.leaflet.blocks.bskyPost", + "pub.leaflet.blocks.standardSitePost", + "pub.leaflet.blocks.standardSitePublication", + "pub.leaflet.blocks.page", + "pub.leaflet.blocks.poll", + "pub.leaflet.blocks.button", + "pub.leaflet.blocks.postsList", + "pub.leaflet.blocks.signup", + "pub.leaflet.blocks.membersOnlyDelimiter" + ] + }, + "alignment": { + "type": "string", + "knownValues": [ + "#textAlignLeft", + "#textAlignCenter", + "#textAlignRight", + "#textAlignJustify" + ] + } + } + }, + "textAlignLeft": { + "type": "token" + }, + "textAlignCenter": { + "type": "token" + }, + "textAlignRight": { + "type": "token" + }, + "textAlignJustify": { + "type": "token" + }, + "quote": { + "type": "object", + "required": [ + "start", + "end" + ], + "properties": { + "start": { + "type": "ref", + "ref": "#position" + }, + "end": { + "type": "ref", + "ref": "#position" + } + } + }, + "position": { + "type": "object", + "required": [ + "block", + "offset" + ], + "properties": { + "block": { + "type": "array", + "items": { + "type": "integer" + } + }, + "offset": { + "type": "integer" + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.richtext.facet.json b/docs/sources/lexicons/pub.leaflet.richtext.facet.json new file mode 100644 index 0000000..01f8295 --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.richtext.facet.json @@ -0,0 +1,176 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.richtext.facet", + "defs": { + "main": { + "type": "object", + "description": "Annotation of a sub-string within rich text.", + "required": [ + "index", + "features" + ], + "properties": { + "index": { + "type": "ref", + "ref": "#byteSlice" + }, + "features": { + "type": "array", + "items": { + "type": "union", + "refs": [ + "#link", + "#didMention", + "#atMention", + "#code", + "#highlight", + "#underline", + "#strikethrough", + "#id", + "#bold", + "#italic", + "#footnote" + ] + } + } + } + }, + "byteSlice": { + "type": "object", + "description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.", + "required": [ + "byteStart", + "byteEnd" + ], + "properties": { + "byteStart": { + "type": "integer", + "minimum": 0 + }, + "byteEnd": { + "type": "integer", + "minimum": 0 + } + } + }, + "link": { + "type": "object", + "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", + "required": [ + "uri" + ], + "properties": { + "uri": { + "type": "string" + } + } + }, + "didMention": { + "type": "object", + "description": "Facet feature for mentioning a did.", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + } + } + }, + "atMention": { + "type": "object", + "description": "Facet feature for mentioning an AT URI.", + "required": [ + "atURI" + ], + "properties": { + "atURI": { + "type": "string", + "format": "uri" + }, + "href": { + "type": "string", + "format": "uri" + } + } + }, + "code": { + "type": "object", + "description": "Facet feature for inline code.", + "required": [], + "properties": {} + }, + "highlight": { + "type": "object", + "description": "Facet feature for highlighted text.", + "required": [], + "properties": { + "color": { + "type": "union", + "refs": [ + "pub.leaflet.theme.color#rgba", + "pub.leaflet.theme.color#rgb" + ] + } + } + }, + "underline": { + "type": "object", + "description": "Facet feature for underline markup", + "required": [], + "properties": {} + }, + "strikethrough": { + "type": "object", + "description": "Facet feature for strikethrough markup", + "required": [], + "properties": {} + }, + "id": { + "type": "object", + "description": "Facet feature for an identifier. Used for linking to a segment", + "required": [], + "properties": { + "id": { + "type": "string" + } + } + }, + "bold": { + "type": "object", + "description": "Facet feature for bold text", + "required": [], + "properties": {} + }, + "italic": { + "type": "object", + "description": "Facet feature for italic text", + "required": [], + "properties": {} + }, + "footnote": { + "type": "object", + "description": "Facet feature for a footnote reference", + "required": [ + "footnoteId", + "contentPlaintext" + ], + "properties": { + "footnoteId": { + "type": "string" + }, + "contentPlaintext": { + "type": "string" + }, + "contentFacets": { + "type": "array", + "items": { + "type": "ref", + "ref": "#main" + } + } + } + } + } +} diff --git a/docs/sources/lexicons/pub.leaflet.theme.color.json b/docs/sources/lexicons/pub.leaflet.theme.color.json new file mode 100644 index 0000000..a06188c --- /dev/null +++ b/docs/sources/lexicons/pub.leaflet.theme.color.json @@ -0,0 +1,62 @@ +{ + "lexicon": 1, + "id": "pub.leaflet.theme.color", + "defs": { + "rgb": { + "type": "object", + "required": [ + "r", + "g", + "b" + ], + "properties": { + "b": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "g": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "r": { + "type": "integer", + "minimum": 0, + "maximum": 255 + } + } + }, + "rgba": { + "type": "object", + "required": [ + "r", + "g", + "b", + "a" + ], + "properties": { + "a": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "b": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "g": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "r": { + "type": "integer", + "minimum": 0, + "maximum": 255 + } + } + } + } +} diff --git a/docs/test/documentation-freshness.test.mjs b/docs/test/documentation-freshness.test.mjs new file mode 100644 index 0000000..d182aee --- /dev/null +++ b/docs/test/documentation-freshness.test.mjs @@ -0,0 +1,147 @@ +import assert from 'node:assert/strict'; +import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import test from 'node:test'; +import { buildDocumentationArtifacts } from '../scripts/openapi-artifacts.mjs'; +import { assertDocumentationFresh } from '../scripts/check-sources.mjs'; +import { collectDocumentationSources } from '../scripts/refresh-sources.mjs'; + +const docsRoot = path.resolve(import.meta.dirname, '..'); + +async function writeJson(file, value) { + await mkdir(path.dirname(file), { recursive: true }); + await writeFile(file, `${JSON.stringify(value, null, 2)}\n`); +} + +async function readJson(file) { + return JSON.parse(await readFile(file, 'utf8')); +} + +async function writeBaselineArtifacts(apiRoot, fixtureDocs, packageRoot) { + await mkdir(path.join(fixtureDocs, 'sources/lexicons'), { recursive: true }); + const { index, snapshots } = await collectDocumentationSources({ + apiRoot, + docsRoot: fixtureDocs, + lexiconPackageRoot: packageRoot, + }); + for (const snapshot of snapshots) await writeFile(snapshot.file, snapshot.content); + const { openapi, coverage } = buildDocumentationArtifacts( + index, + snapshots.map(({ content }) => JSON.parse(content)), + ); + await writeJson(path.join(fixtureDocs, 'sources/index.json'), index); + await writeJson(path.join(fixtureDocs, 'openapi.json'), openapi); + await writeJson(path.join(fixtureDocs, 'coverage.json'), coverage); + return { index, openapi, coverage }; +} + +test('committed snapshots, index metadata, OpenAPI, and coverage match fresh manifest sources', async () => { + await assertDocumentationFresh(); +}); + +test('freshness gate rejects endpoint and pinned support-schema drift without writing artifacts', async (t) => { + const fixtureRoot = await mkdtemp(path.join(os.tmpdir(), 'api-docs-freshness-test-')); + t.after(() => rm(fixtureRoot, { recursive: true, force: true })); + const apiRoot = path.join(fixtureRoot, 'api'); + const fixtureDocs = path.join(fixtureRoot, 'docs'); + const packageRoot = path.join(fixtureRoot, 'pinned-lexicons'); + const moduleDirectory = path.join(apiRoot, 'modules/demo'); + const queryPath = path.join(apiRoot, 'lexicons/demo.search.json'); + const schemaPath = path.join(packageRoot, 'lexicons/demo/support.json'); + + const query = { + lexicon: 1, + id: 'demo.search', + defs: { + main: { + type: 'query', + parameters: { + type: 'params', + properties: { term: { type: 'string', description: 'Original search term.' } }, + }, + output: { encoding: 'application/json', schema: { type: 'ref', ref: '#output' } }, + }, + output: { + type: 'object', + properties: { result: { type: 'ref', ref: 'demo.support#view' } }, + }, + }, + }; + const supportSchema = { + lexicon: 1, + id: 'demo.support', + defs: { + main: { type: 'record', record: { type: 'ref', ref: '#view' } }, + view: { type: 'object', properties: { value: { type: 'string', description: 'Original value.' } } }, + }, + }; + const writeFixture = async () => { + await writeJson(path.join(apiRoot, 'package.json'), { + dependencies: { '@hypercerts-org/lexicon': '1.4.0' }, + }); + await writeJson(path.join(apiRoot, 'manifest.json'), { + modules: ['modules/demo/manifest.json'], + validationLexicons: [{ id: supportSchema.id, packagePath: 'lexicons/demo/support.json' }], + }); + await writeJson(path.join(moduleDirectory, 'manifest.json'), { + assets: [{ kind: 'lexicon', id: query.id, path: '../../lexicons/demo.search.json' }], + }); + await writeJson(queryPath, query); + await writeJson(path.join(packageRoot, 'package.json'), { + name: '@hypercerts-org/lexicon', + version: '1.4.0', + }); + await writeJson(schemaPath, supportSchema); + }; + + await mkdir(moduleDirectory, { recursive: true }); + await mkdir(path.join(apiRoot, 'lexicons'), { recursive: true }); + await mkdir(fixtureDocs, { recursive: true }); + await writeFixture(); + const baseline = await writeBaselineArtifacts(apiRoot, fixtureDocs, packageRoot); + await assertDocumentationFresh({ apiRoot, docsRoot: fixtureDocs, lexiconPackageRoot: packageRoot }); + const baselineOpenApi = await readFile(path.join(fixtureDocs, 'openapi.json'), 'utf8'); + + query.defs.main.parameters.properties.term.description = 'Changed canonical search term.'; + await writeJson(queryPath, query); + await assert.rejects( + assertDocumentationFresh({ apiRoot, docsRoot: fixtureDocs, lexiconPackageRoot: packageRoot }), + (error) => { + assert.match(error.message, /sources\/lexicons\/demo\.search\.json/); + assert.match(error.message, /openapi\.json/); + return true; + }, + ); + assert.equal(await readFile(path.join(fixtureDocs, 'openapi.json'), 'utf8'), baselineOpenApi); + query.defs.main.parameters.properties.term.description = 'Original search term.'; + await writeJson(queryPath, query); + + supportSchema.defs.view.properties.value.type = 'integer'; + await writeJson(schemaPath, supportSchema); + await assert.rejects( + assertDocumentationFresh({ apiRoot, docsRoot: fixtureDocs, lexiconPackageRoot: packageRoot }), + (error) => { + assert.match(error.message, /sources\/lexicons\/demo\.support\.json/); + assert.match(error.message, /openapi\.json/); + return true; + }, + ); + assert.equal(await readFile(path.join(fixtureDocs, 'openapi.json'), 'utf8'), baselineOpenApi); + + const staleCoverage = structuredClone(baseline.coverage); + staleCoverage.runtimeValidation = 'runtime-validated'; + await writeJson(path.join(fixtureDocs, 'coverage.json'), staleCoverage); + await assert.rejects( + assertDocumentationFresh({ apiRoot, docsRoot: fixtureDocs, lexiconPackageRoot: packageRoot }), + /coverage\.json/, + ); + + const staleIndex = structuredClone(baseline.index); + staleIndex.sourcePolicy = 'stale source metadata'; + await writeJson(path.join(fixtureDocs, 'sources/index.json'), staleIndex); + await assert.rejects( + assertDocumentationFresh({ apiRoot, docsRoot: fixtureDocs, lexiconPackageRoot: packageRoot }), + /sources\/index\.json/, + ); +}); diff --git a/docs/test/generator.test.mjs b/docs/test/generator.test.mjs index f5a04d9..642bc25 100644 --- a/docs/test/generator.test.mjs +++ b/docs/test/generator.test.mjs @@ -57,7 +57,7 @@ test('OpenAPI generation preserves query contract and marks unresolved refs', () assert.equal(typeof generator?.buildOpenApi, 'function', 'openapi.mjs exports buildOpenApi'); const document = generator.buildOpenApi([searchLexicon], { - coverage: { 'app.certified.demo.search': 'branch-only' }, + coverage: { 'app.certified.demo.search': 'manifest-registered' }, sources: { 'app.certified.demo.search': { branch: 'api/demo', commit: 'abc123' } }, }); const operation = document.paths['/xrpc/app.certified.demo.search'].get; @@ -70,7 +70,7 @@ test('OpenAPI generation preserves query contract and marks unresolved refs', () { url: 'https://api.test.hypercerts.dev', description: 'API test server (default)' }, ]); assert.equal(operation.description, 'Search demo records.'); - assert.equal(operation['x-hypercerts-coverage'], 'branch-only'); + assert.equal(operation['x-hypercerts-coverage'], 'manifest-registered'); assert.deepEqual(operation['x-hypercerts-source'], { branch: 'api/demo', commit: 'abc123' }); assert.equal(search.required, true); assert.equal(search.description, 'Literal search text.'); diff --git a/docs/test/manifest-sync.test.mjs b/docs/test/manifest-sync.test.mjs new file mode 100644 index 0000000..b69583b --- /dev/null +++ b/docs/test/manifest-sync.test.mjs @@ -0,0 +1,197 @@ +import assert from 'node:assert/strict'; +import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import os from 'node:os'; +import test from 'node:test'; +import { collectDocumentationSources } from '../scripts/refresh-sources.mjs'; + +const docsRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const apiRoot = path.resolve(docsRoot, '..', 'api'); + +async function readJson(file) { + return JSON.parse(await readFile(file, 'utf8')); +} + +async function registeredQueries() { + const manifest = await readJson(path.join(apiRoot, 'manifest.json')); + const endpoints = new Map(); + + for (const moduleRef of manifest.modules) { + const moduleFile = path.resolve(apiRoot, moduleRef); + const moduleManifest = await readJson(moduleFile); + for (const asset of moduleManifest.assets ?? []) { + if (asset.kind !== 'lexicon' || !asset.path) continue; + const lexiconFile = path.resolve(path.dirname(moduleFile), asset.path); + const lexicon = await readJson(lexiconFile); + if (!['query', 'procedure'].includes(lexicon.defs?.main?.type)) continue; + assert.equal(endpoints.has(lexicon.id), false, `duplicate registered endpoint ${lexicon.id}`); + endpoints.set(lexicon.id, { lexicon, module: moduleRef }); + } + } + + return endpoints; +} + +test('source refresh includes registered operations and resolves the full local and pinned schema closure', async (t) => { + const fixtureRoot = await mkdtemp(path.join(os.tmpdir(), 'api-docs-source-test-')); + t.after(() => rm(fixtureRoot, { recursive: true, force: true })); + const fixtureApi = path.join(fixtureRoot, 'api'); + const fixtureDocs = path.join(fixtureRoot, 'docs'); + const moduleDirectory = path.join(fixtureApi, 'modules/feature'); + const unregisteredDirectory = path.join(fixtureApi, 'modules/unregistered'); + const packageRoot = path.join(fixtureRoot, 'pinned-lexicons'); + await Promise.all([ + mkdir(moduleDirectory, { recursive: true }), + mkdir(unregisteredDirectory, { recursive: true }), + mkdir(path.join(fixtureApi, 'lexicons'), { recursive: true }), + mkdir(path.join(packageRoot, 'lexicons/demo'), { recursive: true }), + mkdir(fixtureDocs, { recursive: true }), + ]); + + const activeQuery = { + lexicon: 1, + id: 'demo.feature.search', + defs: { + main: { + type: 'query', + output: { encoding: 'application/json', schema: { type: 'ref', ref: '#output' } }, + }, + output: { + type: 'object', + properties: { result: { type: 'ref', ref: 'demo.schema.outer#view' } }, + }, + }, + }; + const outerSchema = { + lexicon: 1, + id: 'demo.schema.outer', + defs: { + main: { type: 'record', record: { type: 'ref', ref: '#view' } }, + view: { + type: 'object', + properties: { + nested: { type: 'ref', ref: 'demo.schema.inner#item' }, + packageSchema: { type: 'ref', ref: 'demo.schema.package#item' }, + }, + }, + }, + }; + const innerSchema = { + lexicon: 1, + id: 'demo.schema.inner', + defs: { main: { type: 'record', record: { type: 'ref', ref: '#item' } }, item: { type: 'object', properties: { name: { type: 'string' } } } }, + }; + const packageSchema = { + lexicon: 1, + id: 'demo.schema.package', + defs: { + main: { type: 'record', record: { type: 'ref', ref: '#item' } }, + item: { type: 'object', properties: { label: { type: 'string' } } }, + }, + }; + const unregisteredQuery = { + lexicon: 1, + id: 'demo.unregistered.query', + defs: { main: { type: 'query' } }, + }; + const writeJson = async (file, value) => writeFile(file, `${JSON.stringify(value, null, 2)}\n`); + await writeJson(path.join(fixtureApi, 'package.json'), { + dependencies: { '@hypercerts-org/lexicon': '1.4.0' }, + }); + await writeJson(path.join(fixtureApi, 'manifest.json'), { + modules: ['modules/feature/manifest.json'], + validationLexicons: [ + { id: outerSchema.id, path: 'lexicons/demo.schema.outer.json' }, + { id: innerSchema.id, path: 'lexicons/demo.schema.inner.json' }, + { id: packageSchema.id, packagePath: 'lexicons/demo/schema.json' }, + ], + }); + await writeJson(path.join(moduleDirectory, 'manifest.json'), { + assets: [{ kind: 'lexicon', id: activeQuery.id, path: '../../lexicons/demo.feature.search.json' }], + }); + await writeJson(path.join(unregisteredDirectory, 'manifest.json'), { + assets: [{ kind: 'lexicon', id: unregisteredQuery.id, path: '../../lexicons/demo.unregistered.query.json' }], + }); + await Promise.all([ + writeJson(path.join(fixtureApi, 'lexicons/demo.feature.search.json'), activeQuery), + writeJson(path.join(fixtureApi, 'lexicons/demo.schema.outer.json'), outerSchema), + writeJson(path.join(fixtureApi, 'lexicons/demo.schema.inner.json'), innerSchema), + writeJson(path.join(fixtureApi, 'lexicons/demo.unregistered.query.json'), unregisteredQuery), + writeJson(path.join(packageRoot, 'package.json'), { name: '@hypercerts-org/lexicon', version: '1.4.0' }), + writeJson(path.join(packageRoot, 'lexicons/demo/schema.json'), packageSchema), + ]); + + const { index, snapshots } = await collectDocumentationSources({ + apiRoot: fixtureApi, + docsRoot: fixtureDocs, + lexiconPackageRoot: packageRoot, + }); + assert.deepEqual(index.endpoints.map(({ id }) => id), ['demo.feature.search']); + assert.deepEqual(index.lexicons.map(({ id }) => id), [ + 'demo.feature.search', + 'demo.schema.inner', + 'demo.schema.outer', + 'demo.schema.package', + ]); + assert.equal(snapshots.some(({ file }) => file.endsWith('demo.unregistered.query.json')), false); + assert.equal(index.runtimeValidation, 'not-assessed'); + assert.equal(index.deploymentValidation, 'not-assessed'); + + outerSchema.defs.view.properties.nested.ref = 'demo.schema.missing#item'; + await writeJson(path.join(fixtureApi, 'lexicons/demo.schema.outer.json'), outerSchema); + await assert.rejects( + collectDocumentationSources({ apiRoot: fixtureApi, docsRoot: fixtureDocs, lexiconPackageRoot: packageRoot }), + /Cannot resolve Lexicon reference "demo\.schema\.missing#item".*not declared/, + ); +}); + +test('committed explorer operations and endpoint snapshots match the registered Lexicons', async () => { + const activeQueries = await registeredQueries(); + const activeIds = [...activeQueries.keys()].sort(); + const sourceIndex = await readJson(path.join(docsRoot, 'sources/index.json')); + const openapi = await readJson(path.join(docsRoot, 'openapi.json')); + const coverage = await readJson(path.join(docsRoot, 'coverage.json')); + const operationIds = Object.keys(openapi.paths) + .map((operationPath) => operationPath.replace(/^\/xrpc\//, '')) + .sort(); + + assert.deepEqual(sourceIndex.endpoints.map(({ id }) => id).sort(), activeIds); + assert.deepEqual(operationIds, activeIds); + assert.equal(coverage.endpointCount, activeIds.length); + assert.equal(coverage.inclusion['manifest-registered'], activeIds.length); + assert.deepEqual(coverage.unresolvedReferences, []); + assert.equal(sourceIndex.runtimeValidation, 'not-assessed'); + assert.equal(sourceIndex.deploymentValidation, 'not-assessed'); + + for (const [id, { lexicon, module }] of activeQueries) { + const endpoint = sourceIndex.endpoints.find((entry) => entry.id === id); + const snapshot = await readJson(path.join(docsRoot, endpoint.file)); + const operation = openapi.paths[`/xrpc/${id}`][lexicon.defs.main.type === 'query' ? 'get' : 'post']; + assert.deepEqual(snapshot, lexicon, `${id} snapshot must match its canonical module asset`); + assert.equal(endpoint.module, module); + assert.equal(operation['x-hypercerts-coverage'], 'manifest-registered'); + assert.deepEqual(operation['x-lexicon-errors'] ?? [], lexicon.defs.main.errors ?? []); + } + + for (const id of [ + 'app.certified.actor.getOrganization', + 'app.certified.actor.getOrganizations', + 'app.certified.actor.listOrganizations', + 'app.certified.actor.searchOrganizations', + ]) { + const errors = openapi.paths[`/xrpc/${id}`].get['x-lexicon-errors']; + assert.ok(errors.some(({ name }) => name === 'OrganizationQueryFailed'), `${id} documents its operational error`); + } + + for (const [id, definition] of [ + ['app.certified.badge.getBadgeDefinition', 'badgeDefinitionView'], + ['org.hypercerts.claim.getContribution', 'contributionView'], + ['org.hypercerts.funding.getReceipt', 'receiptView'], + ]) { + const lexicon = activeQueries.get(id).lexicon; + assert.ok(lexicon.defs[definition].nullable?.includes('indexedAt')); + const indexedAt = openapi.components.schemas[`${id}.${definition}`].properties.indexedAt; + assert.ok(indexedAt.anyOf?.some((variant) => variant.type === 'null'), `${id}#${definition}.indexedAt is nullable in OpenAPI`); + } +}); diff --git a/docs/test/presentation.test.mjs b/docs/test/presentation.test.mjs index 8a89049..d898d51 100644 --- a/docs/test/presentation.test.mjs +++ b/docs/test/presentation.test.mjs @@ -10,7 +10,7 @@ const { createPresentationSpec, normalizeBaseUrl } = presentationModule; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const source = JSON.parse(await readFile(path.join(root, 'openapi.json'), 'utf8')); -test('presentation keeps the endpoint contract but hides provenance and incomplete-ref annotations', () => { +test('presentation preserves endpoint contracts and explains unresolved schema references', () => { const original = structuredClone(source); const presentation = createPresentationSpec(source); const endpointPath = '/xrpc/org.hypercerts.claim.listActivities'; @@ -39,11 +39,17 @@ test('presentation keeps the endpoint contract but hides provenance and incomple 'the Lexicon grapheme constraint remains in the presentation schema', ); - const placeholderName = Object.keys(source.components.schemas).find((name) => - Object.hasOwn(source.components.schemas[name], 'x-lexicon-ref'), - ); - assert.ok(placeholderName, 'the source snapshot contains an unresolved response reference'); - assert.deepEqual(presentation.components.schemas[placeholderName], {}); + assert.deepEqual(source['x-hypercerts-unresolved-references'], [], 'committed snapshots resolve every schema reference'); + + const incomplete = structuredClone(source); + incomplete.components.schemas['demo.external.record'] = { + description: 'Schema reference demo.external.record is outside the committed local Lexicon snapshot.', + 'x-lexicon-ref': 'demo.external.record', + }; + const incompletePresentation = createPresentationSpec(incomplete); + assert.deepEqual(incompletePresentation.components.schemas['demo.external.record'], { + description: 'Schema reference demo.external.record is outside the committed local Lexicon snapshot.', + }); }); test('build-time HappyView server config keeps an ordered default and rejects invalid settings', () => { diff --git a/docs/test/refresh-sources.test.mjs b/docs/test/refresh-sources.test.mjs index 3d180ca..439b6d5 100644 --- a/docs/test/refresh-sources.test.mjs +++ b/docs/test/refresh-sources.test.mjs @@ -6,11 +6,13 @@ import test from 'node:test'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); -test('source refresh gives an actionable usage error when no checkout root is supplied', () => { - const result = spawnSync(process.execPath, ['scripts/refresh-sources.mjs'], { +test('source refresh help describes the manifest-driven workspace command', () => { + const result = spawnSync(process.execPath, ['scripts/refresh-sources.mjs', '--help'], { cwd: root, encoding: 'utf8', }); - assert.equal(result.status, 2); - assert.match(result.stderr, /Usage: node scripts\/refresh-sources\.mjs /); + assert.equal(result.status, 0); + assert.match(result.stdout, /Usage: pnpm docs:sync/); + assert.match(result.stdout, /api\/manifest\.json/); + assert.equal(result.stderr, ''); }); diff --git a/docs/web/src/presentation.mjs b/docs/web/src/presentation.mjs index a1ff7bc..8575093 100644 --- a/docs/web/src/presentation.mjs +++ b/docs/web/src/presentation.mjs @@ -106,12 +106,6 @@ export function createPresentationSpec(source) { { url: LOCAL_SERVER_URL, description: 'Local (127.0.0.1:8080)' }, ]; - for (const schema of Object.values(spec.components?.schemas ?? {})) { - if (Object.hasOwn(schema, 'x-lexicon-ref')) { - delete schema.description; - } - } - stripPresentationExtensions(spec); return spec; } diff --git a/package.json b/package.json index c6dd644..1c589fc 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "test:http": "pnpm --filter @hypercerts-org/hypercerts-api run test:http", "build": "pnpm --filter @hypercerts-org/hypercerts-api run build:lua && pnpm --filter hypercerts-api-endpoints build", "docs:dev": "pnpm --filter hypercerts-api-endpoints dev", + "docs:sync": "node docs/scripts/refresh-sources.mjs", "changeset": "changeset", "changeset:status": "changeset status", "version:packages": "changeset version && pnpm install --lockfile-only" From beca910db392ac508611569490f32ccdb8919694 Mon Sep 17 00:00:00 2001 From: kzoeps Date: Tue, 6 Oct 2026 23:07:50 +0600 Subject: [PATCH 2/2] docs: address Sonar tooling feedback --- docs/scripts/check-sources.mjs | 6 +- docs/scripts/generate-openapi.mjs | 6 +- docs/scripts/refresh-sources.mjs | 85 +++++++++++++--------- docs/test/documentation-freshness.test.mjs | 2 +- 4 files changed, 59 insertions(+), 40 deletions(-) diff --git a/docs/scripts/check-sources.mjs b/docs/scripts/check-sources.mjs index f8419f5..d963278 100644 --- a/docs/scripts/check-sources.mjs +++ b/docs/scripts/check-sources.mjs @@ -53,8 +53,10 @@ async function main() { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - main().catch((error) => { + try { + await main(); + } catch (error) { console.error(error.message); process.exitCode = 1; - }); + } } diff --git a/docs/scripts/generate-openapi.mjs b/docs/scripts/generate-openapi.mjs index 4bbfc40..ba40234 100644 --- a/docs/scripts/generate-openapi.mjs +++ b/docs/scripts/generate-openapi.mjs @@ -38,8 +38,10 @@ async function main() { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - main().catch((error) => { + try { + await main(); + } catch (error) { console.error(error.message); process.exitCode = 1; - }); + } } diff --git a/docs/scripts/refresh-sources.mjs b/docs/scripts/refresh-sources.mjs index e78d5bc..1fe257a 100644 --- a/docs/scripts/refresh-sources.mjs +++ b/docs/scripts/refresh-sources.mjs @@ -71,6 +71,54 @@ function snapshotFile(id) { return `sources/lexicons/${id}.json`; } +function indexValidationSources(sources) { + const validationSources = new Map(); + for (const source of sources ?? []) { + if (!source.id || validationSources.has(source.id)) { + throw new Error(`api/manifest.json has a missing or duplicate validation Lexicon ID: ${source.id ?? '(missing)'}`); + } + if (!source.path && !source.packagePath) { + throw new Error(`api/manifest.json has no source path for validation Lexicon ${source.id}`); + } + validationSources.set(source.id, source); + } + return validationSources; +} + +async function readActiveEndpoint(asset, moduleRef, moduleFile, readSource) { + if (asset.kind !== 'lexicon') return undefined; + const lexicon = await readSource(asset, path.dirname(moduleFile), `Lexicon asset ${asset.id ?? '(missing ID)'}`); + if (!asset.id || lexicon.id !== asset.id) { + throw new Error(`Lexicon asset ID mismatch in ${moduleRef}: expected ${asset.id ?? '(missing ID)'}, got ${lexicon.id ?? '(missing ID)'}`); + } + const type = lexicon.defs?.main?.type; + if (!['query', 'procedure'].includes(type)) return undefined; + return { + id: lexicon.id, + type, + module: moduleRef, + sourcePath: asset.path ?? asset.packagePath, + lexicon, + }; +} + +async function discoverActiveEndpoints(manifest, currentApiRoot, readSource) { + const activeEndpoints = new Map(); + for (const moduleRef of manifest.modules ?? []) { + const moduleFile = resolveInside(currentApiRoot, moduleRef, 'Module manifest path'); + const moduleManifest = await readJson(moduleFile); + for (const asset of moduleManifest.assets ?? []) { + const endpoint = await readActiveEndpoint(asset, moduleRef, moduleFile, readSource); + if (!endpoint) continue; + if (activeEndpoints.has(endpoint.id)) { + throw new Error(`Duplicate registered query/procedure Lexicon: ${endpoint.id}`); + } + activeEndpoints.set(endpoint.id, endpoint); + } + } + return activeEndpoints; +} + /** * Read active query Lexicons from the aggregate manifest and the reachable * schema Lexicons from its declared validation sources. No remote refs are fetched. @@ -84,17 +132,7 @@ export async function collectDocumentationSources(options = {}) { const apiPackage = await readJson(path.join(currentApiRoot, 'package.json')); const packageName = '@hypercerts-org/lexicon'; const packageVersion = apiPackage.dependencies?.[packageName]; - const validationSources = new Map(); - - for (const source of manifest.validationLexicons ?? []) { - if (!source.id || validationSources.has(source.id)) { - throw new Error(`api/manifest.json has a missing or duplicate validation Lexicon ID: ${source.id ?? '(missing)'}`); - } - if (!source.path && !source.packagePath) { - throw new Error(`api/manifest.json has no source path for validation Lexicon ${source.id}`); - } - validationSources.set(source.id, source); - } + const validationSources = indexValidationSources(manifest.validationLexicons); let packageRoot; async function getPackageRoot() { @@ -125,30 +163,7 @@ export async function collectDocumentationSources(options = {}) { throw new Error(`${label} has neither path nor packagePath.`); } - const activeEndpoints = new Map(); - for (const moduleRef of manifest.modules ?? []) { - const moduleFile = resolveInside(currentApiRoot, moduleRef, 'Module manifest path'); - const moduleManifest = await readJson(moduleFile); - for (const asset of moduleManifest.assets ?? []) { - if (asset.kind !== 'lexicon') continue; - const lexicon = await readSource(asset, path.dirname(moduleFile), `Lexicon asset ${asset.id ?? '(missing ID)'}`); - if (!asset.id || lexicon.id !== asset.id) { - throw new Error(`Lexicon asset ID mismatch in ${moduleRef}: expected ${asset.id ?? '(missing ID)'}, got ${lexicon.id ?? '(missing ID)'}`); - } - const type = lexicon.defs?.main?.type; - if (!['query', 'procedure'].includes(type)) continue; - if (activeEndpoints.has(lexicon.id)) { - throw new Error(`Duplicate registered query/procedure Lexicon: ${lexicon.id}`); - } - activeEndpoints.set(lexicon.id, { - id: lexicon.id, - type, - module: moduleRef, - sourcePath: asset.path ?? asset.packagePath, - lexicon, - }); - } - } + const activeEndpoints = await discoverActiveEndpoints(manifest, currentApiRoot, readSource); const sourceDocuments = new Map(); for (const [id, endpoint] of activeEndpoints) { diff --git a/docs/test/documentation-freshness.test.mjs b/docs/test/documentation-freshness.test.mjs index d182aee..8df664e 100644 --- a/docs/test/documentation-freshness.test.mjs +++ b/docs/test/documentation-freshness.test.mjs @@ -37,7 +37,7 @@ async function writeBaselineArtifacts(apiRoot, fixtureDocs, packageRoot) { } test('committed snapshots, index metadata, OpenAPI, and coverage match fresh manifest sources', async () => { - await assertDocumentationFresh(); + await assert.doesNotReject(() => assertDocumentationFresh()); }); test('freshness gate rejects endpoint and pinned support-schema drift without writing artifacts', async (t) => {