chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
/**
* Showing a commit author as the identity the hosting account claims: an
* atproto profile where the claim names a DID, and a label with a face where
* it names something that holds no account.
*/
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { describeAuthor, loadAuthors } from '../src/lib/authors.js';
import { account } from '../src/lib/git.js';
const OWNER = 'did:plc:owner';
const FRIEND = 'did:plc:friend';
const AVATAR_CID = 'bafkreiavatar';
/**
* @param {Array<Record<string, unknown>>} idents
* @param {Array<Record<string, unknown>>} profiles
*/
function serve(idents, profiles) {
globalThis.fetch = async (/** @type {string|URL|Request} */ input) => {
const url = String(input);
if (url.includes('com.atproto.repo.getRecord')) {
return Response.json({
uri: `at://${OWNER}/dev.pdsjs.git.identity/self`,
value: {
$type: 'dev.pdsjs.git.identity',
idents,
createdAt: '2026-01-01T00:00:00.000Z',
},
});
}
if (url.includes('app.bsky.actor.getProfiles')) {
const wanted = new URL(url).searchParams.getAll('actors');
return Response.json({
profiles: profiles.filter((p) => wanted.includes(String(p.did))),
});
}
return Response.json({ error: 'NotFound' }, { status: 404 });
};
}
describe('describing a commit author', () => {
const original = globalThis.fetch;
const originalLocation = globalThis.location;
beforeEach(() => {
account.did = OWNER;
account.pds = 'https://pds.example';
account.handle = 'owner.example';
// The git service decision probes this page's own origin first; the
// stubs below refuse that probe, so the account's PDS stays the service.
globalThis.location = new URL('https://site.example/');
});
afterEach(() => {
globalThis.fetch = original;
globalThis.location = originalLocation;
account.did = '';
account.pds = '';
account.handle = '';
});
it('takes the claims and faces the server resolves, where it serves them', async () => {
/** @type {string[]} */
const asked = [];
globalThis.fetch = async (/** @type {string|URL|Request} */ input) => {
const url = String(input);
asked.push(url);
if (url.includes('community.lexicon.service.describe')) {
return Response.json({
methods: [{ value: 'dev.pdsjs.git.getAuthors' }],
});
}
if (url.includes('dev.pdsjs.git.getAuthors')) {
return Response.json({
owner: {
did: OWNER,
handle: 'owner.example',
displayName: 'The Account',
avatar: 'https://cdn/owner.jpg',
},
claims: [
{
name: '',
email: 'friend@example.com',
label: '',
did: FRIEND,
avatar: '',
profile: {
did: FRIEND,
handle: 'friend.example',
displayName: 'A Friend',
avatar: 'https://cdn/friend.jpg',
},
},
{
name: '',
email: 'noreply@anthropic.com',
label: 'Claude',
did: '',
avatar: 'https://pds.example/xrpc/com.atproto.sync.getBlob?cid=x',
profile: null,
},
],
});
}
return Response.json({ error: 'NotFound' }, { status: 404 });
};
await loadAuthors();
const friend = describeAuthor('Friend <friend@example.com>');
expect(friend.name).toBe('A Friend');
expect(friend.avatar).toBe('https://cdn/friend.jpg');
expect(friend.atproto).toBe(true);
const tool = describeAuthor('Claude <noreply@anthropic.com>');
expect(tool.name).toBe('Claude');
expect(tool.avatar).toContain('getBlob?cid=x');
expect(asked.some((url) => url.includes('getProfiles'))).toBe(false);
expect(asked.some((url) => url.includes('repo.getRecord'))).toBe(false);
});
it('names a labelled identity itself, not the account holding the claim', async () => {
// The account has a profile of its own, and the claim carries no DID.
// Reading the account's profile here would show every commit the tool
// co-wrote under the account's own name and face.
serve(
[
{
email: 'noreply@anthropic.com',
label: 'Claude',
avatar: { $type: 'blob', ref: { $link: AVATAR_CID } },
},
],
[{ did: OWNER, handle: 'owner.example', displayName: 'The Account' }],
);
await loadAuthors();
const author = describeAuthor('Claude <noreply@anthropic.com>');
expect(author.name).toBe('Claude');
expect(author.atproto).toBe(false);
expect(author.handle).toBe('');
expect(author.avatar).toContain(AVATAR_CID);
expect(author.avatar).toContain(encodeURIComponent(OWNER));
});
it('shows the profile of a claim that names a DID', async () => {
serve(
[{ email: 'friend@example.com', did: FRIEND }],
[
{ did: OWNER, handle: 'owner.example', displayName: 'The Account' },
{
did: FRIEND,
handle: 'friend.example',
displayName: 'A Friend',
avatar: 'https://cdn.example/friend.jpg',
},
],
);
await loadAuthors();
const author = describeAuthor('Friend <friend@example.com>');
expect(author.name).toBe('A Friend');
expect(author.handle).toBe('friend.example');
expect(author.atproto).toBe(true);
expect(author.did).toBe(FRIEND);
});
it('carries the DID of a claimed account that publishes no profile', async () => {
// The DID is what a face with no picture draws its pattern from, so
// dropping it here gave one person an identicon beside their words and a
// bare letter on their commits.
serve(
[{ email: 'friend@example.com', did: FRIEND }],
[{ did: OWNER, handle: 'owner.example', displayName: 'The Account' }],
);
await loadAuthors();
const author = describeAuthor('Friend <friend@example.com>');
expect(author.did).toBe(FRIEND);
expect(author.atproto).toBe(true);
// Nothing said what to call them, so the commit's own name stands.
expect(author.name).toBe('Friend');
expect(author.avatar).toBe('');
});
it('reads a claim with neither DID nor label as the account itself', async () => {
serve(
[{ email: 'owner@example.com' }],
[{ did: OWNER, handle: 'owner.example', displayName: 'The Account' }],
);
await loadAuthors();
const author = describeAuthor('Owner <owner@example.com>');
expect(author.name).toBe('The Account');
expect(author.atproto).toBe(true);
});
it('leaves an unclaimed author as the name the commit carries', async () => {
serve([{ email: 'owner@example.com' }], []);
await loadAuthors();
const author = describeAuthor('Stranger <stranger@example.com>');
expect(author.name).toBe('Stranger');
expect(author.atproto).toBe(false);
expect(author.avatar).toBe('');
});
});