/** * Transport-metadata assembly. * * A chat send carries two kinds of metadata: * - static — fixed per session (locale, slug), set once in config. * - dynamic — recomputed for every send by an optional contributor. * * The dynamic contributor is what enables "capture-on-submit" features * (e.g. a fresh page-context snapshot per message): it runs synchronously * at send time, and its result is merged over the static metadata. */ /** A function that produces extra metadata, freshly, at send time. */ export type DynamicMetadataFn = () => Record | undefined; /** * Invoke a dynamic-metadata contributor, swallowing any throw. * * A contributor that fails (e.g. a snapshot capture errors) must never * break a chat send — the send proceeds with the static metadata only. */ export function safeDynamicMetadata( fn: DynamicMetadataFn, ): Record | undefined { try { return fn(); } catch { return undefined; } } /** * Merge the static metadata with a per-send dynamic contributor. * * Called at each `transport.stream/send` so the contributor runs fresh. * Dynamic keys win over static keys on collision. When there is no * contributor (or it returns nothing) the static metadata passes * through unchanged. */ export function resolveSendMetadata( staticMetadata: Record | undefined, getDynamic: DynamicMetadataFn | undefined, ): Record | undefined { const dynamic = getDynamic ? safeDynamicMetadata(getDynamic) : undefined; if (!dynamic) return staticMetadata; return { ...staticMetadata, ...dynamic }; }