UNPKG

@langchain/langgraph-sdk

Version:

Client library for interacting with the LangGraph API

1 lines • 43.7 kB
{"version":3,"file":"media.cjs","names":["#parts","#blobPromise","#transcriptPromise","#fetchImpl","#blobResolve","#blobReject","#transcriptResolve","#transcriptReject","#lastIndex","#urlSourced","#absorbAudio","#absorbImage","#absorbVideo","#absorbFile","#finished","#settled","#totalBytes","#partialSnapshot","#streamController","#transcriptParts","#fetchUrlSourced","#cachedObjectURL","#stream","#buildUrlStream","#buildInlineStream","#startUrlFetch","#urlFetchPromise","#callbacks","#fetch","#active","#activeByNamespaceNode","#flushSlot","#syntheticCounter","#consumeMediaBlock","#emit"],"sources":["../../../src/client/stream/media.ts"],"sourcesContent":["import type {\n AudioContentBlock,\n ContentBlock,\n FileContentBlock,\n ImageContentBlock,\n MessagesEvent,\n VideoContentBlock,\n} from \"@langchain/protocol\";\n\n/**\n * Block types this assembler knows how to reassemble into media handles.\n */\nexport type MediaBlockType = \"audio\" | \"image\" | \"video\" | \"file\";\n\nconst MEDIA_BLOCK_TYPES: ReadonlySet<string> = new Set<MediaBlockType>([\n \"audio\",\n \"image\",\n \"video\",\n \"file\",\n]);\n\n/**\n * Kinds of failure that can terminate a media handle prematurely.\n *\n * - `\"message-error\"` — the upstream message emitted an `error` event\n * before completion.\n * - `\"stream-closed\"` — the transport stream closed before\n * `message-finish` arrived (thread closed, transport dropped, etc.).\n * - `\"fetch-failed\"` — the block was `url`-sourced and the lazy\n * `fetch()` rejected (CORS, 404, 5xx, network).\n */\nexport type MediaAssemblyErrorKind =\n | \"message-error\"\n | \"stream-closed\"\n | \"fetch-failed\";\n\n/**\n * Typed error thrown through `media.stream` / rejected from\n * `media.blob` / `media.objectURL` when a handle fails before its\n * message completes. Carries the bytes accumulated up to the failure\n * point on `partialBytes` for callers that want to salvage or diagnose.\n */\nexport class MediaAssemblyError extends Error {\n readonly kind: MediaAssemblyErrorKind;\n readonly messageId: string;\n readonly partialBytes: Uint8Array;\n // oxlint-disable-next-line typescript/no-explicit-any\n readonly cause?: unknown;\n\n constructor(\n kind: MediaAssemblyErrorKind,\n messageId: string,\n partialBytes: Uint8Array,\n message?: string,\n options?: { cause?: unknown }\n ) {\n super(message ?? `media ${kind} for message ${messageId}`);\n this.name = \"MediaAssemblyError\";\n this.kind = kind;\n this.messageId = messageId;\n this.partialBytes = partialBytes;\n this.cause = options?.cause;\n }\n}\n\n/**\n * Shared surface across every media handle returned by\n * {@link MediaAssembler}.\n *\n * The handle is live while its parent message is active:\n * - `partialBytes` is a snapshot of all bytes received so far.\n * - `stream` is a lazy, single-consumer byte stream (see\n * accessor docstring).\n * - `blob` / `objectURL` settle on `message-finish`.\n * - `error` becomes set if the handle terminates in any of the\n * {@link MediaAssemblyErrorKind} failure modes.\n */\nexport interface MediaBase {\n readonly messageId: string;\n readonly namespace: string[];\n readonly node?: string;\n /** `id` from the originating content block, if the provider sent one. */\n readonly id?: string;\n readonly mimeType?: string;\n /**\n * Present iff the originating block carried `url` (not `data`).\n * When set, `blob` / `stream` / `objectURL` lazily fetch from here\n * on first access.\n */\n readonly url?: string;\n\n /**\n * Live byte stream.\n *\n * Lazy: not materialised unless accessed. On first access the\n * stream is seeded with every byte already accumulated\n * (`partialBytes`) and then wired to future chunks. For URL-sourced\n * blocks, first access triggers `fetch()` and pipes the response\n * body through.\n *\n * Repeated access returns the same {@link ReadableStream} reference\n * — you can safely read it once, release the lock, and re-acquire a\n * reader later (e.g. React StrictMode effect re-invokes). The\n * standard `ReadableStream.locked` semantics prevent concurrent\n * readers; use `stream.tee()` when you truly need multiple live\n * consumers.\n */\n readonly stream: ReadableStream<Uint8Array>;\n\n /** Resolves on `message-finish` with the concatenated {@link Blob}. */\n readonly blob: Promise<Blob>;\n\n /**\n * Lazy {@link URL.createObjectURL} over {@link blob}. Cached: first\n * access creates the URL, subsequent accesses return the same one.\n * Call {@link revoke} to free the URL slot; the next access creates\n * a fresh URL. `ThreadStream.close()` auto-revokes as a safety net.\n */\n readonly objectURL: Promise<string>;\n\n /** Live view of accumulated bytes. Settles with final bytes on finish. */\n readonly partialBytes: Uint8Array;\n\n /** Set iff the handle settled in an error state. */\n readonly error?: MediaAssemblyError;\n\n /** Diagnostic: `false` if block indices arrived out of order. */\n readonly monotonic: boolean;\n\n /**\n * Revoke the currently-cached object URL (if any). Subsequent\n * accesses to {@link objectURL} create a fresh URL from the Blob.\n * Idempotent.\n */\n revoke(): void;\n}\n\nexport interface AudioMedia extends MediaBase {\n readonly type: \"audio\";\n /**\n * Concatenated transcript across every audio block in this message.\n * Resolves on `message-finish` with the joined string, or `undefined`\n * when no block carried a transcript.\n */\n readonly transcript: Promise<string | undefined>;\n}\n\nexport interface ImageMedia extends MediaBase {\n readonly type: \"image\";\n /** Pixel width, if the provider sent it on the originating block. */\n readonly width?: number;\n /** Pixel height, if the provider sent it on the originating block. */\n readonly height?: number;\n}\n\nexport interface VideoMedia extends MediaBase {\n readonly type: \"video\";\n}\n\nexport interface FileMedia extends MediaBase {\n readonly type: \"file\";\n /** File name hint, if the provider sent it on the originating block. */\n readonly filename?: string;\n}\n\nexport type AnyMediaHandle = AudioMedia | ImageMedia | VideoMedia | FileMedia;\n\nfunction base64ToBytes(b64: string): Uint8Array {\n // atob is standard in all modern browsers and Node >= 16. Works on\n // standard base64 only (no `-`/`_` URL-safe variants; the protocol\n // uses standard base64).\n const binary = atob(b64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nfunction concatBytes(parts: Uint8Array[], totalLength: number): Uint8Array {\n const out = new Uint8Array(totalLength);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.byteLength;\n }\n return out;\n}\n\n/**\n * Concrete handle implementation shared by all four media types.\n *\n * One instance per `(messageId, blockType)` pair created by the\n * assembler on first matching `content-block-start`.\n */\nclass MediaHandleImpl {\n readonly type: MediaBlockType;\n readonly messageId: string;\n readonly namespace: string[];\n readonly node: string | undefined;\n readonly id: string | undefined;\n\n mimeType: string | undefined;\n url: string | undefined;\n width?: number;\n height?: number;\n filename?: string;\n\n monotonic = true;\n error: MediaAssemblyError | undefined;\n\n // Byte accumulation --------------------------------------------------\n\n readonly #parts: Uint8Array[] = [];\n #totalBytes = 0;\n #partialSnapshot: Uint8Array = new Uint8Array(0);\n\n // Stream (lazy, idempotent getter) ----------------------------------\n //\n // First access creates the `ReadableStream` and wires it to future\n // chunks; subsequent accesses return the SAME reference. Consumers\n // that want to fan-out must call `.tee()` explicitly — the native\n // `ReadableStream.locked` flag prevents two concurrent readers.\n\n #stream: ReadableStream<Uint8Array> | undefined;\n #streamController: ReadableStreamDefaultController<Uint8Array> | undefined;\n\n // Blob / URL settlement ---------------------------------------------\n\n #blobResolve!: (value: Blob) => void;\n #blobReject!: (reason: unknown) => void;\n readonly #blobPromise: Promise<Blob>;\n\n #transcriptParts: string[] = [];\n #transcriptResolve!: (value: string | undefined) => void;\n #transcriptReject!: (reason: unknown) => void;\n readonly #transcriptPromise: Promise<string | undefined>;\n\n #cachedObjectURL: string | undefined;\n\n // URL-sourced mode ---------------------------------------------------\n\n #urlSourced = false;\n #urlFetchPromise: Promise<Uint8Array> | undefined;\n\n // Index tracking -----------------------------------------------------\n\n #lastIndex = -1;\n\n // Lifecycle flags ----------------------------------------------------\n\n #finished = false;\n #settled = false;\n\n // Injected dependencies ---------------------------------------------\n\n readonly #fetchImpl: typeof fetch;\n\n constructor(options: {\n type: MediaBlockType;\n messageId: string;\n namespace: string[];\n node: string | undefined;\n id: string | undefined;\n mimeType: string | undefined;\n url: string | undefined;\n fetch: typeof fetch;\n }) {\n this.type = options.type;\n this.messageId = options.messageId;\n this.namespace = options.namespace;\n this.node = options.node;\n this.id = options.id;\n this.mimeType = options.mimeType;\n this.url = options.url;\n this.#fetchImpl = options.fetch;\n\n this.#blobPromise = new Promise<Blob>((resolve, reject) => {\n this.#blobResolve = resolve;\n this.#blobReject = reject;\n });\n // Swallow rejections here so the async promise doesn't surface as\n // an unhandled rejection when callers never touch `.blob`. Any\n // consumer that does await it still observes the original reason.\n this.#blobPromise.catch(() => undefined);\n\n this.#transcriptPromise = new Promise<string | undefined>(\n (resolve, reject) => {\n this.#transcriptResolve = resolve;\n this.#transcriptReject = reject;\n }\n );\n this.#transcriptPromise.catch(() => undefined);\n }\n\n // ---------- Input side (driven by the assembler) ----------\n\n /** Track a block index for the monotonic-ordering diagnostic. */\n observeIndex(index: number): void {\n if (index !== this.#lastIndex + 1 && index !== this.#lastIndex) {\n this.monotonic = false;\n }\n if (index > this.#lastIndex) this.#lastIndex = index;\n }\n\n /** Absorb `mime_type` / per-type extras carried on an incoming block. */\n absorbBlock(block: ContentBlock): void {\n if (this.#urlSourced) return;\n // `Extensible` widens `block.type` from a literal to `string` in\n // the union, defeating TS's discriminated-union narrowing. Cast\n // after the runtime tag check.\n if (block.type === \"audio\") this.#absorbAudio(block as AudioContentBlock);\n else if (block.type === \"image\")\n this.#absorbImage(block as ImageContentBlock);\n else if (block.type === \"video\")\n this.#absorbVideo(block as VideoContentBlock);\n else if (block.type === \"file\") this.#absorbFile(block as FileContentBlock);\n }\n\n /** Record that the originating block arrived with `url` not `data`. */\n enterUrlMode(url: string): void {\n this.#urlSourced = true;\n this.url = url;\n }\n\n /** Push a fresh chunk of bytes into the handle. */\n pushBytes(bytes: Uint8Array): void {\n if (this.#finished || this.#settled) return;\n if (bytes.byteLength === 0) return;\n this.#parts.push(bytes);\n this.#totalBytes += bytes.byteLength;\n this.#partialSnapshot = concatBytes(this.#parts, this.#totalBytes);\n if (this.#streamController != null) {\n try {\n this.#streamController.enqueue(bytes);\n } catch {\n // Reader detached; ignore — `partialBytes`/`blob` still work.\n }\n }\n }\n\n /** Append a transcript fragment from an audio block. */\n pushTranscript(fragment: string): void {\n if (this.type !== \"audio\") return;\n if (this.#finished || this.#settled) return;\n if (fragment.length === 0) return;\n this.#transcriptParts.push(fragment);\n }\n\n /** Called on `message-finish`. Settles blob/transcript/stream. */\n finish(): void {\n if (this.#finished || this.#settled) return;\n this.#finished = true;\n this.#settled = true;\n // Cast: the lib types reject `Uint8Array<ArrayBufferLike>` because\n // `BlobPart` requires `Uint8Array<ArrayBuffer>` specifically. Our\n // accumulator is allocated with `new Uint8Array(len)`, so it is\n // backed by an `ArrayBuffer` (not `SharedArrayBuffer`).\n const blob = new Blob([this.#partialSnapshot as unknown as BlobPart], {\n type: this.mimeType ?? \"\",\n });\n this.#blobResolve(blob);\n this.#transcriptResolve(\n this.#transcriptParts.length === 0\n ? undefined\n : this.#transcriptParts.join(\"\")\n );\n try {\n this.#streamController?.close();\n } catch {\n // Reader may have detached already.\n }\n }\n\n /** Propagate an error through blob/transcript/stream. */\n fail(\n kind: MediaAssemblyErrorKind,\n reason?: string,\n cause?: unknown\n ): MediaAssemblyError {\n if (this.#settled) {\n return (\n this.error ??\n new MediaAssemblyError(\n kind,\n this.messageId,\n this.#partialSnapshot,\n reason,\n { cause }\n )\n );\n }\n this.#settled = true;\n const err = new MediaAssemblyError(\n kind,\n this.messageId,\n this.#partialSnapshot,\n reason,\n { cause }\n );\n this.error = err;\n this.#blobReject(err);\n this.#transcriptReject(err);\n try {\n this.#streamController?.error(err);\n } catch {\n // Reader may have detached already.\n }\n return err;\n }\n\n // ---------- Public surface read by consumers ----------\n\n get partialBytes(): Uint8Array {\n return this.#partialSnapshot;\n }\n\n get blob(): Promise<Blob> {\n if (this.#urlSourced)\n return this.#fetchUrlSourced().then(\n (bytes) =>\n new Blob([bytes as unknown as BlobPart], {\n type: this.mimeType ?? \"\",\n })\n );\n return this.#blobPromise;\n }\n\n get transcript(): Promise<string | undefined> {\n return this.#transcriptPromise;\n }\n\n get objectURL(): Promise<string> {\n if (this.#cachedObjectURL != null) {\n const cached = this.#cachedObjectURL;\n return Promise.resolve(cached);\n }\n return this.blob.then((blob) => {\n // Race-safe: if revoke() fired before we got here, we cache the\n // freshly-created URL for the next reader.\n if (this.#cachedObjectURL != null) return this.#cachedObjectURL;\n const url = URL.createObjectURL(blob);\n this.#cachedObjectURL = url;\n return url;\n });\n }\n\n revoke(): void {\n const url = this.#cachedObjectURL;\n if (url == null) return;\n this.#cachedObjectURL = undefined;\n try {\n URL.revokeObjectURL(url);\n } catch {\n // URL.revokeObjectURL is side-effect-only; very unlikely to throw\n // but keep the handle usable if a polyfill misbehaves.\n }\n }\n\n get stream(): ReadableStream<Uint8Array> {\n if (this.#stream != null) return this.#stream;\n if (this.#urlSourced) return this.#buildUrlStream();\n return this.#buildInlineStream();\n }\n\n // ---------- Internals ----------\n\n #absorbAudio(block: AudioContentBlock): void {\n const mimeType =\n block.mime_type ?? (block as { mimeType?: string }).mimeType;\n if (this.mimeType == null && mimeType != null) this.mimeType = mimeType;\n if (block.transcript != null && block.transcript.length > 0) {\n this.pushTranscript(block.transcript);\n }\n }\n\n #absorbImage(block: ImageContentBlock): void {\n const mimeType =\n block.mime_type ?? (block as { mimeType?: string }).mimeType;\n if (this.mimeType == null && mimeType != null) this.mimeType = mimeType;\n if (this.width == null && block.width != null) this.width = block.width;\n if (this.height == null && block.height != null) this.height = block.height;\n }\n\n #absorbVideo(block: VideoContentBlock): void {\n const mimeType =\n block.mime_type ?? (block as { mimeType?: string }).mimeType;\n if (this.mimeType == null && mimeType != null) this.mimeType = mimeType;\n }\n\n #absorbFile(block: FileContentBlock): void {\n const mimeType =\n block.mime_type ?? (block as { mimeType?: string }).mimeType;\n if (this.mimeType == null && mimeType != null) this.mimeType = mimeType;\n if (this.filename == null && block.filename != null)\n this.filename = block.filename;\n }\n\n #buildInlineStream(): ReadableStream<Uint8Array> {\n const seed = this.#partialSnapshot;\n const alreadyFinished = this.#finished;\n const alreadyErrored = this.error;\n\n this.#stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n this.#streamController = controller;\n if (seed.byteLength > 0) controller.enqueue(seed);\n if (alreadyErrored != null) {\n controller.error(alreadyErrored);\n return;\n }\n if (alreadyFinished) {\n controller.close();\n }\n },\n cancel: () => {\n this.#streamController = undefined;\n },\n });\n return this.#stream;\n }\n\n #buildUrlStream(): ReadableStream<Uint8Array> {\n // For URL-sourced blocks, we pipe the fetch response body directly\n // through. `partialBytes` / `blob` share the same buffered bytes via\n // #fetchUrlSourced().\n const urlSourceFetch = this.#startUrlFetch();\n this.#stream = new ReadableStream<Uint8Array>({\n start: async (controller) => {\n try {\n const response = await urlSourceFetch;\n if (response.body == null) {\n const bytes = new Uint8Array(await response.arrayBuffer());\n if (bytes.byteLength > 0) controller.enqueue(bytes);\n controller.close();\n return;\n }\n const reader = response.body.getReader();\n // oxlint-disable-next-line no-constant-condition\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value != null) controller.enqueue(value);\n }\n controller.close();\n } catch (err) {\n controller.error(\n this.fail(\"fetch-failed\", (err as Error)?.message, err)\n );\n }\n },\n cancel: () => {\n this.#streamController = undefined;\n },\n });\n return this.#stream;\n }\n\n /** Memoised fetch for URL-sourced blocks — returns one `Response`. */\n #startUrlFetch(): Promise<Response> {\n const url = this.url!;\n return this.#fetchImpl(url).then((response) => {\n if (!response.ok) {\n throw new Error(\n `fetch(${url}) failed: ${response.status} ${response.statusText}`\n );\n }\n return response;\n });\n }\n\n /** Fetch + buffer for URL-sourced `blob` access. Memoised. */\n #fetchUrlSourced(): Promise<Uint8Array> {\n if (this.#urlFetchPromise != null) return this.#urlFetchPromise;\n this.#urlFetchPromise = (async () => {\n try {\n const response = await this.#startUrlFetch();\n const bytes = new Uint8Array(await response.arrayBuffer());\n this.#parts.length = 0;\n this.#parts.push(bytes);\n this.#totalBytes = bytes.byteLength;\n this.#partialSnapshot = bytes;\n this.#finished = true;\n this.#settled = true;\n return bytes;\n } catch (err) {\n throw this.fail(\"fetch-failed\", (err as Error)?.message, err);\n }\n })();\n return this.#urlFetchPromise;\n }\n}\n\n/**\n * Dispatch callbacks receive a freshly-started handle on its first\n * matching `content-block-start`. Exactly one callback fires per\n * `(messageId, blockType)` pair.\n */\nexport interface MediaAssemblerCallbacks {\n onAudio?: (media: AudioMedia) => void;\n onImage?: (media: ImageMedia) => void;\n onVideo?: (media: VideoMedia) => void;\n onFile?: (media: FileMedia) => void;\n /**\n * Invoked for every type; receives the same handle as the typed\n * callback. Useful for writing one \"all media\" consumer without\n * registering four type-specific callbacks.\n */\n onMedia?: (media: AnyMediaHandle) => void;\n}\n\nexport interface MediaAssemblerOptions extends MediaAssemblerCallbacks {\n /**\n * Injected `fetch` used for URL-sourced blocks. Defaults to the\n * global `fetch`. Throws on first URL access if neither is present.\n */\n fetch?: typeof fetch;\n}\n\n/**\n * Incrementally folds `messages` events into typed media handles\n * (audio / image / video / file) that buffer bytes, expose a\n * {@link Blob}, and lazily mint an object URL for direct use in\n * `<audio>` / `<img>` / `<video>` tags.\n *\n * Semantics pinned during design:\n * - One handle per `(messageId, blockType)` pair. Mixed-type messages\n * surface on multiple iterables with the same `messageId`.\n * - Handle yielded on the **first matching `content-block-start`** —\n * messages with no matching blocks never yield an item (filters\n * `lc_run--` LangChain terminator noise automatically).\n * - Byte extraction: every `content-block-start` /\n * `content-block-delta` with a `data` field decodes base64 and\n * pushes into the accumulator. `content-block-finish` is a sync\n * point.\n * - URL-sourced blocks: if the initial block carries `url`, the\n * handle enters URL mode — `blob`/`stream`/`objectURL` lazy-fetch\n * on first access, subsequent data/url deltas are ignored.\n * - Fail loud: `message-error`, stream closure before finish, and\n * fetch failures all populate `handle.error`, reject `blob` /\n * `transcript` / `objectURL`, and error the stream.\n * - Diagnostic only: `handle.monotonic` flips false if block indices\n * arrive out of order (protocol guarantees in-order within a\n * subscription, so `false` indicates an upstream bug).\n */\n/**\n * Per-`(namespace, node)` bookkeeping for the active message the\n * assembler is currently folding.\n *\n * Protocol v2 only carries `id` on `message-start`; all\n * subsequent `content-block-*` / `message-finish` / `error` events\n * address the active message by position (same namespace + node). The\n * assembler therefore pins a message entry when `message-start`\n * arrives and resolves follow-ups by looking up `(namespace, node)`.\n */\ninterface ActiveMessage {\n /** `id` from `message-start`, or empty string when synthesized. */\n messageId: string;\n /** `${messageId}::${mediaType}::${index}` keys currently active under this message. */\n keys: Set<string>;\n /** Content-block index → active media key. */\n indexKeys: Map<number, string>;\n}\n\nexport class MediaAssembler {\n readonly #callbacks: MediaAssemblerCallbacks;\n readonly #fetch: typeof fetch;\n // Keyed by `${messageId}::${type}::${blockIndex}`.\n readonly #active = new Map<string, MediaHandleImpl>();\n // Maps `${namespace.join(\"/\")}::${node ?? \"\"}` → active message. One\n // entry per in-flight logical message; replaced on each\n // `message-start`, removed on `message-finish` / `error`.\n readonly #activeByNamespaceNode = new Map<string, ActiveMessage>();\n // Monotonically increasing counter used to mint stable synthetic\n // ids when a `content-block-*` arrives before any `message-start`\n // (e.g. late-attaching subscribers that missed the message-start).\n #syntheticCounter = 0;\n\n constructor(options: MediaAssemblerOptions = {}) {\n this.#callbacks = options;\n if (options.fetch != null) {\n this.#fetch = options.fetch;\n } else if (typeof fetch === \"function\") {\n this.#fetch = fetch;\n } else {\n // Throw lazily on URL access rather than construction, so\n // inline-only flows in Node without a global fetch still work.\n this.#fetch = () => {\n throw new Error(\n \"MediaAssembler: no fetch implementation available. Pass `fetch` in options.\"\n );\n };\n }\n }\n\n /**\n * Fold a single `messages` event. Non-media blocks and\n * informational events (e.g. `content-block-finish`) are no-ops.\n */\n consume(event: MessagesEvent): void {\n const data = event.params.data;\n const namespace = event.params.namespace;\n const node = event.params.node;\n const nsNodeKey = `${namespace.join(\"/\")}::${node ?? \"\"}`;\n\n if (data.event === \"message-start\") {\n // A new `message-start` may arrive before a prior message on\n // the same `(namespace, node)` emitted `message-finish` (the\n // protocol permits trimmed replays on late subscribers). Flush\n // any in-flight handles under that slot before rebinding.\n this.#flushSlot(nsNodeKey, \"finish\");\n this.#activeByNamespaceNode.set(nsNodeKey, {\n messageId: data.id ?? \"\",\n keys: new Set(),\n indexKeys: new Map(),\n });\n return;\n }\n\n if (data.event === \"message-finish\") {\n this.#flushSlot(nsNodeKey, \"finish\");\n this.#activeByNamespaceNode.delete(nsNodeKey);\n return;\n }\n\n if (data.event === \"error\") {\n this.#flushSlot(\n nsNodeKey,\n \"error\",\n (data as { message?: string }).message\n );\n this.#activeByNamespaceNode.delete(nsNodeKey);\n return;\n }\n\n if (\n data.event !== \"content-block-start\" &&\n data.event !== \"content-block-delta\" &&\n data.event !== \"content-block-finish\"\n ) {\n return;\n }\n\n const block = (data as { content?: ContentBlock; index?: number }).content;\n const blockIndex = (data as { index?: number }).index ?? 0;\n\n // Resolve the active message for this `(namespace, node)`. If the\n // subscriber attached mid-message and missed `message-start`,\n // synthesize one so downstream blocks still produce a handle.\n let active = this.#activeByNamespaceNode.get(nsNodeKey);\n if (active == null) {\n active = {\n messageId: `__synthetic_${++this.#syntheticCounter}`,\n keys: new Set(),\n indexKeys: new Map(),\n };\n this.#activeByNamespaceNode.set(nsNodeKey, active);\n }\n\n if (block == null && data.event === \"content-block-delta\") {\n const delta = (data as { delta?: unknown }).delta;\n const deltaKey = active.indexKeys.get(blockIndex);\n const deltaHandle =\n deltaKey != null ? this.#active.get(deltaKey) : undefined;\n if (delta == null || typeof delta !== \"object\") {\n return;\n }\n\n const record = delta as Record<string, unknown>;\n\n if (deltaHandle == null) {\n if (\n record.type !== \"block-delta\" ||\n record.fields == null ||\n typeof record.fields !== \"object\"\n ) {\n return;\n }\n\n const fields = record.fields as ContentBlock;\n if (!MEDIA_BLOCK_TYPES.has(fields.type)) {\n return;\n }\n\n this.#consumeMediaBlock({\n active,\n block: fields,\n blockIndex,\n dataEvent: data.event,\n namespace,\n node,\n terminal: false,\n createIfMissing: true,\n });\n return;\n }\n\n deltaHandle.observeIndex(blockIndex);\n if (record.type === \"data-delta\" && typeof record.data === \"string\") {\n try {\n deltaHandle.pushBytes(base64ToBytes(record.data));\n } catch (err) {\n deltaHandle.fail(\"message-error\", \"invalid base64 on delta\", err);\n }\n return;\n }\n\n if (\n record.type === \"block-delta\" &&\n record.fields != null &&\n typeof record.fields === \"object\"\n ) {\n const fields = record.fields as\n | AudioContentBlock\n | ImageContentBlock\n | VideoContentBlock\n | FileContentBlock;\n deltaHandle.absorbBlock(fields);\n if (!deltaHandle.error && fields.data != null) {\n try {\n deltaHandle.pushBytes(base64ToBytes(fields.data));\n } catch (err) {\n deltaHandle.fail(\"message-error\", \"invalid base64 on delta\", err);\n }\n }\n }\n return;\n }\n\n if (block == null) return;\n const blockType = block.type;\n if (!MEDIA_BLOCK_TYPES.has(blockType)) return;\n\n this.#consumeMediaBlock({\n active,\n block: block as ContentBlock,\n blockIndex,\n dataEvent: data.event,\n namespace,\n node,\n terminal: data.event === \"content-block-finish\",\n createIfMissing:\n data.event === \"content-block-start\" ||\n data.event === \"content-block-finish\",\n });\n }\n\n #consumeMediaBlock({\n active,\n block,\n blockIndex,\n dataEvent,\n namespace,\n node,\n terminal,\n createIfMissing,\n }: {\n active: ActiveMessage;\n block: ContentBlock;\n blockIndex: number;\n dataEvent: string;\n namespace: string[];\n node: string | undefined;\n terminal: boolean;\n createIfMissing: boolean;\n }): void {\n const blockType = block.type;\n if (!MEDIA_BLOCK_TYPES.has(blockType)) return;\n\n const mediaType = blockType as MediaBlockType;\n const key = `${active.messageId}::${mediaType}::${blockIndex}`;\n let handle = this.#active.get(key);\n const isStart = dataEvent === \"content-block-start\";\n\n if (handle == null) {\n const isTerminalBlock = terminal;\n if (!isStart && !isTerminalBlock && !createIfMissing) {\n return;\n }\n const mediaBlock = block as\n | AudioContentBlock\n | ImageContentBlock\n | VideoContentBlock\n | FileContentBlock;\n handle = new MediaHandleImpl({\n type: mediaType,\n messageId: active.messageId,\n namespace: [...namespace],\n node,\n id: (mediaBlock as { id?: string }).id,\n mimeType:\n mediaBlock.mime_type ??\n (mediaBlock as { mimeType?: string }).mimeType,\n url:\n mediaBlock.url != null && mediaBlock.data == null\n ? mediaBlock.url\n : undefined,\n fetch: this.#fetch,\n });\n if (mediaBlock.url != null && mediaBlock.data == null) {\n handle.enterUrlMode(mediaBlock.url);\n }\n handle.observeIndex(blockIndex);\n handle.absorbBlock(block);\n if (mediaBlock.data != null) {\n try {\n handle.pushBytes(base64ToBytes(mediaBlock.data));\n } catch (err) {\n handle.fail(\"message-error\", \"invalid base64 on initial block\", err);\n }\n }\n this.#active.set(key, handle);\n active.keys.add(key);\n active.indexKeys.set(blockIndex, key);\n this.#emit(handle);\n if (isTerminalBlock) {\n handle.finish();\n this.#active.delete(key);\n active.keys.delete(key);\n active.indexKeys.delete(blockIndex);\n }\n return;\n }\n\n // Subsequent event on an existing handle.\n // - `content-block-finish` is a sync point: skip to avoid\n // double-counting bytes when the server echoes the full block\n // on both start and finish (typical for atomic, non-delta\n // blocks such as whole audio frames or single-shot images).\n // A late subscriber may miss `content-block-start`; in that case a\n // terminal block with full content is handled above by creating and\n // immediately settling a one-block handle.\n if (terminal) return;\n\n const mediaBlock = block as\n | AudioContentBlock\n | ImageContentBlock\n | VideoContentBlock\n | FileContentBlock;\n handle.observeIndex(blockIndex);\n handle.absorbBlock(block);\n if (!handle.error && mediaBlock.data != null) {\n try {\n handle.pushBytes(base64ToBytes(mediaBlock.data));\n } catch (err) {\n handle.fail(\"message-error\", \"invalid base64 on delta\", err);\n }\n }\n }\n\n /**\n * Finish or fail every media handle currently active under the\n * given `(namespace, node)` slot and clear its bookkeeping. Called\n * on `message-finish`, `error`, and when a new `message-start`\n * rebinds a still-open slot.\n */\n #flushSlot(\n nsNodeKey: string,\n mode: \"finish\" | \"error\",\n errorMessage?: string\n ): void {\n const active = this.#activeByNamespaceNode.get(nsNodeKey);\n if (active == null) return;\n for (const key of active.keys) {\n const handle = this.#active.get(key);\n if (handle != null) {\n if (mode === \"finish\") handle.finish();\n else handle.fail(\"message-error\", errorMessage);\n }\n this.#active.delete(key);\n }\n active.keys.clear();\n active.indexKeys.clear();\n }\n\n /**\n * Abort all outstanding handles with a `stream-closed` error.\n * Called when the upstream event source terminates before the\n * messages it was assembling had a chance to finish.\n */\n close(): void {\n for (const handle of this.#active.values()) {\n handle.fail(\"stream-closed\", \"upstream event stream closed\");\n }\n this.#active.clear();\n this.#activeByNamespaceNode.clear();\n }\n\n #emit(handle: MediaHandleImpl): void {\n switch (handle.type) {\n case \"audio\":\n this.#callbacks.onAudio?.(handle as unknown as AudioMedia);\n break;\n case \"image\":\n this.#callbacks.onImage?.(handle as unknown as ImageMedia);\n break;\n case \"video\":\n this.#callbacks.onVideo?.(handle as unknown as VideoMedia);\n break;\n case \"file\":\n this.#callbacks.onFile?.(handle as unknown as FileMedia);\n break;\n }\n this.#callbacks.onMedia?.(handle as unknown as AnyMediaHandle);\n }\n}\n"],"mappings":"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"}