chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// A SpaceStoragePort over plain rows, enough for core's space browser and
// the readers the feature packages build over storage: a test names its
// spaces, members and records as data and the port answers from them.
import { cborEncodeDagCbor } from '../../packages/core/src/repo.js';
/**
* @typedef {Object} FakeSpaceRecord
* @property {string} collection
* @property {string} rkey
* @property {string} cid
* @property {string} [indexedAt]
* @property {unknown} value - plain, encoded to DAG-CBOR on read
*/
/**
* @param {Object} [rows]
* @param {Array<Record<string, unknown> & {uri: string}>} [rows.spaces] - SpaceRow-shaped; missing fields read as undefined
* @param {Record<string, string[]>} [rows.members] - by space URI
* @param {Record<string, FakeSpaceRecord[]>} [rows.records] - by space URI, in storage order
*/
export function createFakeSpaceStorage({
spaces = [],
members = {},
records = {},
} = {}) {
/** @param {FakeSpaceRecord} record */
const row = (record) => ({
collection: record.collection,
rkey: record.rkey,
cid: record.cid,
indexedAt: record.indexedAt ?? '2026-01-01T00:00:00.000Z',
value: cborEncodeDagCbor(record.value),
});
return /** @type {any} */ ({
async getSpace(/** @type {string} */ uri) {
return spaces.find((space) => space.uri === uri) ?? null;
},
async listSpaces(/** @type {{limit?: number}} */ { limit = 50 } = {}) {
return { spaces: spaces.slice(0, limit), cursor: null };
},
async listMembers(/** @type {string} */ uri) {
return { dids: members[uri] ?? [], cursor: null };
},
async listAllSpaceRecords(/** @type {string} */ uri) {
return (records[uri] ?? []).map(({ collection, rkey, cid }) => ({
collection,
rkey,
cid,
}));
},
async listSpaceRecords(
/** @type {string} */ uri,
/** @type {{collection?: string|null, limit?: number, reverse?: boolean}} */ {
collection = null,
limit = 50,
reverse = false,
} = {},
) {
let rows = records[uri] ?? [];
if (collection) rows = rows.filter((r) => r.collection === collection);
if (reverse) rows = [...rows].reverse();
return { records: rows.slice(0, limit).map(row), cursor: null };
},
async getSpaceRecord(
/** @type {string} */ uri,
/** @type {string} */ collection,
/** @type {string} */ rkey,
) {
const found = (records[uri] ?? []).find(
(r) => r.collection === collection && r.rkey === rkey,
);
return found ? row(found) : null;
},
});
}