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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
143 lines · 5.0 KB Raw History
// The issue statements as the server reads them: every listed voice from
// their own server, and whoever the discovery port names from theirs.
import { describe, expect, it } from 'vitest';
import { createIssueReader } from '../src/issue-reader.js';

const ALICE = 'did:plc:alice';
const BOB = 'did:plc:bob';
const DAN = 'did:plc:dan';
const REPO = 'demo';
const SUBJECT = `at://${ALICE}/dev.pdsjs.git.repo/${REPO}`;

/** @type {Record<string, {did: string, handle: string, pds: string}>} */
const PEOPLE = {
  [ALICE]: { did: ALICE, handle: 'alice.test', pds: 'https://alice.test' },
  [BOB]: { did: BOB, handle: 'bob.test', pds: 'https://bob.test' },
  [DAN]: { did: DAN, handle: 'dan.test', pds: 'https://dan.test' },
};

/**
 * @param {string} did @param {string} rkey @param {Record<string, unknown>} value
 */
const row = (did, rkey, value) => ({
  uri: `at://${did}/dev.pdsjs.git.issue/${rkey}`,
  cid: `cid-${rkey}`,
  value: {
    subject: { uri: SUBJECT },
    createdAt: '2026-08-01T00:00:00Z',
    ...value,
  },
});

/**
 * @param {Object} [options]
 * @param {Array<{did: string, collection: string, rkey: string}>} [options.about]
 * @param {string[]} [options.unreachable]
 */
function world({ about = [], unreachable = [] } = {}) {
  /** @type {Record<string, unknown[]>} */
  const records = {
    [ALICE]: [row(ALICE, 'a1', { body: 'Resolved.', disposition: 'resolved' })],
    [BOB]: [
      row(BOB, 'b1', { title: 'It breaks', body: 'Steps.' }),
      // About another repository, which a voice's listing carries too.
      {
        ...row(BOB, 'b2', { title: 'Elsewhere' }),
        value: {
          subject: { uri: `at://${ALICE}/dev.pdsjs.git.repo/other` },
          title: 'Elsewhere',
        },
      },
    ],
    [DAN]: [
      row(DAN, 'd1', { title: 'From a stranger' }),
      {
        ...row(DAN, 'd2', { title: 'Lied about' }),
        value: {
          subject: { uri: `at://${ALICE}/dev.pdsjs.git.repo/other` },
          title: 'Lied about',
        },
      },
    ],
  };

  /** @type {typeof fetch} */
  const send = async (input) => {
    const url = new URL(String(input));
    const who = Object.values(PEOPLE).find((one) => one.pds === url.origin);
    if (!who || unreachable.includes(who.did)) {
      throw new TypeError('fetch failed');
    }
    const held = /** @type {{uri: string}[]} */ (records[who.did] ?? []);
    if (url.pathname.endsWith('listRecords')) {
      return Response.json({ records: held, cursor: null });
    }
    const rkey = url.searchParams.get('rkey');
    const found = held.find((one) => one.uri.endsWith(`/${rkey}`));
    return found ? Response.json(found) : new Response('no', { status: 404 });
  };

  const reader = createIssueReader({
    resolveContributor: async (did) => PEOPLE[did] ?? null,
    readRecord: async (_did, collection, rkey) => {
      if (rkey !== REPO) return null;
      return collection === 'dev.pdsjs.git.repo'
        ? { defaultBranch: 'main' }
        : { collaborators: [BOB] };
    },
    discovery: {
      about: async (_subject, collection) =>
        about.filter((entry) => entry.collection === collection),
    },
    fetch: send,
  });
  return reader;
}

describe('createIssueReader', () => {
  it('lists every voice the config names, about this repository only', async () => {
    const answer = await world().listIssues(ALICE, REPO);
    const statements = answer?.statements ?? [];

    expect(statements.map((one) => one.uri)).toEqual([
      `at://${ALICE}/dev.pdsjs.git.issue/a1`,
      `at://${BOB}/dev.pdsjs.git.issue/b1`,
    ]);
    expect(statements[0].by).toEqual(PEOPLE[ALICE]);
    expect(statements[0].disposition).toBe('resolved');
    expect(statements[1].indexed).toBeUndefined();
  });

  it('reads a stranger the port names from their own server, and takes the record over the index', async () => {
    const answer = await world({
      about: [
        { did: DAN, collection: 'dev.pdsjs.git.issue', rkey: 'd1' },
        { did: DAN, collection: 'dev.pdsjs.git.issue', rkey: 'd2' },
        { did: DAN, collection: 'dev.pdsjs.git.issue', rkey: 'missing' },
        // A listed voice's record, read directly already.
        { did: BOB, collection: 'dev.pdsjs.git.issue', rkey: 'b1' },
      ],
    }).listIssues(ALICE, REPO);
    const statements = answer?.statements ?? [];

    const fromDan = statements.filter((one) => one.by.did === DAN);
    expect(fromDan.map((one) => one.uri)).toEqual([
      `at://${DAN}/dev.pdsjs.git.issue/d1`,
    ]);
    expect(fromDan[0]).toMatchObject({
      title: 'From a stranger',
      indexed: true,
    });
    // Bob's word appears once.
    expect(statements.filter((one) => one.uri.endsWith('/b1'))).toHaveLength(1);
  });

  it('costs an unreachable voice their words and nothing else', async () => {
    const answer = await world({ unreachable: [BOB] }).listIssues(ALICE, REPO);
    expect(answer?.statements.map((one) => one.by.did)).toEqual([ALICE]);
  });

  it('answers null for a repository this account does not hold', async () => {
    expect(await world().listIssues(ALICE, 'nothing')).toBeNull();
  });
});