/** * Permissioned data as a server extension: the platform builds the space * storage and passes the factory to core's `extensions` option; core mounts * the space endpoints, the account page's space routes and the admin port * behind them. */ import { createSpaceAccountRoutes } from './account.js'; import { createSpaceAdmin } from './admin.js'; import { createNotifyOutbox } from './outbox.js'; import { createSpaceRoutes } from './routes.js'; /** * @param {Object} [options] * @param {typeof fetch} [options.fetch] - for the outbound notification calls; defaults to the global fetch * @returns {import('@pdsjs/core/ports').ExtensionFactory} */ export function createSpacesExtension({ fetch: fetchImpl } = {}) { return (host) => { const { spaceStorage, actorStorage, blobs, didResolver, verifier } = host; if (!spaceStorage) { throw new Error('The spaces extension needs spaceStorage on the server'); } if (!didResolver || !verifier) { throw new Error( 'The spaces extension needs a didResolver and a verifier on the server', ); } const spaceAdmin = createSpaceAdmin({ spaceStorage, getDid: host.getDid }); const outbox = createNotifyOutbox({ spaceStorage, getDid: host.getDid, getSigner: host.getSigner, resolveDid: didResolver, fetch: fetchImpl ?? ((url, init) => fetch(url, init)), scheduleWakeup: host.scheduleWakeup, background: host.background, }); return { name: 'spaces', // The space endpoints hold records of every collection an app writes // into a space. collections: ['*'], routes: { ...createSpaceRoutes({ spaceStorage, // A space record's blobs are ordinary account blobs, uploaded // through com.atproto.repo.uploadBlob and linked in the one blob // link table, so orphan cleanup sees a space reference the same as // a public one. blobs, linkBlob: (cid, uri, time) => actorStorage.linkBlobToRecord(cid, uri, time), unlinkBlobs: (uri) => actorStorage.unlinkBlobsFromRecord(uri), getDid: host.getDid, // The full document: space auth verifies signatures against the // document's verification methods. resolveDid: didResolver, verifier, recordGuard: host.writeGuard, // Space tokens and service auth are signed by the account's atproto // signing key; its KEM key MACs reads of its space repos. getSigner: host.getSigner, getKemKey: host.getKemKey, fetch: fetchImpl ?? ((url, init) => fetch(url, init)), outbox, background: host.background, }), ...createSpaceAccountRoutes({ host, spaceBrowser: host.spaceBrowser ?? null, spaceAdmin, }), }, features: { spaceAdmin: true }, // Write notices this repo still owes the authorities of spaces it // writes into, and deletion notices owed to the syncers of spaces it // deleted. tick: () => outbox.tick(), }; }; }