chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// @pdsjs/spaces/routes - the space route table.
//
// @pdsjs/core never imports this package. Instead a platform package builds the
// table and passes it to the PDS as `spaceRoutes`, which merges it into its own
// route map. With the feature off, the routes are simply absent and requests
// fall through to the normal 404 — see spec section 3.
import { createAuthRoutes } from './handlers/auth.js';
import { createManageRoutes } from './handlers/manage.js';
import { createReadRoutes } from './handlers/read.js';
import { createWriteRoutes } from './handlers/write.js';
import { createNotifyOutbox } from './outbox.js';
/**
* @param {Object} ctx
* @param {import('@pdsjs/core/ports').SpaceStoragePort} ctx.spaceStorage
* @param {import('@pdsjs/core/ports').BlobPort} ctx.blobs - blobs referenced
* from space records live in the account's ordinary blob store
* @param {(blobCid: string, recordUri: string, recordTime: number|null) => Promise<void>} ctx.linkBlob
* @param {(recordUri: string) => Promise<void>} ctx.unlinkBlobs
* @param {() => Promise<string|null>} ctx.getDid - the hosted account's DID
* @param {() => Promise<import('./token.js').SpaceSigner>} ctx.getSigner
* - the account's atproto signing key, which signs space tokens and
* service auth
* @param {() => Promise<{privateKey: Uint8Array, didKey: string}|null>} ctx.getKemKey
* - the account's `#atproto_kem` key, which MACs its repos' responses
* @param {import('@pdsjs/core/ports').DidResolverPort} ctx.resolveDid - DID document lookup,
* injected so this package stays dependency-free
* @param {import('@pdsjs/core/ports').SignatureVerifierPort} ctx.verifier
* @param {import('@pdsjs/core/ports').RecordWriteGuardPort} [ctx.recordGuard]
* - a veto over writes in its named collections, e.g. git branch protection
* @param {typeof fetch} [ctx.fetch] - injected for the outbound notification
* calls, so tests can observe them without a network
* @param {(work: Promise<unknown>) => void} [ctx.background] - runs write
* notices after the response; absent, they run before it
* @param {ReturnType<typeof import('./outbox.js').createNotifyOutbox>} [ctx.outbox]
* - notices owed to other services; built from ctx when absent
* @returns {import('@pdsjs/core/pds').Routes}
*/
export function createSpaceRoutes(ctx) {
if (!ctx?.spaceStorage) {
throw new Error('createSpaceRoutes requires a spaceStorage port');
}
if (!ctx.getDid || !ctx.getSigner || !ctx.getKemKey) {
throw new Error(
'createSpaceRoutes requires getDid, getSigner and getKemKey',
);
}
if (!ctx.resolveDid || !ctx.verifier) {
throw new Error('createSpaceRoutes requires resolveDid and a verifier');
}
// Refused rather than defaulted: without the link callbacks a space record's
// blobs look unreferenced, and orphan cleanup deletes them.
if (!ctx.blobs || !ctx.linkBlob || !ctx.unlinkBlobs) {
throw new Error(
'createSpaceRoutes requires blobs, linkBlob and unlinkBlobs',
);
}
// One outbox, so write notices and deletion notices share a schedule.
const shared = { ...ctx, outbox: ctx.outbox ?? createNotifyOutbox(ctx) };
return {
...createWriteRoutes(shared),
...createReadRoutes(shared),
...createAuthRoutes(shared),
...createManageRoutes(shared),
};
}