{"version":3,"file":"media.cjs","names":["namespaceKey","isRootNamespace","openProjectionSubscription","MediaAssembler"],"sources":["../../../src/stream/projections/media.ts"],"sourcesContent":["/**\n * Namespace-scoped media projections.\n *\n * Each factory opens `thread.subscribe({ channels: [\"messages\"],\n * namespaces: [ns] })`, runs the events through a {@link MediaAssembler},\n * filters to one media block type, and surfaces an array of handles\n * that grows as messages with that type are observed.\n *\n * Behaviour mirrors the iterables on `ThreadStream` / `SubgraphHandle` /\n * `SubagentHandle`:\n *  - One handle per `(messageId, blockType)`.\n *  - Yielded on the first matching `content-block-start` — messages\n *    with no matching blocks never appear.\n *  - Errors propagate through `handle.error` / rejected `blob` /\n *    rejected `objectURL`, not through the projection itself.\n *\n * `objectURL`s minted from these handles are caller-owned. The\n * companion `useMediaURL` React hook revokes on unmount; non-React\n * consumers should call `media.revoke()` when done.\n */\nimport type { Event, MessagesEvent } from \"@langchain/protocol\";\nimport { MediaAssembler } from \"../../client/stream/media.js\";\nimport type {\n  AudioMedia,\n  FileMedia,\n  ImageMedia,\n  VideoMedia,\n} from \"../../client/stream/media.js\";\nimport type { ProjectionRuntime, ProjectionSpec } from \"../types.js\";\nimport { isRootNamespace, namespaceKey } from \"../namespace.js\";\nimport { openProjectionSubscription } from \"./runtime.js\";\n\ninterface MediaProjectionOptions {\n  /**\n   * Optional `fetch` for URL-sourced blocks. Forwarded into the\n   * underlying {@link MediaAssembler}.\n   */\n  fetch?: typeof fetch;\n}\n\nfunction createMediaProjection<\n  T extends AudioMedia | ImageMedia | VideoMedia | FileMedia,\n>(\n  kind: \"audio\" | \"images\" | \"video\" | \"files\",\n  namespace: readonly string[],\n  buildAssembler: (\n    push: (m: T) => void,\n    options?: MediaProjectionOptions\n  ) => MediaAssembler,\n  options?: MediaProjectionOptions\n): ProjectionSpec<T[]> {\n  const ns = [...namespace];\n  const key = `${kind}|${namespaceKey(ns)}`;\n\n  return {\n    key,\n    namespace: ns,\n    initial: [],\n    open({ thread, store, rootBus }): ProjectionRuntime {\n      const assembler = buildAssembler((media) => {\n        store.setValue([...store.getSnapshot(), media]);\n      }, options);\n\n      // The root pump is already subscribed to `messages` at root depth 1.\n      // Root-scoped projections attach to it instead of opening a second\n      // server subscription.\n      const rootShortCircuit =\n        isRootNamespace(ns) && rootBus.channels.includes(\"messages\");\n\n      if (rootShortCircuit) {\n        const unsubscribe = rootBus.subscribe((event) => {\n          if (event.method !== \"messages\") return;\n          const matches = isRootNamespace(event.params.namespace);\n          if (!matches) return;\n          assembler.consume(event as MessagesEvent);\n        });\n        return {\n          async dispose() {\n            unsubscribe();\n            for (const media of store.getSnapshot()) {\n              try {\n                media.revoke();\n              } catch {\n                // best-effort\n              }\n            }\n            assembler.close();\n          },\n        };\n      }\n\n      const runtime = openProjectionSubscription({\n        thread,\n        channels: [\"messages\"],\n        namespace: ns,\n        onEvent(event: Event) {\n          if (event.method !== \"messages\") return;\n          assembler.consume(event as MessagesEvent);\n        },\n        onFinally() {\n          assembler.close();\n        },\n      });\n\n      return {\n        async dispose() {\n          // Revoke object URLs minted by handles owned by this\n          // projection so unmount cleans up regardless of whether\n          // individual hooks called `revoke()` themselves.\n          for (const media of store.getSnapshot()) {\n            try {\n              media.revoke();\n            } catch {\n              // best-effort\n            }\n          }\n          assembler.close();\n          await runtime.dispose();\n        },\n      };\n    },\n  };\n}\n\nexport function audioProjection(\n  namespace: readonly string[],\n  options?: MediaProjectionOptions\n): ProjectionSpec<AudioMedia[]> {\n  return createMediaProjection<AudioMedia>(\n    \"audio\",\n    namespace,\n    (push, opts) =>\n      new MediaAssembler({ fetch: opts?.fetch, onAudio: (m) => push(m) }),\n    options\n  );\n}\n\nexport function imagesProjection(\n  namespace: readonly string[],\n  options?: MediaProjectionOptions\n): ProjectionSpec<ImageMedia[]> {\n  return createMediaProjection<ImageMedia>(\n    \"images\",\n    namespace,\n    (push, opts) =>\n      new MediaAssembler({ fetch: opts?.fetch, onImage: (m) => push(m) }),\n    options\n  );\n}\n\nexport function videoProjection(\n  namespace: readonly string[],\n  options?: MediaProjectionOptions\n): ProjectionSpec<VideoMedia[]> {\n  return createMediaProjection<VideoMedia>(\n    \"video\",\n    namespace,\n    (push, opts) =>\n      new MediaAssembler({ fetch: opts?.fetch, onVideo: (m) => push(m) }),\n    options\n  );\n}\n\nexport function filesProjection(\n  namespace: readonly string[],\n  options?: MediaProjectionOptions\n): ProjectionSpec<FileMedia[]> {\n  return createMediaProjection<FileMedia>(\n    \"files\",\n    namespace,\n    (push, opts) =>\n      new MediaAssembler({ fetch: opts?.fetch, onFile: (m) => push(m) }),\n    options\n  );\n}\n\nexport type { MediaProjectionOptions };\n"],"mappings":";;;;AAwCA,SAAS,sBAGP,MACA,WACA,gBAIA,SACqB;CACrB,MAAM,KAAK,CAAC,GAAG,UAAU;AAGzB,QAAO;EACL,KAHU,GAAG,KAAK,GAAGA,kBAAAA,aAAa,GAAG;EAIrC,WAAW;EACX,SAAS,EAAE;EACX,KAAK,EAAE,QAAQ,OAAO,WAA8B;GAClD,MAAM,YAAY,gBAAgB,UAAU;AAC1C,UAAM,SAAS,CAAC,GAAG,MAAM,aAAa,EAAE,MAAM,CAAC;MAC9C,QAAQ;AAQX,OAFEC,kBAAAA,gBAAgB,GAAG,IAAI,QAAQ,SAAS,SAAS,WAAW,EAExC;IACpB,MAAM,cAAc,QAAQ,WAAW,UAAU;AAC/C,SAAI,MAAM,WAAW,WAAY;AAEjC,SAAI,CADYA,kBAAAA,gBAAgB,MAAM,OAAO,UAAU,CACzC;AACd,eAAU,QAAQ,MAAuB;MACzC;AACF,WAAO,EACL,MAAM,UAAU;AACd,kBAAa;AACb,UAAK,MAAM,SAAS,MAAM,aAAa,CACrC,KAAI;AACF,YAAM,QAAQ;aACR;AAIV,eAAU,OAAO;OAEpB;;GAGH,MAAM,UAAUC,gBAAAA,2BAA2B;IACzC;IACA,UAAU,CAAC,WAAW;IACtB,WAAW;IACX,QAAQ,OAAc;AACpB,SAAI,MAAM,WAAW,WAAY;AACjC,eAAU,QAAQ,MAAuB;;IAE3C,YAAY;AACV,eAAU,OAAO;;IAEpB,CAAC;AAEF,UAAO,EACL,MAAM,UAAU;AAId,SAAK,MAAM,SAAS,MAAM,aAAa,CACrC,KAAI;AACF,WAAM,QAAQ;YACR;AAIV,cAAU,OAAO;AACjB,UAAM,QAAQ,SAAS;MAE1B;;EAEJ;;AAGH,SAAgB,gBACd,WACA,SAC8B;AAC9B,QAAO,sBACL,SACA,YACC,MAAM,SACL,IAAIC,cAAAA,eAAe;EAAE,OAAO,MAAM;EAAO,UAAU,MAAM,KAAK,EAAE;EAAE,CAAC,EACrE,QACD;;AAGH,SAAgB,iBACd,WACA,SAC8B;AAC9B,QAAO,sBACL,UACA,YACC,MAAM,SACL,IAAIA,cAAAA,eAAe;EAAE,OAAO,MAAM;EAAO,UAAU,MAAM,KAAK,EAAE;EAAE,CAAC,EACrE,QACD;;AAGH,SAAgB,gBACd,WACA,SAC8B;AAC9B,QAAO,sBACL,SACA,YACC,MAAM,SACL,IAAIA,cAAAA,eAAe;EAAE,OAAO,MAAM;EAAO,UAAU,MAAM,KAAK,EAAE;EAAE,CAAC,EACrE,QACD;;AAGH,SAAgB,gBACd,WACA,SAC6B;AAC7B,QAAO,sBACL,SACA,YACC,MAAM,SACL,IAAIA,cAAAA,eAAe;EAAE,OAAO,MAAM;EAAO,SAAS,MAAM,KAAK,EAAE;EAAE,CAAC,EACpE,QACD"}