import type { JetstreamEvent } from '@atcute/jetstream'; import type { RecordValue, UrlEntry } from './types'; import { sanitizeLastmod } from './xml'; // uri is the record's at-uri, deletes carry no record so it's what finds their url export type ExtractedAction = | { type: 'delete'; uri: string; url: string } | { type: 'upsert'; uri: string; url: string; entry: UrlEntry }; interface CollectionSpec { buildPath: (owner: string, rkey: string, record?: RecordValue) => string; changefreq: UrlEntry['changefreq']; priority: number; } export const COLLECTION_SPECS: Record = { 'sh.tangled.repo': { buildPath: (owner, rkey, record) => `${owner}/${(record?.name as string) || rkey}`, changefreq: 'daily', priority: 0.8, }, 'sh.tangled.actor.profile': { buildPath: (owner) => owner, changefreq: 'daily', priority: 0.8, }, 'sh.tangled.string': { buildPath: (owner, rkey) => `strings/${owner}/${rkey}`, changefreq: 'weekly', priority: 0.6, }, }; // owners are dids rather than handles, so a url never moves when someone // changes handle, and the web redirects it to the handle anyway export function extractUrlAction( event: JetstreamEvent, baseUrl: string, now = new Date(), ): ExtractedAction | null { if (event.kind !== 'commit') { return null; } const { collection, rkey, operation } = event.commit; const spec = COLLECTION_SPECS[collection]; if (!spec) { return null; } const record = event.commit.operation === 'delete' ? undefined : (event.commit.record as RecordValue); const uri = `at://${event.did}/${collection}/${rkey}`; const normalizedBase = baseUrl.replace(/\/+$/, ''); const path = spec.buildPath(event.did, rkey, record); const url = `${normalizedBase}/${path}`; if (operation === 'delete') { return { type: 'delete', uri, url }; } const lastmod = sanitizeLastmod(record?.createdAt as string | undefined, now); return { type: 'upsert', uri, url, entry: { loc: url, lastmod, changefreq: spec.changefreq, priority: spec.priority, }, }; }