@ascorbic/pds 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -0
- package/dist/cli.js +528 -0
- package/dist/index.d.ts +332 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3130 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["sql: SqlStorage","missing: CID[]","sql: SqlStorage","eventPayload: Omit<CommitEvent, \"seq\">","cborEncode","cborDecode","r2: R2Bucket","did: string","env","collections: string[]","createOp: RecordCreateOp","commitData: CommitData","deleteOp: RecordDeleteOp","op: RecordWriteOp","ops: RecordWriteOp[]","results: Array<{\n\t\t\t$type: string;\n\t\t\turi?: string;\n\t\t\tcid?: string;\n\t\t\tvalidationStatus?: string;\n\t\t\tcollection: string;\n\t\t\trkey: string;\n\t\t\taction: WriteOpAction;\n\t\t}>","op: RecordCreateOp","op: RecordUpdateOp","op: RecordDeleteOp","finalResults: Array<{\n\t\t\t$type: string;\n\t\t\turi?: string;\n\t\t\tcid?: string;\n\t\t\tvalidationStatus?: string;\n\t\t}>","opsWithCids: Array<RecordWriteOp & { cid?: CID | null }>","cborEncode","result: Partial<T>","result: { cids: string[]; cursor?: string }","env","_env","keypairPromise: Promise<Secp256k1Keypair> | null","id","sync.getRepo","sync.getRepoStatus","sync.getBlob","sync.listRepos","sync.listBlobs","repo.describeRepo","repo.getRecord","repo.listRecords","repo.createRecord","repo.deleteRecord","repo.uploadBlob","repo.applyWrites","repo.putRecord","repo.importRepo","server.describeServer","server.createSession","server.refreshSession","server.getSession","server.deleteSession","server.getAccountStatus","headers: Record<string, string>","userDid: string","reqInit: RequestInit"],"sources":["../src/storage.ts","../src/sequencer.ts","../src/blobs.ts","../src/account-do.ts","../src/session.ts","../src/middleware/auth.ts","../src/service-auth.ts","../src/xrpc/sync.ts","../src/lexicons/app.bsky.actor.profile.json","../src/lexicons/app.bsky.embed.external.json","../src/lexicons/app.bsky.embed.images.json","../src/lexicons/app.bsky.embed.record.json","../src/lexicons/app.bsky.embed.recordWithMedia.json","../src/lexicons/app.bsky.feed.like.json","../src/lexicons/app.bsky.feed.post.json","../src/lexicons/app.bsky.feed.repost.json","../src/lexicons/app.bsky.feed.threadgate.json","../src/lexicons/app.bsky.graph.block.json","../src/lexicons/app.bsky.graph.follow.json","../src/lexicons/app.bsky.graph.list.json","../src/lexicons/app.bsky.graph.listitem.json","../src/lexicons/app.bsky.richtext.facet.json","../src/lexicons/com.atproto.label.defs.json","../src/lexicons/com.atproto.repo.strongRef.json","../src/validation.ts","../src/xrpc/repo.ts","../src/xrpc/server.ts","../package.json","../src/index.ts"],"sourcesContent":["import { CID } from \"@atproto/lex-data\";\nimport { BlockMap, type CommitData } from \"@atproto/repo\";\nimport { ReadableBlockstore, type RepoStorage } from \"@atproto/repo\";\n\n/**\n * SQLite-backed repository storage for Cloudflare Durable Objects.\n *\n * Implements the RepoStorage interface from @atproto/repo, storing blocks\n * in a SQLite database within a Durable Object.\n */\nexport class SqliteRepoStorage\n\textends ReadableBlockstore\n\timplements RepoStorage\n{\n\tconstructor(private sql: SqlStorage) {\n\t\tsuper();\n\t}\n\n\t/**\n\t * Initialize the database schema. Should be called once on DO startup.\n\t */\n\tinitSchema(): void {\n\t\tthis.sql.exec(`\n\t\t\t-- Block storage (MST nodes + record blocks)\n\t\t\tCREATE TABLE IF NOT EXISTS blocks (\n\t\t\t\tcid TEXT PRIMARY KEY,\n\t\t\t\tbytes BLOB NOT NULL,\n\t\t\t\trev TEXT NOT NULL\n\t\t\t);\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_blocks_rev ON blocks(rev);\n\n\t\t\t-- Repo state (single row)\n\t\t\tCREATE TABLE IF NOT EXISTS repo_state (\n\t\t\t\tid INTEGER PRIMARY KEY CHECK (id = 1),\n\t\t\t\troot_cid TEXT,\n\t\t\t\trev TEXT,\n\t\t\t\tseq INTEGER NOT NULL DEFAULT 0\n\t\t\t);\n\n\t\t\t-- Initialize with empty state if not exists\n\t\t\tINSERT OR IGNORE INTO repo_state (id, root_cid, rev, seq)\n\t\t\tVALUES (1, NULL, NULL, 0);\n\n\t\t\t-- Firehose events (sequenced commit log)\n\t\t\tCREATE TABLE IF NOT EXISTS firehose_events (\n\t\t\t\tseq INTEGER PRIMARY KEY AUTOINCREMENT,\n\t\t\t\tevent_type TEXT NOT NULL,\n\t\t\t\tpayload BLOB NOT NULL,\n\t\t\t\tcreated_at TEXT NOT NULL DEFAULT (datetime('now'))\n\t\t\t);\n\n\t\t\tCREATE INDEX IF NOT EXISTS idx_firehose_created_at ON firehose_events(created_at);\n\t\t`);\n\t}\n\n\t/**\n\t * Get the current root CID of the repository.\n\t */\n\tasync getRoot(): Promise<CID | null> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\"SELECT root_cid FROM repo_state WHERE id = 1\")\n\t\t\t.toArray();\n\t\tif (rows.length === 0 || !rows[0]?.root_cid) {\n\t\t\treturn null;\n\t\t}\n\t\treturn CID.parse(rows[0]!.root_cid as string);\n\t}\n\n\t/**\n\t * Get the current revision string.\n\t */\n\tasync getRev(): Promise<string | null> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\"SELECT rev FROM repo_state WHERE id = 1\")\n\t\t\t.toArray();\n\t\treturn rows.length > 0 ? ((rows[0]!.rev as string) ?? null) : null;\n\t}\n\n\t/**\n\t * Get the current sequence number for firehose events.\n\t */\n\tasync getSeq(): Promise<number> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\"SELECT seq FROM repo_state WHERE id = 1\")\n\t\t\t.toArray();\n\t\treturn rows.length > 0 ? ((rows[0]!.seq as number) ?? 0) : 0;\n\t}\n\n\t/**\n\t * Increment and return the next sequence number.\n\t */\n\tasync nextSeq(): Promise<number> {\n\t\tthis.sql.exec(\"UPDATE repo_state SET seq = seq + 1 WHERE id = 1\");\n\t\treturn this.getSeq();\n\t}\n\n\t/**\n\t * Get the raw bytes for a block by CID.\n\t */\n\tasync getBytes(cid: CID): Promise<Uint8Array | null> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\"SELECT bytes FROM blocks WHERE cid = ?\", cid.toString())\n\t\t\t.toArray();\n\t\tif (rows.length === 0 || !rows[0]?.bytes) {\n\t\t\treturn null;\n\t\t}\n\t\t// SQLite returns ArrayBuffer, convert to Uint8Array\n\t\treturn new Uint8Array(rows[0]!.bytes as ArrayBuffer);\n\t}\n\n\t/**\n\t * Check if a block exists.\n\t */\n\tasync has(cid: CID): Promise<boolean> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\"SELECT 1 FROM blocks WHERE cid = ? LIMIT 1\", cid.toString())\n\t\t\t.toArray();\n\t\treturn rows.length > 0;\n\t}\n\n\t/**\n\t * Get multiple blocks at once.\n\t */\n\tasync getBlocks(cids: CID[]): Promise<{ blocks: BlockMap; missing: CID[] }> {\n\t\tconst blocks = new BlockMap();\n\t\tconst missing: CID[] = [];\n\n\t\tfor (const cid of cids) {\n\t\t\tconst bytes = await this.getBytes(cid);\n\t\t\tif (bytes) {\n\t\t\t\tblocks.set(cid, bytes);\n\t\t\t} else {\n\t\t\t\tmissing.push(cid);\n\t\t\t}\n\t\t}\n\n\t\treturn { blocks, missing };\n\t}\n\n\t/**\n\t * Store a single block.\n\t */\n\tasync putBlock(cid: CID, block: Uint8Array, rev: string): Promise<void> {\n\t\tthis.sql.exec(\n\t\t\t\"INSERT OR REPLACE INTO blocks (cid, bytes, rev) VALUES (?, ?, ?)\",\n\t\t\tcid.toString(),\n\t\t\tblock,\n\t\t\trev,\n\t\t);\n\t}\n\n\t/**\n\t * Store multiple blocks at once.\n\t */\n\tasync putMany(blocks: BlockMap, rev: string): Promise<void> {\n\t\t// Access BlockMap's internal map to avoid iterator issues in Workers environment\n\t\t// BlockMap stores data in a Map<string, Uint8Array> internally as 'map' (private field)\n\t\tconst internalMap = (blocks as unknown as { map: Map<string, Uint8Array> })\n\t\t\t.map;\n\t\tif (internalMap) {\n\t\t\tfor (const [cidStr, bytes] of internalMap) {\n\t\t\t\tthis.sql.exec(\n\t\t\t\t\t\"INSERT OR REPLACE INTO blocks (cid, bytes, rev) VALUES (?, ?, ?)\",\n\t\t\t\t\tcidStr,\n\t\t\t\t\tbytes,\n\t\t\t\t\trev,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Update the repository root.\n\t */\n\tasync updateRoot(cid: CID, rev: string): Promise<void> {\n\t\tthis.sql.exec(\n\t\t\t\"UPDATE repo_state SET root_cid = ?, rev = ? WHERE id = 1\",\n\t\t\tcid.toString(),\n\t\t\trev,\n\t\t);\n\t}\n\n\t/**\n\t * Apply a commit atomically: add new blocks, remove old blocks, update root.\n\t */\n\tasync applyCommit(commit: CommitData): Promise<void> {\n\t\t// Note: Durable Object SQLite doesn't support BEGIN/COMMIT,\n\t\t// but operations within a single DO request are already atomic.\n\n\t\t// Access BlockMap's internal map to avoid iterator issues in Workers environment\n\t\tconst internalMap = (\n\t\t\tcommit.newBlocks as unknown as { map: Map<string, Uint8Array> }\n\t\t).map;\n\t\tif (internalMap) {\n\t\t\tfor (const [cidStr, bytes] of internalMap) {\n\t\t\t\tthis.sql.exec(\n\t\t\t\t\t\"INSERT OR REPLACE INTO blocks (cid, bytes, rev) VALUES (?, ?, ?)\",\n\t\t\t\t\tcidStr,\n\t\t\t\t\tbytes,\n\t\t\t\t\tcommit.rev,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Remove old blocks - access CidSet's internal set to avoid CID.parse shim issues\n\t\tconst removedSet = (commit.removedCids as unknown as { set: Set<string> })\n\t\t\t.set;\n\t\tif (removedSet) {\n\t\t\tfor (const cidStr of removedSet) {\n\t\t\t\tthis.sql.exec(\"DELETE FROM blocks WHERE cid = ?\", cidStr);\n\t\t\t}\n\t\t}\n\n\t\t// Update root\n\t\tawait this.updateRoot(commit.cid, commit.rev);\n\t}\n\n\t/**\n\t * Get total storage size in bytes.\n\t */\n\tasync sizeInBytes(): Promise<number> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\"SELECT SUM(LENGTH(bytes)) as total FROM blocks\")\n\t\t\t.toArray();\n\t\treturn rows.length > 0 ? ((rows[0]!.total as number) ?? 0) : 0;\n\t}\n\n\t/**\n\t * Clear all data (for testing).\n\t */\n\tasync destroy(): Promise<void> {\n\t\tthis.sql.exec(\"DELETE FROM blocks\");\n\t\tthis.sql.exec(\n\t\t\t\"UPDATE repo_state SET root_cid = NULL, rev = NULL WHERE id = 1\",\n\t\t);\n\t}\n\n\t/**\n\t * Count the number of blocks stored.\n\t */\n\tasync countBlocks(): Promise<number> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\"SELECT COUNT(*) as count FROM blocks\")\n\t\t\t.toArray();\n\t\treturn rows.length > 0 ? ((rows[0]!.count as number) ?? 0) : 0;\n\t}\n}\n","import {\n\tencode as cborEncode,\n\tdecode as cborDecode,\n\ttype LexValue,\n} from \"@atproto/lex-cbor\";\nimport { CID } from \"@atproto/lex-data\";\nimport { blocksToCarFile, type BlockMap } from \"@atproto/repo\";\nimport type { RecordWriteOp } from \"@atproto/repo\";\n\n/**\n * Commit event payload for the firehose\n */\nexport interface CommitEvent {\n\tseq: number;\n\trebase: boolean;\n\ttooBig: boolean;\n\trepo: string;\n\tcommit: CID;\n\trev: string;\n\tsince: string | null;\n\tblocks: Uint8Array;\n\tops: RepoOp[];\n\tblobs: CID[];\n\ttime: string;\n}\n\n/**\n * Repository operation in a commit\n */\nexport interface RepoOp {\n\taction: \"create\" | \"update\" | \"delete\";\n\tpath: string;\n\tcid: CID | null;\n}\n\n/**\n * Sequenced event wrapper\n */\nexport interface SeqEvent {\n\tseq: number;\n\ttype: \"commit\";\n\tevent: CommitEvent;\n\ttime: string;\n}\n\n/**\n * Data needed to sequence a commit\n */\nexport interface CommitData {\n\tdid: string;\n\tcommit: CID;\n\trev: string;\n\tsince: string | null;\n\tnewBlocks: BlockMap;\n\tops: Array<RecordWriteOp & { cid?: CID | null }>;\n}\n\n/**\n * Sequencer manages the firehose event log.\n *\n * Stores commit events in SQLite and provides methods for:\n * - Sequencing new commits\n * - Backfilling events from a cursor\n * - Getting the latest sequence number\n */\nexport class Sequencer {\n\tconstructor(private sql: SqlStorage) {}\n\n\t/**\n\t * Add a commit to the firehose sequence.\n\t * Returns the complete sequenced event for broadcasting.\n\t */\n\tasync sequenceCommit(data: CommitData): Promise<SeqEvent> {\n\t\t// Create CAR slice with commit diff\n\t\tconst carBytes = await blocksToCarFile(data.commit, data.newBlocks);\n\t\tconst time = new Date().toISOString();\n\n\t\t// Build event payload\n\t\tconst eventPayload: Omit<CommitEvent, \"seq\"> = {\n\t\t\trepo: data.did,\n\t\t\tcommit: data.commit,\n\t\t\trev: data.rev,\n\t\t\tsince: data.since,\n\t\t\tblocks: carBytes,\n\t\t\tops: data.ops.map(\n\t\t\t\t(op): RepoOp => ({\n\t\t\t\t\taction: op.action as \"create\" | \"update\" | \"delete\",\n\t\t\t\t\tpath: `${op.collection}/${op.rkey}`,\n\t\t\t\t\tcid: (\"cid\" in op && op.cid ? op.cid : null) as CID | null,\n\t\t\t\t}),\n\t\t\t),\n\t\t\trebase: false,\n\t\t\ttooBig: carBytes.length > 1_000_000,\n\t\t\tblobs: [],\n\t\t\ttime,\n\t\t};\n\n\t\t// Store in SQLite\n\t\t// Type assertion: CBOR handles CID/Uint8Array serialization\n\t\tconst payload = cborEncode(eventPayload as {} as LexValue);\n\t\tconst result = this.sql\n\t\t\t.exec(\n\t\t\t\t`INSERT INTO firehose_events (event_type, payload)\n VALUES ('commit', ?)\n RETURNING seq`,\n\t\t\t\tpayload,\n\t\t\t)\n\t\t\t.one();\n\n\t\tconst seq = result.seq as number;\n\n\t\treturn {\n\t\t\tseq,\n\t\t\ttype: \"commit\",\n\t\t\tevent: {\n\t\t\t\t...eventPayload,\n\t\t\t\tseq,\n\t\t\t},\n\t\t\ttime,\n\t\t};\n\t}\n\n\t/**\n\t * Get events from a cursor position.\n\t * Returns up to `limit` events after the cursor.\n\t */\n\tasync getEventsSince(cursor: number, limit = 100): Promise<SeqEvent[]> {\n\t\tconst rows = this.sql\n\t\t\t.exec(\n\t\t\t\t`SELECT seq, event_type, payload, created_at\n FROM firehose_events\n WHERE seq > ?\n ORDER BY seq ASC\n LIMIT ?`,\n\t\t\t\tcursor,\n\t\t\t\tlimit,\n\t\t\t)\n\t\t\t.toArray();\n\n\t\treturn rows.map((row) => {\n\t\t\tconst payload = new Uint8Array(row.payload as ArrayBuffer);\n\t\t\tconst decoded = cborDecode(payload);\n\n\t\t\treturn {\n\t\t\t\tseq: row.seq as number,\n\t\t\t\ttype: \"commit\",\n\t\t\t\tevent: {\n\t\t\t\t\t...(decoded as unknown as Omit<CommitEvent, \"seq\">),\n\t\t\t\t\tseq: row.seq as number,\n\t\t\t\t},\n\t\t\t\ttime: row.created_at as string,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Get the latest sequence number.\n\t * Returns 0 if no events have been sequenced yet.\n\t */\n\tgetLatestSeq(): number {\n\t\tconst result = this.sql\n\t\t\t.exec(\"SELECT MAX(seq) as seq FROM firehose_events\")\n\t\t\t.one();\n\t\treturn (result?.seq as number) ?? 0;\n\t}\n\n\t/**\n\t * Prune old events to keep the log from growing indefinitely.\n\t * Keeps the most recent `keepCount` events.\n\t */\n\tasync pruneOldEvents(keepCount = 10000): Promise<void> {\n\t\tthis.sql.exec(\n\t\t\t`DELETE FROM firehose_events\n WHERE seq < (SELECT MAX(seq) - ? FROM firehose_events)`,\n\t\t\tkeepCount,\n\t\t);\n\t}\n}\n","import { CID } from \"@atproto/lex-data\";\nimport { cidForRawBytes } from \"@atproto/lex-cbor\";\n\nexport interface BlobRef {\n\t$type: \"blob\";\n\tref: { $link: string };\n\tmimeType: string;\n\tsize: number;\n}\n\n/**\n * BlobStore manages blob storage in R2.\n * Blobs are stored with CID-based keys prefixed by the account's DID.\n */\nexport class BlobStore {\n\tconstructor(\n\t\tprivate r2: R2Bucket,\n\t\tprivate did: string,\n\t) {}\n\n\t/**\n\t * Upload a blob to R2 and return a BlobRef.\n\t */\n\tasync putBlob(bytes: Uint8Array, mimeType: string): Promise<BlobRef> {\n\t\t// Compute CID using SHA-256 (RAW codec)\n\t\tconst cid = await cidForRawBytes(bytes);\n\n\t\t// Store in R2 with DID prefix for isolation\n\t\tconst key = `${this.did}/${cid.toString()}`;\n\t\tawait this.r2.put(key, bytes, {\n\t\t\thttpMetadata: { contentType: mimeType },\n\t\t});\n\n\t\treturn {\n\t\t\t$type: \"blob\",\n\t\t\tref: { $link: cid.toString() },\n\t\t\tmimeType,\n\t\t\tsize: bytes.length,\n\t\t};\n\t}\n\n\t/**\n\t * Retrieve a blob from R2 by CID.\n\t */\n\tasync getBlob(cid: CID): Promise<R2ObjectBody | null> {\n\t\tconst key = `${this.did}/${cid.toString()}`;\n\t\treturn this.r2.get(key);\n\t}\n\n\t/**\n\t * Check if a blob exists in R2.\n\t */\n\tasync hasBlob(cid: CID): Promise<boolean> {\n\t\tconst key = `${this.did}/${cid.toString()}`;\n\t\tconst head = await this.r2.head(key);\n\t\treturn head !== null;\n\t}\n}\n","import { DurableObject } from \"cloudflare:workers\";\nimport {\n\tRepo,\n\tWriteOpAction,\n\tBlockMap,\n\tblocksToCarFile,\n\treadCarWithRoot,\n\ttype RecordCreateOp,\n\ttype RecordUpdateOp,\n\ttype RecordDeleteOp,\n\ttype RecordWriteOp,\n} from \"@atproto/repo\";\nimport type { RepoRecord } from \"@atproto/lexicon\";\nimport { Secp256k1Keypair } from \"@atproto/crypto\";\nimport { CID } from \"@atproto/lex-data\";\nimport { TID } from \"@atproto/common-web\";\nimport { AtUri } from \"@atproto/syntax\";\nimport { encode as cborEncode } from \"@atproto/lex-cbor\";\nimport { SqliteRepoStorage } from \"./storage\";\nimport { Sequencer, type SeqEvent, type CommitData } from \"./sequencer\";\nimport { BlobStore, type BlobRef } from \"./blobs\";\nimport type { PDSEnv } from \"./types\";\n\n/**\n * Account Durable Object - manages a single user's AT Protocol repository.\n *\n * This DO provides:\n * - SQLite-backed block storage for the repository\n * - AT Protocol Repo instance for repository operations\n * - Firehose WebSocket connections\n * - Sequence number management\n */\nexport class AccountDurableObject extends DurableObject<PDSEnv> {\n\tprivate storage: SqliteRepoStorage | null = null;\n\tprivate repo: Repo | null = null;\n\tprivate keypair: Secp256k1Keypair | null = null;\n\tprivate sequencer: Sequencer | null = null;\n\tprivate blobStore: BlobStore | null = null;\n\tprivate storageInitialized = false;\n\tprivate repoInitialized = false;\n\n\tconstructor(ctx: DurableObjectState, env: PDSEnv) {\n\t\tsuper(ctx, env);\n\n\t\t// Validate required environment variables at startup\n\t\tif (!env.SIGNING_KEY) {\n\t\t\tthrow new Error(\"Missing required environment variable: SIGNING_KEY\");\n\t\t}\n\t\tif (!env.DID) {\n\t\t\tthrow new Error(\"Missing required environment variable: DID\");\n\t\t}\n\n\t\t// Initialize BlobStore if R2 bucket is available\n\t\tif (env.BLOBS) {\n\t\t\tthis.blobStore = new BlobStore(env.BLOBS, env.DID);\n\t\t}\n\t}\n\n\t/**\n\t * Initialize the storage adapter. Called lazily on first storage access.\n\t */\n\tprivate async ensureStorageInitialized(): Promise<void> {\n\t\tif (!this.storageInitialized) {\n\t\t\tawait this.ctx.blockConcurrencyWhile(async () => {\n\t\t\t\tif (this.storageInitialized) return; // Double-check after acquiring lock\n\n\t\t\t\tthis.storage = new SqliteRepoStorage(this.ctx.storage.sql);\n\t\t\t\tthis.storage.initSchema();\n\t\t\t\tthis.sequencer = new Sequencer(this.ctx.storage.sql);\n\t\t\t\tthis.storageInitialized = true;\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Initialize the Repo instance. Called lazily on first repo access.\n\t */\n\tprivate async ensureRepoInitialized(): Promise<void> {\n\t\tawait this.ensureStorageInitialized();\n\n\t\tif (!this.repoInitialized) {\n\t\t\tawait this.ctx.blockConcurrencyWhile(async () => {\n\t\t\t\tif (this.repoInitialized) return; // Double-check after acquiring lock\n\n\t\t\t\t// Load signing key\n\t\t\t\tthis.keypair = await Secp256k1Keypair.import(this.env.SIGNING_KEY);\n\n\t\t\t\t// Load or create repo\n\t\t\t\tconst root = await this.storage!.getRoot();\n\t\t\t\tif (root) {\n\t\t\t\t\tthis.repo = await Repo.load(this.storage!, root);\n\t\t\t\t} else {\n\t\t\t\t\tthis.repo = await Repo.create(\n\t\t\t\t\t\tthis.storage!,\n\t\t\t\t\t\tthis.env.DID,\n\t\t\t\t\t\tthis.keypair,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.repoInitialized = true;\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Get the storage adapter for direct access (used by tests and internal operations).\n\t */\n\tasync getStorage(): Promise<SqliteRepoStorage> {\n\t\tawait this.ensureStorageInitialized();\n\t\treturn this.storage!;\n\t}\n\n\t/**\n\t * Get the Repo instance for repository operations.\n\t */\n\tasync getRepo(): Promise<Repo> {\n\t\tawait this.ensureRepoInitialized();\n\t\treturn this.repo!;\n\t}\n\n\t/**\n\t * Get the signing keypair for repository operations.\n\t */\n\tasync getKeypair(): Promise<Secp256k1Keypair> {\n\t\tawait this.ensureRepoInitialized();\n\t\treturn this.keypair!;\n\t}\n\n\t/**\n\t * Update the Repo instance after mutations.\n\t */\n\tasync setRepo(repo: Repo): Promise<void> {\n\t\tthis.repo = repo;\n\t}\n\n\t/**\n\t * RPC method: Get repo metadata for describeRepo\n\t */\n\tasync rpcDescribeRepo(): Promise<{\n\t\tdid: string;\n\t\tcollections: string[];\n\t\tcid: string;\n\t}> {\n\t\tconst repo = await this.getRepo();\n\t\tconst collections: string[] = [];\n\t\tconst seenCollections = new Set<string>();\n\n\t\tfor await (const record of repo.walkRecords()) {\n\t\t\tif (!seenCollections.has(record.collection)) {\n\t\t\t\tseenCollections.add(record.collection);\n\t\t\t\tcollections.push(record.collection);\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tdid: repo.did,\n\t\t\tcollections,\n\t\t\tcid: repo.cid.toString(),\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: Get a single record\n\t */\n\tasync rpcGetRecord(\n\t\tcollection: string,\n\t\trkey: string,\n\t): Promise<{\n\t\tcid: string;\n\t\trecord: Rpc.Serializable<any>;\n\t} | null> {\n\t\tconst repo = await this.getRepo();\n\n\t\t// Get the CID from the MST\n\t\tconst dataKey = `${collection}/${rkey}`;\n\t\tconst recordCid = await repo.data.get(dataKey);\n\t\tif (!recordCid) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst record = (await repo.getRecord(\n\t\t\tcollection,\n\t\t\trkey,\n\t\t)) as Rpc.Serializable<any>;\n\n\t\tif (!record) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\tcid: recordCid.toString(),\n\t\t\trecord,\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: List records in a collection\n\t */\n\tasync rpcListRecords(\n\t\tcollection: string,\n\t\topts: {\n\t\t\tlimit: number;\n\t\t\tcursor?: string;\n\t\t\treverse?: boolean;\n\t\t},\n\t): Promise<{\n\t\trecords: Array<{ uri: string; cid: string; value: unknown }>;\n\t\tcursor?: string;\n\t}> {\n\t\tconst repo = await this.getRepo();\n\t\tconst records = [];\n\t\tconst startFrom = opts.cursor || `${collection}/`;\n\n\t\tfor await (const record of repo.walkRecords(startFrom)) {\n\t\t\tif (record.collection !== collection) {\n\t\t\t\tif (records.length > 0) break;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trecords.push({\n\t\t\t\turi: AtUri.make(repo.did, record.collection, record.rkey).toString(),\n\t\t\t\tcid: record.cid.toString(),\n\t\t\t\tvalue: record.record,\n\t\t\t});\n\n\t\t\tif (records.length >= opts.limit + 1) break;\n\t\t}\n\n\t\tif (opts.reverse) {\n\t\t\trecords.reverse();\n\t\t}\n\n\t\tconst hasMore = records.length > opts.limit;\n\t\tconst results = hasMore ? records.slice(0, opts.limit) : records;\n\t\tconst cursor = hasMore\n\t\t\t? `${collection}/${results[results.length - 1]?.uri.split(\"/\").pop() ?? \"\"}`\n\t\t\t: undefined;\n\n\t\treturn { records: results, cursor };\n\t}\n\n\t/**\n\t * RPC method: Create a record\n\t */\n\tasync rpcCreateRecord(\n\t\tcollection: string,\n\t\trkey: string | undefined,\n\t\trecord: unknown,\n\t): Promise<{\n\t\turi: string;\n\t\tcid: string;\n\t\tcommit: { cid: string; rev: string };\n\t}> {\n\t\tconst repo = await this.getRepo();\n\t\tconst keypair = await this.getKeypair();\n\n\t\tconst actualRkey = rkey || TID.nextStr();\n\t\tconst createOp: RecordCreateOp = {\n\t\t\taction: WriteOpAction.Create,\n\t\t\tcollection,\n\t\t\trkey: actualRkey,\n\t\t\trecord: record as RepoRecord,\n\t\t};\n\n\t\tconst prevRev = repo.commit.rev;\n\t\tconst updatedRepo = await repo.applyWrites([createOp], keypair);\n\t\tthis.repo = updatedRepo;\n\n\t\t// Get the CID for the created record from the MST\n\t\tconst dataKey = `${collection}/${actualRkey}`;\n\t\tconst recordCid = await this.repo.data.get(dataKey);\n\n\t\tif (!recordCid) {\n\t\t\tthrow new Error(`Failed to create record: ${collection}/${actualRkey}`);\n\t\t}\n\n\t\t// Sequence the commit for firehose\n\t\tif (this.sequencer) {\n\t\t\t// Get blocks that changed\n\t\t\tconst newBlocks = new BlockMap();\n\t\t\tconst rows = this.ctx.storage.sql\n\t\t\t\t.exec(\n\t\t\t\t\t\"SELECT cid, bytes FROM blocks WHERE rev = ?\",\n\t\t\t\t\tthis.repo.commit.rev,\n\t\t\t\t)\n\t\t\t\t.toArray();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst cid = CID.parse(row.cid as string);\n\t\t\t\tconst bytes = new Uint8Array(row.bytes as ArrayBuffer);\n\t\t\t\tnewBlocks.set(cid, bytes);\n\t\t\t}\n\n\t\t\t// Include the record CID in the op for the firehose\n\t\t\tconst opWithCid = { ...createOp, cid: recordCid };\n\n\t\t\tconst commitData: CommitData = {\n\t\t\t\tdid: this.repo.did,\n\t\t\t\tcommit: this.repo.cid,\n\t\t\t\trev: this.repo.commit.rev,\n\t\t\t\tsince: prevRev,\n\t\t\t\tnewBlocks,\n\t\t\t\tops: [opWithCid],\n\t\t\t};\n\n\t\t\tconst event = await this.sequencer.sequenceCommit(commitData);\n\t\t\tawait this.broadcastCommit(event);\n\t\t}\n\n\t\treturn {\n\t\t\turi: AtUri.make(this.repo.did, collection, actualRkey).toString(),\n\t\t\tcid: recordCid.toString(),\n\t\t\tcommit: {\n\t\t\t\tcid: this.repo.cid.toString(),\n\t\t\t\trev: this.repo.commit.rev,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: Delete a record\n\t */\n\tasync rpcDeleteRecord(\n\t\tcollection: string,\n\t\trkey: string,\n\t): Promise<{ commit: { cid: string; rev: string } } | null> {\n\t\tconst repo = await this.getRepo();\n\t\tconst keypair = await this.getKeypair();\n\n\t\tconst existing = await repo.getRecord(collection, rkey);\n\t\tif (!existing) return null;\n\n\t\tconst deleteOp: RecordDeleteOp = {\n\t\t\taction: WriteOpAction.Delete,\n\t\t\tcollection,\n\t\t\trkey,\n\t\t};\n\n\t\tconst prevRev = repo.commit.rev;\n\t\tconst updatedRepo = await repo.applyWrites([deleteOp], keypair);\n\t\tthis.repo = updatedRepo;\n\n\t\t// Sequence the commit for firehose\n\t\tif (this.sequencer) {\n\t\t\t// Get blocks that changed\n\t\t\tconst newBlocks = new BlockMap();\n\t\t\tconst rows = this.ctx.storage.sql\n\t\t\t\t.exec(\n\t\t\t\t\t\"SELECT cid, bytes FROM blocks WHERE rev = ?\",\n\t\t\t\t\tthis.repo.commit.rev,\n\t\t\t\t)\n\t\t\t\t.toArray();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst cid = CID.parse(row.cid as string);\n\t\t\t\tconst bytes = new Uint8Array(row.bytes as ArrayBuffer);\n\t\t\t\tnewBlocks.set(cid, bytes);\n\t\t\t}\n\n\t\t\tconst commitData: CommitData = {\n\t\t\t\tdid: this.repo.did,\n\t\t\t\tcommit: this.repo.cid,\n\t\t\t\trev: this.repo.commit.rev,\n\t\t\t\tsince: prevRev,\n\t\t\t\tnewBlocks,\n\t\t\t\tops: [deleteOp],\n\t\t\t};\n\n\t\t\tconst event = await this.sequencer.sequenceCommit(commitData);\n\t\t\tawait this.broadcastCommit(event);\n\t\t}\n\n\t\treturn {\n\t\t\tcommit: {\n\t\t\t\tcid: updatedRepo.cid.toString(),\n\t\t\t\trev: updatedRepo.commit.rev,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: Put a record (create or update)\n\t */\n\tasync rpcPutRecord(\n\t\tcollection: string,\n\t\trkey: string,\n\t\trecord: unknown,\n\t): Promise<{\n\t\turi: string;\n\t\tcid: string;\n\t\tcommit: { cid: string; rev: string };\n\t\tvalidationStatus: string;\n\t}> {\n\t\tconst repo = await this.getRepo();\n\t\tconst keypair = await this.getKeypair();\n\n\t\t// Check if record exists to determine create vs update\n\t\tconst existing = await repo.getRecord(collection, rkey);\n\t\tconst isUpdate = existing !== null;\n\n\t\tconst op: RecordWriteOp = isUpdate\n\t\t\t? ({\n\t\t\t\t\taction: WriteOpAction.Update,\n\t\t\t\t\tcollection,\n\t\t\t\t\trkey,\n\t\t\t\t\trecord: record as RepoRecord,\n\t\t\t\t} as RecordUpdateOp)\n\t\t\t: ({\n\t\t\t\t\taction: WriteOpAction.Create,\n\t\t\t\t\tcollection,\n\t\t\t\t\trkey,\n\t\t\t\t\trecord: record as RepoRecord,\n\t\t\t\t} as RecordCreateOp);\n\n\t\tconst prevRev = repo.commit.rev;\n\t\tconst updatedRepo = await repo.applyWrites([op], keypair);\n\t\tthis.repo = updatedRepo;\n\n\t\t// Get the CID for the record from the MST\n\t\tconst dataKey = `${collection}/${rkey}`;\n\t\tconst recordCid = await this.repo.data.get(dataKey);\n\n\t\tif (!recordCid) {\n\t\t\tthrow new Error(`Failed to put record: ${collection}/${rkey}`);\n\t\t}\n\n\t\t// Sequence the commit for firehose\n\t\tif (this.sequencer) {\n\t\t\tconst newBlocks = new BlockMap();\n\t\t\tconst rows = this.ctx.storage.sql\n\t\t\t\t.exec(\n\t\t\t\t\t\"SELECT cid, bytes FROM blocks WHERE rev = ?\",\n\t\t\t\t\tthis.repo.commit.rev,\n\t\t\t\t)\n\t\t\t\t.toArray();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst cid = CID.parse(row.cid as string);\n\t\t\t\tconst bytes = new Uint8Array(row.bytes as ArrayBuffer);\n\t\t\t\tnewBlocks.set(cid, bytes);\n\t\t\t}\n\n\t\t\tconst opWithCid = { ...op, cid: recordCid };\n\n\t\t\tconst commitData: CommitData = {\n\t\t\t\tdid: this.repo.did,\n\t\t\t\tcommit: this.repo.cid,\n\t\t\t\trev: this.repo.commit.rev,\n\t\t\t\tsince: prevRev,\n\t\t\t\tnewBlocks,\n\t\t\t\tops: [opWithCid],\n\t\t\t};\n\n\t\t\tconst event = await this.sequencer.sequenceCommit(commitData);\n\t\t\tawait this.broadcastCommit(event);\n\t\t}\n\n\t\treturn {\n\t\t\turi: AtUri.make(this.repo.did, collection, rkey).toString(),\n\t\t\tcid: recordCid.toString(),\n\t\t\tcommit: {\n\t\t\t\tcid: this.repo.cid.toString(),\n\t\t\t\trev: this.repo.commit.rev,\n\t\t\t},\n\t\t\tvalidationStatus: \"valid\",\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: Apply multiple writes (batch create/update/delete)\n\t */\n\tasync rpcApplyWrites(\n\t\twrites: Array<{\n\t\t\t$type: string;\n\t\t\tcollection: string;\n\t\t\trkey?: string;\n\t\t\tvalue?: unknown;\n\t\t}>,\n\t): Promise<{\n\t\tcommit: { cid: string; rev: string };\n\t\tresults: Array<{\n\t\t\t$type: string;\n\t\t\turi?: string;\n\t\t\tcid?: string;\n\t\t\tvalidationStatus?: string;\n\t\t}>;\n\t}> {\n\t\tconst repo = await this.getRepo();\n\t\tconst keypair = await this.getKeypair();\n\n\t\t// Convert input writes to RecordWriteOp format\n\t\tconst ops: RecordWriteOp[] = [];\n\t\tconst results: Array<{\n\t\t\t$type: string;\n\t\t\turi?: string;\n\t\t\tcid?: string;\n\t\t\tvalidationStatus?: string;\n\t\t\tcollection: string;\n\t\t\trkey: string;\n\t\t\taction: WriteOpAction;\n\t\t}> = [];\n\n\t\tfor (const write of writes) {\n\t\t\tif (write.$type === \"com.atproto.repo.applyWrites#create\") {\n\t\t\t\tconst rkey = write.rkey || TID.nextStr();\n\t\t\t\tconst op: RecordCreateOp = {\n\t\t\t\t\taction: WriteOpAction.Create,\n\t\t\t\t\tcollection: write.collection,\n\t\t\t\t\trkey,\n\t\t\t\t\trecord: write.value as RepoRecord,\n\t\t\t\t};\n\t\t\t\tops.push(op);\n\t\t\t\tresults.push({\n\t\t\t\t\t$type: \"com.atproto.repo.applyWrites#createResult\",\n\t\t\t\t\tcollection: write.collection,\n\t\t\t\t\trkey,\n\t\t\t\t\taction: WriteOpAction.Create,\n\t\t\t\t});\n\t\t\t} else if (write.$type === \"com.atproto.repo.applyWrites#update\") {\n\t\t\t\tif (!write.rkey) {\n\t\t\t\t\tthrow new Error(\"Update requires rkey\");\n\t\t\t\t}\n\t\t\t\tconst op: RecordUpdateOp = {\n\t\t\t\t\taction: WriteOpAction.Update,\n\t\t\t\t\tcollection: write.collection,\n\t\t\t\t\trkey: write.rkey,\n\t\t\t\t\trecord: write.value as RepoRecord,\n\t\t\t\t};\n\t\t\t\tops.push(op);\n\t\t\t\tresults.push({\n\t\t\t\t\t$type: \"com.atproto.repo.applyWrites#updateResult\",\n\t\t\t\t\tcollection: write.collection,\n\t\t\t\t\trkey: write.rkey,\n\t\t\t\t\taction: WriteOpAction.Update,\n\t\t\t\t});\n\t\t\t} else if (write.$type === \"com.atproto.repo.applyWrites#delete\") {\n\t\t\t\tif (!write.rkey) {\n\t\t\t\t\tthrow new Error(\"Delete requires rkey\");\n\t\t\t\t}\n\t\t\t\tconst op: RecordDeleteOp = {\n\t\t\t\t\taction: WriteOpAction.Delete,\n\t\t\t\t\tcollection: write.collection,\n\t\t\t\t\trkey: write.rkey,\n\t\t\t\t};\n\t\t\t\tops.push(op);\n\t\t\t\tresults.push({\n\t\t\t\t\t$type: \"com.atproto.repo.applyWrites#deleteResult\",\n\t\t\t\t\tcollection: write.collection,\n\t\t\t\t\trkey: write.rkey,\n\t\t\t\t\taction: WriteOpAction.Delete,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Unknown write type: ${write.$type}`);\n\t\t\t}\n\t\t}\n\n\t\tconst prevRev = repo.commit.rev;\n\t\tconst updatedRepo = await repo.applyWrites(ops, keypair);\n\t\tthis.repo = updatedRepo;\n\n\t\t// Build final results with CIDs and prepare ops with CIDs for firehose\n\t\tconst finalResults: Array<{\n\t\t\t$type: string;\n\t\t\turi?: string;\n\t\t\tcid?: string;\n\t\t\tvalidationStatus?: string;\n\t\t}> = [];\n\t\tconst opsWithCids: Array<RecordWriteOp & { cid?: CID | null }> = [];\n\n\t\tfor (let i = 0; i < results.length; i++) {\n\t\t\tconst result = results[i]!;\n\t\t\tconst op = ops[i]!;\n\n\t\t\tif (result.action === WriteOpAction.Delete) {\n\t\t\t\tfinalResults.push({\n\t\t\t\t\t$type: result.$type,\n\t\t\t\t});\n\t\t\t\topsWithCids.push(op);\n\t\t\t} else {\n\t\t\t\t// Get the CID for create/update\n\t\t\t\tconst dataKey = `${result.collection}/${result.rkey}`;\n\t\t\t\tconst recordCid = await this.repo.data.get(dataKey);\n\t\t\t\tfinalResults.push({\n\t\t\t\t\t$type: result.$type,\n\t\t\t\t\turi: AtUri.make(\n\t\t\t\t\t\tthis.repo.did,\n\t\t\t\t\t\tresult.collection,\n\t\t\t\t\t\tresult.rkey,\n\t\t\t\t\t).toString(),\n\t\t\t\t\tcid: recordCid?.toString(),\n\t\t\t\t\tvalidationStatus: \"valid\",\n\t\t\t\t});\n\t\t\t\t// Include the record CID in the op for the firehose\n\t\t\t\topsWithCids.push({ ...op, cid: recordCid });\n\t\t\t}\n\t\t}\n\n\t\t// Sequence the commit for firehose\n\t\tif (this.sequencer) {\n\t\t\tconst newBlocks = new BlockMap();\n\t\t\tconst rows = this.ctx.storage.sql\n\t\t\t\t.exec(\n\t\t\t\t\t\"SELECT cid, bytes FROM blocks WHERE rev = ?\",\n\t\t\t\t\tthis.repo.commit.rev,\n\t\t\t\t)\n\t\t\t\t.toArray();\n\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst cid = CID.parse(row.cid as string);\n\t\t\t\tconst bytes = new Uint8Array(row.bytes as ArrayBuffer);\n\t\t\t\tnewBlocks.set(cid, bytes);\n\t\t\t}\n\n\t\t\tconst commitData: CommitData = {\n\t\t\t\tdid: this.repo.did,\n\t\t\t\tcommit: this.repo.cid,\n\t\t\t\trev: this.repo.commit.rev,\n\t\t\t\tsince: prevRev,\n\t\t\t\tnewBlocks,\n\t\t\t\tops: opsWithCids,\n\t\t\t};\n\n\t\t\tconst event = await this.sequencer.sequenceCommit(commitData);\n\t\t\tawait this.broadcastCommit(event);\n\t\t}\n\n\t\treturn {\n\t\t\tcommit: {\n\t\t\t\tcid: this.repo.cid.toString(),\n\t\t\t\trev: this.repo.commit.rev,\n\t\t\t},\n\t\t\tresults: finalResults,\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: Get repo status\n\t */\n\tasync rpcGetRepoStatus(): Promise<{\n\t\tdid: string;\n\t\thead: string;\n\t\trev: string;\n\t}> {\n\t\tconst repo = await this.getRepo();\n\t\treturn {\n\t\t\tdid: repo.did,\n\t\t\thead: repo.cid.toString(),\n\t\t\trev: repo.commit.rev,\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: Export repo as CAR\n\t */\n\tasync rpcGetRepoCar(): Promise<Uint8Array> {\n\t\tconst storage = await this.getStorage();\n\t\tconst root = await storage.getRoot();\n\n\t\tif (!root) {\n\t\t\tthrow new Error(\"No repository root found\");\n\t\t}\n\n\t\t// Get all blocks from SQLite storage\n\t\tconst rows = this.ctx.storage.sql\n\t\t\t.exec(\"SELECT cid, bytes FROM blocks\")\n\t\t\t.toArray();\n\n\t\t// Build BlockMap\n\t\tconst blocks = new BlockMap();\n\t\tfor (const row of rows) {\n\t\t\tconst cid = CID.parse(row.cid as string);\n\t\t\tconst bytes = new Uint8Array(row.bytes as ArrayBuffer);\n\t\t\tblocks.set(cid, bytes);\n\t\t}\n\n\t\t// Use the official CAR builder\n\t\treturn blocksToCarFile(root, blocks);\n\t}\n\n\t/**\n\t * RPC method: Import repo from CAR file\n\t * This is used for account migration - importing an existing repository\n\t * from another PDS.\n\t */\n\tasync rpcImportRepo(carBytes: Uint8Array): Promise<{\n\t\tdid: string;\n\t\trev: string;\n\t\tcid: string;\n\t}> {\n\t\tawait this.ensureStorageInitialized();\n\n\t\t// Check if repo already exists\n\t\tconst existingRoot = await this.storage!.getRoot();\n\t\tif (existingRoot) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Repository already exists. Cannot import over existing repository.\",\n\t\t\t);\n\t\t}\n\n\t\t// Use official @atproto/repo utilities to read and validate CAR\n\t\t// readCarWithRoot validates single root requirement and returns BlockMap\n\t\tconst { root: rootCid, blocks } = await readCarWithRoot(carBytes);\n\n\t\t// Import all blocks into storage using putMany (more efficient than individual putBlock)\n\t\tconst importRev = TID.nextStr();\n\t\tawait this.storage!.putMany(blocks, importRev);\n\n\t\t// Load the repo to verify it's valid and get the actual revision\n\t\tthis.keypair = await Secp256k1Keypair.import(this.env.SIGNING_KEY);\n\t\tthis.repo = await Repo.load(this.storage!, rootCid);\n\n\t\t// Verify the DID matches to prevent incorrect migrations\n\t\tif (this.repo.did !== this.env.DID) {\n\t\t\t// Clean up imported blocks\n\t\t\tawait this.storage!.destroy();\n\t\t\tthrow new Error(\n\t\t\t\t`DID mismatch: CAR file contains DID ${this.repo.did}, but expected ${this.env.DID}`,\n\t\t\t);\n\t\t}\n\n\t\tthis.repoInitialized = true;\n\n\t\treturn {\n\t\t\tdid: this.repo.did,\n\t\t\trev: this.repo.commit.rev,\n\t\t\tcid: rootCid.toString(),\n\t\t};\n\t}\n\n\t/**\n\t * RPC method: Upload a blob to R2\n\t */\n\tasync rpcUploadBlob(bytes: Uint8Array, mimeType: string): Promise<BlobRef> {\n\t\tif (!this.blobStore) {\n\t\t\tthrow new Error(\"Blob storage not configured\");\n\t\t}\n\n\t\t// Enforce size limit (5MB)\n\t\tconst MAX_BLOB_SIZE = 5 * 1024 * 1024;\n\t\tif (bytes.length > MAX_BLOB_SIZE) {\n\t\t\tthrow new Error(\n\t\t\t\t`Blob too large: ${bytes.length} bytes (max ${MAX_BLOB_SIZE})`,\n\t\t\t);\n\t\t}\n\n\t\treturn this.blobStore.putBlob(bytes, mimeType);\n\t}\n\n\t/**\n\t * RPC method: Get a blob from R2\n\t */\n\tasync rpcGetBlob(cidStr: string): Promise<R2ObjectBody | null> {\n\t\tif (!this.blobStore) {\n\t\t\tthrow new Error(\"Blob storage not configured\");\n\t\t}\n\n\t\tconst cid = CID.parse(cidStr);\n\t\treturn this.blobStore.getBlob(cid);\n\t}\n\n\t/**\n\t * Encode a firehose frame (header + body CBOR).\n\t */\n\tprivate encodeFrame(header: object, body: object): Uint8Array {\n\t\tconst headerBytes = cborEncode(header as any);\n\t\tconst bodyBytes = cborEncode(body as any);\n\n\t\tconst frame = new Uint8Array(headerBytes.length + bodyBytes.length);\n\t\tframe.set(headerBytes, 0);\n\t\tframe.set(bodyBytes, headerBytes.length);\n\n\t\treturn frame;\n\t}\n\n\t/**\n\t * Encode a commit event frame.\n\t */\n\tprivate encodeCommitFrame(event: SeqEvent): Uint8Array {\n\t\tconst header = { op: 1, t: \"#commit\" };\n\t\treturn this.encodeFrame(header, event.event);\n\t}\n\n\t/**\n\t * Encode an error frame.\n\t */\n\tprivate encodeErrorFrame(error: string, message: string): Uint8Array {\n\t\tconst header = { op: -1 };\n\t\tconst body = { error, message };\n\t\treturn this.encodeFrame(header, body);\n\t}\n\n\t/**\n\t * Backfill firehose events from a cursor.\n\t */\n\tprivate async backfillFirehose(ws: WebSocket, cursor: number): Promise<void> {\n\t\tif (!this.sequencer) {\n\t\t\tthrow new Error(\"Sequencer not initialized\");\n\t\t}\n\n\t\tconst latestSeq = this.sequencer.getLatestSeq();\n\n\t\t// Check if cursor is in the future\n\t\tif (cursor > latestSeq) {\n\t\t\tconst frame = this.encodeErrorFrame(\n\t\t\t\t\"FutureCursor\",\n\t\t\t\t\"Cursor is in the future\",\n\t\t\t);\n\t\t\tws.send(frame);\n\t\t\tws.close(1008, \"FutureCursor\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Backfill from cursor\n\t\tconst events = await this.sequencer.getEventsSince(cursor, 1000);\n\n\t\tfor (const event of events) {\n\t\t\tconst frame = this.encodeCommitFrame(event);\n\t\t\tws.send(frame);\n\t\t}\n\n\t\t// Update cursor in attachment\n\t\tif (events.length > 0) {\n\t\t\tconst lastEvent = events[events.length - 1];\n\t\t\tif (lastEvent) {\n\t\t\t\tconst attachment = ws.deserializeAttachment() as { cursor: number };\n\t\t\t\tattachment.cursor = lastEvent.seq;\n\t\t\t\tws.serializeAttachment(attachment);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Broadcast a commit event to all connected firehose clients.\n\t */\n\tprivate async broadcastCommit(event: SeqEvent): Promise<void> {\n\t\tconst frame = this.encodeCommitFrame(event);\n\n\t\tfor (const ws of this.ctx.getWebSockets()) {\n\t\t\ttry {\n\t\t\t\tws.send(frame);\n\n\t\t\t\t// Update cursor\n\t\t\t\tconst attachment = ws.deserializeAttachment() as { cursor: number };\n\t\t\t\tattachment.cursor = event.seq;\n\t\t\t\tws.serializeAttachment(attachment);\n\t\t\t} catch (e) {\n\t\t\t\t// Client disconnected, will be cleaned up\n\t\t\t\tconsole.error(\"Error broadcasting to WebSocket:\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Handle WebSocket upgrade for firehose (subscribeRepos).\n\t */\n\tasync handleFirehoseUpgrade(request: Request): Promise<Response> {\n\t\tawait this.ensureStorageInitialized();\n\n\t\tconst url = new URL(request.url);\n\t\tconst cursorParam = url.searchParams.get(\"cursor\");\n\t\tconst cursor = cursorParam ? parseInt(cursorParam, 10) : null;\n\n\t\t// Create WebSocket pair\n\t\tconst pair = new WebSocketPair();\n\t\tconst client = pair[0];\n\t\tconst server = pair[1];\n\n\t\t// Accept with hibernation\n\t\tthis.ctx.acceptWebSocket(server);\n\n\t\t// Store cursor in attachment\n\t\tserver.serializeAttachment({\n\t\t\tcursor: cursor ?? 0,\n\t\t\tconnectedAt: Date.now(),\n\t\t});\n\n\t\t// Backfill if cursor provided\n\t\tif (cursor !== null) {\n\t\t\tawait this.backfillFirehose(server, cursor);\n\t\t}\n\n\t\treturn new Response(null, {\n\t\t\tstatus: 101,\n\t\t\twebSocket: client,\n\t\t});\n\t}\n\n\t/**\n\t * WebSocket message handler (hibernation API).\n\t */\n\toverride webSocketMessage(\n\t\t_ws: WebSocket,\n\t\t_message: string | ArrayBuffer,\n\t): void {\n\t\t// Firehose is server-push only, ignore client messages\n\t}\n\n\t/**\n\t * WebSocket close handler (hibernation API).\n\t */\n\toverride webSocketClose(\n\t\t_ws: WebSocket,\n\t\t_code: number,\n\t\t_reason: string,\n\t\t_wasClean: boolean,\n\t): void {\n\t\t// Cleanup handled automatically by hibernation API\n\t}\n\n\t/**\n\t * WebSocket error handler (hibernation API).\n\t */\n\toverride webSocketError(_ws: WebSocket, error: Error): void {\n\t\tconsole.error(\"WebSocket error:\", error);\n\t}\n\n\t/**\n\t * Emit an identity event to notify downstream services to refresh identity cache.\n\t */\n\tasync rpcEmitIdentityEvent(handle: string): Promise<{ seq: number }> {\n\t\tawait this.ensureStorageInitialized();\n\n\t\tconst time = new Date().toISOString();\n\n\t\t// Get next sequence number\n\t\tconst result = this.ctx.storage.sql\n\t\t\t.exec(\n\t\t\t\t`INSERT INTO firehose_events (event_type, payload)\n\t\t\t\t VALUES ('identity', ?)\n\t\t\t\t RETURNING seq`,\n\t\t\t\tnew Uint8Array(0), // Empty payload, we just need seq\n\t\t\t)\n\t\t\t.one();\n\t\tconst seq = result.seq as number;\n\n\t\t// Build identity event frame\n\t\tconst header = { op: 1, t: \"#identity\" };\n\t\tconst body = {\n\t\t\tseq,\n\t\t\tdid: this.env.DID,\n\t\t\ttime,\n\t\t\thandle,\n\t\t};\n\n\t\tconst headerBytes = cborEncode(\n\t\t\theader as unknown as import(\"@atproto/lex-cbor\").LexValue,\n\t\t);\n\t\tconst bodyBytes = cborEncode(\n\t\t\tbody as unknown as import(\"@atproto/lex-cbor\").LexValue,\n\t\t);\n\t\tconst frame = new Uint8Array(headerBytes.length + bodyBytes.length);\n\t\tframe.set(headerBytes, 0);\n\t\tframe.set(bodyBytes, headerBytes.length);\n\n\t\t// Broadcast to all connected clients\n\t\tfor (const ws of this.ctx.getWebSockets()) {\n\t\t\ttry {\n\t\t\t\tws.send(frame);\n\t\t\t} catch (e) {\n\t\t\t\tconsole.error(\"Error broadcasting identity event:\", e);\n\t\t\t}\n\t\t}\n\n\t\treturn { seq };\n\t}\n\n\t/**\n\t * HTTP fetch handler for WebSocket upgrades.\n\t * This is used instead of RPC to avoid WebSocket serialization errors.\n\t */\n\toverride async fetch(request: Request): Promise<Response> {\n\t\t// Only handle WebSocket upgrades via fetch\n\t\tconst url = new URL(request.url);\n\t\tif (url.pathname === \"/xrpc/com.atproto.sync.subscribeRepos\") {\n\t\t\treturn this.handleFirehoseUpgrade(request);\n\t\t}\n\n\t\t// All other requests should use RPC methods, not fetch\n\t\treturn new Response(\"Method not allowed\", { status: 405 });\n\t}\n}\n","import { SignJWT, jwtVerify, decodeJwt } from \"jose\";\nimport { compare } from \"bcryptjs\";\n\nconst ACCESS_TOKEN_LIFETIME = \"2h\";\nconst REFRESH_TOKEN_LIFETIME = \"90d\";\n\nexport interface JwtPayload {\n\tiss: string;\n\taud: string;\n\tsub: string;\n\tiat?: number;\n\texp?: number;\n\tscope: string;\n\tjti?: string;\n}\n\n/**\n * Create a secret key from string for HS256 signing\n */\nfunction createSecretKey(secret: string): Uint8Array {\n\treturn new TextEncoder().encode(secret);\n}\n\n/**\n * Create an access token (short-lived, 2 hours)\n */\nexport async function createAccessToken(\n\tjwtSecret: string,\n\tuserDid: string,\n\tserviceDid: string,\n): Promise<string> {\n\tconst secret = createSecretKey(jwtSecret);\n\n\treturn new SignJWT({ scope: \"atproto\" })\n\t\t.setProtectedHeader({ alg: \"HS256\", typ: \"at+jwt\" })\n\t\t.setIssuedAt()\n\t\t.setIssuer(serviceDid)\n\t\t.setAudience(serviceDid)\n\t\t.setSubject(userDid)\n\t\t.setExpirationTime(ACCESS_TOKEN_LIFETIME)\n\t\t.sign(secret);\n}\n\n/**\n * Create a refresh token (long-lived, 90 days)\n */\nexport async function createRefreshToken(\n\tjwtSecret: string,\n\tuserDid: string,\n\tserviceDid: string,\n): Promise<string> {\n\tconst secret = createSecretKey(jwtSecret);\n\tconst jti = crypto.randomUUID();\n\n\treturn new SignJWT({ scope: \"com.atproto.refresh\", jti })\n\t\t.setProtectedHeader({ alg: \"HS256\", typ: \"refresh+jwt\" })\n\t\t.setIssuedAt()\n\t\t.setIssuer(serviceDid)\n\t\t.setAudience(serviceDid)\n\t\t.setSubject(userDid)\n\t\t.setExpirationTime(REFRESH_TOKEN_LIFETIME)\n\t\t.sign(secret);\n}\n\n/**\n * Verify an access token and return the payload\n */\nexport async function verifyAccessToken(\n\ttoken: string,\n\tjwtSecret: string,\n\tserviceDid: string,\n): Promise<JwtPayload> {\n\tconst secret = createSecretKey(jwtSecret);\n\n\tconst { payload, protectedHeader } = await jwtVerify(token, secret, {\n\t\tissuer: serviceDid,\n\t\taudience: serviceDid,\n\t});\n\n\t// Check token type\n\tif (protectedHeader.typ !== \"at+jwt\") {\n\t\tthrow new Error(\"Invalid token type\");\n\t}\n\n\t// Check scope\n\tif (payload.scope !== \"atproto\") {\n\t\tthrow new Error(\"Invalid scope\");\n\t}\n\n\treturn payload as unknown as JwtPayload;\n}\n\n/**\n * Verify a refresh token and return the payload\n */\nexport async function verifyRefreshToken(\n\ttoken: string,\n\tjwtSecret: string,\n\tserviceDid: string,\n): Promise<JwtPayload> {\n\tconst secret = createSecretKey(jwtSecret);\n\n\tconst { payload, protectedHeader } = await jwtVerify(token, secret, {\n\t\tissuer: serviceDid,\n\t\taudience: serviceDid,\n\t});\n\n\t// Check token type\n\tif (protectedHeader.typ !== \"refresh+jwt\") {\n\t\tthrow new Error(\"Invalid token type\");\n\t}\n\n\t// Check scope\n\tif (payload.scope !== \"com.atproto.refresh\") {\n\t\tthrow new Error(\"Invalid scope\");\n\t}\n\n\t// Require token ID\n\tif (!payload.jti) {\n\t\tthrow new Error(\"Missing token ID\");\n\t}\n\n\treturn payload as unknown as JwtPayload;\n}\n\n/**\n * Decode a JWT without verification (for reading claims only)\n */\nexport function decodeToken(token: string): JwtPayload {\n\treturn decodeJwt(token) as unknown as JwtPayload;\n}\n\n/**\n * Verify a password against a bcrypt hash\n */\nexport async function verifyPassword(\n\tpassword: string,\n\thash: string,\n): Promise<boolean> {\n\treturn compare(password, hash);\n}\n","import type { Context, Next } from \"hono\";\nimport { verifyAccessToken } from \"../session\";\nimport type { PDSEnv } from \"../types\";\n\nexport interface AuthInfo {\n\tdid: string;\n\tscope: string;\n}\n\nexport type AuthVariables = {\n\tauth: AuthInfo;\n};\n\nexport async function requireAuth(\n\tc: Context<{ Bindings: PDSEnv; Variables: AuthVariables }>,\n\tnext: Next,\n): Promise<Response | void> {\n\tconst auth = c.req.header(\"Authorization\");\n\n\tif (!auth?.startsWith(\"Bearer \")) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"AuthMissing\",\n\t\t\t\tmessage: \"Authorization header required\",\n\t\t\t},\n\t\t\t401,\n\t\t);\n\t}\n\n\tconst token = auth.slice(7);\n\n\t// Try static token first (backwards compatibility)\n\tif (token === c.env.AUTH_TOKEN) {\n\t\treturn next();\n\t}\n\n\t// Try JWT verification\n\tconst serviceDid = `did:web:${c.env.PDS_HOSTNAME}`;\n\ttry {\n\t\tconst payload = await verifyAccessToken(\n\t\t\ttoken,\n\t\t\tc.env.JWT_SECRET,\n\t\t\tserviceDid,\n\t\t);\n\n\t\t// Verify subject matches our DID\n\t\tif (payload.sub !== c.env.DID) {\n\t\t\treturn c.json(\n\t\t\t\t{\n\t\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\t\tmessage: \"Invalid access token\",\n\t\t\t\t},\n\t\t\t\t401,\n\t\t\t);\n\t\t}\n\n\t\t// Store auth info in context for downstream use\n\t\tc.set(\"auth\", { did: payload.sub, scope: payload.scope });\n\t\treturn next();\n\t} catch {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\tmessage: \"Invalid authentication token\",\n\t\t\t},\n\t\t\t401,\n\t\t);\n\t}\n}\n","import { Secp256k1Keypair, randomStr } from \"@atproto/crypto\";\n\nconst MINUTE = 60 * 1000;\n\ntype ServiceJwtParams = {\n\tiss: string;\n\taud: string;\n\tlxm: string | null;\n\tkeypair: Secp256k1Keypair;\n};\n\nfunction jsonToB64Url(json: Record<string, unknown>): string {\n\treturn Buffer.from(JSON.stringify(json)).toString(\"base64url\");\n}\n\nfunction noUndefinedVals<T extends Record<string, unknown>>(\n\tobj: T,\n): Partial<T> {\n\tconst result: Partial<T> = {};\n\tfor (const [key, val] of Object.entries(obj)) {\n\t\tif (val !== undefined) {\n\t\t\tresult[key as keyof T] = val as T[keyof T];\n\t\t}\n\t}\n\treturn result;\n}\n\n/**\n * Create a service JWT for proxied requests to AppView.\n * The JWT asserts that the PDS vouches for the user identified by `iss`.\n */\nexport async function createServiceJwt(\n\tparams: ServiceJwtParams,\n): Promise<string> {\n\tconst { iss, aud, keypair } = params;\n\tconst iat = Math.floor(Date.now() / 1000);\n\tconst exp = iat + MINUTE / 1000;\n\tconst lxm = params.lxm ?? undefined;\n\tconst jti = randomStr(16, \"hex\");\n\n\tconst header = {\n\t\ttyp: \"JWT\",\n\t\talg: keypair.jwtAlg,\n\t};\n\n\tconst payload = noUndefinedVals({\n\t\tiat,\n\t\tiss,\n\t\taud,\n\t\texp,\n\t\tlxm,\n\t\tjti,\n\t});\n\n\tconst toSignStr = `${jsonToB64Url(header)}.${jsonToB64Url(payload as Record<string, unknown>)}`;\n\tconst toSign = Buffer.from(toSignStr, \"utf8\");\n\tconst sig = Buffer.from(await keypair.sign(toSign));\n\n\treturn `${toSignStr}.${sig.toString(\"base64url\")}`;\n}\n","import type { Context } from \"hono\";\nimport { ensureValidDid } from \"@atproto/syntax\";\nimport type { AccountDurableObject } from \"../account-do.js\";\nimport type { AppEnv } from \"../types\";\n\nexport async function getRepo(\n\tc: Context<AppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst did = c.req.query(\"did\");\n\n\tif (!did) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameter: did\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate DID format\n\ttry {\n\t\tensureValidDid(did);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (did !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoNotFound\",\n\t\t\t\tmessage: `Repository not found for DID: ${did}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\tconst carBytes = await accountDO.rpcGetRepoCar();\n\n\treturn new Response(carBytes, {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/vnd.ipld.car\",\n\t\t\t\"Content-Length\": carBytes.length.toString(),\n\t\t},\n\t});\n}\n\nexport async function getRepoStatus(\n\tc: Context<AppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst did = c.req.query(\"did\");\n\n\tif (!did) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameter: did\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate DID format\n\ttry {\n\t\tensureValidDid(did);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (did !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoNotFound\",\n\t\t\t\tmessage: `Repository not found for DID: ${did}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\tconst data = await accountDO.rpcGetRepoStatus();\n\n\treturn c.json({\n\t\tdid: data.did,\n\t\tactive: true,\n\t\tstatus: \"active\",\n\t\trev: data.rev,\n\t});\n}\n\nexport async function listRepos(\n\tc: Context<AppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\t// Single-user PDS - just return our one repo\n\tconst data = await accountDO.rpcGetRepoStatus();\n\n\treturn c.json({\n\t\trepos: [\n\t\t\t{\n\t\t\t\tdid: data.did,\n\t\t\t\thead: data.head,\n\t\t\t\trev: data.rev,\n\t\t\t\tactive: true,\n\t\t\t},\n\t\t],\n\t});\n}\n\nexport async function listBlobs(\n\tc: Context<AppEnv>,\n\t_accountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst did = c.req.query(\"did\");\n\n\tif (!did) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameter: did\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate DID format\n\ttry {\n\t\tensureValidDid(did);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (did !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoNotFound\",\n\t\t\t\tmessage: `Repository not found for DID: ${did}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\t// Check if blob storage is configured\n\tif (!c.env.BLOBS) {\n\t\t// No blobs configured, return empty list\n\t\treturn c.json({ cids: [] });\n\t}\n\n\t// List blobs from R2 with prefix\n\tconst prefix = `${did}/`;\n\tconst cursor = c.req.query(\"cursor\");\n\tconst limit = Math.min(Number(c.req.query(\"limit\")) || 500, 1000);\n\n\tconst listed = await c.env.BLOBS.list({\n\t\tprefix,\n\t\tlimit,\n\t\tcursor: cursor || undefined,\n\t});\n\n\t// Extract CIDs from keys (keys are \"${did}/${cid}\")\n\tconst cids = listed.objects.map((obj) => obj.key.slice(prefix.length));\n\n\tconst result: { cids: string[]; cursor?: string } = { cids };\n\tif (listed.truncated && listed.cursor) {\n\t\tresult.cursor = listed.cursor;\n\t}\n\n\treturn c.json(result);\n}\n\nexport async function getBlob(\n\tc: Context<AppEnv>,\n\t_accountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst did = c.req.query(\"did\");\n\tconst cid = c.req.query(\"cid\");\n\n\tif (!did || !cid) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameters: did, cid\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate DID format\n\ttry {\n\t\tensureValidDid(did);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (did !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoNotFound\",\n\t\t\t\tmessage: `Repository not found for DID: ${did}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\t// Check if blob storage is configured\n\tif (!c.env.BLOBS) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"ServiceUnavailable\",\n\t\t\t\tmessage: \"Blob storage is not configured\",\n\t\t\t},\n\t\t\t503,\n\t\t);\n\t}\n\n\t// Access R2 directly (R2ObjectBody can't be serialized across RPC)\n\tconst key = `${did}/${cid}`;\n\tconst blob = await c.env.BLOBS.get(key);\n\n\tif (!blob) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"BlobNotFound\",\n\t\t\t\tmessage: `Blob not found: ${cid}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\treturn new Response(blob.body, {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\":\n\t\t\t\tblob.httpMetadata?.contentType || \"application/octet-stream\",\n\t\t\t\"Content-Length\": blob.size.toString(),\n\t\t},\n\t});\n}\n","","","","","","","","","","","","","","","","","import { Lexicons, type LexiconDoc } from \"@atproto/lexicon\";\n\n/**\n * Record validator for AT Protocol records.\n *\n * Validates records against official Bluesky lexicon schemas.\n * Uses optimistic validation strategy:\n * - If a lexicon schema is loaded for the collection, validate the record\n * - If no schema is loaded, allow the record (fail-open)\n *\n * This allows the PDS to accept records for new or unknown collection types\n * while still validating known types when schemas are available.\n */\nexport class RecordValidator {\n\tprivate lex: Lexicons;\n\tprivate strictMode: boolean;\n\n\tconstructor(options: { strict?: boolean; lexicons?: Lexicons } = {}) {\n\t\tthis.lex = options.lexicons ?? new Lexicons();\n\t\tthis.strictMode = options.strict ?? false;\n\n\t\t// Load official Bluesky schemas\n\t\tthis.loadBlueskySchemas();\n\t}\n\n\t/**\n\t * Load official Bluesky lexicon schemas from vendored JSON files.\n\t * Uses Vite's glob import to automatically load all schema files.\n\t */\n\tprivate loadBlueskySchemas(): void {\n\t\t// Import all lexicon JSON files using Vite's glob import\n\t\tconst schemas = import.meta.glob<{ default: LexiconDoc }>(\n\t\t\t\"./lexicons/*.json\",\n\t\t\t{ eager: true },\n\t\t);\n\n\t\t// Load each schema into the Lexicons instance\n\t\tfor (const schema of Object.values(schemas)) {\n\t\t\tthis.lex.add(schema.default);\n\t\t}\n\t}\n\n\t/**\n\t * Validate a record against its lexicon schema.\n\t *\n\t * @param collection - The NSID of the record type (e.g., \"app.bsky.feed.post\")\n\t * @param record - The record object to validate\n\t * @throws {Error} If validation fails and schema is loaded\n\t */\n\tvalidateRecord(collection: string, record: unknown): void {\n\t\t// Check if we have a schema for this collection\n\t\tconst hasSchema = this.hasSchema(collection);\n\n\t\tif (!hasSchema) {\n\t\t\t// Optimistic validation: if we don't have the schema, allow it\n\t\t\tif (this.strictMode) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No lexicon schema loaded for collection: ${collection}. Enable optimistic validation or add the schema.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// In non-strict mode, we allow unknown schemas\n\t\t\treturn;\n\t\t}\n\n\t\t// We have a schema, so validate against it\n\t\ttry {\n\t\t\tthis.lex.assertValidRecord(collection, record);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new Error(\n\t\t\t\t`Lexicon validation failed for ${collection}: ${message}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Check if a schema is loaded for a collection.\n\t */\n\thasSchema(collection: string): boolean {\n\t\ttry {\n\t\t\t// Try to get the schema - if it exists, this won't throw\n\t\t\tthis.lex.getDefOrThrow(collection);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Add a lexicon schema to the validator.\n\t *\n\t * @param doc - The lexicon document to add\n\t *\n\t * @example\n\t * ```ts\n\t * validator.addSchema({\n\t * lexicon: 1,\n\t * id: \"com.example.post\",\n\t * defs: { ... }\n\t * })\n\t * ```\n\t */\n\taddSchema(doc: any): void {\n\t\tthis.lex.add(doc);\n\t}\n\n\t/**\n\t * Get list of all loaded schema NSIDs.\n\t */\n\tgetLoadedSchemas(): string[] {\n\t\t// Convert the Lexicons iterable to an array and extract IDs\n\t\treturn Array.from(this.lex).map((doc) => doc.id);\n\t}\n\n\t/**\n\t * Get the underlying Lexicons instance for advanced usage.\n\t */\n\tgetLexicons(): Lexicons {\n\t\treturn this.lex;\n\t}\n}\n\n/**\n * Shared validator instance (singleton pattern).\n * Uses optimistic validation by default (strict: false).\n *\n * Automatically loads all schemas from ./lexicons/*.json\n *\n * Additional schemas can be added:\n * ```ts\n * import { validator } from './validation'\n * validator.addSchema(myCustomSchema)\n * ```\n */\nexport const validator = new RecordValidator({ strict: false });\n","import type { Context } from \"hono\";\nimport { AtUri, ensureValidDid } from \"@atproto/syntax\";\nimport { AccountDurableObject } from \"../account-do\";\nimport type { AppEnv, AuthedAppEnv } from \"../types\";\nimport { validator } from \"../validation\";\n\nfunction invalidRecordError(\n\tc: Context<AuthedAppEnv>,\n\terr: unknown,\n\tprefix?: string,\n): Response {\n\tconst message = err instanceof Error ? err.message : String(err);\n\treturn c.json(\n\t\t{\n\t\t\terror: \"InvalidRecord\",\n\t\t\tmessage: prefix ? `${prefix}: ${message}` : message,\n\t\t},\n\t\t400,\n\t);\n}\n\nexport async function describeRepo(\n\tc: Context<AppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst repo = c.req.query(\"repo\");\n\n\tif (!repo) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameter: repo\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate DID format\n\ttry {\n\t\tensureValidDid(repo);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (repo !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoNotFound\",\n\t\t\t\tmessage: `Repository not found: ${repo}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\tconst data = await accountDO.rpcDescribeRepo();\n\n\treturn c.json({\n\t\tdid: c.env.DID,\n\t\thandle: c.env.HANDLE,\n\t\tdidDoc: {\n\t\t\t\"@context\": [\"https://www.w3.org/ns/did/v1\"],\n\t\t\tid: c.env.DID,\n\t\t\talsoKnownAs: [`at://${c.env.HANDLE}`],\n\t\t\tverificationMethod: [\n\t\t\t\t{\n\t\t\t\t\tid: `${c.env.DID}#atproto`,\n\t\t\t\t\ttype: \"Multikey\",\n\t\t\t\t\tcontroller: c.env.DID,\n\t\t\t\t\tpublicKeyMultibase: c.env.SIGNING_KEY_PUBLIC,\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tcollections: data.collections,\n\t\thandleIsCorrect: true,\n\t});\n}\n\nexport async function getRecord(\n\tc: Context<AppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst repo = c.req.query(\"repo\");\n\tconst collection = c.req.query(\"collection\");\n\tconst rkey = c.req.query(\"rkey\");\n\n\tif (!repo || !collection || !rkey) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameters: repo, collection, rkey\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate DID format\n\ttry {\n\t\tensureValidDid(repo);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (repo !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoNotFound\",\n\t\t\t\tmessage: `Repository not found: ${repo}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\tconst result = await accountDO.rpcGetRecord(collection, rkey);\n\n\tif (!result) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RecordNotFound\",\n\t\t\t\tmessage: `Record not found: ${collection}/${rkey}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\treturn c.json({\n\t\turi: AtUri.make(repo, collection, rkey).toString(),\n\t\tcid: result.cid,\n\t\tvalue: result.record,\n\t});\n}\n\nexport async function listRecords(\n\tc: Context<AppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst repo = c.req.query(\"repo\");\n\tconst collection = c.req.query(\"collection\");\n\tconst limitStr = c.req.query(\"limit\");\n\tconst cursor = c.req.query(\"cursor\");\n\tconst reverseStr = c.req.query(\"reverse\");\n\n\tif (!repo || !collection) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameters: repo, collection\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate DID format\n\ttry {\n\t\tensureValidDid(repo);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (repo !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoNotFound\",\n\t\t\t\tmessage: `Repository not found: ${repo}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\tconst limit = Math.min(limitStr ? Number.parseInt(limitStr, 10) : 50, 100);\n\tconst reverse = reverseStr === \"true\";\n\n\tconst result = await accountDO.rpcListRecords(collection, {\n\t\tlimit,\n\t\tcursor,\n\t\treverse,\n\t});\n\n\treturn c.json(result);\n}\n\nexport async function createRecord(\n\tc: Context<AuthedAppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst body = await c.req.json();\n\tconst { repo, collection, rkey, record } = body;\n\n\tif (!repo || !collection || !record) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameters: repo, collection, record\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (repo !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRepo\",\n\t\t\t\tmessage: `Invalid repository: ${repo}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate record against lexicon schema\n\ttry {\n\t\tvalidator.validateRecord(collection, record);\n\t} catch (err) {\n\t\treturn invalidRecordError(c, err);\n\t}\n\n\tconst result = await accountDO.rpcCreateRecord(collection, rkey, record);\n\n\treturn c.json(result);\n}\n\nexport async function deleteRecord(\n\tc: Context<AuthedAppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst body = await c.req.json();\n\tconst { repo, collection, rkey } = body;\n\n\tif (!repo || !collection || !rkey) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameters: repo, collection, rkey\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (repo !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRepo\",\n\t\t\t\tmessage: `Invalid repository: ${repo}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tconst result = await accountDO.rpcDeleteRecord(collection, rkey);\n\n\tif (!result) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RecordNotFound\",\n\t\t\t\tmessage: `Record not found: ${collection}/${rkey}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\treturn c.json(result);\n}\n\nexport async function putRecord(\n\tc: Context<AuthedAppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst body = await c.req.json();\n\tconst { repo, collection, rkey, record } = body;\n\n\tif (!repo || !collection || !rkey || !record) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameters: repo, collection, rkey, record\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (repo !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRepo\",\n\t\t\t\tmessage: `Invalid repository: ${repo}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate record against lexicon schema\n\ttry {\n\t\tvalidator.validateRecord(collection, record);\n\t} catch (err) {\n\t\treturn invalidRecordError(c, err);\n\t}\n\n\ttry {\n\t\tconst result = await accountDO.rpcPutRecord(collection, rkey, record);\n\t\treturn c.json(result);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n}\n\nexport async function applyWrites(\n\tc: Context<AuthedAppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst body = await c.req.json();\n\tconst { repo, writes } = body;\n\n\tif (!repo || !writes || !Array.isArray(writes)) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameters: repo, writes\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (repo !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRepo\",\n\t\t\t\tmessage: `Invalid repository: ${repo}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (writes.length > 200) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Too many writes. Max: 200\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate all records in create and update operations\n\tfor (let i = 0; i < writes.length; i++) {\n\t\tconst write = writes[i];\n\t\tif (\n\t\t\twrite.$type === \"com.atproto.repo.applyWrites#create\" ||\n\t\t\twrite.$type === \"com.atproto.repo.applyWrites#update\"\n\t\t) {\n\t\t\ttry {\n\t\t\t\tvalidator.validateRecord(write.collection, write.value);\n\t\t\t} catch (err) {\n\t\t\t\treturn invalidRecordError(c, err, `Write ${i}`);\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\tconst result = await accountDO.rpcApplyWrites(writes);\n\t\treturn c.json(result);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n}\n\nexport async function uploadBlob(\n\tc: Context<AuthedAppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst contentType =\n\t\tc.req.header(\"Content-Type\") || \"application/octet-stream\";\n\tconst bytes = new Uint8Array(await c.req.arrayBuffer());\n\n\t// Size limit check (5MB)\n\tconst MAX_BLOB_SIZE = 5 * 1024 * 1024;\n\tif (bytes.length > MAX_BLOB_SIZE) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"BlobTooLarge\",\n\t\t\t\tmessage: `Blob size ${bytes.length} exceeds maximum of ${MAX_BLOB_SIZE} bytes`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\ttry {\n\t\tconst blobRef = await accountDO.rpcUploadBlob(bytes, contentType);\n\t\treturn c.json({ blob: blobRef });\n\t} catch (err) {\n\t\tif (\n\t\t\terr instanceof Error &&\n\t\t\terr.message.includes(\"Blob storage not configured\")\n\t\t) {\n\t\t\treturn c.json(\n\t\t\t\t{\n\t\t\t\t\terror: \"ServiceUnavailable\",\n\t\t\t\t\tmessage: \"Blob storage is not configured\",\n\t\t\t\t},\n\t\t\t\t503,\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nexport async function importRepo(\n\tc: Context<AuthedAppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\tconst contentType = c.req.header(\"Content-Type\");\n\n\t// Verify content type\n\tif (contentType !== \"application/vnd.ipld.car\") {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"Content-Type must be application/vnd.ipld.car for repository import\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Get CAR file bytes\n\tconst carBytes = new Uint8Array(await c.req.arrayBuffer());\n\n\tif (carBytes.length === 0) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Empty CAR file\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Size limit check (100MB for repo imports)\n\tconst MAX_CAR_SIZE = 100 * 1024 * 1024;\n\tif (carBytes.length > MAX_CAR_SIZE) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"RepoTooLarge\",\n\t\t\t\tmessage: `Repository size ${carBytes.length} exceeds maximum of ${MAX_CAR_SIZE} bytes`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\ttry {\n\t\tconst result = await accountDO.rpcImportRepo(carBytes);\n\t\treturn c.json(result);\n\t} catch (err) {\n\t\tif (err instanceof Error) {\n\t\t\tif (err.message.includes(\"already exists\")) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror: \"RepoAlreadyExists\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"Repository already exists. Cannot import over existing data.\",\n\t\t\t\t\t},\n\t\t\t\t\t409,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (err.message.includes(\"DID mismatch\")) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror: \"InvalidRepo\",\n\t\t\t\t\t\tmessage: err.message,\n\t\t\t\t\t},\n\t\t\t\t\t400,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\terr.message.includes(\"no roots\") ||\n\t\t\t\terr.message.includes(\"no blocks\") ||\n\t\t\t\terr.message.includes(\"Invalid root\")\n\t\t\t) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror: \"InvalidRepo\",\n\t\t\t\t\t\tmessage: `Invalid CAR file: ${err.message}`,\n\t\t\t\t\t},\n\t\t\t\t\t400,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthrow err;\n\t}\n}\n","import type { Context } from \"hono\";\nimport { ensureValidHandle } from \"@atproto/syntax\";\nimport type { AccountDurableObject } from \"../account-do\";\nimport {\n\tcreateAccessToken,\n\tcreateRefreshToken,\n\tverifyPassword,\n\tverifyAccessToken,\n\tverifyRefreshToken,\n} from \"../session\";\nimport type { AppEnv, AuthedAppEnv } from \"../types\";\n\nexport async function describeServer(c: Context<AppEnv>): Promise<Response> {\n\treturn c.json({\n\t\tdid: c.env.DID,\n\t\tavailableUserDomains: [],\n\t\tinviteCodeRequired: false,\n\t});\n}\n\nexport async function resolveHandle(c: Context<AppEnv>): Promise<Response> {\n\tconst handle = c.req.query(\"handle\");\n\n\tif (!handle) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing required parameter: handle\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Validate handle format\n\ttry {\n\t\tensureValidHandle(handle);\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: `Invalid handle format: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\tif (handle !== c.env.HANDLE) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"HandleNotFound\",\n\t\t\t\tmessage: `Handle not found: ${handle}`,\n\t\t\t},\n\t\t\t404,\n\t\t);\n\t}\n\n\treturn c.json({\n\t\tdid: c.env.DID,\n\t});\n}\n\n/**\n * Create a new session (login)\n */\nexport async function createSession(c: Context<AppEnv>): Promise<Response> {\n\tconst body = await c.req.json<{\n\t\tidentifier: string;\n\t\tpassword: string;\n\t}>();\n\n\tconst { identifier, password } = body;\n\n\tif (!identifier || !password) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidRequest\",\n\t\t\t\tmessage: \"Missing identifier or password\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Check identifier matches handle or DID\n\tif (identifier !== c.env.HANDLE && identifier !== c.env.DID) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\tmessage: \"Invalid identifier or password\",\n\t\t\t},\n\t\t\t401,\n\t\t);\n\t}\n\n\t// Verify password\n\tconst passwordValid = await verifyPassword(password, c.env.PASSWORD_HASH);\n\tif (!passwordValid) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\tmessage: \"Invalid identifier or password\",\n\t\t\t},\n\t\t\t401,\n\t\t);\n\t}\n\n\t// Create tokens\n\tconst serviceDid = `did:web:${c.env.PDS_HOSTNAME}`;\n\tconst accessJwt = await createAccessToken(\n\t\tc.env.JWT_SECRET,\n\t\tc.env.DID,\n\t\tserviceDid,\n\t);\n\tconst refreshJwt = await createRefreshToken(\n\t\tc.env.JWT_SECRET,\n\t\tc.env.DID,\n\t\tserviceDid,\n\t);\n\n\treturn c.json({\n\t\taccessJwt,\n\t\trefreshJwt,\n\t\thandle: c.env.HANDLE,\n\t\tdid: c.env.DID,\n\t\tactive: true,\n\t});\n}\n\n/**\n * Refresh a session\n */\nexport async function refreshSession(c: Context<AppEnv>): Promise<Response> {\n\tconst authHeader = c.req.header(\"Authorization\");\n\n\tif (!authHeader?.startsWith(\"Bearer \")) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\tmessage: \"Refresh token required\",\n\t\t\t},\n\t\t\t401,\n\t\t);\n\t}\n\n\tconst token = authHeader.slice(7);\n\tconst serviceDid = `did:web:${c.env.PDS_HOSTNAME}`;\n\n\ttry {\n\t\tconst payload = await verifyRefreshToken(\n\t\t\ttoken,\n\t\t\tc.env.JWT_SECRET,\n\t\t\tserviceDid,\n\t\t);\n\n\t\t// Verify the subject matches our DID\n\t\tif (payload.sub !== c.env.DID) {\n\t\t\treturn c.json(\n\t\t\t\t{\n\t\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\t\tmessage: \"Invalid refresh token\",\n\t\t\t\t},\n\t\t\t\t401,\n\t\t\t);\n\t\t}\n\n\t\t// Create new tokens\n\t\tconst accessJwt = await createAccessToken(\n\t\t\tc.env.JWT_SECRET,\n\t\t\tc.env.DID,\n\t\t\tserviceDid,\n\t\t);\n\t\tconst refreshJwt = await createRefreshToken(\n\t\t\tc.env.JWT_SECRET,\n\t\t\tc.env.DID,\n\t\t\tserviceDid,\n\t\t);\n\n\t\treturn c.json({\n\t\t\taccessJwt,\n\t\t\trefreshJwt,\n\t\t\thandle: c.env.HANDLE,\n\t\t\tdid: c.env.DID,\n\t\t\tactive: true,\n\t\t});\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"ExpiredToken\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Invalid refresh token\",\n\t\t\t},\n\t\t\t400,\n\t\t);\n\t}\n}\n\n/**\n * Get current session info\n */\nexport async function getSession(c: Context<AppEnv>): Promise<Response> {\n\tconst authHeader = c.req.header(\"Authorization\");\n\n\tif (!authHeader?.startsWith(\"Bearer \")) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\tmessage: \"Access token required\",\n\t\t\t},\n\t\t\t401,\n\t\t);\n\t}\n\n\tconst token = authHeader.slice(7);\n\tconst serviceDid = `did:web:${c.env.PDS_HOSTNAME}`;\n\n\t// First try static token\n\tif (token === c.env.AUTH_TOKEN) {\n\t\treturn c.json({\n\t\t\thandle: c.env.HANDLE,\n\t\t\tdid: c.env.DID,\n\t\t\tactive: true,\n\t\t});\n\t}\n\n\t// Try JWT\n\ttry {\n\t\tconst payload = await verifyAccessToken(\n\t\t\ttoken,\n\t\t\tc.env.JWT_SECRET,\n\t\t\tserviceDid,\n\t\t);\n\n\t\tif (payload.sub !== c.env.DID) {\n\t\t\treturn c.json(\n\t\t\t\t{\n\t\t\t\t\terror: \"AuthenticationRequired\",\n\t\t\t\t\tmessage: \"Invalid access token\",\n\t\t\t\t},\n\t\t\t\t401,\n\t\t\t);\n\t\t}\n\n\t\treturn c.json({\n\t\t\thandle: c.env.HANDLE,\n\t\t\tdid: c.env.DID,\n\t\t\tactive: true,\n\t\t});\n\t} catch (err) {\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\terror: \"InvalidToken\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Invalid access token\",\n\t\t\t},\n\t\t\t401,\n\t\t);\n\t}\n}\n\n/**\n * Delete current session (logout)\n */\nexport async function deleteSession(c: Context<AppEnv>): Promise<Response> {\n\t// For a single-user PDS with stateless JWTs, we don't need to do anything\n\t// The client just needs to delete its stored tokens\n\t// In a full implementation, we'd revoke the refresh token\n\treturn c.json({});\n}\n\n/**\n * Get account status - used for migration checks\n */\nexport async function getAccountStatus(\n\tc: Context<AuthedAppEnv>,\n\taccountDO: DurableObjectStub<AccountDurableObject>,\n): Promise<Response> {\n\ttry {\n\t\t// Check if repo exists\n\t\tconst status = await accountDO.rpcGetRepoStatus();\n\n\t\treturn c.json({\n\t\t\tactivated: true,\n\t\t\tvalidDid: true,\n\t\t\trepoRev: status.rev,\n\t\t\trepoBlocks: null, // Could implement block counting if needed\n\t\t\tindexedRecords: null, // Could implement record counting if needed\n\t\t\tprivateStateValues: null,\n\t\t\texpectedBlobs: null,\n\t\t\timportedBlobs: null,\n\t\t});\n\t} catch (err) {\n\t\t// If repo doesn't exist yet, return empty status\n\t\treturn c.json({\n\t\t\tactivated: false,\n\t\t\tvalidDid: true,\n\t\t\trepoRev: null,\n\t\t\trepoBlocks: null,\n\t\t\tindexedRecords: null,\n\t\t\tprivateStateValues: null,\n\t\t\texpectedBlobs: null,\n\t\t\timportedBlobs: null,\n\t\t});\n\t}\n}\n","","// Public API\nexport { AccountDurableObject } from \"./account-do\";\nexport type { PDSEnv } from \"./types\";\n\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { env as _env } from \"cloudflare:workers\";\nimport { Secp256k1Keypair } from \"@atproto/crypto\";\nimport { ensureValidDid, ensureValidHandle } from \"@atproto/syntax\";\nimport { requireAuth } from \"./middleware/auth\";\nimport { createServiceJwt } from \"./service-auth\";\nimport { verifyAccessToken } from \"./session\";\nimport * as sync from \"./xrpc/sync\";\nimport * as repo from \"./xrpc/repo\";\nimport * as server from \"./xrpc/server\";\nimport type { PDSEnv } from \"./types\";\n\nimport { version } from \"../package.json\" with { type: \"json\" };\n\n// Cast env to PDSEnv for type safety\nconst env = _env as PDSEnv;\n\n// Validate required environment variables at module load\nconst required = [\n\t\"DID\",\n\t\"HANDLE\",\n\t\"PDS_HOSTNAME\",\n\t\"AUTH_TOKEN\",\n\t\"SIGNING_KEY\",\n\t\"SIGNING_KEY_PUBLIC\",\n\t\"JWT_SECRET\",\n\t\"PASSWORD_HASH\",\n] as const;\n\nfor (const key of required) {\n\tif (!env[key]) {\n\t\tthrow new Error(`Missing required environment variable: ${key}`);\n\t}\n}\n\n// Validate DID and handle formats\ntry {\n\tensureValidDid(env.DID);\n\tensureValidHandle(env.HANDLE);\n} catch (err) {\n\tthrow new Error(\n\t\t`Invalid DID or handle: ${err instanceof Error ? err.message : String(err)}`,\n\t);\n}\n\n// Bluesky service DIDs for service auth\nconst APPVIEW_DID = \"did:web:api.bsky.app\";\nconst CHAT_DID = \"did:web:api.bsky.chat\";\n\n// Lazy-loaded keypair for service auth\nlet keypairPromise: Promise<Secp256k1Keypair> | null = null;\nfunction getKeypair(): Promise<Secp256k1Keypair> {\n\tif (!keypairPromise) {\n\t\tkeypairPromise = Secp256k1Keypair.import(env.SIGNING_KEY);\n\t}\n\treturn keypairPromise;\n}\n\nconst app = new Hono<{ Bindings: PDSEnv }>();\n\n// CORS middleware for all routes\napp.use(\n\t\"*\",\n\tcors({\n\t\torigin: \"*\",\n\t\tallowMethods: [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"],\n\t\tallowHeaders: [\"*\"],\n\t\texposeHeaders: [\"Content-Type\"],\n\t\tmaxAge: 86400,\n\t}),\n);\n\n// Helper to get Account DO stub\nfunction getAccountDO(env: PDSEnv) {\n\tconst id = env.ACCOUNT.idFromName(\"account\");\n\treturn env.ACCOUNT.get(id);\n}\n\n// DID document for did:web resolution\napp.get(\"/.well-known/did.json\", (c) => {\n\tconst didDocument = {\n\t\t\"@context\": [\n\t\t\t\"https://www.w3.org/ns/did/v1\",\n\t\t\t\"https://w3id.org/security/multikey/v1\",\n\t\t\t\"https://w3id.org/security/suites/secp256k1-2019/v1\",\n\t\t],\n\t\tid: c.env.DID,\n\t\talsoKnownAs: [`at://${c.env.HANDLE}`],\n\t\tverificationMethod: [\n\t\t\t{\n\t\t\t\tid: `${c.env.DID}#atproto`,\n\t\t\t\ttype: \"Multikey\",\n\t\t\t\tcontroller: c.env.DID,\n\t\t\t\tpublicKeyMultibase: c.env.SIGNING_KEY_PUBLIC,\n\t\t\t},\n\t\t],\n\t\tservice: [\n\t\t\t{\n\t\t\t\tid: \"#atproto_pds\",\n\t\t\t\ttype: \"AtprotoPersonalDataServer\",\n\t\t\t\tserviceEndpoint: `https://${c.env.PDS_HOSTNAME}`,\n\t\t\t},\n\t\t],\n\t};\n\treturn c.json(didDocument);\n});\n\n// Handle verification for AT Protocol\n// Only served if handle matches PDS hostname\napp.get(\"/.well-known/atproto-did\", (c) => {\n\tif (c.env.HANDLE !== c.env.PDS_HOSTNAME) {\n\t\treturn c.notFound();\n\t}\n\treturn new Response(c.env.DID, {\n\t\theaders: { \"Content-Type\": \"text/plain\" },\n\t});\n});\n\n// Health check\napp.get(\"/health\", (c) =>\n\tc.json({\n\t\tstatus: \"ok\",\n\t\tversion,\n\t}),\n);\n\n// Sync endpoints (federation)\napp.get(\"/xrpc/com.atproto.sync.getRepo\", (c) =>\n\tsync.getRepo(c, getAccountDO(c.env)),\n);\napp.get(\"/xrpc/com.atproto.sync.getRepoStatus\", (c) =>\n\tsync.getRepoStatus(c, getAccountDO(c.env)),\n);\napp.get(\"/xrpc/com.atproto.sync.getBlob\", (c) =>\n\tsync.getBlob(c, getAccountDO(c.env)),\n);\napp.get(\"/xrpc/com.atproto.sync.listRepos\", (c) =>\n\tsync.listRepos(c, getAccountDO(c.env)),\n);\napp.get(\"/xrpc/com.atproto.sync.listBlobs\", (c) =>\n\tsync.listBlobs(c, getAccountDO(c.env)),\n);\n\n// WebSocket firehose\napp.get(\"/xrpc/com.atproto.sync.subscribeRepos\", async (c) => {\n\tconst upgradeHeader = c.req.header(\"Upgrade\");\n\tif (upgradeHeader !== \"websocket\") {\n\t\treturn c.json(\n\t\t\t{ error: \"InvalidRequest\", message: \"Expected WebSocket upgrade\" },\n\t\t\t400,\n\t\t);\n\t}\n\n\t// Use fetch() instead of RPC to avoid WebSocket serialization error\n\tconst accountDO = getAccountDO(c.env);\n\treturn accountDO.fetch(c.req.raw);\n});\n\n// Repository operations\napp.get(\"/xrpc/com.atproto.repo.describeRepo\", (c) =>\n\trepo.describeRepo(c, getAccountDO(c.env)),\n);\napp.get(\"/xrpc/com.atproto.repo.getRecord\", (c) =>\n\trepo.getRecord(c, getAccountDO(c.env)),\n);\napp.get(\"/xrpc/com.atproto.repo.listRecords\", (c) =>\n\trepo.listRecords(c, getAccountDO(c.env)),\n);\n\n// Write operations require authentication\napp.post(\"/xrpc/com.atproto.repo.createRecord\", requireAuth, (c) =>\n\trepo.createRecord(c, getAccountDO(c.env)),\n);\napp.post(\"/xrpc/com.atproto.repo.deleteRecord\", requireAuth, (c) =>\n\trepo.deleteRecord(c, getAccountDO(c.env)),\n);\napp.post(\"/xrpc/com.atproto.repo.uploadBlob\", requireAuth, (c) =>\n\trepo.uploadBlob(c, getAccountDO(c.env)),\n);\napp.post(\"/xrpc/com.atproto.repo.applyWrites\", requireAuth, (c) =>\n\trepo.applyWrites(c, getAccountDO(c.env)),\n);\napp.post(\"/xrpc/com.atproto.repo.putRecord\", requireAuth, (c) =>\n\trepo.putRecord(c, getAccountDO(c.env)),\n);\napp.post(\"/xrpc/com.atproto.repo.importRepo\", requireAuth, (c) =>\n\trepo.importRepo(c, getAccountDO(c.env)),\n);\n\n// Server identity\napp.get(\"/xrpc/com.atproto.server.describeServer\", server.describeServer);\n\n// Handle resolution - return our DID for our handle, let others fall through to proxy\napp.use(\"/xrpc/com.atproto.identity.resolveHandle\", async (c, next) => {\n\tconst handle = c.req.query(\"handle\");\n\tif (handle === c.env.HANDLE) {\n\t\treturn c.json({ did: c.env.DID });\n\t}\n\tawait next();\n});\n\n// Session management\napp.post(\"/xrpc/com.atproto.server.createSession\", server.createSession);\napp.post(\"/xrpc/com.atproto.server.refreshSession\", server.refreshSession);\napp.get(\"/xrpc/com.atproto.server.getSession\", server.getSession);\napp.post(\"/xrpc/com.atproto.server.deleteSession\", server.deleteSession);\n\n// Account migration\napp.get(\"/xrpc/com.atproto.server.getAccountStatus\", requireAuth, (c) =>\n\tserver.getAccountStatus(c, getAccountDO(c.env)),\n);\n\n// Actor preferences (stub - returns empty preferences)\napp.get(\"/xrpc/app.bsky.actor.getPreferences\", requireAuth, (c) => {\n\treturn c.json({ preferences: [] });\n});\napp.post(\"/xrpc/app.bsky.actor.putPreferences\", requireAuth, async (c) => {\n\t// TODO: persist preferences in DO\n\treturn c.json({});\n});\n\n// Age assurance (stub - self-hosted users are pre-verified)\napp.get(\"/xrpc/app.bsky.ageassurance.getState\", requireAuth, (c) => {\n\treturn c.json({\n\t\tstate: {\n\t\t\tstatus: \"assured\",\n\t\t\taccess: \"full\",\n\t\t\tlastInitiatedAt: new Date().toISOString(),\n\t\t},\n\t\tmetadata: {\n\t\t\taccountCreatedAt: new Date().toISOString(),\n\t\t},\n\t});\n});\n\n// Admin: Emit identity event to refresh handle verification\napp.post(\"/admin/emit-identity\", requireAuth, async (c) => {\n\tconst accountDO = getAccountDO(c.env);\n\tconst result = await accountDO.rpcEmitIdentityEvent(c.env.HANDLE);\n\treturn c.json(result);\n});\n\n// Proxy unhandled XRPC requests to Bluesky services\napp.all(\"/xrpc/*\", async (c) => {\n\tconst url = new URL(c.req.url);\n\turl.protocol = \"https:\";\n\n\t// Extract XRPC method name from path (e.g., \"app.bsky.feed.getTimeline\")\n\tconst lxm = url.pathname.replace(\"/xrpc/\", \"\");\n\n\t// Route to appropriate service based on lexicon namespace\n\tconst isChat = lxm.startsWith(\"chat.bsky.\");\n\turl.host = isChat ? \"api.bsky.chat\" : \"api.bsky.app\";\n\tconst audienceDid = isChat ? CHAT_DID : APPVIEW_DID;\n\n\t// Check for authorization header\n\tconst auth = c.req.header(\"Authorization\");\n\tlet headers: Record<string, string> = {};\n\n\tif (auth?.startsWith(\"Bearer \")) {\n\t\tconst token = auth.slice(7);\n\t\tconst serviceDid = `did:web:${c.env.PDS_HOSTNAME}`;\n\n\t\t// Try to verify the token - if valid, create a service JWT\n\t\ttry {\n\t\t\t// Check static token first\n\t\t\tlet userDid: string;\n\t\t\tif (token === c.env.AUTH_TOKEN) {\n\t\t\t\tuserDid = c.env.DID;\n\t\t\t} else {\n\t\t\t\t// Verify JWT\n\t\t\t\tconst payload = await verifyAccessToken(\n\t\t\t\t\ttoken,\n\t\t\t\t\tc.env.JWT_SECRET,\n\t\t\t\t\tserviceDid,\n\t\t\t\t);\n\t\t\t\tuserDid = payload.sub;\n\t\t\t}\n\n\t\t\t// Create service JWT for target service\n\t\t\tconst keypair = await getKeypair();\n\t\t\tconst serviceJwt = await createServiceJwt({\n\t\t\t\tiss: userDid,\n\t\t\t\taud: audienceDid,\n\t\t\t\tlxm,\n\t\t\t\tkeypair,\n\t\t\t});\n\t\t\theaders[\"Authorization\"] = `Bearer ${serviceJwt}`;\n\t\t} catch {\n\t\t\t// Token verification failed - forward without auth\n\t\t\t// Target service will return appropriate error\n\t\t}\n\t}\n\n\t// Forward request with potentially replaced auth header\n\t// Remove original authorization header to prevent conflicts\n\tconst originalHeaders = Object.fromEntries(c.req.raw.headers);\n\tdelete originalHeaders[\"authorization\"];\n\n\tconst reqInit: RequestInit = {\n\t\tmethod: c.req.method,\n\t\theaders: {\n\t\t\t...originalHeaders,\n\t\t\t...headers,\n\t\t},\n\t};\n\n\t// Include body for non-GET requests\n\tif (c.req.method !== \"GET\" && c.req.method !== \"HEAD\") {\n\t\treqInit.body = c.req.raw.body;\n\t}\n\n\treturn fetch(url.toString(), reqInit);\n});\n\nexport default app;\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ascorbic/pds",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "AT Protocol PDS on Cloudflare Workers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"pds": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@atproto/common-web": "^0.4.7",
|
|
18
|
+
"@atproto/crypto": "^0.4.5",
|
|
19
|
+
"@atproto/lex-cbor": "^0.0.3",
|
|
20
|
+
"@atproto/lex-data": "^0.0.3",
|
|
21
|
+
"@atproto/lexicon": "^0.6.0",
|
|
22
|
+
"@atproto/repo": "^0.8.12",
|
|
23
|
+
"@atproto/syntax": "^0.4.2",
|
|
24
|
+
"@clack/prompts": "^0.11.0",
|
|
25
|
+
"bcryptjs": "^3.0.3",
|
|
26
|
+
"citty": "^0.1.6",
|
|
27
|
+
"hono": "^4.11.3",
|
|
28
|
+
"jose": "^6.1.3"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
32
|
+
"@cloudflare/vitest-pool-workers": "https://pkg.pr.new/@cloudflare/vitest-pool-workers@11632",
|
|
33
|
+
"@cloudflare/workers-types": "^4.20251225.0",
|
|
34
|
+
"@ipld/car": "^5.4.2",
|
|
35
|
+
"@types/ws": "^8.18.1",
|
|
36
|
+
"publint": "^0.3.16",
|
|
37
|
+
"tsdown": "^0.18.3",
|
|
38
|
+
"tsx": "^4.21.0",
|
|
39
|
+
"typescript": "^5.9.3",
|
|
40
|
+
"vitest": "^4.0.0",
|
|
41
|
+
"wrangler": "^4.54.0",
|
|
42
|
+
"ws": "^8.18.3"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/ascorbic/atproto-worker.git",
|
|
47
|
+
"directory": "packages/pds"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"wrangler": "^4.54.0"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/ascorbic/atproto-worker",
|
|
53
|
+
"keywords": [
|
|
54
|
+
"atproto",
|
|
55
|
+
"bluesky",
|
|
56
|
+
"pds",
|
|
57
|
+
"cloudflare-workers"
|
|
58
|
+
],
|
|
59
|
+
"author": "Matt Kane",
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsdown",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"test:cli": "vitest run --config vitest.config.cli.ts",
|
|
65
|
+
"check": "publint && attw --pack --ignore-rules=cjs-resolves-to-esm"
|
|
66
|
+
}
|
|
67
|
+
}
|