// A local fixture registry for the atlibs CLI (no network): a fake PLC, handle resolver and PDS // (node:http) for alice.test. publish(name) puts examples/rust/'s `cargo wasm` build, its // module.json and its records/.json as alice.test's records. It also answers an http // composition handler (POST /handler), app-password sign-in with refresh-token rotation (auth counts // the calls), and the writes `atlibs publish` makes (listRecords, putRecord/createRecord, uploadBlob). // `env` points the CLI at it: ATLIBS_PLC_URL, ATLIBS_HANDLE_RESOLVER, ATLIBS_ALLOW_PRIVATE. // // const registry = await fixtureRegistry(); // await registry.publish("compose"); // // atlibs add alice.test/compose, with registry.env // registry.close(); import { createHash } from "node:crypto"; import { readFile } from "node:fs/promises"; import { createServer } from "node:http"; import { modulePin } from "../../javascript/src/run-module/index.js"; const ROOT = new URL("../../", import.meta.url); export const DID = "did:plc:alicetest"; const sha256 = (bytes) => createHash("sha256").update(bytes).digest(); // CIDv1 base32: raw (blobs) or dag-cbor (records; any stable hash will do for a fake PDS) export function cid(codec, bytes) { const raw = [0x01, codec, 0x12, 0x20, ...sha256(bytes)]; let bits = 0, value = 0, out = "b"; for (const byte of raw) { value = (value << 8) | byte; bits += 8; while (bits >= 5) out += "abcdefghijklmnopqrstuvwxyz234567"[(value >>> (bits -= 5)) & 31]; } return bits ? out + "abcdefghijklmnopqrstuvwxyz234567"[(value << (5 - bits)) & 31] : out; } export async function fixtureRegistry({ port = 0 } = {}) { const records = new Map(); // "collection/rkey" -> { cid, value } const blobs = new Map(); const auth = { refreshes: 0, creates: 0 }; const seen = []; // the requests' paths let base; const put = (collection, rkey, value) => { const entry = { cid: cid(0x71, Buffer.from(JSON.stringify(value))), value }; records.set(`${collection}/${rkey}`, entry); return { uri: `at://${DID}/${collection}/${rkey}`, cid: entry.cid }; }; async function publish(name) { const wasm = await readFile(new URL(`target/wasm32-unknown-unknown/release/${name.replaceAll("-", "_")}.wasm`, ROOT)); const spec = JSON.parse(await readFile(new URL(`examples/rust/${name}/module.json`, ROOT), "utf8")); const blob = cid(0x55, wasm); blobs.set(blob, wasm); const session = spec.session && put("com.fedfork.exec.oauth.clientMetadata", spec.session, JSON.parse(await readFile(new URL(`records/${spec.session}.json`, ROOT), "utf8"))); const record = { $type: "com.fedfork.exec.wasm.module", ...spec, ...(session && { session }), module: { $type: "blob", ref: { $link: blob }, mimeType: "application/wasm", size: wasm.length }, abi: "emdash-wasm-json-v1", actions: spec.actions ?? [], createdAt: "2026-11-08T00:00:00.000Z" }; // as `atlibs publish` writes it: at its name and at its version (.<32 hex of its pin>) put("com.fedfork.exec.wasm.module", `${name}.${(await modulePin({ ...spec, module: wasm })).slice(0, 32)}`, record); put("com.fedfork.exec.wasm.module", name, record); return { wasm, blob }; } const server = createServer((req, res) => { const url = new URL(req.url, base); seen.push(url.pathname); const json = (status, body) => res.writeHead(status, { "content-type": "application/json" }).end(JSON.stringify(body)); if (url.pathname === `/plc/${DID}`) return json(200, { id: DID, alsoKnownAs: ["at://alice.test"], service: [{ id: "#atproto_pds", type: "AtprotoPersonalDataServer", serviceEndpoint: base }] }); if (url.pathname === "/xrpc/com.atproto.identity.resolveHandle") return url.searchParams.get("handle") === "alice.test" ? json(200, { did: DID }) : json(400, { error: "InvalidRequest" }); if (url.pathname === "/xrpc/com.atproto.sync.getBlob") return blobs.has(url.searchParams.get("cid")) ? res.end(blobs.get(url.searchParams.get("cid"))) : json(404, { error: "BlobNotFound" }); if (url.pathname === "/xrpc/com.atproto.repo.getRecord") { const found = records.get(`${url.searchParams.get("collection")}/${url.searchParams.get("rkey")}`); return found ? json(200, { uri: `at://${DID}/${url.searchParams.get("collection")}/${url.searchParams.get("rkey")}`, ...found }) : json(400, { error: "RecordNotFound", message: "Could not locate record" }); } // an http handler (atlibs run --handler confirm=http://127.0.0.1/handler): answers with what it got if (url.pathname === "/handler" && req.method === "POST") { let body = ""; req.on("data", (chunk) => (body += chunk)); return req.on("end", () => json(200, { approved: true, got: JSON.parse(body), auth: req.headers.authorization ?? null })); } // sign-in (the shared-session test): each refresh token is taken once (rotation), a1 is expired, then invalid const late = (status, body) => setTimeout(() => json(status, body), 10); // concurrent runs overlap const bearer = req.headers.authorization ?? ""; const didDoc = { service: [{ id: "#atproto_pds", serviceEndpoint: base }] }; if (url.pathname === "/xrpc/com.atproto.server.createSession") return auth.creates++, late(200, { did: DID, handle: "alice.test", accessJwt: "a9", refreshJwt: "r9", didDoc }); if (url.pathname === "/xrpc/com.atproto.server.refreshSession") return ++auth.refreshes === 1 && bearer === "Bearer r1" ? late(200, { did: DID, handle: "alice.test", accessJwt: "a2", refreshJwt: "r2", didDoc }) : late(400, { error: "ExpiredToken", message: "refresh token already used" }); if (url.pathname === "/xrpc/com.atproto.repo.deleteRecord") return bearer === "Bearer a1" ? (auth.refreshes ? late(401, { error: "InvalidToken" }) : late(400, { error: "ExpiredToken" })) : late(200, {}); // publishing (the `atlibs publish` test): listRecords, putRecord/createRecord, uploadBlob if (url.pathname === "/xrpc/com.atproto.repo.listRecords") { const prefix = `${url.searchParams.get("collection")}/`; return json(200, { records: [...records].filter(([key]) => key.startsWith(prefix)).map(([key, r]) => ({ uri: `at://${DID}/${key}`, ...r })) }); } if (req.method === "POST" && /^\/xrpc\/com\.atproto\.repo\.(put|create)Record$/.test(url.pathname) || url.pathname === "/xrpc/com.atproto.repo.uploadBlob") { const chunks = []; req.on("data", (chunk) => chunks.push(chunk)); return req.on("end", () => { const body = Buffer.concat(chunks); if (url.pathname.endsWith("uploadBlob")) { blobs.set(cid(0x55, body), body); return json(200, { blob: { $type: "blob", ref: { $link: cid(0x55, body) }, mimeType: req.headers["content-type"], size: body.length } }); } const { collection, rkey, record } = JSON.parse(body); json(200, put(collection, rkey, record)); }); } json(501, { error: "MethodNotImplemented" }); }); await new Promise((resolve) => server.listen(port, "127.0.0.1", resolve)); base = `http://127.0.0.1:${server.address().port}`; const env = { ATLIBS_PLC_URL: `${base}/plc`, ATLIBS_HANDLE_RESOLVER: base, ATLIBS_ALLOW_PRIVATE: "1" }; return { base, env, records, blobs, auth, seen, put, publish, close: () => server.close() }; }