Something went wrong. Try again.
Browser extension: detect and subscribe to standard.site publications on ATProto
Something went wrong. Try again.
9.0 kB · 235 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236// End-to-end detection against a stubbed network, using the shapes real// publications are served with: a well-known endpoint that answers with a// handle-form at-uri, and a publication record whose `icon` is a path string// rather than a blob ref. Both shapes are real and neither is what the lexicon// suggests, which is why they are pinned here rather than in a live capture.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'import { detectPage, dropProbes, wellKnownUrl } from './detection'
const DID = 'did:plc:detectiontest0000000000'const URI = `at://${DID}/site.standard.publication/self`
/** The backing map of the current chrome.storage.session stub, for assertions. */let sessionStore = new Map<string, unknown>()
/** chrome.storage.session, which probeWellKnown uses as its probe cache. */function stubChromeStorage() { const store = new Map<string, unknown>() sessionStore = store vi.stubGlobal('chrome', { storage: { session: { get: async (key: string | null) => key === null ? Object.fromEntries(store) : store.has(key) ? { [key]: store.get(key) } : {}, set: async (items: Record<string, unknown>) => { for (const [k, v] of Object.entries(items)) store.set(k, v) }, remove: async (keys: string | string[]) => { for (const k of [keys].flat()) store.delete(k) }, }, }, })}
function stubNetwork(record: Record<string, unknown>) { const fetchMock = vi.fn(async (input: string) => { const url = String(input) if (url === 'https://pub.example/.well-known/site.standard.publication') { return new Response(`at://pub.example/site.standard.publication/self\n`) } if (url.includes('com.atproto.identity.resolveHandle')) { return new Response(JSON.stringify({ did: DID })) } if (url === `https://plc.directory/${DID}`) { return new Response( JSON.stringify({ alsoKnownAs: ['at://pub.example'], service: [ { id: '#atproto_pds', type: 'AtprotoPersonalDataServer', serviceEndpoint: 'https://pds.example.com', }, ], }), ) } if (url.includes('com.atproto.repo.getRecord')) { return new Response(JSON.stringify({ uri: URI, cid: 'bafcid', value: record })) } return new Response('not found', { status: 404, statusText: 'Not Found' }) }) vi.stubGlobal('fetch', fetchMock) return fetchMock}
const baseRecord = { $type: 'site.standard.publication', url: 'https://pub.example', name: 'Detection Fixture', description: 'wobbegong',}
beforeEach(stubChromeStorage)afterEach(() => { vi.unstubAllGlobals()})
describe('detectPage', () => { it('verifies a publication whose well-known returns a handle-form at-uri', async () => { stubNetwork(baseRecord) const { pub } = await detectPage('https://pub.example/') expect(pub?.uri).toBe(URI) expect(pub?.verified).toBe(true) expect(pub?.handle).toBe('pub.example') expect(pub?.iconUrl).toBeUndefined() })
it('builds an icon url from a real blob ref', async () => { stubNetwork({ ...baseRecord, icon: { $type: 'blob', ref: { $link: 'bafkreiicon' }, mimeType: 'image/png', size: 9 }, }) const { pub } = await detectPage('https://pub.example/') expect(pub?.iconUrl).toBe( `https://pds.example.com/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(DID)}&cid=bafkreiicon`, ) })
it('still detects when the record puts a path string in `icon`', async () => { // Regression: reading blob.ref.$link off a string threw, and the throw // surfaced as a fetch-failed error instead of a detected publication. stubNetwork({ ...baseRecord, icon: '/assets/images/favicon/apple-touch-icon.png' }) const { pub } = await detectPage('https://pub.example/') expect(pub?.verified).toBe(true) expect(pub?.iconUrl).toBeUndefined() })})
/** Counts well-known probes and answers every one of them with a miss. */function missingWellKnown() { const fetchSpy = vi.fn(async () => new Response('', { status: 404, statusText: 'Not Found' })) vi.stubGlobal('fetch', fetchSpy) return fetchSpy}
describe('wellKnownUrl', () => { it('probes the origin for a root publication', () => { expect(wellKnownUrl('https://example.test')).toBe( 'https://example.test/.well-known/site.standard.publication', ) })
it('ignores a trailing slash', () => { expect(wellKnownUrl('https://example.test/')).toBe( 'https://example.test/.well-known/site.standard.publication', ) })
// atproto.com/blog is a real publication of this shape: the origin 404s and // only the path-suffixed probe answers. it('appends the base path for a publication below the root', () => { expect(wellKnownUrl('https://atproto.test/blog')).toBe( 'https://atproto.test/.well-known/site.standard.publication/blog', ) })
it('drops query and fragment', () => { expect(wellKnownUrl('https://example.test/blog?x=1#y')).toBe( 'https://example.test/.well-known/site.standard.publication/blog', ) })})
describe('well-known probe caching', () => { it('asks an origin once and serves later pages under it from cache', async () => { const fetchSpy = missingWellKnown() await detectPage('https://example.test/blog/one') const afterFirst = fetchSpy.mock.calls.length await detectPage('https://example.test/blog/two') await detectPage('https://example.test/blog/three') expect(fetchSpy.mock.calls.length).toBe(afterFirst) })
// The Refresh button existed but could not do the one thing it is for: a // publisher fixing their well-known kept seeing the cached miss. it('re-asks the origin when the caller forces a refresh', async () => { const fetchSpy = missingWellKnown() await detectPage('https://example.test/one') const afterFirst = fetchSpy.mock.calls.length await detectPage('https://example.test/one', undefined, undefined, true) expect(fetchSpy.mock.calls.length).toBeGreaterThan(afterFirst) })
it('collapses concurrent probes of one origin into a single request', async () => { const fetchSpy = missingWellKnown() await Promise.all([ detectPage('https://example.test/a'), detectPage('https://example.test/b'), detectPage('https://example.test/c'), ]) // Origin probe plus the one path-level probe each page triggers on a miss; // without dedupe this would be three times as many. expect(fetchSpy.mock.calls.length).toBeLessThanOrEqual(4) })
it('records a miss so a second visit does not re-ask', async () => { missingWellKnown() await detectPage('https://example.test/one') const keys = [...sessionStore.keys()].filter((k) => k.startsWith('wk:')) expect(keys.length).toBeGreaterThan(0) expect(sessionStore.get(keys[0]!)).toMatchObject({ uri: null }) })
// The dev build's Drop cache: this cache is the one nothing else empties, // since Refresh only bypasses it for the page it is walking. it('re-asks every origin after the probe answers are dropped', async () => { const fetchSpy = missingWellKnown() await detectPage('https://example.test/one') const afterFirst = fetchSpy.mock.calls.length
expect(await dropProbes()).toBeGreaterThan(0) expect([...sessionStore.keys()].filter((k) => k.startsWith('wk:'))).toEqual([])
await detectPage('https://example.test/one') expect(fetchSpy.mock.calls.length).toBeGreaterThan(afterFirst) })
it('leaves everything that is not a probe answer alone', async () => { missingWellKnown() // A per-tab detection state: the worker's, dropped by the worker, and not // something a sweep of this cache may take with it. sessionStore.set('tab:7', { url: 'https://example.test/one' }) await detectPage('https://example.test/one') await dropProbes() expect(sessionStore.get('tab:7')).toEqual({ url: 'https://example.test/one' }) })
it('reports no publication rather than throwing when every probe misses', async () => { missingWellKnown() await expect(detectPage('https://example.test/one')).resolves.toEqual({ pub: undefined, doc: undefined, }) })
/** * Documents a real cost rather than asserting a wish: on an origin with no * publication, each new *first path segment* earns its own probe, because a * publication may legitimately live under one path and not another. See * TODO.md — narrowing this needs a heuristic with false negatives. */ it('probes each distinct first path segment on an origin with no publication', async () => { const fetchSpy = missingWellKnown() await detectPage('https://example.test/one') const afterFirst = fetchSpy.mock.calls.length await detectPage('https://example.test/two') expect(fetchSpy.mock.calls.length).toBe(afterFirst + 1) })})