@langchain/react 0.3.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +48 -523
  2. package/dist/context.cjs +12 -30
  3. package/dist/context.cjs.map +1 -1
  4. package/dist/context.d.cts +22 -39
  5. package/dist/context.d.cts.map +1 -1
  6. package/dist/context.d.ts +22 -39
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/context.js +11 -29
  9. package/dist/context.js.map +1 -1
  10. package/dist/index.cjs +29 -30
  11. package/dist/index.d.cts +10 -7
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +10 -7
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +10 -6
  16. package/dist/selectors.cjs +178 -0
  17. package/dist/selectors.cjs.map +1 -0
  18. package/dist/selectors.d.cts +183 -0
  19. package/dist/selectors.d.cts.map +1 -0
  20. package/dist/selectors.d.ts +183 -0
  21. package/dist/selectors.d.ts.map +1 -0
  22. package/dist/selectors.js +168 -0
  23. package/dist/selectors.js.map +1 -0
  24. package/dist/suspense-stream.cjs +34 -159
  25. package/dist/suspense-stream.cjs.map +1 -1
  26. package/dist/suspense-stream.d.cts +15 -71
  27. package/dist/suspense-stream.d.cts.map +1 -1
  28. package/dist/suspense-stream.d.ts +15 -71
  29. package/dist/suspense-stream.d.ts.map +1 -1
  30. package/dist/suspense-stream.js +35 -158
  31. package/dist/suspense-stream.js.map +1 -1
  32. package/dist/use-audio-player.cjs +679 -0
  33. package/dist/use-audio-player.cjs.map +1 -0
  34. package/dist/use-audio-player.d.cts +161 -0
  35. package/dist/use-audio-player.d.cts.map +1 -0
  36. package/dist/use-audio-player.d.ts +161 -0
  37. package/dist/use-audio-player.d.ts.map +1 -0
  38. package/dist/use-audio-player.js +679 -0
  39. package/dist/use-audio-player.js.map +1 -0
  40. package/dist/use-media-url.cjs +49 -0
  41. package/dist/use-media-url.cjs.map +1 -0
  42. package/dist/use-media-url.d.cts +28 -0
  43. package/dist/use-media-url.d.cts.map +1 -0
  44. package/dist/use-media-url.d.ts +28 -0
  45. package/dist/use-media-url.d.ts.map +1 -0
  46. package/dist/use-media-url.js +49 -0
  47. package/dist/use-media-url.js.map +1 -0
  48. package/dist/use-projection.cjs +41 -0
  49. package/dist/use-projection.cjs.map +1 -0
  50. package/dist/use-projection.d.cts +27 -0
  51. package/dist/use-projection.d.cts.map +1 -0
  52. package/dist/use-projection.d.ts +27 -0
  53. package/dist/use-projection.d.ts.map +1 -0
  54. package/dist/use-projection.js +41 -0
  55. package/dist/use-projection.js.map +1 -0
  56. package/dist/use-stream.cjs +185 -0
  57. package/dist/use-stream.cjs.map +1 -0
  58. package/dist/use-stream.d.cts +184 -0
  59. package/dist/use-stream.d.cts.map +1 -0
  60. package/dist/use-stream.d.ts +184 -0
  61. package/dist/use-stream.d.ts.map +1 -0
  62. package/dist/use-stream.js +183 -0
  63. package/dist/use-stream.js.map +1 -0
  64. package/dist/use-video-player.cjs +218 -0
  65. package/dist/use-video-player.cjs.map +1 -0
  66. package/dist/use-video-player.d.cts +65 -0
  67. package/dist/use-video-player.d.cts.map +1 -0
  68. package/dist/use-video-player.d.ts +65 -0
  69. package/dist/use-video-player.d.ts.map +1 -0
  70. package/dist/use-video-player.js +218 -0
  71. package/dist/use-video-player.js.map +1 -0
  72. package/package.json +9 -8
  73. package/dist/stream.cjs +0 -18
  74. package/dist/stream.cjs.map +0 -1
  75. package/dist/stream.custom.cjs +0 -209
  76. package/dist/stream.custom.cjs.map +0 -1
  77. package/dist/stream.custom.d.cts +0 -3
  78. package/dist/stream.custom.d.ts +0 -3
  79. package/dist/stream.custom.js +0 -209
  80. package/dist/stream.custom.js.map +0 -1
  81. package/dist/stream.d.cts +0 -174
  82. package/dist/stream.d.cts.map +0 -1
  83. package/dist/stream.d.ts +0 -174
  84. package/dist/stream.d.ts.map +0 -1
  85. package/dist/stream.js +0 -18
  86. package/dist/stream.js.map +0 -1
  87. package/dist/stream.lgp.cjs +0 -671
  88. package/dist/stream.lgp.cjs.map +0 -1
  89. package/dist/stream.lgp.js +0 -671
  90. package/dist/stream.lgp.js.map +0 -1
  91. package/dist/thread.cjs +0 -18
  92. package/dist/thread.cjs.map +0 -1
  93. package/dist/thread.js +0 -18
  94. package/dist/thread.js.map +0 -1
  95. package/dist/types.d.cts +0 -109
  96. package/dist/types.d.cts.map +0 -1
  97. package/dist/types.d.ts +0 -109
  98. package/dist/types.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-audio-player.js","names":[],"sources":["../src/use-audio-player.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { AudioMedia, MediaBase } from \"@langchain/langgraph-sdk/stream\";\n\n/**\n * Lifecycle state of an audio or video player returned by\n * {@link useAudioPlayer} and {@link useVideoPlayer}.\n *\n * Single-enum design (vs. multiple booleans) eliminates impossible\n * states and gives callers a clean `switch` target.\n */\nexport type PlayerStatus =\n | \"idle\"\n | \"buffering\"\n | \"playing\"\n | \"paused\"\n | \"finished\"\n | \"error\";\n\n/**\n * Options for {@link useAudioPlayer}.\n *\n * All fields optional; defaults cover OpenAI `gpt-4o-audio-preview`\n * pcm16 streams (`sampleRate: 24000`, `channels: 1`) and any WAV\n * stream the upstream model emits.\n */\nexport interface UseAudioPlayerOptions {\n /**\n * Begin playback as soon as the first byte arrives (PCM strategy)\n * or the blob settles (`element` strategy). Subject to browser\n * autoplay policies — on sites without prior user gesture, the\n * underlying `play()` may be rejected and the hook transitions to\n * `\"error\"` with a descriptive message.\n */\n autoPlay?: boolean;\n\n /**\n * Overrides for the PCM strategy. Ignored by the `element` strategy\n * and by WAV streams (the RIFF `fmt ` chunk is authoritative there).\n */\n pcm?: {\n /** Sample rate in Hz. Defaults to `24000`. */\n sampleRate?: number;\n /** Channel count. Defaults to `1` (mono). */\n channels?: number;\n };\n\n /**\n * Additional mime prefixes that should be treated as raw PCM16\n * (in addition to `audio/pcm` / `audio/L16`). Use when upstream\n * reports a custom mime like `audio/pcm16`.\n */\n pcmMimePrefixes?: readonly string[];\n\n /**\n * Force a specific playback strategy. Default `\"auto\"` picks `\"pcm\"`\n * for PCM / L16 / WAV mime types and `\"element\"` for everything else.\n */\n strategy?: \"auto\" | \"pcm\" | \"element\";\n}\n\n/**\n * Player controls + live state returned by {@link useAudioPlayer}.\n *\n * Shape is shared with {@link useVideoPlayer} where possible — learn\n * one surface, use it for both.\n */\nexport interface AudioPlayerHandle {\n /** Current lifecycle state. See {@link PlayerStatus}. */\n status: PlayerStatus;\n\n /**\n * Which implementation is active. `\"pcm\"` scheduling through\n * `AudioContext` starts within one chunk of the first byte; `\"element\"`\n * waits for `message-finish` before a hidden `HTMLAudioElement` takes\n * over.\n */\n strategy: \"pcm\" | \"element\";\n\n /** Start (or resume) playback. No-op while `status === \"error\"`. */\n play(): void;\n /** Pause without discarding buffered samples / element position. */\n pause(): void;\n /**\n * Hard stop: tears down the `AudioContext` (PCM) or detaches the\n * element (`element`) and drops any scheduled work.\n */\n stop(): void;\n /** Sugar for `status === \"playing\" ? pause() : play()`. */\n toggle(): void;\n /**\n * Reset back to `\"idle\"` and drop any transient error. The next\n * `play()` starts fresh from the current position.\n */\n reset(): void;\n /**\n * Resolve on the next terminal transition (`finished` | `paused` |\n * `idle`). Reject on transitions to `\"error\"`. Calling `stop()` or\n * `reset()` resolves the pending promise immediately. Calling\n * `playToEnd` also triggers `play()` if currently paused/idle.\n */\n playToEnd(): Promise<void>;\n\n /**\n * Seconds of audio consumed since the current `play()` call. Resets\n * on `reset()` and on media changes.\n */\n currentTime: number;\n\n /**\n * Total duration in seconds, when knowable. The `element` strategy\n * exposes this once `loadedmetadata` fires; the `pcm` strategy leaves\n * it `undefined` (PCM duration isn't known until `message-finish`).\n */\n duration?: number;\n\n /**\n * Seek to an absolute timestamp in seconds. Only defined on the\n * `element` strategy; `undefined` on `pcm` (random-access seeking of\n * a live scheduled buffer is not supported).\n */\n seek?(seconds: number): void;\n\n /**\n * RMS level of the last analysed frame, normalised to `[0, 1]`.\n * Drop-in for a VU meter. `0` when no analyser frame has been read\n * yet, when paused, or before `play()`.\n */\n level: number;\n\n /**\n * Current 256-bin frequency-domain snapshot from the internal\n * {@link AnalyserNode}. Returns `undefined` before the graph is\n * connected or on environments without Web Audio. Safe to poll\n * inside `requestAnimationFrame`.\n */\n getFrequencyData(): Uint8Array | undefined;\n\n /**\n * Current 256-sample waveform snapshot (byte time-domain) from the\n * internal {@link AnalyserNode}. Returns `undefined` before the graph\n * is connected or on environments without Web Audio.\n */\n getTimeDomainData(): Uint8Array | undefined;\n\n /** Last error raised by the stream reader, decoder, or element. */\n error: Error | undefined;\n}\n\nconst DEFAULT_SAMPLE_RATE = 24_000;\nconst DEFAULT_CHANNELS = 1;\nconst ANALYSER_FFT_SIZE = 512;\n\n/**\n * Per-stream audio format descriptor. Populated eagerly for raw PCM\n * streams and lazily (from the RIFF `fmt ` chunk) for WAV streams.\n * `scheduleChunk` and `ensureContext` both refuse to run until this\n * has been resolved.\n */\ninterface AudioFormat {\n readonly sampleRate: number;\n readonly channels: number;\n readonly bitsPerSample: number;\n}\n\n/**\n * Events fanned out from a shared pump controller to each hook\n * subscription.\n */\ntype PumpEvent =\n | { readonly type: \"chunk\"; readonly bytes: Uint8Array }\n | { readonly type: \"finished\" }\n | { readonly type: \"error\"; readonly error: Error };\n\ntype PumpListener = (event: PumpEvent) => void;\n\ninterface PumpController {\n readonly chunks: Uint8Array[];\n finished: boolean;\n error: Error | undefined;\n readonly listeners: Set<PumpListener>;\n}\n\n/**\n * Module-level registry of shared readers keyed by {@link MediaBase}\n * identity. We hold exactly one {@link ReadableStreamDefaultReader} per\n * media handle and fan the chunks out to every live hook subscriber.\n *\n * Keying on identity (WeakMap) gives us three properties for free:\n * - React StrictMode's simulated unmount/remount finds the same\n * controller on re-attach, so we never `getReader()` twice on the\n * same locked stream.\n * - New media instances get a fresh reader — no cross-talk.\n * - When callers drop their last reference to the media handle, the\n * WeakMap entry is reclaimed alongside it.\n */\nconst pumpRegistry = new WeakMap<MediaBase, PumpController>();\n\nfunction attachToPump(media: MediaBase, listener: PumpListener): () => void {\n let controller = pumpRegistry.get(media);\n if (controller == null) {\n const reader = media.stream.getReader();\n controller = {\n chunks: [],\n finished: false,\n error: undefined,\n listeners: new Set<PumpListener>(),\n };\n pumpRegistry.set(media, controller);\n const owned = controller;\n void (async () => {\n try {\n // oxlint-disable-next-line no-constant-condition\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n if (value == null || value.byteLength === 0) continue;\n owned.chunks.push(value);\n for (const l of owned.listeners) {\n try {\n l({ type: \"chunk\", bytes: value });\n } catch {\n // A misbehaving listener must not take down the pump.\n }\n }\n }\n owned.finished = true;\n for (const l of owned.listeners) {\n try {\n l({ type: \"finished\" });\n } catch {\n // Swallow — see above.\n }\n }\n } catch (err) {\n owned.error = err as Error;\n for (const l of owned.listeners) {\n try {\n l({ type: \"error\", error: err as Error });\n } catch {\n // Swallow — see above.\n }\n }\n } finally {\n try {\n reader.releaseLock();\n } catch {\n // best-effort\n }\n }\n })();\n }\n\n // Replay buffered state so late subscribers (StrictMode remount or\n // a downstream consumer that mounts after the stream has already\n // delivered bytes) catch up to the current position.\n for (const chunk of controller.chunks) {\n listener({ type: \"chunk\", bytes: chunk });\n }\n if (controller.finished) listener({ type: \"finished\" });\n if (controller.error != null) {\n listener({ type: \"error\", error: controller.error });\n }\n\n controller.listeners.add(listener);\n\n return () => {\n controller!.listeners.delete(listener);\n };\n}\n\ntype WavHeaderResult =\n | { readonly status: \"need-more\" }\n | { readonly status: \"invalid\"; readonly reason: string }\n | {\n readonly status: \"parsed\";\n readonly format: AudioFormat;\n /** Byte offset within the input where PCM samples begin. */\n readonly dataOffset: number;\n };\n\n/**\n * Parse the RIFF/WAVE header of a WAV stream. Only the `fmt ` and `data`\n * chunks are interpreted; other chunks are skipped. The parser\n * requires the complete `fmt ` chunk and the `data` chunk header before\n * returning `\"parsed\"`, so callers may need several retries while\n * buffering incoming bytes. WAV uses little-endian integers throughout.\n */\nfunction tryParseWavHeader(bytes: Uint8Array): WavHeaderResult {\n if (bytes.byteLength < 12) return { status: \"need-more\" };\n\n if (\n bytes[0] !== 0x52 ||\n bytes[1] !== 0x49 ||\n bytes[2] !== 0x46 ||\n bytes[3] !== 0x46 ||\n bytes[8] !== 0x57 ||\n bytes[9] !== 0x41 ||\n bytes[10] !== 0x56 ||\n bytes[11] !== 0x45\n ) {\n return { status: \"invalid\", reason: \"not a RIFF/WAVE stream\" };\n }\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let fmt: {\n audioFormat: number;\n sampleRate: number;\n channels: number;\n bitsPerSample: number;\n } | null = null;\n\n let offset = 12;\n while (offset + 8 <= bytes.byteLength) {\n const id = String.fromCharCode(\n bytes[offset]!,\n bytes[offset + 1]!,\n bytes[offset + 2]!,\n bytes[offset + 3]!\n );\n const size = view.getUint32(offset + 4, true);\n const payloadStart = offset + 8;\n\n if (id === \"fmt \") {\n if (payloadStart + 16 > bytes.byteLength) return { status: \"need-more\" };\n fmt = {\n audioFormat: view.getUint16(payloadStart, true),\n channels: view.getUint16(payloadStart + 2, true),\n sampleRate: view.getUint32(payloadStart + 4, true),\n bitsPerSample: view.getUint16(payloadStart + 14, true),\n };\n if (fmt.audioFormat !== 1) {\n return {\n status: \"invalid\",\n reason: `unsupported WAV audioFormat=${fmt.audioFormat} (expected 1, linear PCM)`,\n };\n }\n if (fmt.bitsPerSample !== 16) {\n return {\n status: \"invalid\",\n reason: `unsupported WAV bitsPerSample=${fmt.bitsPerSample} (expected 16)`,\n };\n }\n } else if (id === \"data\") {\n if (fmt == null) {\n return { status: \"invalid\", reason: \"data chunk preceded fmt chunk\" };\n }\n return {\n status: \"parsed\",\n format: {\n sampleRate: fmt.sampleRate,\n channels: fmt.channels,\n bitsPerSample: fmt.bitsPerSample,\n },\n dataOffset: payloadStart,\n };\n }\n\n // Chunks are word-aligned: an odd-sized payload carries one pad byte.\n offset = payloadStart + size + (size & 1);\n }\n\n return { status: \"need-more\" };\n}\n\nfunction concatChunks(chunks: readonly Uint8Array[]): Uint8Array {\n if (chunks.length === 1) return chunks[0]!;\n let total = 0;\n for (const c of chunks) total += c.byteLength;\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) {\n out.set(c, at);\n at += c.byteLength;\n }\n return out;\n}\n\nfunction resolveAudioContextCtor(): typeof AudioContext | undefined {\n if (typeof window === \"undefined\") return undefined;\n return (\n (window as unknown as { AudioContext?: typeof AudioContext })\n .AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext })\n .webkitAudioContext\n );\n}\n\n/**\n * Decide which playback strategy the hook should use for a handle.\n * PCM16 / L16 / WAV flow through the progressive Web Audio path; every\n * other mime drops to a hidden `HTMLAudioElement`.\n */\nfunction detectStrategy(\n mimeType: string | undefined,\n override: UseAudioPlayerOptions[\"strategy\"],\n pcmPrefixes: readonly string[] | undefined\n): \"pcm\" | \"element\" {\n if (override === \"pcm\" || override === \"element\") return override;\n const m = mimeType ?? \"\";\n const isPcm =\n m === \"audio/pcm\" ||\n m === \"audio/L16\" ||\n m.startsWith(\"audio/pcm;\") ||\n m.startsWith(\"audio/L16;\") ||\n m === \"audio/wav\" ||\n m === \"audio/wave\" ||\n m === \"audio/x-wav\" ||\n m === \"audio/vnd.wave\" ||\n (pcmPrefixes?.some((p) => m.startsWith(p)) ?? false);\n return isPcm ? \"pcm\" : \"element\";\n}\n\n/**\n * Progressive audio playback for {@link AudioMedia} handles with a\n * uniform surface across PCM (streamed) and container (`HTMLAudioElement`)\n * strategies.\n *\n * ### Behaviour\n *\n * - Strategy selection is derived from `media.mimeType` and may be\n * overridden via `options.strategy`. PCM / L16 / WAV all flow through\n * the progressive Web Audio path; every other mime uses a hidden\n * `HTMLAudioElement`.\n *\n * - **PCM strategy.** Chunks are decoded in real time and scheduled on\n * a single `AudioContext`; playback begins within one chunk of the\n * first byte. `seek` / `duration` are `undefined` because random\n * access on a live scheduled buffer is not supported.\n *\n * - **Element strategy.** `status` stays in `\"buffering\"` until\n * `message-finish` materialises a blob URL; the element then owns\n * playback. `seek` / `duration` are available.\n *\n * - Both strategies expose `level`, `getFrequencyData()`, and\n * `getTimeDomainData()` by tapping an {@link AnalyserNode} in the\n * audio graph.\n *\n * - React StrictMode's simulated unmount/remount is safe: the shared\n * reader and replay buffer mean a second attach sees the same bytes\n * that the first one did.\n *\n * @param media - Audio handle from `useAudio` etc.\n * @param options - Strategy overrides and PCM format hints.\n */\nexport function useAudioPlayer(\n media: AudioMedia | undefined,\n options?: UseAudioPlayerOptions\n): AudioPlayerHandle {\n const sampleRate = options?.pcm?.sampleRate ?? DEFAULT_SAMPLE_RATE;\n const channels = options?.pcm?.channels ?? DEFAULT_CHANNELS;\n const pcmPrefixes = options?.pcmMimePrefixes;\n const strategyOverride = options?.strategy ?? \"auto\";\n const autoPlay = options?.autoPlay ?? false;\n\n const strategy: \"pcm\" | \"element\" = media\n ? detectStrategy(media.mimeType, strategyOverride, pcmPrefixes)\n : \"element\";\n\n const [status, setStatus] = useState<PlayerStatus>(\"idle\");\n const [error, setError] = useState<Error | undefined>(undefined);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState<number | undefined>(undefined);\n const [level, setLevel] = useState(0);\n\n // ── Shared refs ─────────────────────────────────────────────────────\n const ctxRef = useRef<AudioContext | null>(null);\n const analyserRef = useRef<AnalyserNode | null>(null);\n const freqBufRef = useRef<Uint8Array<ArrayBuffer> | null>(null);\n const timeBufRef = useRef<Uint8Array<ArrayBuffer> | null>(null);\n const rafRef = useRef<number | null>(null);\n const playStartCtxTimeRef = useRef<number>(0);\n\n // ── PCM strategy refs ───────────────────────────────────────────────\n const nextStartTimeRef = useRef<number>(0);\n const shouldPlayRef = useRef<boolean>(false);\n const pendingChunksRef = useRef<Uint8Array[]>([]);\n const activeSourcesRef = useRef<Set<AudioBufferSourceNode>>(new Set());\n const formatRef = useRef<AudioFormat | null>(null);\n const upstreamFinishedRef = useRef(false);\n\n // ── Element strategy refs ───────────────────────────────────────────\n const audioElRef = useRef<HTMLAudioElement | null>(null);\n const elementSourceRef = useRef<MediaElementAudioSourceNode | null>(null);\n const pendingSrcRef = useRef<string | undefined>(undefined);\n\n const statusRef = useRef<PlayerStatus>(\"idle\");\n useEffect(() => {\n statusRef.current = status;\n }, [status]);\n\n // ── `playToEnd` bookkeeping ─────────────────────────────────────────\n const pendingResolveRef = useRef<(() => void) | null>(null);\n const pendingRejectRef = useRef<((err: Error) => void) | null>(null);\n\n const resolvePending = useCallback(() => {\n const resolve = pendingResolveRef.current;\n pendingResolveRef.current = null;\n pendingRejectRef.current = null;\n resolve?.();\n }, []);\n\n const rejectPending = useCallback((err: Error) => {\n const reject = pendingRejectRef.current;\n pendingResolveRef.current = null;\n pendingRejectRef.current = null;\n reject?.(err);\n }, []);\n\n // Fire pending resolvers whenever the status transitions to a\n // terminal state. Effect depends on `status` so it runs exactly once\n // per transition.\n useEffect(() => {\n if (status === \"finished\" || status === \"paused\" || status === \"idle\") {\n resolvePending();\n } else if (status === \"error\") {\n rejectPending(error ?? new Error(\"playback error\"));\n }\n }, [status, error, resolvePending, rejectPending]);\n\n // ── Analyser loop ───────────────────────────────────────────────────\n const tickAnalyser = useCallback(() => {\n const analyser = analyserRef.current;\n const ctx = ctxRef.current;\n if (analyser == null) {\n rafRef.current = null;\n return;\n }\n const buf = timeBufRef.current;\n if (buf != null) {\n analyser.getByteTimeDomainData(buf);\n // RMS on bytes in [0, 255] centred at 128.\n let sum = 0;\n for (let i = 0; i < buf.length; i += 1) {\n const v = (buf[i]! - 128) / 128;\n sum += v * v;\n }\n setLevel(Math.sqrt(sum / buf.length));\n }\n if (ctx != null && statusRef.current === \"playing\") {\n setCurrentTime(ctx.currentTime - playStartCtxTimeRef.current);\n }\n if (typeof window !== \"undefined\") {\n rafRef.current = window.requestAnimationFrame(tickAnalyser);\n }\n }, []);\n\n const startAnalyserLoop = useCallback(() => {\n if (rafRef.current != null) return;\n if (typeof window === \"undefined\") return;\n rafRef.current = window.requestAnimationFrame(tickAnalyser);\n }, [tickAnalyser]);\n\n const stopAnalyserLoop = useCallback(() => {\n if (rafRef.current == null) return;\n if (typeof window !== \"undefined\") {\n window.cancelAnimationFrame(rafRef.current);\n }\n rafRef.current = null;\n setLevel(0);\n }, []);\n\n // ── AudioContext / AnalyserNode ─────────────────────────────────────\n const ensureAnalyser = useCallback((ctx: AudioContext): AnalyserNode => {\n if (analyserRef.current != null) return analyserRef.current;\n const analyser = ctx.createAnalyser();\n analyser.fftSize = ANALYSER_FFT_SIZE;\n analyser.connect(ctx.destination);\n analyserRef.current = analyser;\n freqBufRef.current = new Uint8Array(analyser.frequencyBinCount);\n timeBufRef.current = new Uint8Array(analyser.fftSize);\n return analyser;\n }, []);\n\n const ensureContextForPcm = useCallback((): AudioContext | null => {\n if (ctxRef.current != null) return ctxRef.current;\n const format = formatRef.current;\n if (format == null) return null;\n const AudioCtx = resolveAudioContextCtor();\n if (AudioCtx == null) {\n setError(new Error(\"Web Audio API is not available in this environment\"));\n setStatus(\"error\");\n return null;\n }\n const ctx = new AudioCtx({ sampleRate: format.sampleRate });\n ctxRef.current = ctx;\n nextStartTimeRef.current = ctx.currentTime;\n ensureAnalyser(ctx);\n return ctx;\n }, [ensureAnalyser]);\n\n const ensureContextForElement = useCallback((): AudioContext | null => {\n if (ctxRef.current != null) return ctxRef.current;\n const AudioCtx = resolveAudioContextCtor();\n if (AudioCtx == null) return null;\n // Match device rate — browsers otherwise resample the element.\n const ctx = new AudioCtx();\n ctxRef.current = ctx;\n ensureAnalyser(ctx);\n return ctx;\n }, [ensureAnalyser]);\n\n // ── PCM scheduling ──────────────────────────────────────────────────\n const scheduleChunk = useCallback((ctx: AudioContext, bytes: Uint8Array) => {\n const format = formatRef.current;\n const analyser = analyserRef.current;\n if (format == null || analyser == null) return;\n const { sampleRate: bufSampleRate, channels: bufChannels } = format;\n\n const sampleCount = Math.floor(bytes.byteLength / 2);\n if (sampleCount === 0) return;\n const framesPerChannel = Math.floor(sampleCount / bufChannels);\n if (framesPerChannel === 0) return;\n\n const buffer = ctx.createBuffer(\n bufChannels,\n framesPerChannel,\n bufSampleRate\n );\n const view = new DataView(\n bytes.buffer,\n bytes.byteOffset,\n framesPerChannel * bufChannels * 2\n );\n for (let channel = 0; channel < bufChannels; channel += 1) {\n const channelData = buffer.getChannelData(channel);\n for (let frame = 0; frame < framesPerChannel; frame += 1) {\n const sampleOffset = (frame * bufChannels + channel) * 2;\n const int = view.getInt16(sampleOffset, true);\n channelData[frame] = int < 0 ? int / 0x8000 : int / 0x7fff;\n }\n }\n\n const source = ctx.createBufferSource();\n source.buffer = buffer;\n source.connect(analyser);\n const now = ctx.currentTime;\n const startAt = Math.max(now, nextStartTimeRef.current);\n source.start(startAt);\n nextStartTimeRef.current = startAt + buffer.duration;\n activeSourcesRef.current.add(source);\n source.onended = () => {\n activeSourcesRef.current.delete(source);\n if (\n activeSourcesRef.current.size === 0 &&\n upstreamFinishedRef.current &&\n pendingChunksRef.current.length === 0\n ) {\n setStatus(\"finished\");\n }\n };\n }, []);\n\n const flushPendingPcm = useCallback(() => {\n if (!shouldPlayRef.current) return;\n const ctx = ensureContextForPcm();\n if (ctx == null) return;\n if (ctx.state === \"suspended\") void ctx.resume();\n const chunks = pendingChunksRef.current;\n pendingChunksRef.current = [];\n for (const bytes of chunks) scheduleChunk(ctx, bytes);\n if (chunks.length > 0 && statusRef.current !== \"playing\") {\n playStartCtxTimeRef.current = ctx.currentTime;\n setCurrentTime(0);\n setStatus(\"playing\");\n startAnalyserLoop();\n }\n }, [ensureContextForPcm, scheduleChunk, startAnalyserLoop]);\n\n // ── Public controls ─────────────────────────────────────────────────\n const play = useCallback(() => {\n if (media == null) return;\n if (statusRef.current === \"error\") return;\n\n if (strategy === \"pcm\") {\n shouldPlayRef.current = true;\n if (statusRef.current !== \"playing\") setStatus(\"buffering\");\n const ctx = ensureContextForPcm();\n if (ctx != null && ctx.state === \"suspended\") void ctx.resume();\n flushPendingPcm();\n return;\n }\n\n // element strategy\n const audio = audioElRef.current;\n if (audio == null) {\n // Buffer hasn't materialised yet — remember the intent and let\n // the blob-ready path below start playback.\n shouldPlayRef.current = true;\n setStatus(\"buffering\");\n return;\n }\n shouldPlayRef.current = true;\n const ctx = ensureContextForElement();\n if (ctx != null && ctx.state === \"suspended\") void ctx.resume();\n audio.play().catch((err) => {\n setError(err as Error);\n setStatus(\"error\");\n });\n }, [\n media,\n strategy,\n ensureContextForPcm,\n ensureContextForElement,\n flushPendingPcm,\n ]);\n\n const pause = useCallback(() => {\n shouldPlayRef.current = false;\n if (strategy === \"pcm\") {\n const ctx = ctxRef.current;\n if (ctx != null && ctx.state === \"running\") void ctx.suspend();\n } else {\n audioElRef.current?.pause();\n }\n if (statusRef.current === \"playing\" || statusRef.current === \"buffering\") {\n setStatus(\"paused\");\n }\n }, [strategy]);\n\n const stop = useCallback(() => {\n shouldPlayRef.current = false;\n stopAnalyserLoop();\n\n if (strategy === \"pcm\") {\n for (const source of activeSourcesRef.current) {\n try {\n source.stop();\n } catch {\n // Already stopped\n }\n }\n activeSourcesRef.current.clear();\n pendingChunksRef.current = [];\n nextStartTimeRef.current = 0;\n } else {\n const audio = audioElRef.current;\n if (audio != null) {\n audio.pause();\n audio.currentTime = 0;\n }\n }\n\n const ctx = ctxRef.current;\n ctxRef.current = null;\n analyserRef.current = null;\n freqBufRef.current = null;\n timeBufRef.current = null;\n elementSourceRef.current = null;\n if (ctx != null) void ctx.close();\n\n setCurrentTime(0);\n setStatus(media == null ? \"idle\" : \"paused\");\n }, [strategy, media, stopAnalyserLoop]);\n\n const reset = useCallback(() => {\n stop();\n setError(undefined);\n setDuration(undefined);\n upstreamFinishedRef.current = false;\n setStatus(\"idle\");\n }, [stop]);\n\n const toggle = useCallback(() => {\n if (statusRef.current === \"playing\") pause();\n else play();\n }, [play, pause]);\n\n const playToEnd = useCallback((): Promise<void> => {\n // Drop any prior caller — they'd have resolved on the next\n // terminal anyway, which is about to happen again.\n pendingResolveRef.current?.();\n pendingResolveRef.current = null;\n pendingRejectRef.current = null;\n\n return new Promise<void>((resolve, reject) => {\n pendingResolveRef.current = resolve;\n pendingRejectRef.current = reject;\n play();\n });\n }, [play]);\n\n const seek = useCallback(\n (seconds: number) => {\n if (strategy !== \"element\") return;\n const audio = audioElRef.current;\n if (audio == null) return;\n audio.currentTime = seconds;\n setCurrentTime(seconds);\n },\n [strategy]\n );\n\n const getFrequencyData = useCallback((): Uint8Array | undefined => {\n const analyser = analyserRef.current;\n const buf = freqBufRef.current;\n if (analyser == null || buf == null) return undefined;\n analyser.getByteFrequencyData(buf);\n return buf;\n }, []);\n\n const getTimeDomainData = useCallback((): Uint8Array | undefined => {\n const analyser = analyserRef.current;\n const buf = timeBufRef.current;\n if (analyser == null || buf == null) return undefined;\n analyser.getByteTimeDomainData(buf);\n return buf;\n }, []);\n\n // ── Media binding ───────────────────────────────────────────────────\n const autoPlayRef = useRef(autoPlay);\n useEffect(() => {\n autoPlayRef.current = autoPlay;\n }, [autoPlay]);\n\n // Surface a media-level error as soon as the handle reports one.\n useEffect(() => {\n if (media?.error == null) return;\n setError(new Error(media.error.message));\n setStatus(\"error\");\n }, [media]);\n\n // PCM effect — lifts from upstream stream → `AudioContext`.\n useEffect(() => {\n if (media == null || strategy !== \"pcm\") return undefined;\n\n setError(undefined);\n setStatus(\"buffering\");\n setCurrentTime(0);\n setDuration(undefined);\n upstreamFinishedRef.current = false;\n pendingChunksRef.current = [];\n\n const mimeType = media.mimeType ?? \"\";\n const isRawPcm =\n mimeType === \"audio/pcm\" ||\n mimeType === \"audio/L16\" ||\n mimeType.startsWith(\"audio/pcm;\") ||\n mimeType.startsWith(\"audio/L16;\") ||\n (pcmPrefixes != null &&\n pcmPrefixes.some((prefix) => mimeType.startsWith(prefix)));\n const isWav =\n mimeType === \"audio/wav\" ||\n mimeType === \"audio/wave\" ||\n mimeType === \"audio/x-wav\" ||\n mimeType === \"audio/vnd.wave\";\n\n if (isRawPcm) {\n formatRef.current = {\n sampleRate,\n channels,\n bitsPerSample: 16,\n };\n } else if (isWav) {\n formatRef.current = null;\n } else {\n // Explicit `strategy: \"pcm\"` override on a non-PCM mime: fail loud.\n setError(\n new Error(\n `useAudioPlayer: forced PCM strategy for unsupported mime ${JSON.stringify(mimeType)}`\n )\n );\n setStatus(\"error\");\n return undefined;\n }\n\n // WAV parser state (ignored on the PCM path). Accumulate inbound\n // bytes until the RIFF chunks resolve, then flip to passthrough.\n const wavHeaderChunks: Uint8Array[] = [];\n let wavHeaderParsed = !isWav;\n let wavHeaderFailed = false;\n\n const routeChunk = (bytes: Uint8Array) => {\n if (wavHeaderFailed) return;\n\n if (wavHeaderParsed) {\n pendingChunksRef.current.push(bytes);\n if (shouldPlayRef.current) flushPendingPcm();\n return;\n }\n\n wavHeaderChunks.push(bytes);\n const combined = concatChunks(wavHeaderChunks);\n const result = tryParseWavHeader(combined);\n if (result.status === \"need-more\") return;\n if (result.status === \"invalid\") {\n wavHeaderFailed = true;\n setError(\n new Error(`useAudioPlayer: invalid WAV stream: ${result.reason}`)\n );\n setStatus(\"error\");\n return;\n }\n\n formatRef.current = result.format;\n wavHeaderParsed = true;\n wavHeaderChunks.length = 0;\n\n const tail = combined.subarray(result.dataOffset);\n if (tail.byteLength > 0) {\n pendingChunksRef.current.push(tail);\n if (shouldPlayRef.current) flushPendingPcm();\n }\n };\n\n if (autoPlayRef.current) {\n shouldPlayRef.current = true;\n }\n\n const unsubscribe = attachToPump(media, (event) => {\n switch (event.type) {\n case \"chunk\":\n routeChunk(event.bytes);\n break;\n case \"finished\":\n upstreamFinishedRef.current = true;\n if (\n pendingChunksRef.current.length === 0 &&\n activeSourcesRef.current.size === 0\n ) {\n setStatus(\"finished\");\n }\n break;\n case \"error\":\n setError(event.error);\n setStatus(\"error\");\n break;\n }\n });\n\n return () => {\n unsubscribe();\n stop();\n };\n // `flushPendingPcm` and `stop` are stable via useCallback deps;\n // include them to satisfy exhaustive-deps without re-running.\n }, [\n media,\n strategy,\n sampleRate,\n channels,\n pcmPrefixes,\n flushPendingPcm,\n stop,\n ]);\n\n // Element effect — await blob URL then wire an HTMLAudioElement.\n useEffect(() => {\n if (media == null || strategy !== \"element\") return undefined;\n if (typeof window === \"undefined\") return undefined;\n\n setError(undefined);\n setStatus(\"buffering\");\n setCurrentTime(0);\n setDuration(undefined);\n\n let cancelled = false;\n let audio: HTMLAudioElement | null = null;\n\n media.objectURL.then(\n (resolved) => {\n if (cancelled) return;\n pendingSrcRef.current = resolved;\n audio = new Audio(resolved);\n audio.preload = \"auto\";\n audioElRef.current = audio;\n\n const onPlay = () => {\n if (statusRef.current === \"error\") return;\n const ctx = ensureContextForElement();\n if (\n ctx != null &&\n elementSourceRef.current == null &&\n audio != null\n ) {\n try {\n const src = ctx.createMediaElementSource(audio);\n src.connect(analyserRef.current!);\n elementSourceRef.current = src;\n } catch {\n // Some browsers reject a second `createMediaElementSource`\n // on the same element; fall through without visualization.\n }\n }\n playStartCtxTimeRef.current = 0;\n setCurrentTime(audio?.currentTime ?? 0);\n setStatus(\"playing\");\n startAnalyserLoop();\n };\n const onPause = () => {\n if (audio != null && audio.ended) return; // ended fires pause too\n if (statusRef.current === \"playing\") setStatus(\"paused\");\n };\n const onEnded = () => {\n setStatus(\"finished\");\n };\n const onTimeUpdate = () => {\n if (audio != null) setCurrentTime(audio.currentTime);\n };\n const onLoadedMetadata = () => {\n if (audio != null && Number.isFinite(audio.duration)) {\n setDuration(audio.duration);\n }\n };\n const onError = () => {\n setError(new Error(\"HTMLAudioElement error\"));\n setStatus(\"error\");\n };\n\n audio.addEventListener(\"play\", onPlay);\n audio.addEventListener(\"pause\", onPause);\n audio.addEventListener(\"ended\", onEnded);\n audio.addEventListener(\"timeupdate\", onTimeUpdate);\n audio.addEventListener(\"loadedmetadata\", onLoadedMetadata);\n audio.addEventListener(\"error\", onError);\n\n if (shouldPlayRef.current || autoPlayRef.current) {\n audio.play().catch((err) => {\n setError(err as Error);\n setStatus(\"error\");\n });\n } else {\n setStatus(\"paused\");\n }\n },\n () => {\n if (!cancelled) {\n setError(new Error(\"media failed to materialise\"));\n setStatus(\"error\");\n }\n }\n );\n\n return () => {\n cancelled = true;\n const el = audioElRef.current;\n audioElRef.current = null;\n elementSourceRef.current = null;\n if (el != null) {\n try {\n el.pause();\n el.removeAttribute(\"src\");\n el.load();\n } catch {\n // best-effort teardown\n }\n }\n stop();\n try {\n media.revoke();\n } catch {\n // best-effort\n }\n };\n }, [media, strategy, ensureContextForElement, startAnalyserLoop, stop]);\n\n // Reset state when the media handle is cleared.\n useEffect(() => {\n if (media != null) return;\n setStatus(\"idle\");\n setError(undefined);\n setCurrentTime(0);\n setDuration(undefined);\n setLevel(0);\n upstreamFinishedRef.current = false;\n }, [media]);\n\n return {\n status,\n strategy,\n play,\n pause,\n stop,\n toggle,\n reset,\n playToEnd,\n currentTime,\n duration: strategy === \"element\" ? duration : undefined,\n seek: strategy === \"element\" ? seek : undefined,\n level,\n getFrequencyData,\n getTimeDomainData,\n error,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ let react = require("react");
