chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// The query gate in createServer: experimental.sites.queries composes the engine's route
// and commit tap in; absent, the endpoint does not exist. Driven through
// pds.fetch and the real write path, so the tap itself is covered too.
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import { createServer } from '../src/index.js';
const DID = 'did:plc:querywiringtestaccountaa';
const PASSWORD = 'wiring-test-password';
const BASE = 'https://pds.example.com';
const RUN = `${BASE}/xrpc/dev.pdsjs.query.run`;
/** @type {Array<{close: () => Promise<void>}>} */
const servers = [];
/** @type {string[]} */
const dirs = [];
async function makeServer(opts = {}) {
const dir = mkdtempSync(join(tmpdir(), 'pdsjs-query-wiring-'));
dirs.push(dir);
const server = await createServer({
dbPath: join(dir, 'pds.db'),
blobsDir: join(dir, 'blobs'),
jwtSecret: 'wiring-test-secret',
hostname: 'pds.example.com',
password: PASSWORD,
...opts,
});
servers.push(server);
return server;
}
/** @param {import('@pdsjs/core').PersonalDataServer} pds */
async function initAccount(pds) {
const init = await pds.fetch(
new Request(`${BASE}/init?did=${DID}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
did: DID,
privateKey: Buffer.from(
crypto.getRandomValues(new Uint8Array(32)),
).toString('hex'),
handle: 'query.test',
password: PASSWORD,
}),
}),
);
expect(init.ok).toBe(true);
const session = await pds.fetch(
new Request(`${BASE}/xrpc/com.atproto.server.createSession`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identifier: DID, password: PASSWORD }),
}),
);
const { accessJwt } = await session.json();
return { Authorization: `Bearer ${accessJwt}` };
}
/**
* @param {import('@pdsjs/core').PersonalDataServer} pds
* @param {Record<string, string>} auth
* @param {string} collection
* @param {string} rkey
* @param {Record<string, unknown>} record
*/
async function put(pds, auth, collection, rkey, record) {
const response = await pds.fetch(
new Request(`${BASE}/xrpc/com.atproto.repo.putRecord`, {
method: 'POST',
headers: { ...auth, 'Content-Type': 'application/json' },
body: JSON.stringify({
repo: DID,
collection,
rkey,
record: { $type: collection, ...record },
}),
}),
);
expect(response.ok).toBe(true);
}
afterEach(async () => {
for (const server of servers.splice(0)) {
await server.close().catch(() => {});
}
for (const dir of dirs.splice(0))
rmSync(dir, { recursive: true, force: true });
});
describe('node query wiring', () => {
it('serves no query endpoint without experimental.sites.queries', async () => {
const { pds } = await makeServer();
const response = await pds.fetch(new Request(`${RUN}?query=x`));
expect(response.status).toBe(501);
});
it('answers queries and sees writes through the commit tap', async () => {
const { pds } = await makeServer({
experimental: { sites: { queries: true } },
});
const auth = await initAccount(pds);
await put(pds, auth, 'dev.pdsjs.query.def', 'notes', {
collection: 'com.example.note',
sort: { field: 'title', direction: 'asc' },
createdAt: new Date().toISOString(),
});
await put(pds, auth, 'com.example.note', '3aaa', { title: 'beta' });
const first = await pds.fetch(new Request(`${RUN}?query=notes`));
expect(first.status).toBe(200);
expect(first.headers.get('access-control-allow-origin')).toBe('*');
const body = await first.json();
expect(body.records.map((/** @type {any} */ r) => r.value.title)).toEqual([
'beta',
]);
// A write after the collection is cached arrives via the tap
await put(pds, auth, 'com.example.note', '3bbb', { title: 'alpha' });
const second = await pds.fetch(new Request(`${RUN}?query=notes`));
const updated = await second.json();
expect(
updated.records.map((/** @type {any} */ r) => r.value.title),
).toEqual(['alpha', 'beta']);
});
});