···508508}
509509510510/**
511511+ * User-Agent sent on every AppView request so Bluesky ops can identify our
512512+ * traffic and contact us if we're misbehaving.
513513+ */
514514+export const ATPROTO_USER_AGENT = 'favs.blue (+https://favs.blue)'
515515+516516+/**
511517 * Create a production AtprotoClient pointed at public.api.bsky.app.
512518 *
513519 * Per spec §6: "All three methods MUST go through public.api.bsky.app (not
514520 * bsky.social) — that's the public unauthenticated endpoint with the more
515521 * generous rate limits."
522522+ *
523523+ * The optional `fetchImpl` override is used by tests to observe the outbound
524524+ * request (e.g. to assert the User-Agent header is set).
516525 */
517517-export function createAtprotoClient(onRequest?: AtprotoRequestHook): AtprotoClient {
518518- const agent = new Agent({ service: 'https://public.api.bsky.app' } as never)
526526+export function createAtprotoClient(
527527+ onRequest?: AtprotoRequestHook,
528528+ fetchImpl?: typeof globalThis.fetch
529529+): AtprotoClient {
530530+ const agent = new Agent({
531531+ service: 'https://public.api.bsky.app',
532532+ headers: { 'User-Agent': ATPROTO_USER_AGENT },
533533+ ...(fetchImpl ? { fetch: fetchImpl } : {}),
534534+ } as never)
519535 return new AtprotoClient(agent as unknown as AgentLike, defaultSleep, onRequest)
520536}