// The sites extension against a fake host: a domain turns on the host // handler, browser and installer; without one only the installs routes // mount; queries add their route and commit tap. import { CID, cborEncodeDagCbor, cidToString, createBlobCid, createCid, } from '@pdsjs/core/repo'; import { beforeEach, describe, expect, it } from 'vitest'; import { createSitesExtension } from '../src/extension.js'; import { clearManifestCache } from '../src/manifest.js'; const DID = 'did:plc:sitesextension'; /** * One-file site record for `name`, plus the blob it points at. * @param {string} name */ async function siteFixture(name) { const data = new TextEncoder().encode(`

${name}

`); const cidBytes = await createBlobCid(data); const record = cborEncodeDagCbor({ $type: 'dev.pdsjs.site.deploy', files: [ { path: 'index.html', blob: { $type: 'blob', ref: new CID(cidBytes), mimeType: 'text/html', size: data.length, }, }, ], createdAt: '2026-08-06T00:00:00.000Z', }); return { uri: `at://${DID}/dev.pdsjs.site.deploy/${name}`, row: { cid: cidToString(await createCid(record)), value: record }, blobCid: cidToString(cidBytes), data, }; } /** @param {Awaited>[]} sites */ function createHost(sites) { const records = new Map(sites.map((site) => [site.uri, site.row])); const blobs = new Map(sites.map((site) => [site.blobCid, site.data])); const host = { actorStorage: { getRecord: async (/** @type {string} */ uri) => records.get(uri) ?? null, listRecords: async () => ({ records: [], cursor: null }), linkBlobToRecord: async () => {}, }, blobs: { get: async (/** @type {string} */ _did, /** @type {string} */ cid) => { const data = blobs.get(cid); return data ? { data, mimeType: 'text/html' } : null; }, }, readOnly: false, plcUrl: 'https://plc.directory', blobUploadLimit: 1024, getDid: async () => DID, xrpc: async () => new Response('{}'), createCommit: async () => ({ cid: 'bafy', rev: '3l' }), reapOrphanedBlobs: async () => {}, accountApi: async () => new Response('{}'), }; return /** @type {import('@pdsjs/core/ports').ExtensionHost} */ ( /** @type {unknown} */ (host) ); } beforeEach(() => clearManifestCache()); describe('createSitesExtension', () => { it('serves the apex from the named site when a domain is set', async () => { const www = await siteFixture('www'); const extension = createSitesExtension({ domain: 'example.com', apexName: 'www', })(createHost([www])); expect(extension.name).toBe('sites'); expect(extension.features).toEqual({ sites: true }); expect(Object.keys(extension.routes ?? {})).toEqual( expect.arrayContaining(['/account/api/sites', '/account/api/installs']), ); const url = new URL('https://example.com/'); const response = await extension.handleHost?.(new Request(url), url); expect(response?.status).toBe(200); expect(await response?.text()).toBe('

www

'); }); it('mounts only the installs routes without a domain', async () => { const extension = createSitesExtension()(createHost([])); expect(extension.features).toEqual({ sites: false }); expect(extension.handleHost).toBeUndefined(); expect(extension.onCommit).toBeUndefined(); const routes = Object.keys(extension.routes ?? {}); expect(routes).toContain('/account/api/installs'); expect(routes).toContain('/account/api/sites'); expect(routes).not.toContain('/xrpc/dev.pdsjs.query.run'); }); it('adds the query route and commit tap when asked', async () => { const extension = createSitesExtension({ queries: true })(createHost([])); expect(extension.routes?.['/xrpc/dev.pdsjs.query.run']).toBeDefined(); expect(typeof extension.onCommit).toBe('function'); }); });