3
+ //#region src/use-media-url.ts
4
+ /**
5
+ * Resolve the lazy {@link MediaBase.objectURL} promise into a string
6
+ * suitable for direct use in `<audio src>` / `<img src>` /
7
+ * `<video src>` / `<a href download>`. Returns `undefined` until the
8
+ * URL is available.
9
+ *
10
+ * Lifecycle:
11
+ * - On mount (or when `media` changes) the hook awaits
12
+ * `media.objectURL`, then commits the resolved string to state.
13
+ * - On unmount (or when `media` changes) the hook calls
14
+ * `media.revoke()` to free the object URL slot. The next consumer
15
+ * that accesses `media.objectURL` mints a fresh URL from the same
16
+ * `Blob`, so live re-renders just work.
17
+ * - If the underlying handle errored before settling, the promise
18
+ * rejects and `useMediaURL` stays at `undefined`. Inspect
19
+ * `media.error` to surface the failure.
20
+ *
21
+ * Pair with {@link useAudio} / {@link useImages} / {@link useVideo} /
22
+ * {@link useFiles} to bridge SDK media handles into React DOM nodes
23
+ * without manual `URL.createObjectURL` bookkeeping.
24
+ */
25
+ function useMediaURL(media) {
26
+ const [url, setUrl] = (0, react.useState)();
27
+ (0, react.useEffect)(() => {
28
+ if (media == null) {
29
+ setUrl(void 0);
30
+ return;
31
+ }
32
+ let cancelled = false;
33
+ media.objectURL.then((resolved) => {
34
+ if (!cancelled) setUrl(resolved);
35
+ }, () => {});
36
+ return () => {
37
+ cancelled = true;
38
+ setUrl(void 0);
39
+ try {
40
+ media.revoke();
41
+ } catch {}
42
+ };
43
+ }, [media]);
44
+ return url;
45
+ }
46
+ //#endregion
47
+ exports.useMediaURL = useMediaURL;
48
+
49
+ //# sourceMappingURL=use-media-url.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-media-url.cjs","names":[],"sources":["../src/use-media-url.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { MediaBase } from \"@langchain/langgraph-sdk/stream\";\n\n/**\n * Resolve the lazy {@link MediaBase.objectURL} promise into a string\n * suitable for direct use in `<audio src>` / `<img src>` /\n * `<video src>` / `<a href download>`. Returns `undefined` until the\n * URL is available.\n *\n * Lifecycle:\n * - On mount (or when `media` changes) the hook awaits\n * `media.objectURL`, then commits the resolved string to state.\n * - On unmount (or when `media` changes) the hook calls\n * `media.revoke()` to free the object URL slot. The next consumer\n * that accesses `media.objectURL` mints a fresh URL from the same\n * `Blob`, so live re-renders just work.\n * - If the underlying handle errored before settling, the promise\n * rejects and `useMediaURL` stays at `undefined`. Inspect\n * `media.error` to surface the failure.\n *\n * Pair with {@link useAudio} / {@link useImages} / {@link useVideo} /\n * {@link useFiles} to bridge SDK media handles into React DOM nodes\n * without manual `URL.createObjectURL` bookkeeping.\n */\nexport function useMediaURL(media: MediaBase | undefined): string | undefined {\n const [url, setUrl] = useState<string>();\n\n useEffect(() => {\n if (media == null) {\n setUrl(undefined);\n return undefined;\n }\n let cancelled = false;\n media.objectURL.then(\n (resolved) => {\n if (!cancelled) setUrl(resolved);\n },\n () => {\n // Errors surfaced via `media.error`; keep `url` undefined so\n // consumers fall through to a no-src render.\n }\n );\n return () => {\n cancelled = true;\n setUrl(undefined);\n try {\n media.revoke();\n } catch {\n // best-effort\n }\n };\n }, [media]);\n\n return url;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,YAAY,OAAkD;CAC5E,MAAM,CAAC,KAAK,WAAA,GAAA,MAAA,WAA4B;AAExC,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,SAAS,MAAM;AACjB,UAAO,KAAA,EAAU;AACjB;;EAEF,IAAI,YAAY;AAChB,QAAM,UAAU,MACb,aAAa;AACZ,OAAI,CAAC,UAAW,QAAO,SAAS;WAE5B,GAIP;AACD,eAAa;AACX,eAAY;AACZ,UAAO,KAAA,EAAU;AACjB,OAAI;AACF,UAAM,QAAQ;WACR;;IAIT,CAAC,MAAM,CAAC;AAEX,QAAO"}
@@ -0,0 +1,28 @@
1
+ import { MediaBase } from "@langchain/langgraph-sdk/stream";
2
+
3
+ //#region src/use-media-url.d.ts
4
+ /**
5
+ * Resolve the lazy {@link MediaBase.objectURL} promise into a string
6
+ * suitable for direct use in `<audio src>` / `<img src>` /
7
+ * `<video src>` / `<a href download>`. Returns `undefined` until the
8
+ * URL is available.
9
+ *
10
+ * Lifecycle:
11
+ * - On mount (or when `media` changes) the hook awaits
12
+ * `media.objectURL`, then commits the resolved string to state.
13
+ * - On unmount (or when `media` changes) the hook calls
14
+ * `media.revoke()` to free the object URL slot. The next consumer
15
+ * that accesses `media.objectURL` mints a fresh URL from the same
16
+ * `Blob`, so live re-renders just work.
17
+ * - If the underlying handle errored before settling, the promise
18
+ * rejects and `useMediaURL` stays at `undefined`. Inspect
19
+ * `media.error` to surface the failure.
20
+ *
21
+ * Pair with {@link useAudio} / {@link useImages} / {@link useVideo} /
22
+ * {@link useFiles} to bridge SDK media handles into React DOM nodes
23
+ * without manual `URL.createObjectURL` bookkeeping.
24
+ */
25
+ declare function useMediaURL(media: MediaBase | undefined): string | undefined;
26
+ //#endregion
27
+ export { useMediaURL };
28
+ //# sourceMappingURL=use-media-url.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-media-url.d.cts","names":[],"sources":["../src/use-media-url.ts"],"mappings":";;;;;AA4BA;;;;;;;;;;;;;;;;;;;iBAAgB,WAAA,CAAY,KAAA,EAAO,SAAA"}
@@ -0,0 +1,28 @@
1
+ import { MediaBase } from "@langchain/langgraph-sdk/stream";
2
+
3
+ //#region src/use-media-url.d.ts
4
+ /**
5
+ * Resolve the lazy {@link MediaBase.objectURL} promise into a string
6
+ * suitable for direct use in `<audio src>` / `<img src>` /
7
+ * `<video src>` / `<a href download>`. Returns `undefined` until the
8
+ * URL is available.
9
+ *
10
+ * Lifecycle:
11
+ * - On mount (or when `media` changes) the hook awaits
12
+ * `media.objectURL`, then commits the resolved string to state.
13
+ * - On unmount (or when `media` changes) the hook calls
14
+ * `media.revoke()` to free the object URL slot. The next consumer
15
+ * that accesses `media.objectURL` mints a fresh URL from the same
16
+ * `Blob`, so live re-renders just work.
17
+ * - If the underlying handle errored before settling, the promise
18
+ * rejects and `useMediaURL` stays at `undefined`. Inspect
19
+ * `media.error` to surface the failure.
20
+ *
21
+ * Pair with {@link useAudio} / {@link useImages} / {@link useVideo} /
22
+ * {@link useFiles} to bridge SDK media handles into React DOM nodes
23
+ * without manual `URL.createObjectURL` bookkeeping.
24
+ */
25
+ declare function useMediaURL(media: MediaBase | undefined): string | undefined;
26
+ //#endregion
27
+ export { useMediaURL };
28
+ //# sourceMappingURL=use-media-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-media-url.d.ts","names":[],"sources":["../src/use-media-url.ts"],"mappings":";;;;;AA4BA;;;;;;;;;;;;;;;;;;;iBAAgB,WAAA,CAAY,KAAA,EAAO,SAAA"}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { useEffect, useState } from "react";
3
+ //#region src/use-media-url.ts
4
+ /**
5
+ * Resolve the lazy {@link MediaBase.objectURL} promise into a string
6
+ * suitable for direct use in `<audio src>` / `<img src>` /
7
+ * `<video src>` / `<a href download>`. Returns `undefined` until the
8
+ * URL is available.
9
+ *
10
+ * Lifecycle:
11
+ * - On mount (or when `media` changes) the hook awaits
12
+ * `media.objectURL`, then commits the resolved string to state.
13
+ * - On unmount (or when `media` changes) the hook calls
14
+ * `media.revoke()` to free the object URL slot. The next consumer
15
+ * that accesses `media.objectURL` mints a fresh URL from the same
16
+ * `Blob`, so live re-renders just work.
17
+ * - If the underlying handle errored before settling, the promise
18
+ * rejects and `useMediaURL` stays at `undefined`. Inspect
19
+ * `media.error` to surface the failure.
20
+ *
21
+ * Pair with {@link useAudio} / {@link useImages} / {@link useVideo} /
22
+ * {@link useFiles} to bridge SDK media handles into React DOM nodes
23
+ * without manual `URL.createObjectURL` bookkeeping.
24
+ */
25
+ function useMediaURL(media) {
26
+ const [url, setUrl] = useState();
27
+ useEffect(() => {
28
+ if (media == null) {
29
+ setUrl(void 0);
30
+ return;
31
+ }
32
+ let cancelled = false;
33
+ media.objectURL.then((resolved) => {
34
+ if (!cancelled) setUrl(resolved);
35
+ }, () => {});
36
+ return () => {
37
+ cancelled = true;
38
+ setUrl(void 0);
39
+ try {
40
+ media.revoke();
41
+ } catch {}
42
+ };
43
+ }, [media]);
44
+ return url;
45
+ }
46
+ //#endregion
47
+ export { useMediaURL };
48
+
49
+ //# sourceMappingURL=use-media-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-media-url.js","names":[],"sources":["../src/use-media-url.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { MediaBase } from \"@langchain/langgraph-sdk/stream\";\n\n/**\n * Resolve the lazy {@link MediaBase.objectURL} promise into a string\n * suitable for direct use in `<audio src>` / `<img src>` /\n * `<video src>` / `<a href download>`. Returns `undefined` until the\n * URL is available.\n *\n * Lifecycle:\n * - On mount (or when `media` changes) the hook awaits\n * `media.objectURL`, then commits the resolved string to state.\n * - On unmount (or when `media` changes) the hook calls\n * `media.revoke()` to free the object URL slot. The next consumer\n * that accesses `media.objectURL` mints a fresh URL from the same\n * `Blob`, so live re-renders just work.\n * - If the underlying handle errored before settling, the promise\n * rejects and `useMediaURL` stays at `undefined`. Inspect\n * `media.error` to surface the failure.\n *\n * Pair with {@link useAudio} / {@link useImages} / {@link useVideo} /\n * {@link useFiles} to bridge SDK media handles into React DOM nodes\n * without manual `URL.createObjectURL` bookkeeping.\n */\nexport function useMediaURL(media: MediaBase | undefined): string | undefined {\n const [url, setUrl] = useState<string>();\n\n useEffect(() => {\n if (media == null) {\n setUrl(undefined);\n return undefined;\n }\n let cancelled = false;\n media.objectURL.then(\n (resolved) => {\n if (!cancelled) setUrl(resolved);\n },\n () => {\n // Errors surfaced via `media.error`; keep `url` undefined so\n // consumers fall through to a no-src render.\n }\n );\n return () => {\n cancelled = true;\n setUrl(undefined);\n try {\n media.revoke();\n } catch {\n // best-effort\n }\n };\n }, [media]);\n\n return url;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,YAAY,OAAkD;CAC5E,MAAM,CAAC,KAAK,UAAU,UAAkB;AAExC,iBAAgB;AACd,MAAI,SAAS,MAAM;AACjB,UAAO,KAAA,EAAU;AACjB;;EAEF,IAAI,YAAY;AAChB,QAAM,UAAU,MACb,aAAa;AACZ,OAAI,CAAC,UAAW,QAAO,SAAS;WAE5B,GAIP;AACD,eAAa;AACX,eAAY;AACZ,UAAO,KAAA,EAAU;AACjB,OAAI;AACF,UAAM,QAAQ;WACR;;IAIT,CAAC,MAAM,CAAC;AAEX,QAAO"}
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ let react = require("react");
3
+ //#region src/use-projection.ts
4
+ /**
5
+ * React-side primitive that composes {@link ChannelRegistry.acquire}
6
+ * with {@link useSyncExternalStore}.
7
+ *
8
+ * Contract:
9
+ * - On mount (or when `key` changes) this hook acquires a
10
+ * ref-counted projection from the registry and subscribes to its
11
+ * store. React re-renders automatically whenever the store's
12
+ * snapshot changes.
13
+ * - On unmount (or when `key` changes) the previous acquisition is
14
+ * released. If this was the last consumer of that spec, the
15
+ * registry closes the underlying server subscription.
16
+ *
17
+ * The first render (before `useEffect` runs) returns `initialValue`.
18
+ * Subsequent renders read from the acquired store.
19
+ *
20
+ * Framework bindings for Vue/Svelte/Angular follow the same shape —
21
+ * acquire on setup/mount, release on scope-dispose/unmount — but use
22
+ * their own reactivity primitive instead of `useSyncExternalStore`.
23
+ */
24
+ function useProjection(registry, specFactory, key, initialValue) {
25
+ const [store, setStore] = (0, react.useState)(null);
26
+ (0, react.useEffect)(() => {
27
+ if (registry == null) return void 0;
28
+ const acquired = registry.acquire(specFactory());
29
+ setStore(acquired.store);
30
+ return () => {
31
+ acquired.release();
32
+ setStore(null);
33
+ };
34
+ }, [registry, key]);
35
+ return (0, react.useSyncExternalStore)(store != null ? store.subscribe : NOOP_SUBSCRIBE, () => store != null ? store.getSnapshot() : initialValue, () => initialValue);
36
+ }
37
+ const NOOP_SUBSCRIBE = () => () => {};
38
+ //#endregion
39
+ exports.useProjection = useProjection;
40
+
41
+ //# sourceMappingURL=use-projection.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-projection.cjs","names":[],"sources":["../src/use-projection.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport type {\n ChannelRegistry,\n ProjectionSpec,\n StreamStore,\n} from \"@langchain/langgraph-sdk/stream\";\n\n/**\n * React-side primitive that composes {@link ChannelRegistry.acquire}\n * with {@link useSyncExternalStore}.\n *\n * Contract:\n * - On mount (or when `key` changes) this hook acquires a\n * ref-counted projection from the registry and subscribes to its\n * store. React re-renders automatically whenever the store's\n * snapshot changes.\n * - On unmount (or when `key` changes) the previous acquisition is\n * released. If this was the last consumer of that spec, the\n * registry closes the underlying server subscription.\n *\n * The first render (before `useEffect` runs) returns `initialValue`.\n * Subsequent renders read from the acquired store.\n *\n * Framework bindings for Vue/Svelte/Angular follow the same shape —\n * acquire on setup/mount, release on scope-dispose/unmount — but use\n * their own reactivity primitive instead of `useSyncExternalStore`.\n */\nexport function useProjection<T>(\n registry: ChannelRegistry | null | undefined,\n specFactory: () => ProjectionSpec<T>,\n key: string,\n initialValue: T\n): T {\n const [store, setStore] = useState<StreamStore<T> | null>(null);\n\n useEffect(() => {\n if (registry == null) return undefined;\n const acquired = registry.acquire(specFactory());\n setStore(acquired.store);\n return () => {\n acquired.release();\n setStore(null);\n };\n // `specFactory` is intentionally not in the dep array: identity\n // of the factory function is not meaningful — only the `key` is.\n // Callers construct the spec inline and rely on the key to dedupe.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [registry, key]);\n\n return useSyncExternalStore<T>(\n store != null ? store.subscribe : NOOP_SUBSCRIBE,\n () => (store != null ? store.getSnapshot() : initialValue),\n () => initialValue\n );\n}\n\nconst NOOP_SUBSCRIBE: (listener: () => void) => () => void = () => () => {};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,cACd,UACA,aACA,KACA,cACG;CACH,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,UAA4C,KAAK;AAE/D,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,YAAY,KAAM,QAAO,KAAA;EAC7B,MAAM,WAAW,SAAS,QAAQ,aAAa,CAAC;AAChD,WAAS,SAAS,MAAM;AACxB,eAAa;AACX,YAAS,SAAS;AAClB,YAAS,KAAK;;IAMf,CAAC,UAAU,IAAI,CAAC;AAEnB,SAAA,GAAA,MAAA,sBACE,SAAS,OAAO,MAAM,YAAY,sBAC3B,SAAS,OAAO,MAAM,aAAa,GAAG,oBACvC,aACP;;AAGH,MAAM,6BAAmE"}
@@ -0,0 +1,27 @@
1
+ import { ChannelRegistry, ProjectionSpec } from "@langchain/langgraph-sdk/stream";
2
+
3
+ //#region src/use-projection.d.ts
4
+ /**
5
+ * React-side primitive that composes {@link ChannelRegistry.acquire}
6
+ * with {@link useSyncExternalStore}.
7
+ *
8
+ * Contract:
9
+ * - On mount (or when `key` changes) this hook acquires a
10
+ * ref-counted projection from the registry and subscribes to its
11
+ * store. React re-renders automatically whenever the store's
12
+ * snapshot changes.
13
+ * - On unmount (or when `key` changes) the previous acquisition is
14
+ * released. If this was the last consumer of that spec, the
15
+ * registry closes the underlying server subscription.
16
+ *
17
+ * The first render (before `useEffect` runs) returns `initialValue`.
18
+ * Subsequent renders read from the acquired store.
19
+ *
20
+ * Framework bindings for Vue/Svelte/Angular follow the same shape —
21
+ * acquire on setup/mount, release on scope-dispose/unmount — but use
22
+ * their own reactivity primitive instead of `useSyncExternalStore`.
23
+ */
24
+ declare function useProjection<T>(registry: ChannelRegistry | null | undefined, specFactory: () => ProjectionSpec<T>, key: string, initialValue: T): T;
25
+ //#endregion
26
+ export { useProjection };
27
+ //# sourceMappingURL=use-projection.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-projection.d.cts","names":[],"sources":["../src/use-projection.ts"],"mappings":";;;;;AA+BA;;;;;;;;;;;;;;;;;;iBAAgB,aAAA,GAAA,CACd,QAAA,EAAU,eAAA,qBACV,WAAA,QAAmB,cAAA,CAAe,CAAA,GAClC,GAAA,UACA,YAAA,EAAc,CAAA,GACb,CAAA"}
@@ -0,0 +1,27 @@
1
+ import { ChannelRegistry, ProjectionSpec } from "@langchain/langgraph-sdk/stream";
2
+
3
+ //#region src/use-projection.d.ts
4
+ /**
5
+ * React-side primitive that composes {@link ChannelRegistry.acquire}
6
+ * with {@link useSyncExternalStore}.
7
+ *
8
+ * Contract:
9
+ * - On mount (or when `key` changes) this hook acquires a
10
+ * ref-counted projection from the registry and subscribes to its
11
+ * store. React re-renders automatically whenever the store's
12
+ * snapshot changes.
13
+ * - On unmount (or when `key` changes) the previous acquisition is
14
+ * released. If this was the last consumer of that spec, the
15
+ * registry closes the underlying server subscription.
16
+ *
17
+ * The first render (before `useEffect` runs) returns `initialValue`.
18
+ * Subsequent renders read from the acquired store.
19
+ *
20
+ * Framework bindings for Vue/Svelte/Angular follow the same shape —
21
+ * acquire on setup/mount, release on scope-dispose/unmount — but use
22
+ * their own reactivity primitive instead of `useSyncExternalStore`.
23
+ */
24
+ declare function useProjection<T>(registry: ChannelRegistry | null | undefined, specFactory: () => ProjectionSpec<T>, key: string, initialValue: T): T;
25
+ //#endregion
26
+ export { useProjection };
27
+ //# sourceMappingURL=use-projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-projection.d.ts","names":[],"sources":["../src/use-projection.ts"],"mappings":";;;;;AA+BA;;;;;;;;;;;;;;;;;;iBAAgB,aAAA,GAAA,CACd,QAAA,EAAU,eAAA,qBACV,WAAA,QAAmB,cAAA,CAAe,CAAA,GAClC,GAAA,UACA,YAAA,EAAc,CAAA,GACb,CAAA"}
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { useEffect, useState, useSyncExternalStore } from "react";
3
+ //#region src/use-projection.ts
4
+ /**
5
+ * React-side primitive that composes {@link ChannelRegistry.acquire}
6
+ * with {@link useSyncExternalStore}.
7
+ *
8
+ * Contract:
9
+ * - On mount (or when `key` changes) this hook acquires a
10
+ * ref-counted projection from the registry and subscribes to its
11
+ * store. React re-renders automatically whenever the store's
12
+ * snapshot changes.
13
+ * - On unmount (or when `key` changes) the previous acquisition is
14
+ * released. If this was the last consumer of that spec, the
15
+ * registry closes the underlying server subscription.
16
+ *
17
+ * The first render (before `useEffect` runs) returns `initialValue`.
18
+ * Subsequent renders read from the acquired store.
19
+ *
20
+ * Framework bindings for Vue/Svelte/Angular follow the same shape —
21
+ * acquire on setup/mount, release on scope-dispose/unmount — but use
22
+ * their own reactivity primitive instead of `useSyncExternalStore`.
23
+ */
24
+ function useProjection(registry, specFactory, key, initialValue) {
25
+ const [store, setStore] = useState(null);
26
+ useEffect(() => {
27
+ if (registry == null) return void 0;
28
+ const acquired = registry.acquire(specFactory());
29
+ setStore(acquired.store);
30
+ return () => {
31
+ acquired.release();
32
+ setStore(null);
33
+ };
34
+ }, [registry, key]);
35
+ return useSyncExternalStore(store != null ? store.subscribe : NOOP_SUBSCRIBE, () => store != null ? store.getSnapshot() : initialValue, () => initialValue);
36
+ }
37
+ const NOOP_SUBSCRIBE = () => () => {};
38
+ //#endregion
39
+ export { useProjection };
40
+
41
+ //# sourceMappingURL=use-projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-projection.js","names":[],"sources":["../src/use-projection.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport type {\n ChannelRegistry,\n ProjectionSpec,\n StreamStore,\n} from \"@langchain/langgraph-sdk/stream\";\n\n/**\n * React-side primitive that composes {@link ChannelRegistry.acquire}\n * with {@link useSyncExternalStore}.\n *\n * Contract:\n * - On mount (or when `key` changes) this hook acquires a\n * ref-counted projection from the registry and subscribes to its\n * store. React re-renders automatically whenever the store's\n * snapshot changes.\n * - On unmount (or when `key` changes) the previous acquisition is\n * released. If this was the last consumer of that spec, the\n * registry closes the underlying server subscription.\n *\n * The first render (before `useEffect` runs) returns `initialValue`.\n * Subsequent renders read from the acquired store.\n *\n * Framework bindings for Vue/Svelte/Angular follow the same shape —\n * acquire on setup/mount, release on scope-dispose/unmount — but use\n * their own reactivity primitive instead of `useSyncExternalStore`.\n */\nexport function useProjection<T>(\n registry: ChannelRegistry | null | undefined,\n specFactory: () => ProjectionSpec<T>,\n key: string,\n initialValue: T\n): T {\n const [store, setStore] = useState<StreamStore<T> | null>(null);\n\n useEffect(() => {\n if (registry == null) return undefined;\n const acquired = registry.acquire(specFactory());\n setStore(acquired.store);\n return () => {\n acquired.release();\n setStore(null);\n };\n // `specFactory` is intentionally not in the dep array: identity\n // of the factory function is not meaningful — only the `key` is.\n // Callers construct the spec inline and rely on the key to dedupe.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [registry, key]);\n\n return useSyncExternalStore<T>(\n store != null ? store.subscribe : NOOP_SUBSCRIBE,\n () => (store != null ? store.getSnapshot() : initialValue),\n () => initialValue\n );\n}\n\nconst NOOP_SUBSCRIBE: (listener: () => void) => () => void = () => () => {};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,cACd,UACA,aACA,KACA,cACG;CACH,MAAM,CAAC,OAAO,YAAY,SAAgC,KAAK;AAE/D,iBAAgB;AACd,MAAI,YAAY,KAAM,QAAO,KAAA;EAC7B,MAAM,WAAW,SAAS,QAAQ,aAAa,CAAC;AAChD,WAAS,SAAS,MAAM;AACxB,eAAa;AACX,YAAS,SAAS;AAClB,YAAS,KAAK;;IAMf,CAAC,UAAU,IAAI,CAAC;AAEnB,QAAO,qBACL,SAAS,OAAO,MAAM,YAAY,sBAC3B,SAAS,OAAO,MAAM,aAAa,GAAG,oBACvC,aACP;;AAGH,MAAM,6BAAmE"}