From 2ffede9cd415a0f7fdf14ccd1928666962a75399 Mon Sep 17 00:00:00 2001 From: Yuto Nishida Date: Fri, 26 Dec 2025 17:47:03 -0800 Subject: [PATCH] [ATMii] First working copy --- experimental/atmii/.gitignore | 36 + .../atmii/lexicons/app/bsky/actor/defs.json | 31 + .../lexicons/app/bsky/actor/profile.json | 53 + .../lexicons/com/atproto/label/defs.json | 156 ++ .../com/atproto/repo/applyWrites.json | 131 + .../com/atproto/repo/createRecord.json | 73 + .../atmii/lexicons/com/atproto/repo/defs.json | 14 + .../com/atproto/repo/deleteRecord.json | 57 + .../com/atproto/repo/describeRepo.json | 51 + .../lexicons/com/atproto/repo/getRecord.json | 49 + .../lexicons/com/atproto/repo/importRepo.json | 13 + .../com/atproto/repo/listMissingBlobs.json | 44 + .../com/atproto/repo/listRecords.json | 69 + .../lexicons/com/atproto/repo/putRecord.json | 74 + .../lexicons/com/atproto/repo/strongRef.json | 15 + .../lexicons/com/atproto/repo/uploadBlob.json | 23 + .../atmii/lexicons/xyz/statusphere/defs.json | 29 + .../lexicons/xyz/statusphere/getStatuses.json | 37 + .../lexicons/xyz/statusphere/getUser.json | 31 + .../lexicons/xyz/statusphere/sendStatus.json | 38 + .../lexicons/xyz/statusphere/status.json | 23 + .../atmii/packages/client/package.json | 2 +- .../atmii/packages/client/src/App.tsx | 14 +- .../packages/client/src/hooks/useAuth.tsx | 138 + .../atmii/packages/client/src/main.tsx | 6 +- .../atmii/packages/client/src/services/api.ts | 80 + .../atmii/packages/client/tsconfig.json | 2 +- .../atmii/packages/lexicon/package.json | 2 +- .../atmii/packages/lexicon/src/index.ts | 382 +++ .../atmii/packages/lexicon/src/lexicons.ts | 1249 +++++++++ .../lexicon/src/types/app/bsky/actor/defs.ts | 35 + .../src/types/app/bsky/actor/profile.ts | 40 + .../src/types/com/atproto/label/defs.ts | 143 + .../src/types/com/atproto/repo/applyWrites.ts | 164 ++ .../types/com/atproto/repo/createRecord.ts | 65 + .../src/types/com/atproto/repo/defs.ts | 28 + .../types/com/atproto/repo/deleteRecord.ts | 60 + .../types/com/atproto/repo/describeRepo.ts | 46 + .../src/types/com/atproto/repo/getRecord.ts | 57 + .../src/types/com/atproto/repo/importRepo.ts | 33 + .../com/atproto/repo/listMissingBlobs.ts | 56 + .../src/types/com/atproto/repo/listRecords.ts | 68 + .../src/types/com/atproto/repo/putRecord.ts | 67 + .../src/types/com/atproto/repo/strongRef.ts | 28 + .../src/types/com/atproto/repo/uploadBlob.ts | 38 + .../lexicon/src/types/xyz/statusphere/defs.ts | 46 + .../src/types/xyz/statusphere/getStatuses.ts | 39 + .../src/types/xyz/statusphere/getUser.ts | 39 + .../src/types/xyz/statusphere/sendStatus.ts | 41 + .../src/types/xyz/statusphere/status.ts | 29 + experimental/atmii/pnpm-lock.yaml | 2426 +++++++++++------ 51 files changed, 5674 insertions(+), 796 deletions(-) create mode 100644 experimental/atmii/lexicons/app/bsky/actor/defs.json create mode 100644 experimental/atmii/lexicons/app/bsky/actor/profile.json create mode 100644 experimental/atmii/lexicons/com/atproto/label/defs.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/applyWrites.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/createRecord.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/defs.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/deleteRecord.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/describeRepo.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/getRecord.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/importRepo.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/listMissingBlobs.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/listRecords.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/putRecord.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/strongRef.json create mode 100644 experimental/atmii/lexicons/com/atproto/repo/uploadBlob.json create mode 100644 experimental/atmii/lexicons/xyz/statusphere/defs.json create mode 100644 experimental/atmii/lexicons/xyz/statusphere/getStatuses.json create mode 100644 experimental/atmii/lexicons/xyz/statusphere/getUser.json create mode 100644 experimental/atmii/lexicons/xyz/statusphere/sendStatus.json create mode 100644 experimental/atmii/lexicons/xyz/statusphere/status.json create mode 100644 experimental/atmii/packages/client/src/hooks/useAuth.tsx create mode 100644 experimental/atmii/packages/client/src/services/api.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/app/bsky/actor/defs.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/app/bsky/actor/profile.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/label/defs.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/applyWrites.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/createRecord.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/defs.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/deleteRecord.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/describeRepo.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/getRecord.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/importRepo.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/listMissingBlobs.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/listRecords.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/putRecord.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/strongRef.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/com/atproto/repo/uploadBlob.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/xyz/statusphere/defs.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/xyz/statusphere/getStatuses.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/xyz/statusphere/getUser.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/xyz/statusphere/sendStatus.ts create mode 100644 experimental/atmii/packages/lexicon/src/types/xyz/statusphere/status.ts diff --git a/experimental/atmii/.gitignore b/experimental/atmii/.gitignore index 5bb30a7..6475fa6 100644 --- a/experimental/atmii/.gitignore +++ b/experimental/atmii/.gitignore @@ -46,3 +46,39 @@ lerna-debug.log* # Database *.sqlite *.sqlite-journal + +# Parcel +.cache/ +.idea/ +.nyc_output/ +.parcel-cache/ +.verdaccio_storage/ +.vscode/* +!.vscode/extensions.json +coverage/ +dist/ +lib/ +logs/ +node_modules/ +parcel-bundle-reports/ +tmp + +/target/ + +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +.DS_Store +npm-debug.log* +package-lock.json +sourcemap-info.json +yarn-debug.log* +yarn-error.log* +*.log +*.min.js +*.node \ No newline at end of file diff --git a/experimental/atmii/lexicons/app/bsky/actor/defs.json b/experimental/atmii/lexicons/app/bsky/actor/defs.json new file mode 100644 index 0000000..b2ce1f5 --- /dev/null +++ b/experimental/atmii/lexicons/app/bsky/actor/defs.json @@ -0,0 +1,31 @@ +{ + "lexicon": 1, + "id": "app.bsky.actor.defs", + "defs": { + "profileView": { + "type": "object", + "required": ["did", "handle"], + "properties": { + "did": { "type": "string", "format": "did" }, + "handle": { "type": "string", "format": "handle" }, + "displayName": { + "type": "string", + "maxGraphemes": 64, + "maxLength": 640 + }, + "description": { + "type": "string", + "maxGraphemes": 256, + "maxLength": 2560 + }, + "avatar": { "type": "string", "format": "uri" }, + "indexedAt": { "type": "string", "format": "datetime" }, + "createdAt": { "type": "string", "format": "datetime" }, + "labels": { + "type": "array", + "items": { "type": "ref", "ref": "com.atproto.label.defs#label" } + } + } + } + } +} diff --git a/experimental/atmii/lexicons/app/bsky/actor/profile.json b/experimental/atmii/lexicons/app/bsky/actor/profile.json new file mode 100644 index 0000000..911d7a0 --- /dev/null +++ b/experimental/atmii/lexicons/app/bsky/actor/profile.json @@ -0,0 +1,53 @@ +{ + "lexicon": 1, + "id": "app.bsky.actor.profile", + "defs": { + "main": { + "type": "record", + "description": "A declaration of a Bluesky account profile.", + "key": "literal:self", + "record": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "maxGraphemes": 64, + "maxLength": 640 + }, + "description": { + "type": "string", + "description": "Free-form profile description text.", + "maxGraphemes": 256, + "maxLength": 2560 + }, + "avatar": { + "type": "blob", + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'", + "accept": ["image/png", "image/jpeg"], + "maxSize": 1000000 + }, + "banner": { + "type": "blob", + "description": "Larger horizontal image to display behind profile view.", + "accept": ["image/png", "image/jpeg"], + "maxSize": 1000000 + }, + "labels": { + "type": "union", + "description": "Self-label values, specific to the Bluesky application, on the overall account.", + "refs": ["com.atproto.label.defs#selfLabels"] + }, + "joinedViaStarterPack": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "pinnedPost": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "createdAt": { "type": "string", "format": "datetime" } + } + } + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/label/defs.json b/experimental/atmii/lexicons/com/atproto/label/defs.json new file mode 100644 index 0000000..6f4c1ab --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/label/defs.json @@ -0,0 +1,156 @@ +{ + "lexicon": 1, + "id": "com.atproto.label.defs", + "defs": { + "label": { + "type": "object", + "description": "Metadata tag on an atproto resource (eg, repo or record).", + "required": ["src", "uri", "val", "cts"], + "properties": { + "ver": { + "type": "integer", + "description": "The AT Protocol version of the label object." + }, + "src": { + "type": "string", + "format": "did", + "description": "DID of the actor who created this label." + }, + "uri": { + "type": "string", + "format": "uri", + "description": "AT URI of the record, repository (account), or other resource that this label applies to." + }, + "cid": { + "type": "string", + "format": "cid", + "description": "Optionally, CID specifying the specific version of 'uri' resource this label applies to." + }, + "val": { + "type": "string", + "maxLength": 128, + "description": "The short string name of the value or type of this label." + }, + "neg": { + "type": "boolean", + "description": "If true, this is a negation label, overwriting a previous label." + }, + "cts": { + "type": "string", + "format": "datetime", + "description": "Timestamp when this label was created." + }, + "exp": { + "type": "string", + "format": "datetime", + "description": "Timestamp at which this label expires (no longer applies)." + }, + "sig": { + "type": "bytes", + "description": "Signature of dag-cbor encoded label." + } + } + }, + "selfLabels": { + "type": "object", + "description": "Metadata tags on an atproto record, published by the author within the record.", + "required": ["values"], + "properties": { + "values": { + "type": "array", + "items": { "type": "ref", "ref": "#selfLabel" }, + "maxLength": 10 + } + } + }, + "selfLabel": { + "type": "object", + "description": "Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.", + "required": ["val"], + "properties": { + "val": { + "type": "string", + "maxLength": 128, + "description": "The short string name of the value or type of this label." + } + } + }, + "labelValueDefinition": { + "type": "object", + "description": "Declares a label value and its expected interpretations and behaviors.", + "required": ["identifier", "severity", "blurs", "locales"], + "properties": { + "identifier": { + "type": "string", + "description": "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", + "maxLength": 100, + "maxGraphemes": 100 + }, + "severity": { + "type": "string", + "description": "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", + "knownValues": ["inform", "alert", "none"] + }, + "blurs": { + "type": "string", + "description": "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", + "knownValues": ["content", "media", "none"] + }, + "defaultSetting": { + "type": "string", + "description": "The default setting for this label.", + "knownValues": ["ignore", "warn", "hide"], + "default": "warn" + }, + "adultOnly": { + "type": "boolean", + "description": "Does the user need to have adult content enabled in order to configure this label?" + }, + "locales": { + "type": "array", + "items": { "type": "ref", "ref": "#labelValueDefinitionStrings" } + } + } + }, + "labelValueDefinitionStrings": { + "type": "object", + "description": "Strings which describe the label in the UI, localized into a specific language.", + "required": ["lang", "name", "description"], + "properties": { + "lang": { + "type": "string", + "description": "The code of the language these strings are written in.", + "format": "language" + }, + "name": { + "type": "string", + "description": "A short human-readable name for the label.", + "maxGraphemes": 64, + "maxLength": 640 + }, + "description": { + "type": "string", + "description": "A longer description of what the label means and why it might be applied.", + "maxGraphemes": 10000, + "maxLength": 100000 + } + } + }, + "labelValue": { + "type": "string", + "knownValues": [ + "!hide", + "!no-promote", + "!warn", + "!no-unauthenticated", + "dmca-violation", + "doxxing", + "porn", + "sexual", + "nudity", + "nsfl", + "gore" + ] + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/applyWrites.json b/experimental/atmii/lexicons/com/atproto/repo/applyWrites.json new file mode 100644 index 0000000..11a1f1f --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/applyWrites.json @@ -0,0 +1,131 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.applyWrites", + "defs": { + "main": { + "type": "procedure", + "description": "Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.", + "input": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["repo", "writes"], + "properties": { + "repo": { + "type": "string", + "format": "at-identifier", + "description": "The handle or DID of the repo (aka, current account)." + }, + "validate": { + "type": "boolean", + "description": "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons." + }, + "writes": { + "type": "array", + "items": { + "type": "union", + "refs": ["#create", "#update", "#delete"], + "closed": true + } + }, + "swapCommit": { + "type": "string", + "description": "If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.", + "format": "cid" + } + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": [], + "properties": { + "commit": { + "type": "ref", + "ref": "com.atproto.repo.defs#commitMeta" + }, + "results": { + "type": "array", + "items": { + "type": "union", + "refs": ["#createResult", "#updateResult", "#deleteResult"], + "closed": true + } + } + } + } + }, + "errors": [ + { + "name": "InvalidSwap", + "description": "Indicates that the 'swapCommit' parameter did not match current commit." + } + ] + }, + "create": { + "type": "object", + "description": "Operation which creates a new record.", + "required": ["collection", "value"], + "properties": { + "collection": { "type": "string", "format": "nsid" }, + "rkey": { + "type": "string", + "maxLength": 512, + "format": "record-key", + "description": "NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility." + }, + "value": { "type": "unknown" } + } + }, + "update": { + "type": "object", + "description": "Operation which updates an existing record.", + "required": ["collection", "rkey", "value"], + "properties": { + "collection": { "type": "string", "format": "nsid" }, + "rkey": { "type": "string", "format": "record-key" }, + "value": { "type": "unknown" } + } + }, + "delete": { + "type": "object", + "description": "Operation which deletes an existing record.", + "required": ["collection", "rkey"], + "properties": { + "collection": { "type": "string", "format": "nsid" }, + "rkey": { "type": "string", "format": "record-key" } + } + }, + "createResult": { + "type": "object", + "required": ["uri", "cid"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "validationStatus": { + "type": "string", + "knownValues": ["valid", "unknown"] + } + } + }, + "updateResult": { + "type": "object", + "required": ["uri", "cid"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "validationStatus": { + "type": "string", + "knownValues": ["valid", "unknown"] + } + } + }, + "deleteResult": { + "type": "object", + "required": [], + "properties": {} + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/createRecord.json b/experimental/atmii/lexicons/com/atproto/repo/createRecord.json new file mode 100644 index 0000000..de5f408 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/createRecord.json @@ -0,0 +1,73 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.createRecord", + "defs": { + "main": { + "type": "procedure", + "description": "Create a single new repository record. Requires auth, implemented by PDS.", + "input": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["repo", "collection", "record"], + "properties": { + "repo": { + "type": "string", + "format": "at-identifier", + "description": "The handle or DID of the repo (aka, current account)." + }, + "collection": { + "type": "string", + "format": "nsid", + "description": "The NSID of the record collection." + }, + "rkey": { + "type": "string", + "format": "record-key", + "description": "The Record Key.", + "maxLength": 512 + }, + "validate": { + "type": "boolean", + "description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons." + }, + "record": { + "type": "unknown", + "description": "The record itself. Must contain a $type field." + }, + "swapCommit": { + "type": "string", + "format": "cid", + "description": "Compare and swap with the previous commit by CID." + } + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["uri", "cid"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "commit": { + "type": "ref", + "ref": "com.atproto.repo.defs#commitMeta" + }, + "validationStatus": { + "type": "string", + "knownValues": ["valid", "unknown"] + } + } + } + }, + "errors": [ + { + "name": "InvalidSwap", + "description": "Indicates that 'swapCommit' didn't match current repo commit." + } + ] + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/defs.json b/experimental/atmii/lexicons/com/atproto/repo/defs.json new file mode 100644 index 0000000..8752599 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/defs.json @@ -0,0 +1,14 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.defs", + "defs": { + "commitMeta": { + "type": "object", + "required": ["cid", "rev"], + "properties": { + "cid": { "type": "string", "format": "cid" }, + "rev": { "type": "string", "format": "tid" } + } + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/deleteRecord.json b/experimental/atmii/lexicons/com/atproto/repo/deleteRecord.json new file mode 100644 index 0000000..9831da1 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/deleteRecord.json @@ -0,0 +1,57 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.deleteRecord", + "defs": { + "main": { + "type": "procedure", + "description": "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.", + "input": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["repo", "collection", "rkey"], + "properties": { + "repo": { + "type": "string", + "format": "at-identifier", + "description": "The handle or DID of the repo (aka, current account)." + }, + "collection": { + "type": "string", + "format": "nsid", + "description": "The NSID of the record collection." + }, + "rkey": { + "type": "string", + "format": "record-key", + "description": "The Record Key." + }, + "swapRecord": { + "type": "string", + "format": "cid", + "description": "Compare and swap with the previous record by CID." + }, + "swapCommit": { + "type": "string", + "format": "cid", + "description": "Compare and swap with the previous commit by CID." + } + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "ref", + "ref": "com.atproto.repo.defs#commitMeta" + } + } + } + }, + "errors": [{ "name": "InvalidSwap" }] + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/describeRepo.json b/experimental/atmii/lexicons/com/atproto/repo/describeRepo.json new file mode 100644 index 0000000..b1ce2b6 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/describeRepo.json @@ -0,0 +1,51 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.describeRepo", + "defs": { + "main": { + "type": "query", + "description": "Get information about an account and repository, including the list of collections. Does not require auth.", + "parameters": { + "type": "params", + "required": ["repo"], + "properties": { + "repo": { + "type": "string", + "format": "at-identifier", + "description": "The handle or DID of the repo." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": [ + "handle", + "did", + "didDoc", + "collections", + "handleIsCorrect" + ], + "properties": { + "handle": { "type": "string", "format": "handle" }, + "did": { "type": "string", "format": "did" }, + "didDoc": { + "type": "unknown", + "description": "The complete DID document for this account." + }, + "collections": { + "type": "array", + "description": "List of all the collections (NSIDs) for which this repo contains at least one record.", + "items": { "type": "string", "format": "nsid" } + }, + "handleIsCorrect": { + "type": "boolean", + "description": "Indicates if handle is currently valid (resolves bi-directionally)" + } + } + } + } + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/getRecord.json b/experimental/atmii/lexicons/com/atproto/repo/getRecord.json new file mode 100644 index 0000000..388b9c5 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/getRecord.json @@ -0,0 +1,49 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.getRecord", + "defs": { + "main": { + "type": "query", + "description": "Get a single record from a repository. Does not require auth.", + "parameters": { + "type": "params", + "required": ["repo", "collection", "rkey"], + "properties": { + "repo": { + "type": "string", + "format": "at-identifier", + "description": "The handle or DID of the repo." + }, + "collection": { + "type": "string", + "format": "nsid", + "description": "The NSID of the record collection." + }, + "rkey": { + "type": "string", + "description": "The Record Key.", + "format": "record-key" + }, + "cid": { + "type": "string", + "format": "cid", + "description": "The CID of the version of the record. If not specified, then return the most recent version." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["uri", "value"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "value": { "type": "unknown" } + } + } + }, + "errors": [{ "name": "RecordNotFound" }] + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/importRepo.json b/experimental/atmii/lexicons/com/atproto/repo/importRepo.json new file mode 100644 index 0000000..fc850b1 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/importRepo.json @@ -0,0 +1,13 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.importRepo", + "defs": { + "main": { + "type": "procedure", + "description": "Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.", + "input": { + "encoding": "application/vnd.ipld.car" + } + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/listMissingBlobs.json b/experimental/atmii/lexicons/com/atproto/repo/listMissingBlobs.json new file mode 100644 index 0000000..c39913d --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/listMissingBlobs.json @@ -0,0 +1,44 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.listMissingBlobs", + "defs": { + "main": { + "type": "query", + "description": "Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.", + "parameters": { + "type": "params", + "properties": { + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "default": 500 + }, + "cursor": { "type": "string" } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["blobs"], + "properties": { + "cursor": { "type": "string" }, + "blobs": { + "type": "array", + "items": { "type": "ref", "ref": "#recordBlob" } + } + } + } + } + }, + "recordBlob": { + "type": "object", + "required": ["cid", "recordUri"], + "properties": { + "cid": { "type": "string", "format": "cid" }, + "recordUri": { "type": "string", "format": "at-uri" } + } + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/listRecords.json b/experimental/atmii/lexicons/com/atproto/repo/listRecords.json new file mode 100644 index 0000000..bc91c95 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/listRecords.json @@ -0,0 +1,69 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.listRecords", + "defs": { + "main": { + "type": "query", + "description": "List a range of records in a repository, matching a specific collection. Does not require auth.", + "parameters": { + "type": "params", + "required": ["repo", "collection"], + "properties": { + "repo": { + "type": "string", + "format": "at-identifier", + "description": "The handle or DID of the repo." + }, + "collection": { + "type": "string", + "format": "nsid", + "description": "The NSID of the record type." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50, + "description": "The number of records to return." + }, + "cursor": { "type": "string" }, + "rkeyStart": { + "type": "string", + "description": "DEPRECATED: The lowest sort-ordered rkey to start from (exclusive)" + }, + "rkeyEnd": { + "type": "string", + "description": "DEPRECATED: The highest sort-ordered rkey to stop at (exclusive)" + }, + "reverse": { + "type": "boolean", + "description": "Flag to reverse the order of the returned records." + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["records"], + "properties": { + "cursor": { "type": "string" }, + "records": { + "type": "array", + "items": { "type": "ref", "ref": "#record" } + } + } + } + } + }, + "record": { + "type": "object", + "required": ["uri", "cid", "value"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "value": { "type": "unknown" } + } + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/putRecord.json b/experimental/atmii/lexicons/com/atproto/repo/putRecord.json new file mode 100644 index 0000000..830fade --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/putRecord.json @@ -0,0 +1,74 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.putRecord", + "defs": { + "main": { + "type": "procedure", + "description": "Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.", + "input": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["repo", "collection", "rkey", "record"], + "nullable": ["swapRecord"], + "properties": { + "repo": { + "type": "string", + "format": "at-identifier", + "description": "The handle or DID of the repo (aka, current account)." + }, + "collection": { + "type": "string", + "format": "nsid", + "description": "The NSID of the record collection." + }, + "rkey": { + "type": "string", + "format": "record-key", + "description": "The Record Key.", + "maxLength": 512 + }, + "validate": { + "type": "boolean", + "description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons." + }, + "record": { + "type": "unknown", + "description": "The record to write." + }, + "swapRecord": { + "type": "string", + "format": "cid", + "description": "Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation" + }, + "swapCommit": { + "type": "string", + "format": "cid", + "description": "Compare and swap with the previous commit by CID." + } + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["uri", "cid"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "cid": { "type": "string", "format": "cid" }, + "commit": { + "type": "ref", + "ref": "com.atproto.repo.defs#commitMeta" + }, + "validationStatus": { + "type": "string", + "knownValues": ["valid", "unknown"] + } + } + } + }, + "errors": [{ "name": "InvalidSwap" }] + } + } +} diff --git a/experimental/atmii/lexicons/com/atproto/repo/strongRef.json b/experimental/atmii/lexicons/com/atproto/repo/strongRef.json new file mode 100644 index 0000000..cb79625 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/strongRef.json @@ -0,0 +1,15 @@ +{ + "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/experimental/atmii/lexicons/com/atproto/repo/uploadBlob.json b/experimental/atmii/lexicons/com/atproto/repo/uploadBlob.json new file mode 100644 index 0000000..547a995 --- /dev/null +++ b/experimental/atmii/lexicons/com/atproto/repo/uploadBlob.json @@ -0,0 +1,23 @@ +{ + "lexicon": 1, + "id": "com.atproto.repo.uploadBlob", + "defs": { + "main": { + "type": "procedure", + "description": "Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.", + "input": { + "encoding": "*/*" + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["blob"], + "properties": { + "blob": { "type": "blob" } + } + } + } + } + } +} diff --git a/experimental/atmii/lexicons/xyz/statusphere/defs.json b/experimental/atmii/lexicons/xyz/statusphere/defs.json new file mode 100644 index 0000000..90c4488 --- /dev/null +++ b/experimental/atmii/lexicons/xyz/statusphere/defs.json @@ -0,0 +1,29 @@ +{ + "lexicon": 1, + "id": "xyz.statusphere.defs", + "defs": { + "statusView": { + "type": "object", + "required": ["uri", "status", "profile", "createdAt"], + "properties": { + "uri": { "type": "string", "format": "at-uri" }, + "status": { + "type": "string", + "minLength": 1, + "maxGraphemes": 1, + "maxLength": 32 + }, + "createdAt": { "type": "string", "format": "datetime" }, + "profile": { "type": "ref", "ref": "#profileView" } + } + }, + "profileView": { + "type": "object", + "required": ["did", "handle"], + "properties": { + "did": { "type": "string", "format": "did" }, + "handle": { "type": "string", "format": "handle" } + } + } + } +} diff --git a/experimental/atmii/lexicons/xyz/statusphere/getStatuses.json b/experimental/atmii/lexicons/xyz/statusphere/getStatuses.json new file mode 100644 index 0000000..156515a --- /dev/null +++ b/experimental/atmii/lexicons/xyz/statusphere/getStatuses.json @@ -0,0 +1,37 @@ +{ + "lexicon": 1, + "id": "xyz.statusphere.getStatuses", + "defs": { + "main": { + "type": "query", + "description": "Get a list of the most recent statuses on the network.", + "parameters": { + "type": "params", + "properties": { + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["statuses"], + "properties": { + "statuses": { + "type": "array", + "items": { + "type": "ref", + "ref": "xyz.statusphere.defs#statusView" + } + } + } + } + } + } + } +} diff --git a/experimental/atmii/lexicons/xyz/statusphere/getUser.json b/experimental/atmii/lexicons/xyz/statusphere/getUser.json new file mode 100644 index 0000000..c26bb7b --- /dev/null +++ b/experimental/atmii/lexicons/xyz/statusphere/getUser.json @@ -0,0 +1,31 @@ +{ + "lexicon": 1, + "id": "xyz.statusphere.getUser", + "defs": { + "main": { + "type": "query", + "description": "Get the current user's profile and status.", + "parameters": { + "type": "params", + "properties": {} + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["profile"], + "properties": { + "profile": { + "type": "ref", + "ref": "app.bsky.actor.defs#profileView" + }, + "status": { + "type": "ref", + "ref": "xyz.statusphere.defs#statusView" + } + } + } + } + } + } +} diff --git a/experimental/atmii/lexicons/xyz/statusphere/sendStatus.json b/experimental/atmii/lexicons/xyz/statusphere/sendStatus.json new file mode 100644 index 0000000..c13556f --- /dev/null +++ b/experimental/atmii/lexicons/xyz/statusphere/sendStatus.json @@ -0,0 +1,38 @@ +{ + "lexicon": 1, + "id": "xyz.statusphere.sendStatus", + "defs": { + "main": { + "type": "procedure", + "description": "Send a status into the ATmosphere.", + "input": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["status"], + "properties": { + "status": { + "type": "string", + "minLength": 1, + "maxGraphemes": 1, + "maxLength": 32 + } + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["status"], + "properties": { + "status": { + "type": "ref", + "ref": "xyz.statusphere.defs#statusView" + } + } + } + } + } + } +} diff --git a/experimental/atmii/lexicons/xyz/statusphere/status.json b/experimental/atmii/lexicons/xyz/statusphere/status.json new file mode 100644 index 0000000..e4bf5a9 --- /dev/null +++ b/experimental/atmii/lexicons/xyz/statusphere/status.json @@ -0,0 +1,23 @@ +{ + "lexicon": 1, + "id": "xyz.statusphere.status", + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": ["status", "createdAt"], + "properties": { + "status": { + "type": "string", + "minLength": 1, + "maxGraphemes": 1, + "maxLength": 32 + }, + "createdAt": { "type": "string", "format": "datetime" } + } + } + } + } +} diff --git a/experimental/atmii/packages/client/package.json b/experimental/atmii/packages/client/package.json index d74cf1e..a5de759 100644 --- a/experimental/atmii/packages/client/package.json +++ b/experimental/atmii/packages/client/package.json @@ -21,7 +21,7 @@ "iron-session": "^8.0.4", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router-dom": "^7.2.0" + "react-router": "^7.2.0" }, "devDependencies": { "@types/react": "^19.0.10", diff --git a/experimental/atmii/packages/client/src/App.tsx b/experimental/atmii/packages/client/src/App.tsx index a5804d6..88f5bd5 100644 --- a/experimental/atmii/packages/client/src/App.tsx +++ b/experimental/atmii/packages/client/src/App.tsx @@ -1,10 +1,10 @@ -import { Route, Routes } from 'react-router-dom' +import { Route, Routes } from 'react-router' -import { AuthProvider } from '#/hooks/useAuth' -import HomePage from '#/pages/HomePage' -import LoginPage from '#/pages/LoginPage' -import OAuthCallbackPage from '#/pages/OAuthCallbackPage' -import Channel from '#/pages/Channel' +import { AuthProvider } from 'client-src/hooks/useAuth' +//import HomePage from '#/pages/HomePage' +//import LoginPage from '#/pages/LoginPage' +//import OAuthCallbackPage from '#/pages/OAuthCallbackPage' +//import Channel from '#/pages/Channel' function App() { return ( @@ -12,10 +12,12 @@ function App() {
+ {/* } /> } /> } /> } /> + */}
diff --git a/experimental/atmii/packages/client/src/hooks/useAuth.tsx b/experimental/atmii/packages/client/src/hooks/useAuth.tsx new file mode 100644 index 0000000..7f62a20 --- /dev/null +++ b/experimental/atmii/packages/client/src/hooks/useAuth.tsx @@ -0,0 +1,138 @@ +import { createContext, ReactNode, useContext, useState } from 'react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { XRPCError } from '@atproto/xrpc' +import { XyzStatusphereGetUser } from '@atmii/lexicon' + +import api from 'client-src/services/api' + +interface AuthContextType { + user: XyzStatusphereGetUser.OutputSchema | null + loading: boolean + error: string | null + login: (handle: string) => Promise<{ redirectUrl: string }> + logout: () => Promise +} + +const AuthContext = createContext(undefined) + +export function AuthProvider({ children }: { children: ReactNode }) { + const [error, setError] = useState(null) + const queryClient = useQueryClient() + + // Use React Query to fetch and manage user data + const { + data: user, + isLoading: loading, + error: queryError, + } = useQuery({ + queryKey: ['currentUser'], + queryFn: async () => { + // Check for error parameter in URL (from OAuth redirect) + const urlParams = new URLSearchParams(window.location.search) + const errorParam = urlParams.get('error') + + if (errorParam) { + setError('Authentication failed. Please try again.') + + // Remove the error parameter from the URL + const newUrl = window.location.pathname + window.history.replaceState({}, document.title, newUrl) + return null + } + + try { + const { data: userData } = await api.getCurrentUser({}) + + // Clean up URL if needed + if (window.location.search && userData) { + window.history.replaceState( + {}, + document.title, + window.location.pathname, + ) + } + + return userData + } catch (apiErr) { + if ( + apiErr instanceof XRPCError && + apiErr.error === 'AuthenticationRequired' + ) { + return null + } + + console.error('🚫 API error during auth check:', apiErr) + + // If it's a network error, provide a more helpful message + if ( + apiErr instanceof TypeError && + apiErr.message.includes('Failed to fetch') + ) { + throw new Error( + 'Cannot connect to API server. Please check your network connection or server status.', + ) + } + + throw apiErr + } + }, + retry: false, + staleTime: 5 * 60 * 1000, // 5 minutes + }) + + const login = async (handle: string) => { + setError(null) + + try { + return await api.login(handle) + } catch (err) { + const message = err instanceof Error ? err.message : 'Login failed' + setError(message) + throw err + } + } + + const logout = async () => { + try { + await api.logout() + // Reset the user data in React Query cache + queryClient.setQueryData(['currentUser'], null) + // Invalidate any user-dependent queries + queryClient.invalidateQueries({ queryKey: ['statuses'] }) + } catch (err) { + const message = err instanceof Error ? err.message : 'Logout failed' + setError(message) + throw err + } + } + + // Combine state error with query error + const combinedError = + error || (queryError instanceof Error ? queryError.message : null) + + return ( + + {children} + + ) +} + +export function useAuth() { + const context = useContext(AuthContext) + + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider') + } + + return context +} + +export default useAuth diff --git a/experimental/atmii/packages/client/src/main.tsx b/experimental/atmii/packages/client/src/main.tsx index eb786e2..0edb035 100644 --- a/experimental/atmii/packages/client/src/main.tsx +++ b/experimental/atmii/packages/client/src/main.tsx @@ -1,11 +1,11 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import { BrowserRouter } from 'react-router-dom' +import { BrowserRouter } from 'react-router' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import App from '#/App' +import App from 'client-src/App' -import '#/index.css' +//import 'client-src/index.css' const queryClient = new QueryClient() diff --git a/experimental/atmii/packages/client/src/services/api.ts b/experimental/atmii/packages/client/src/services/api.ts new file mode 100644 index 0000000..caa92a1 --- /dev/null +++ b/experimental/atmii/packages/client/src/services/api.ts @@ -0,0 +1,80 @@ +import * as Lexicon from '@atmii/lexicon' +import type { + XyzStatusphereGetStatuses, + XyzStatusphereGetUser, + XyzStatusphereSendStatus, +} from '@atmii/lexicon' + +class StatusphereAgent extends Lexicon.AtpBaseClient { + constructor() { + super(StatusphereAgent.fetchHandler) + } + + private static fetchHandler: Lexicon.AtpBaseClient['fetchHandler'] = ( + path, + options, + ) => { + return fetch(path, { + ...options, + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + }) + } +} + +const agent = new StatusphereAgent() + +// API service +export const api = { + // Login + async login(handle: string) { + const response = await fetch('/oauth/initiate', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + body: JSON.stringify({ handle }), + }) + + if (!response.ok) { + const error = await response.json() + throw new Error(error.error || 'Login failed') + } + + return response.json() + }, + + // Logout + async logout() { + const response = await fetch('/oauth/logout', { + method: 'POST', + credentials: 'include', + }) + + if (!response.ok) { + throw new Error('Logout failed') + } + + return response.json() + }, + + // Get current user + getCurrentUser(params: XyzStatusphereGetUser.QueryParams) { + return agent.xyz.statusphere.getUser(params) + }, + + // Get statuses + getStatuses(params: XyzStatusphereGetStatuses.QueryParams) { + return agent.xyz.statusphere.getStatuses(params) + }, + + // Create status + createStatus(params: XyzStatusphereSendStatus.InputSchema) { + return agent.xyz.statusphere.sendStatus(params) + }, +} + +export default api diff --git a/experimental/atmii/packages/client/tsconfig.json b/experimental/atmii/packages/client/tsconfig.json index 8643591..c8b3cff 100644 --- a/experimental/atmii/packages/client/tsconfig.json +++ b/experimental/atmii/packages/client/tsconfig.json @@ -17,7 +17,7 @@ "noFallthroughCasesInSwitch": true, "baseUrl": ".", "paths": { - "#/*": ["./src/*"] + "client-src/*": ["./src/*"] } }, "include": ["src"] diff --git a/experimental/atmii/packages/lexicon/package.json b/experimental/atmii/packages/lexicon/package.json index 5a9ff12..f337dd8 100644 --- a/experimental/atmii/packages/lexicon/package.json +++ b/experimental/atmii/packages/lexicon/package.json @@ -20,7 +20,7 @@ "dev": "tsup --watch", "clean": "rimraf dist", "typecheck": "tsc --noEmit", - "lexgen": "lex gen-api ./src ../../lexicons/app/andref/*/* ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* --yes && pnpm format", + "lexgen": "lex gen-api ./src ../../lexicons/xyz/statusphere/* ../../lexicons/app/andref/*/* ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* --yes && pnpm format", "format": "prettier --write src" }, "dependencies": { diff --git a/experimental/atmii/packages/lexicon/src/index.ts b/experimental/atmii/packages/lexicon/src/index.ts index 1c17bc8..5600925 100644 --- a/experimental/atmii/packages/lexicon/src/index.ts +++ b/experimental/atmii/packages/lexicon/src/index.ts @@ -6,16 +6,60 @@ import { CID } from 'multiformats/cid' import { schemas } from './lexicons.js' import * as AppAndrefAtmiiMii from './types/app/andref/atmii/mii.js' +import * as AppBskyActorDefs from './types/app/bsky/actor/defs.js' +import * as AppBskyActorProfile from './types/app/bsky/actor/profile.js' +import * as ComAtprotoLabelDefs from './types/com/atproto/label/defs.js' +import * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites.js' +import * as ComAtprotoRepoCreateRecord from './types/com/atproto/repo/createRecord.js' +import * as ComAtprotoRepoDefs from './types/com/atproto/repo/defs.js' +import * as ComAtprotoRepoDeleteRecord from './types/com/atproto/repo/deleteRecord.js' +import * as ComAtprotoRepoDescribeRepo from './types/com/atproto/repo/describeRepo.js' +import * as ComAtprotoRepoGetRecord from './types/com/atproto/repo/getRecord.js' +import * as ComAtprotoRepoImportRepo from './types/com/atproto/repo/importRepo.js' +import * as ComAtprotoRepoListMissingBlobs from './types/com/atproto/repo/listMissingBlobs.js' +import * as ComAtprotoRepoListRecords from './types/com/atproto/repo/listRecords.js' +import * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord.js' +import * as ComAtprotoRepoStrongRef from './types/com/atproto/repo/strongRef.js' +import * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob.js' +import * as XyzStatusphereDefs from './types/xyz/statusphere/defs.js' +import * as XyzStatusphereGetStatuses from './types/xyz/statusphere/getStatuses.js' +import * as XyzStatusphereGetUser from './types/xyz/statusphere/getUser.js' +import * as XyzStatusphereSendStatus from './types/xyz/statusphere/sendStatus.js' +import * as XyzStatusphereStatus from './types/xyz/statusphere/status.js' import { OmitKey, Un$Typed } from './util.js' +export * as XyzStatusphereDefs from './types/xyz/statusphere/defs.js' +export * as XyzStatusphereGetStatuses from './types/xyz/statusphere/getStatuses.js' +export * as XyzStatusphereGetUser from './types/xyz/statusphere/getUser.js' +export * as XyzStatusphereSendStatus from './types/xyz/statusphere/sendStatus.js' +export * as XyzStatusphereStatus from './types/xyz/statusphere/status.js' export * as AppAndrefAtmiiMii from './types/app/andref/atmii/mii.js' +export * as ComAtprotoLabelDefs from './types/com/atproto/label/defs.js' +export * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites.js' +export * as ComAtprotoRepoCreateRecord from './types/com/atproto/repo/createRecord.js' +export * as ComAtprotoRepoDefs from './types/com/atproto/repo/defs.js' +export * as ComAtprotoRepoDeleteRecord from './types/com/atproto/repo/deleteRecord.js' +export * as ComAtprotoRepoDescribeRepo from './types/com/atproto/repo/describeRepo.js' +export * as ComAtprotoRepoGetRecord from './types/com/atproto/repo/getRecord.js' +export * as ComAtprotoRepoImportRepo from './types/com/atproto/repo/importRepo.js' +export * as ComAtprotoRepoListMissingBlobs from './types/com/atproto/repo/listMissingBlobs.js' +export * as ComAtprotoRepoListRecords from './types/com/atproto/repo/listRecords.js' +export * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord.js' +export * as ComAtprotoRepoStrongRef from './types/com/atproto/repo/strongRef.js' +export * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob.js' +export * as AppBskyActorDefs from './types/app/bsky/actor/defs.js' +export * as AppBskyActorProfile from './types/app/bsky/actor/profile.js' export class AtpBaseClient extends XrpcClient { + xyz: XyzNS app: AppNS + com: ComNS constructor(options: FetchHandler | FetchHandlerOptions) { super(options, schemas) + this.xyz = new XyzNS(this) this.app = new AppNS(this) + this.com = new ComNS(this) } /** @deprecated use `this` instead */ @@ -24,13 +68,122 @@ export class AtpBaseClient extends XrpcClient { } } +export class XyzNS { + _client: XrpcClient + statusphere: XyzStatusphereNS + + constructor(client: XrpcClient) { + this._client = client + this.statusphere = new XyzStatusphereNS(client) + } +} + +export class XyzStatusphereNS { + _client: XrpcClient + status: StatusRecord + + constructor(client: XrpcClient) { + this._client = client + this.status = new StatusRecord(client) + } + + getStatuses( + params?: XyzStatusphereGetStatuses.QueryParams, + opts?: XyzStatusphereGetStatuses.CallOptions, + ): Promise { + return this._client.call( + 'xyz.statusphere.getStatuses', + params, + undefined, + opts, + ) + } + + getUser( + params?: XyzStatusphereGetUser.QueryParams, + opts?: XyzStatusphereGetUser.CallOptions, + ): Promise { + return this._client.call('xyz.statusphere.getUser', params, undefined, opts) + } + + sendStatus( + data?: XyzStatusphereSendStatus.InputSchema, + opts?: XyzStatusphereSendStatus.CallOptions, + ): Promise { + return this._client.call('xyz.statusphere.sendStatus', opts?.qp, data, opts) + } +} + +export class StatusRecord { + _client: XrpcClient + + constructor(client: XrpcClient) { + this._client = client + } + + async list( + params: OmitKey, + ): Promise<{ + cursor?: string + records: { uri: string; value: XyzStatusphereStatus.Record }[] + }> { + const res = await this._client.call('com.atproto.repo.listRecords', { + collection: 'xyz.statusphere.status', + ...params, + }) + return res.data + } + + async get( + params: OmitKey, + ): Promise<{ uri: string; cid: string; value: XyzStatusphereStatus.Record }> { + const res = await this._client.call('com.atproto.repo.getRecord', { + collection: 'xyz.statusphere.status', + ...params, + }) + return res.data + } + + async create( + params: OmitKey< + ComAtprotoRepoCreateRecord.InputSchema, + 'collection' | 'record' + >, + record: Un$Typed, + headers?: Record, + ): Promise<{ uri: string; cid: string }> { + const collection = 'xyz.statusphere.status' + const res = await this._client.call( + 'com.atproto.repo.createRecord', + undefined, + { collection, ...params, record: { ...record, $type: collection } }, + { encoding: 'application/json', headers }, + ) + return res.data + } + + async delete( + params: OmitKey, + headers?: Record, + ): Promise { + await this._client.call( + 'com.atproto.repo.deleteRecord', + undefined, + { collection: 'xyz.statusphere.status', ...params }, + { headers }, + ) + } +} + export class AppNS { _client: XrpcClient andref: AppAndrefNS + bsky: AppBskyNS constructor(client: XrpcClient) { this._client = client this.andref = new AppAndrefNS(client) + this.bsky = new AppBskyNS(client) } } @@ -114,3 +267,232 @@ export class MiiRecord { ) } } + +export class AppBskyNS { + _client: XrpcClient + actor: AppBskyActorNS + + constructor(client: XrpcClient) { + this._client = client + this.actor = new AppBskyActorNS(client) + } +} + +export class AppBskyActorNS { + _client: XrpcClient + profile: ProfileRecord + + constructor(client: XrpcClient) { + this._client = client + this.profile = new ProfileRecord(client) + } +} + +export class ProfileRecord { + _client: XrpcClient + + constructor(client: XrpcClient) { + this._client = client + } + + async list( + params: OmitKey, + ): Promise<{ + cursor?: string + records: { uri: string; value: AppBskyActorProfile.Record }[] + }> { + const res = await this._client.call('com.atproto.repo.listRecords', { + collection: 'app.bsky.actor.profile', + ...params, + }) + return res.data + } + + async get( + params: OmitKey, + ): Promise<{ uri: string; cid: string; value: AppBskyActorProfile.Record }> { + const res = await this._client.call('com.atproto.repo.getRecord', { + collection: 'app.bsky.actor.profile', + ...params, + }) + return res.data + } + + async create( + params: OmitKey< + ComAtprotoRepoCreateRecord.InputSchema, + 'collection' | 'record' + >, + record: Un$Typed, + headers?: Record, + ): Promise<{ uri: string; cid: string }> { + const collection = 'app.bsky.actor.profile' + const res = await this._client.call( + 'com.atproto.repo.createRecord', + undefined, + { + collection, + rkey: 'self', + ...params, + record: { ...record, $type: collection }, + }, + { encoding: 'application/json', headers }, + ) + return res.data + } + + async delete( + params: OmitKey, + headers?: Record, + ): Promise { + await this._client.call( + 'com.atproto.repo.deleteRecord', + undefined, + { collection: 'app.bsky.actor.profile', ...params }, + { headers }, + ) + } +} + +export class ComNS { + _client: XrpcClient + atproto: ComAtprotoNS + + constructor(client: XrpcClient) { + this._client = client + this.atproto = new ComAtprotoNS(client) + } +} + +export class ComAtprotoNS { + _client: XrpcClient + repo: ComAtprotoRepoNS + + constructor(client: XrpcClient) { + this._client = client + this.repo = new ComAtprotoRepoNS(client) + } +} + +export class ComAtprotoRepoNS { + _client: XrpcClient + + constructor(client: XrpcClient) { + this._client = client + } + + applyWrites( + data?: ComAtprotoRepoApplyWrites.InputSchema, + opts?: ComAtprotoRepoApplyWrites.CallOptions, + ): Promise { + return this._client + .call('com.atproto.repo.applyWrites', opts?.qp, data, opts) + .catch((e) => { + throw ComAtprotoRepoApplyWrites.toKnownErr(e) + }) + } + + createRecord( + data?: ComAtprotoRepoCreateRecord.InputSchema, + opts?: ComAtprotoRepoCreateRecord.CallOptions, + ): Promise { + return this._client + .call('com.atproto.repo.createRecord', opts?.qp, data, opts) + .catch((e) => { + throw ComAtprotoRepoCreateRecord.toKnownErr(e) + }) + } + + deleteRecord( + data?: ComAtprotoRepoDeleteRecord.InputSchema, + opts?: ComAtprotoRepoDeleteRecord.CallOptions, + ): Promise { + return this._client + .call('com.atproto.repo.deleteRecord', opts?.qp, data, opts) + .catch((e) => { + throw ComAtprotoRepoDeleteRecord.toKnownErr(e) + }) + } + + describeRepo( + params?: ComAtprotoRepoDescribeRepo.QueryParams, + opts?: ComAtprotoRepoDescribeRepo.CallOptions, + ): Promise { + return this._client.call( + 'com.atproto.repo.describeRepo', + params, + undefined, + opts, + ) + } + + getRecord( + params?: ComAtprotoRepoGetRecord.QueryParams, + opts?: ComAtprotoRepoGetRecord.CallOptions, + ): Promise { + return this._client + .call('com.atproto.repo.getRecord', params, undefined, opts) + .catch((e) => { + throw ComAtprotoRepoGetRecord.toKnownErr(e) + }) + } + + importRepo( + data?: ComAtprotoRepoImportRepo.InputSchema, + opts?: ComAtprotoRepoImportRepo.CallOptions, + ): Promise { + return this._client.call( + 'com.atproto.repo.importRepo', + opts?.qp, + data, + opts, + ) + } + + listMissingBlobs( + params?: ComAtprotoRepoListMissingBlobs.QueryParams, + opts?: ComAtprotoRepoListMissingBlobs.CallOptions, + ): Promise { + return this._client.call( + 'com.atproto.repo.listMissingBlobs', + params, + undefined, + opts, + ) + } + + listRecords( + params?: ComAtprotoRepoListRecords.QueryParams, + opts?: ComAtprotoRepoListRecords.CallOptions, + ): Promise { + return this._client.call( + 'com.atproto.repo.listRecords', + params, + undefined, + opts, + ) + } + + putRecord( + data?: ComAtprotoRepoPutRecord.InputSchema, + opts?: ComAtprotoRepoPutRecord.CallOptions, + ): Promise { + return this._client + .call('com.atproto.repo.putRecord', opts?.qp, data, opts) + .catch((e) => { + throw ComAtprotoRepoPutRecord.toKnownErr(e) + }) + } + + uploadBlob( + data?: ComAtprotoRepoUploadBlob.InputSchema, + opts?: ComAtprotoRepoUploadBlob.CallOptions, + ): Promise { + return this._client.call( + 'com.atproto.repo.uploadBlob', + opts?.qp, + data, + opts, + ) + } +} diff --git a/experimental/atmii/packages/lexicon/src/lexicons.ts b/experimental/atmii/packages/lexicon/src/lexicons.ts index 7dcfed0..50f7563 100644 --- a/experimental/atmii/packages/lexicon/src/lexicons.ts +++ b/experimental/atmii/packages/lexicon/src/lexicons.ts @@ -11,6 +11,182 @@ import { import { $Typed, is$typed, maybe$typed } from './util.js' export const schemaDict = { + XyzStatusphereDefs: { + lexicon: 1, + id: 'xyz.statusphere.defs', + defs: { + statusView: { + type: 'object', + required: ['uri', 'status', 'profile', 'createdAt'], + properties: { + uri: { + type: 'string', + format: 'at-uri', + }, + status: { + type: 'string', + minLength: 1, + maxGraphemes: 1, + maxLength: 32, + }, + createdAt: { + type: 'string', + format: 'datetime', + }, + profile: { + type: 'ref', + ref: 'lex:xyz.statusphere.defs#profileView', + }, + }, + }, + profileView: { + type: 'object', + required: ['did', 'handle'], + properties: { + did: { + type: 'string', + format: 'did', + }, + handle: { + type: 'string', + format: 'handle', + }, + }, + }, + }, + }, + XyzStatusphereGetStatuses: { + lexicon: 1, + id: 'xyz.statusphere.getStatuses', + defs: { + main: { + type: 'query', + description: 'Get a list of the most recent statuses on the network.', + parameters: { + type: 'params', + properties: { + limit: { + type: 'integer', + minimum: 1, + maximum: 100, + default: 50, + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['statuses'], + properties: { + statuses: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:xyz.statusphere.defs#statusView', + }, + }, + }, + }, + }, + }, + }, + }, + XyzStatusphereGetUser: { + lexicon: 1, + id: 'xyz.statusphere.getUser', + defs: { + main: { + type: 'query', + description: "Get the current user's profile and status.", + parameters: { + type: 'params', + properties: {}, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['profile'], + properties: { + profile: { + type: 'ref', + ref: 'lex:app.bsky.actor.defs#profileView', + }, + status: { + type: 'ref', + ref: 'lex:xyz.statusphere.defs#statusView', + }, + }, + }, + }, + }, + }, + }, + XyzStatusphereSendStatus: { + lexicon: 1, + id: 'xyz.statusphere.sendStatus', + defs: { + main: { + type: 'procedure', + description: 'Send a status into the ATmosphere.', + input: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['status'], + properties: { + status: { + type: 'string', + minLength: 1, + maxGraphemes: 1, + maxLength: 32, + }, + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['status'], + properties: { + status: { + type: 'ref', + ref: 'lex:xyz.statusphere.defs#statusView', + }, + }, + }, + }, + }, + }, + }, + XyzStatusphereStatus: { + lexicon: 1, + id: 'xyz.statusphere.status', + defs: { + main: { + type: 'record', + key: 'tid', + record: { + type: 'object', + required: ['status', 'createdAt'], + properties: { + status: { + type: 'string', + minLength: 1, + maxGraphemes: 1, + maxLength: 32, + }, + createdAt: { + type: 'string', + format: 'datetime', + }, + }, + }, + }, + }, + }, AppAndrefAtmiiMii: { lexicon: 1, id: 'app.andref.atmii.mii', @@ -25,6 +201,1059 @@ export const schemaDict = { }, }, }, + ComAtprotoLabelDefs: { + lexicon: 1, + id: 'com.atproto.label.defs', + defs: { + label: { + type: 'object', + description: + 'Metadata tag on an atproto resource (eg, repo or record).', + required: ['src', 'uri', 'val', 'cts'], + properties: { + ver: { + type: 'integer', + description: 'The AT Protocol version of the label object.', + }, + src: { + type: 'string', + format: 'did', + description: 'DID of the actor who created this label.', + }, + uri: { + type: 'string', + format: 'uri', + description: + 'AT URI of the record, repository (account), or other resource that this label applies to.', + }, + cid: { + type: 'string', + format: 'cid', + description: + "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", + }, + val: { + type: 'string', + maxLength: 128, + description: + 'The short string name of the value or type of this label.', + }, + neg: { + type: 'boolean', + description: + 'If true, this is a negation label, overwriting a previous label.', + }, + cts: { + type: 'string', + format: 'datetime', + description: 'Timestamp when this label was created.', + }, + exp: { + type: 'string', + format: 'datetime', + description: + 'Timestamp at which this label expires (no longer applies).', + }, + sig: { + type: 'bytes', + description: 'Signature of dag-cbor encoded label.', + }, + }, + }, + selfLabels: { + type: 'object', + description: + 'Metadata tags on an atproto record, published by the author within the record.', + required: ['values'], + properties: { + values: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:com.atproto.label.defs#selfLabel', + }, + maxLength: 10, + }, + }, + }, + selfLabel: { + type: 'object', + description: + 'Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.', + required: ['val'], + properties: { + val: { + type: 'string', + maxLength: 128, + description: + 'The short string name of the value or type of this label.', + }, + }, + }, + labelValueDefinition: { + type: 'object', + description: + 'Declares a label value and its expected interpretations and behaviors.', + required: ['identifier', 'severity', 'blurs', 'locales'], + properties: { + identifier: { + type: 'string', + description: + "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", + maxLength: 100, + maxGraphemes: 100, + }, + severity: { + type: 'string', + description: + "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", + knownValues: ['inform', 'alert', 'none'], + }, + blurs: { + type: 'string', + description: + "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", + knownValues: ['content', 'media', 'none'], + }, + defaultSetting: { + type: 'string', + description: 'The default setting for this label.', + knownValues: ['ignore', 'warn', 'hide'], + default: 'warn', + }, + adultOnly: { + type: 'boolean', + description: + 'Does the user need to have adult content enabled in order to configure this label?', + }, + locales: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:com.atproto.label.defs#labelValueDefinitionStrings', + }, + }, + }, + }, + labelValueDefinitionStrings: { + type: 'object', + description: + 'Strings which describe the label in the UI, localized into a specific language.', + required: ['lang', 'name', 'description'], + properties: { + lang: { + type: 'string', + description: + 'The code of the language these strings are written in.', + format: 'language', + }, + name: { + type: 'string', + description: 'A short human-readable name for the label.', + maxGraphemes: 64, + maxLength: 640, + }, + description: { + type: 'string', + description: + 'A longer description of what the label means and why it might be applied.', + maxGraphemes: 10000, + maxLength: 100000, + }, + }, + }, + labelValue: { + type: 'string', + knownValues: [ + '!hide', + '!no-promote', + '!warn', + '!no-unauthenticated', + 'dmca-violation', + 'doxxing', + 'porn', + 'sexual', + 'nudity', + 'nsfl', + 'gore', + ], + }, + }, + }, + ComAtprotoRepoApplyWrites: { + lexicon: 1, + id: 'com.atproto.repo.applyWrites', + defs: { + main: { + type: 'procedure', + description: + 'Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.', + input: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['repo', 'writes'], + properties: { + repo: { + type: 'string', + format: 'at-identifier', + description: + 'The handle or DID of the repo (aka, current account).', + }, + validate: { + type: 'boolean', + description: + "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons.", + }, + writes: { + type: 'array', + items: { + type: 'union', + refs: [ + 'lex:com.atproto.repo.applyWrites#create', + 'lex:com.atproto.repo.applyWrites#update', + 'lex:com.atproto.repo.applyWrites#delete', + ], + closed: true, + }, + }, + swapCommit: { + type: 'string', + description: + 'If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.', + format: 'cid', + }, + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: [], + properties: { + commit: { + type: 'ref', + ref: 'lex:com.atproto.repo.defs#commitMeta', + }, + results: { + type: 'array', + items: { + type: 'union', + refs: [ + 'lex:com.atproto.repo.applyWrites#createResult', + 'lex:com.atproto.repo.applyWrites#updateResult', + 'lex:com.atproto.repo.applyWrites#deleteResult', + ], + closed: true, + }, + }, + }, + }, + }, + errors: [ + { + name: 'InvalidSwap', + description: + "Indicates that the 'swapCommit' parameter did not match current commit.", + }, + ], + }, + create: { + type: 'object', + description: 'Operation which creates a new record.', + required: ['collection', 'value'], + properties: { + collection: { + type: 'string', + format: 'nsid', + }, + rkey: { + type: 'string', + maxLength: 512, + format: 'record-key', + description: + 'NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility.', + }, + value: { + type: 'unknown', + }, + }, + }, + update: { + type: 'object', + description: 'Operation which updates an existing record.', + required: ['collection', 'rkey', 'value'], + properties: { + collection: { + type: 'string', + format: 'nsid', + }, + rkey: { + type: 'string', + format: 'record-key', + }, + value: { + type: 'unknown', + }, + }, + }, + delete: { + type: 'object', + description: 'Operation which deletes an existing record.', + required: ['collection', 'rkey'], + properties: { + collection: { + type: 'string', + format: 'nsid', + }, + rkey: { + type: 'string', + format: 'record-key', + }, + }, + }, + createResult: { + type: 'object', + required: ['uri', 'cid'], + properties: { + uri: { + type: 'string', + format: 'at-uri', + }, + cid: { + type: 'string', + format: 'cid', + }, + validationStatus: { + type: 'string', + knownValues: ['valid', 'unknown'], + }, + }, + }, + updateResult: { + type: 'object', + required: ['uri', 'cid'], + properties: { + uri: { + type: 'string', + format: 'at-uri', + }, + cid: { + type: 'string', + format: 'cid', + }, + validationStatus: { + type: 'string', + knownValues: ['valid', 'unknown'], + }, + }, + }, + deleteResult: { + type: 'object', + required: [], + properties: {}, + }, + }, + }, + ComAtprotoRepoCreateRecord: { + lexicon: 1, + id: 'com.atproto.repo.createRecord', + defs: { + main: { + type: 'procedure', + description: + 'Create a single new repository record. Requires auth, implemented by PDS.', + input: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['repo', 'collection', 'record'], + properties: { + repo: { + type: 'string', + format: 'at-identifier', + description: + 'The handle or DID of the repo (aka, current account).', + }, + collection: { + type: 'string', + format: 'nsid', + description: 'The NSID of the record collection.', + }, + rkey: { + type: 'string', + format: 'record-key', + description: 'The Record Key.', + maxLength: 512, + }, + validate: { + type: 'boolean', + description: + "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", + }, + record: { + type: 'unknown', + description: 'The record itself. Must contain a $type field.', + }, + swapCommit: { + type: 'string', + format: 'cid', + description: + 'Compare and swap with the previous commit by CID.', + }, + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['uri', 'cid'], + properties: { + uri: { + type: 'string', + format: 'at-uri', + }, + cid: { + type: 'string', + format: 'cid', + }, + commit: { + type: 'ref', + ref: 'lex:com.atproto.repo.defs#commitMeta', + }, + validationStatus: { + type: 'string', + knownValues: ['valid', 'unknown'], + }, + }, + }, + }, + errors: [ + { + name: 'InvalidSwap', + description: + "Indicates that 'swapCommit' didn't match current repo commit.", + }, + ], + }, + }, + }, + ComAtprotoRepoDefs: { + lexicon: 1, + id: 'com.atproto.repo.defs', + defs: { + commitMeta: { + type: 'object', + required: ['cid', 'rev'], + properties: { + cid: { + type: 'string', + format: 'cid', + }, + rev: { + type: 'string', + format: 'tid', + }, + }, + }, + }, + }, + ComAtprotoRepoDeleteRecord: { + lexicon: 1, + id: 'com.atproto.repo.deleteRecord', + defs: { + main: { + type: 'procedure', + description: + "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.", + input: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['repo', 'collection', 'rkey'], + properties: { + repo: { + type: 'string', + format: 'at-identifier', + description: + 'The handle or DID of the repo (aka, current account).', + }, + collection: { + type: 'string', + format: 'nsid', + description: 'The NSID of the record collection.', + }, + rkey: { + type: 'string', + format: 'record-key', + description: 'The Record Key.', + }, + swapRecord: { + type: 'string', + format: 'cid', + description: + 'Compare and swap with the previous record by CID.', + }, + swapCommit: { + type: 'string', + format: 'cid', + description: + 'Compare and swap with the previous commit by CID.', + }, + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + properties: { + commit: { + type: 'ref', + ref: 'lex:com.atproto.repo.defs#commitMeta', + }, + }, + }, + }, + errors: [ + { + name: 'InvalidSwap', + }, + ], + }, + }, + }, + ComAtprotoRepoDescribeRepo: { + lexicon: 1, + id: 'com.atproto.repo.describeRepo', + defs: { + main: { + type: 'query', + description: + 'Get information about an account and repository, including the list of collections. Does not require auth.', + parameters: { + type: 'params', + required: ['repo'], + properties: { + repo: { + type: 'string', + format: 'at-identifier', + description: 'The handle or DID of the repo.', + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: [ + 'handle', + 'did', + 'didDoc', + 'collections', + 'handleIsCorrect', + ], + properties: { + handle: { + type: 'string', + format: 'handle', + }, + did: { + type: 'string', + format: 'did', + }, + didDoc: { + type: 'unknown', + description: 'The complete DID document for this account.', + }, + collections: { + type: 'array', + description: + 'List of all the collections (NSIDs) for which this repo contains at least one record.', + items: { + type: 'string', + format: 'nsid', + }, + }, + handleIsCorrect: { + type: 'boolean', + description: + 'Indicates if handle is currently valid (resolves bi-directionally)', + }, + }, + }, + }, + }, + }, + }, + ComAtprotoRepoGetRecord: { + lexicon: 1, + id: 'com.atproto.repo.getRecord', + defs: { + main: { + type: 'query', + description: + 'Get a single record from a repository. Does not require auth.', + parameters: { + type: 'params', + required: ['repo', 'collection', 'rkey'], + properties: { + repo: { + type: 'string', + format: 'at-identifier', + description: 'The handle or DID of the repo.', + }, + collection: { + type: 'string', + format: 'nsid', + description: 'The NSID of the record collection.', + }, + rkey: { + type: 'string', + description: 'The Record Key.', + format: 'record-key', + }, + cid: { + type: 'string', + format: 'cid', + description: + 'The CID of the version of the record. If not specified, then return the most recent version.', + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['uri', 'value'], + properties: { + uri: { + type: 'string', + format: 'at-uri', + }, + cid: { + type: 'string', + format: 'cid', + }, + value: { + type: 'unknown', + }, + }, + }, + }, + errors: [ + { + name: 'RecordNotFound', + }, + ], + }, + }, + }, + ComAtprotoRepoImportRepo: { + lexicon: 1, + id: 'com.atproto.repo.importRepo', + defs: { + main: { + type: 'procedure', + description: + 'Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.', + input: { + encoding: 'application/vnd.ipld.car', + }, + }, + }, + }, + ComAtprotoRepoListMissingBlobs: { + lexicon: 1, + id: 'com.atproto.repo.listMissingBlobs', + defs: { + main: { + type: 'query', + description: + 'Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.', + parameters: { + type: 'params', + properties: { + limit: { + type: 'integer', + minimum: 1, + maximum: 1000, + default: 500, + }, + cursor: { + type: 'string', + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['blobs'], + properties: { + cursor: { + type: 'string', + }, + blobs: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:com.atproto.repo.listMissingBlobs#recordBlob', + }, + }, + }, + }, + }, + }, + recordBlob: { + type: 'object', + required: ['cid', 'recordUri'], + properties: { + cid: { + type: 'string', + format: 'cid', + }, + recordUri: { + type: 'string', + format: 'at-uri', + }, + }, + }, + }, + }, + ComAtprotoRepoListRecords: { + lexicon: 1, + id: 'com.atproto.repo.listRecords', + defs: { + main: { + type: 'query', + description: + 'List a range of records in a repository, matching a specific collection. Does not require auth.', + parameters: { + type: 'params', + required: ['repo', 'collection'], + properties: { + repo: { + type: 'string', + format: 'at-identifier', + description: 'The handle or DID of the repo.', + }, + collection: { + type: 'string', + format: 'nsid', + description: 'The NSID of the record type.', + }, + limit: { + type: 'integer', + minimum: 1, + maximum: 100, + default: 50, + description: 'The number of records to return.', + }, + cursor: { + type: 'string', + }, + rkeyStart: { + type: 'string', + description: + 'DEPRECATED: The lowest sort-ordered rkey to start from (exclusive)', + }, + rkeyEnd: { + type: 'string', + description: + 'DEPRECATED: The highest sort-ordered rkey to stop at (exclusive)', + }, + reverse: { + type: 'boolean', + description: 'Flag to reverse the order of the returned records.', + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['records'], + properties: { + cursor: { + type: 'string', + }, + records: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:com.atproto.repo.listRecords#record', + }, + }, + }, + }, + }, + }, + record: { + type: 'object', + required: ['uri', 'cid', 'value'], + properties: { + uri: { + type: 'string', + format: 'at-uri', + }, + cid: { + type: 'string', + format: 'cid', + }, + value: { + type: 'unknown', + }, + }, + }, + }, + }, + ComAtprotoRepoPutRecord: { + lexicon: 1, + id: 'com.atproto.repo.putRecord', + defs: { + main: { + type: 'procedure', + description: + 'Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.', + input: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['repo', 'collection', 'rkey', 'record'], + nullable: ['swapRecord'], + properties: { + repo: { + type: 'string', + format: 'at-identifier', + description: + 'The handle or DID of the repo (aka, current account).', + }, + collection: { + type: 'string', + format: 'nsid', + description: 'The NSID of the record collection.', + }, + rkey: { + type: 'string', + format: 'record-key', + description: 'The Record Key.', + maxLength: 512, + }, + validate: { + type: 'boolean', + description: + "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", + }, + record: { + type: 'unknown', + description: 'The record to write.', + }, + swapRecord: { + type: 'string', + format: 'cid', + description: + 'Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation', + }, + swapCommit: { + type: 'string', + format: 'cid', + description: + 'Compare and swap with the previous commit by CID.', + }, + }, + }, + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['uri', 'cid'], + properties: { + uri: { + type: 'string', + format: 'at-uri', + }, + cid: { + type: 'string', + format: 'cid', + }, + commit: { + type: 'ref', + ref: 'lex:com.atproto.repo.defs#commitMeta', + }, + validationStatus: { + type: 'string', + knownValues: ['valid', 'unknown'], + }, + }, + }, + }, + errors: [ + { + name: 'InvalidSwap', + }, + ], + }, + }, + }, + ComAtprotoRepoStrongRef: { + 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', + }, + }, + }, + }, + }, + ComAtprotoRepoUploadBlob: { + lexicon: 1, + id: 'com.atproto.repo.uploadBlob', + defs: { + main: { + type: 'procedure', + description: + 'Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.', + input: { + encoding: '*/*', + }, + output: { + encoding: 'application/json', + schema: { + type: 'object', + required: ['blob'], + properties: { + blob: { + type: 'blob', + }, + }, + }, + }, + }, + }, + }, + AppBskyActorDefs: { + lexicon: 1, + id: 'app.bsky.actor.defs', + defs: { + profileView: { + type: 'object', + required: ['did', 'handle'], + properties: { + did: { + type: 'string', + format: 'did', + }, + handle: { + type: 'string', + format: 'handle', + }, + displayName: { + type: 'string', + maxGraphemes: 64, + maxLength: 640, + }, + description: { + type: 'string', + maxGraphemes: 256, + maxLength: 2560, + }, + avatar: { + type: 'string', + format: 'uri', + }, + indexedAt: { + type: 'string', + format: 'datetime', + }, + createdAt: { + type: 'string', + format: 'datetime', + }, + labels: { + type: 'array', + items: { + type: 'ref', + ref: 'lex:com.atproto.label.defs#label', + }, + }, + }, + }, + }, + }, + AppBskyActorProfile: { + lexicon: 1, + id: 'app.bsky.actor.profile', + defs: { + main: { + type: 'record', + description: 'A declaration of a Bluesky account profile.', + key: 'literal:self', + record: { + type: 'object', + properties: { + displayName: { + type: 'string', + maxGraphemes: 64, + maxLength: 640, + }, + description: { + type: 'string', + description: 'Free-form profile description text.', + maxGraphemes: 256, + maxLength: 2560, + }, + avatar: { + type: 'blob', + description: + "Small image to be displayed next to posts from account. AKA, 'profile picture'", + accept: ['image/png', 'image/jpeg'], + maxSize: 1000000, + }, + banner: { + type: 'blob', + description: + 'Larger horizontal image to display behind profile view.', + accept: ['image/png', 'image/jpeg'], + maxSize: 1000000, + }, + labels: { + type: 'union', + description: + 'Self-label values, specific to the Bluesky application, on the overall account.', + refs: ['lex:com.atproto.label.defs#selfLabels'], + }, + joinedViaStarterPack: { + type: 'ref', + ref: 'lex:com.atproto.repo.strongRef', + }, + pinnedPost: { + type: 'ref', + ref: 'lex:com.atproto.repo.strongRef', + }, + createdAt: { + type: 'string', + format: 'datetime', + }, + }, + }, + }, + }, + }, } as const satisfies Record export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] @@ -59,5 +1288,25 @@ export function validate( } export const ids = { + XyzStatusphereDefs: 'xyz.statusphere.defs', + XyzStatusphereGetStatuses: 'xyz.statusphere.getStatuses', + XyzStatusphereGetUser: 'xyz.statusphere.getUser', + XyzStatusphereSendStatus: 'xyz.statusphere.sendStatus', + XyzStatusphereStatus: 'xyz.statusphere.status', AppAndrefAtmiiMii: 'app.andref.atmii.mii', + ComAtprotoLabelDefs: 'com.atproto.label.defs', + ComAtprotoRepoApplyWrites: 'com.atproto.repo.applyWrites', + ComAtprotoRepoCreateRecord: 'com.atproto.repo.createRecord', + ComAtprotoRepoDefs: 'com.atproto.repo.defs', + ComAtprotoRepoDeleteRecord: 'com.atproto.repo.deleteRecord', + ComAtprotoRepoDescribeRepo: 'com.atproto.repo.describeRepo', + ComAtprotoRepoGetRecord: 'com.atproto.repo.getRecord', + ComAtprotoRepoImportRepo: 'com.atproto.repo.importRepo', + ComAtprotoRepoListMissingBlobs: 'com.atproto.repo.listMissingBlobs', + ComAtprotoRepoListRecords: 'com.atproto.repo.listRecords', + ComAtprotoRepoPutRecord: 'com.atproto.repo.putRecord', + ComAtprotoRepoStrongRef: 'com.atproto.repo.strongRef', + ComAtprotoRepoUploadBlob: 'com.atproto.repo.uploadBlob', + AppBskyActorDefs: 'app.bsky.actor.defs', + AppBskyActorProfile: 'app.bsky.actor.profile', } as const diff --git a/experimental/atmii/packages/lexicon/src/types/app/bsky/actor/defs.ts b/experimental/atmii/packages/lexicon/src/types/app/bsky/actor/defs.ts new file mode 100644 index 0000000..5f65150 --- /dev/null +++ b/experimental/atmii/packages/lexicon/src/types/app/bsky/actor/defs.ts @@ -0,0 +1,35 @@ +/** + * GENERATED CODE - DO NOT MODIFY + */ +import { BlobRef, ValidationResult } from '@atproto/lexicon' +import { CID } from 'multiformats/cid' + +import { validate as _validate } from '../../../../lexicons' +import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' +import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.js' + +const is$typed = _is$typed, + validate = _validate +const id = 'app.bsky.actor.defs' + +export interface ProfileView { + $type?: 'app.bsky.actor.defs#profileView' + did: string + handle: string + displayName?: string + description?: string + avatar?: string + indexedAt?: string + createdAt?: string + labels?: ComAtprotoLabelDefs.Label[] +} + +const hashProfileView = 'profileView' + +export function isProfileView(v: V) { + return is$typed(v, id, hashProfileView) +} + +export function validateProfileView(v: V) { + return validate(v, id, hashProfileView) +} diff --git a/experimental/atmii/packages/lexicon/src/types/app/bsky/actor/profile.ts b/experimental/atmii/packages/lexicon/src/types/app/bsky/actor/profile.ts new file mode 100644 index 0000000..579bcae --- /dev/null +++ b/experimental/atmii/packages/lexicon/src/types/app/bsky/actor/profile.ts @@ -0,0 +1,40 @@ +/** + * GENERATED CODE - DO NOT MODIFY + */ +import { BlobRef, ValidationResult } from '@atproto/lexicon' +import { CID } from 'multiformats/cid' + +import { validate as _validate } from '../../../../lexicons' +import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' +import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.js' +import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef.js' + +const is$typed = _is$typed, + validate = _validate +const id = 'app.bsky.actor.profile' + +export interface Record { + $type: 'app.bsky.actor.profile' + displayName?: string + /** Free-form profile description text. */ + description?: string + /** Small image to be displayed next to posts from account. AKA, 'profile picture' */ + avatar?: BlobRef + /** Larger horizontal image to display behind profile view. */ + banner?: BlobRef + labels?: $Typed | { $type: string } + joinedViaStarterPack?: ComAtprotoRepoStrongRef.Main + pinnedPost?: ComAtprotoRepoStrongRef.Main + createdAt?: string + [k: string]: unknown +} + +const hashRecord = 'main' + +export function isRecord(v: V) { + return is$typed(v, id, hashRecord) +} + +export function validateRecord(v: V) { + return validate(v, id, hashRecord, true) +} diff --git a/experimental/atmii/packages/lexicon/src/types/com/atproto/label/defs.ts b/experimental/atmii/packages/lexicon/src/types/com/atproto/label/defs.ts new file mode 100644 index 0000000..af45f4a --- /dev/null +++ b/experimental/atmii/packages/lexicon/src/types/com/atproto/label/defs.ts @@ -0,0 +1,143 @@ +/** + * GENERATED CODE - DO NOT MODIFY + */ +import { BlobRef, ValidationResult } from '@atproto/lexicon' +import { CID } from 'multiformats/cid' + +import { validate as _validate } from '../../../../lexicons' +import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' + +const is$typed = _is$typed, + validate = _validate +const id = 'com.atproto.label.defs' + +/** Metadata tag on an atproto resource (eg, repo or record). */ +export interface Label { + $type?: 'com.atproto.label.defs#label' + /** The AT Protocol version of the label object. */ + ver?: number + /** DID of the actor who created this label. */ + src: string + /** AT URI of the record, repository (account), or other resource that this label applies to. */ + uri: string + /** Optionally, CID specifying the specific version of 'uri' resource this label applies to. */ + cid?: string + /** The short string name of the value or type of this label. */ + val: string + /** If true, this is a negation label, overwriting a previous label. */ + neg?: boolean + /** Timestamp when this label was created. */ + cts: string + /** Timestamp at which this label expires (no longer applies). */ + exp?: string + /** Signature of dag-cbor encoded label. */ + sig?: Uint8Array +} + +const hashLabel = 'label' + +export function isLabel(v: V) { + return is$typed(v, id, hashLabel) +} + +export function validateLabel(v: V) { + return validate