~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
75 lines · 2.5 KB Raw History
// 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;
    },
  });
}