Something went wrong. Try again.
Bluesky app fork with some witchin' additions 💫 forked from jollywhoppers.com/witchsky.app
Something went wrong. Try again.
3.7 kB · 97 lines
TypeScript
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798import { type Agent, type AgentOptions, buildAgent, Client, type ClientOptions,} from '@atproto/lex'
import {getDeviceId, getSessionId} from '#/analytics/identifiers'
type CreateLexClientOptions = ClientOptions & { /** * Include the stable device ID and current session ID on every request. * Enable only when the request is bound for a trusted service. */ includeDeviceSessionHeaders?: boolean}
/** * Adds the current analytics identifiers to every AT Protocol request. Values * are read at dispatch time so session rotation does not require rebuilding * long-lived clients. */function withDeviceSessionHeaders(agentOptions: Agent | AgentOptions): Agent { const agent = buildAgent(agentOptions) return { get did() { return agent.did }, fetchHandler(path, init) { const headers = new Headers(init.headers) const deviceId = getDeviceId() const sessionId = getSessionId() if (deviceId) { headers.set('x-atproto-device-id', deviceId) } if (sessionId) { headers.set('x-atproto-session-id', sessionId) } return agent.fetchHandler(path, {...init, headers}) }, }}
/** * App-standard factory for lex {@link Client}s. Use this instead of `new * Client(...)` so every client shares the same lenient response processing. * * lex-client defaults to strict Lex processing, which rejects responses * containing the LEGACY blob reference format (objects with `cid` and * `mimeType` properties instead of `$type: 'blob'`). Older records on the * network still carry these, and rejecting them would drop records the app * currently renders. Lenient mode also relaxes datetime format checks (e.g. * missing timezones) and blob MIME/size constraints. `Client.configure` only * accepts `appLabelers` globally, so the option is defaulted here, per * constructed client. */export function createLexClient( agent: Agent | AgentOptions, options: CreateLexClientOptions = {},): Client { const {includeDeviceSessionHeaders, ...clientOptions} = options return new Client( includeDeviceSessionHeaders ? withDeviceSessionHeaders(agent) : agent, { strictResponseProcessing: false, ...clientOptions, }, )}
/** * An unauthenticated {@link Client} pointed at an arbitrary service URL, for * the PRE-AUTH flows that talk to a host the user typed or picked (hosting * provider description, password reset, handle availability). Those requests * cannot go through a session-scoped client, because there is no session yet. * * Deliberately NOT memoized: `service` is user-supplied and changes as the user * edits it, so there is no stable key to cache on. Each call builds a fresh * client, mirroring the ad-hoc agents these call sites constructed inline. That * keeps client identity per-render, so callers must not put the returned client * in a React Query key or a dependency array. * * Requests use PLAIN `fetch`, not `networkAwareFetch`: the host is untrusted * input, and a typo'd or dead service must not be reported as the app losing * network reachability. * * The default `includeDeviceSessionHeaders: false` keeps stable analytics IDs * off these requests. `appLabelers: null` also suppresses the global * `Client.appLabelers` static: these are `com.atproto.server` calls to a host the * user typed, which have no use for moderation labels, and the header would * disclose the app's configured moderation authorities to an arbitrary * third-party server. */export function createServiceClient(service: string): Client { return createLexClient({service}, {appLabelers: null})}