@langchain/langgraph-sdk 1.8.9 → 1.9.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.
- package/README.md +90 -43
- package/dist/client/assistants/index.cjs +180 -0
- package/dist/client/assistants/index.cjs.map +1 -0
- package/dist/client/assistants/index.d.cts +155 -0
- package/dist/client/assistants/index.d.cts.map +1 -0
- package/dist/client/assistants/index.d.ts +155 -0
- package/dist/client/assistants/index.d.ts.map +1 -0
- package/dist/client/assistants/index.js +180 -0
- package/dist/client/assistants/index.js.map +1 -0
- package/dist/client/base.cjs +190 -0
- package/dist/client/base.cjs.map +1 -0
- package/dist/client/base.d.cts +84 -0
- package/dist/client/base.d.cts.map +1 -0
- package/dist/client/base.d.ts +84 -0
- package/dist/client/base.d.ts.map +1 -0
- package/dist/client/base.js +188 -0
- package/dist/client/base.js.map +1 -0
- package/dist/client/crons/index.cjs +159 -0
- package/dist/client/crons/index.cjs.map +1 -0
- package/dist/client/crons/index.d.cts +71 -0
- package/dist/client/crons/index.d.cts.map +1 -0
- package/dist/client/crons/index.d.ts +71 -0
- package/dist/client/crons/index.d.ts.map +1 -0
- package/dist/client/crons/index.js +159 -0
- package/dist/client/crons/index.js.map +1 -0
- package/dist/client/index.cjs +84 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +63 -0
- package/dist/client/index.d.cts.map +1 -0
- package/dist/client/index.d.ts +63 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +83 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/runs/index.cjs +275 -0
- package/dist/client/runs/index.cjs.map +1 -0
- package/dist/client/runs/index.d.cts +123 -0
- package/dist/client/runs/index.d.cts.map +1 -0
- package/dist/client/runs/index.d.ts +123 -0
- package/dist/client/runs/index.d.ts.map +1 -0
- package/dist/client/runs/index.js +275 -0
- package/dist/client/runs/index.js.map +1 -0
- package/dist/client/store/index.cjs +128 -0
- package/dist/client/store/index.cjs.map +1 -0
- package/dist/client/store/index.d.cts +75 -0
- package/dist/client/store/index.d.cts.map +1 -0
- package/dist/client/store/index.d.ts +75 -0
- package/dist/client/store/index.d.ts.map +1 -0
- package/dist/client/store/index.js +128 -0
- package/dist/client/store/index.js.map +1 -0
- package/dist/client/stream/error.cjs +18 -0
- package/dist/client/stream/error.cjs.map +1 -0
- package/dist/client/stream/error.d.cts +14 -0
- package/dist/client/stream/error.d.cts.map +1 -0
- package/dist/client/stream/error.d.ts +14 -0
- package/dist/client/stream/error.d.ts.map +1 -0
- package/dist/client/stream/error.js +18 -0
- package/dist/client/stream/error.js.map +1 -0
- package/dist/client/stream/handles/index.cjs +3 -0
- package/dist/client/stream/handles/index.d.ts +3 -0
- package/dist/client/stream/handles/index.js +4 -0
- package/dist/client/stream/handles/subagents.cjs +263 -0
- package/dist/client/stream/handles/subagents.cjs.map +1 -0
- package/dist/client/stream/handles/subagents.d.cts +45 -0
- package/dist/client/stream/handles/subagents.d.cts.map +1 -0
- package/dist/client/stream/handles/subagents.d.ts +45 -0
- package/dist/client/stream/handles/subagents.d.ts.map +1 -0
- package/dist/client/stream/handles/subagents.js +262 -0
- package/dist/client/stream/handles/subagents.js.map +1 -0
- package/dist/client/stream/handles/subgraphs.cjs +352 -0
- package/dist/client/stream/handles/subgraphs.cjs.map +1 -0
- package/dist/client/stream/handles/subgraphs.d.cts +82 -0
- package/dist/client/stream/handles/subgraphs.d.cts.map +1 -0
- package/dist/client/stream/handles/subgraphs.d.ts +82 -0
- package/dist/client/stream/handles/subgraphs.d.ts.map +1 -0
- package/dist/client/stream/handles/subgraphs.js +351 -0
- package/dist/client/stream/handles/subgraphs.js.map +1 -0
- package/dist/client/stream/handles/tools.cjs +92 -0
- package/dist/client/stream/handles/tools.cjs.map +1 -0
- package/dist/client/stream/handles/tools.d.cts +26 -0
- package/dist/client/stream/handles/tools.d.cts.map +1 -0
- package/dist/client/stream/handles/tools.d.ts +26 -0
- package/dist/client/stream/handles/tools.d.ts.map +1 -0
- package/dist/client/stream/handles/tools.js +92 -0
- package/dist/client/stream/handles/tools.js.map +1 -0
- package/dist/client/stream/index.cjs +1368 -0
- package/dist/client/stream/index.cjs.map +1 -0
- package/dist/client/stream/index.d.cts +238 -0
- package/dist/client/stream/index.d.cts.map +1 -0
- package/dist/client/stream/index.d.ts +238 -0
- package/dist/client/stream/index.d.ts.map +1 -0
- package/dist/client/stream/index.js +1367 -0
- package/dist/client/stream/index.js.map +1 -0
- package/dist/client/stream/media.cjs +506 -0
- package/dist/client/stream/media.cjs.map +1 -0
- package/dist/client/stream/media.d.cts +164 -0
- package/dist/client/stream/media.d.cts.map +1 -0
- package/dist/client/stream/media.d.ts +164 -0
- package/dist/client/stream/media.d.ts.map +1 -0
- package/dist/client/stream/media.js +505 -0
- package/dist/client/stream/media.js.map +1 -0
- package/dist/client/stream/messages.cjs +635 -0
- package/dist/client/stream/messages.cjs.map +1 -0
- package/dist/client/stream/messages.d.cts +139 -0
- package/dist/client/stream/messages.d.cts.map +1 -0
- package/dist/client/stream/messages.d.ts +139 -0
- package/dist/client/stream/messages.d.ts.map +1 -0
- package/dist/client/stream/messages.js +631 -0
- package/dist/client/stream/messages.js.map +1 -0
- package/dist/client/stream/multi-cursor-buffer.cjs +55 -0
- package/dist/client/stream/multi-cursor-buffer.cjs.map +1 -0
- package/dist/client/stream/multi-cursor-buffer.js +55 -0
- package/dist/client/stream/multi-cursor-buffer.js.map +1 -0
- package/dist/client/stream/subscription.cjs +85 -0
- package/dist/client/stream/subscription.cjs.map +1 -0
- package/dist/client/stream/subscription.d.cts +22 -0
- package/dist/client/stream/subscription.d.cts.map +1 -0
- package/dist/client/stream/subscription.d.ts +22 -0
- package/dist/client/stream/subscription.d.ts.map +1 -0
- package/dist/client/stream/subscription.js +84 -0
- package/dist/client/stream/subscription.js.map +1 -0
- package/dist/client/stream/transport/agent-server.cjs +45 -0
- package/dist/client/stream/transport/agent-server.cjs.map +1 -0
- package/dist/client/stream/transport/agent-server.d.cts +39 -0
- package/dist/client/stream/transport/agent-server.d.cts.map +1 -0
- package/dist/client/stream/transport/agent-server.d.ts +39 -0
- package/dist/client/stream/transport/agent-server.d.ts.map +1 -0
- package/dist/client/stream/transport/agent-server.js +45 -0
- package/dist/client/stream/transport/agent-server.js.map +1 -0
- package/dist/client/stream/transport/constants.cjs +10 -0
- package/dist/client/stream/transport/constants.cjs.map +1 -0
- package/dist/client/stream/transport/constants.js +10 -0
- package/dist/client/stream/transport/constants.js.map +1 -0
- package/dist/client/stream/transport/decoder.cjs +115 -0
- package/dist/client/stream/transport/decoder.cjs.map +1 -0
- package/dist/client/stream/transport/decoder.js +114 -0
- package/dist/client/stream/transport/decoder.js.map +1 -0
- package/dist/client/stream/transport/http.cjs +183 -0
- package/dist/client/stream/transport/http.cjs.map +1 -0
- package/dist/client/stream/transport/http.d.cts +45 -0
- package/dist/client/stream/transport/http.d.cts.map +1 -0
- package/dist/client/stream/transport/http.d.ts +45 -0
- package/dist/client/stream/transport/http.d.ts.map +1 -0
- package/dist/client/stream/transport/http.js +183 -0
- package/dist/client/stream/transport/http.js.map +1 -0
- package/dist/client/stream/transport/index.cjs +3 -0
- package/dist/client/stream/transport/index.js +4 -0
- package/dist/client/stream/transport/queue.cjs +55 -0
- package/dist/client/stream/transport/queue.cjs.map +1 -0
- package/dist/client/stream/transport/queue.js +55 -0
- package/dist/client/stream/transport/queue.js.map +1 -0
- package/dist/client/stream/transport/stream.cjs +79 -0
- package/dist/client/stream/transport/stream.cjs.map +1 -0
- package/dist/client/stream/transport/stream.js +79 -0
- package/dist/client/stream/transport/stream.js.map +1 -0
- package/dist/client/stream/transport/types.d.cts +29 -0
- package/dist/client/stream/transport/types.d.cts.map +1 -0
- package/dist/client/stream/transport/types.d.ts +29 -0
- package/dist/client/stream/transport/types.d.ts.map +1 -0
- package/dist/client/stream/transport/utils.cjs +45 -0
- package/dist/client/stream/transport/utils.cjs.map +1 -0
- package/dist/client/stream/transport/utils.js +39 -0
- package/dist/client/stream/transport/utils.js.map +1 -0
- package/dist/client/stream/transport/websocket.cjs +155 -0
- package/dist/client/stream/transport/websocket.cjs.map +1 -0
- package/dist/client/stream/transport/websocket.d.cts +36 -0
- package/dist/client/stream/transport/websocket.d.cts.map +1 -0
- package/dist/client/stream/transport/websocket.d.ts +36 -0
- package/dist/client/stream/transport/websocket.d.ts.map +1 -0
- package/dist/client/stream/transport/websocket.js +155 -0
- package/dist/client/stream/transport/websocket.js.map +1 -0
- package/dist/client/stream/transport.d.cts +104 -0
- package/dist/client/stream/transport.d.cts.map +1 -0
- package/dist/client/stream/transport.d.ts +104 -0
- package/dist/client/stream/transport.d.ts.map +1 -0
- package/dist/client/stream/types.d.cts +208 -0
- package/dist/client/stream/types.d.cts.map +1 -0
- package/dist/client/stream/types.d.ts +208 -0
- package/dist/client/stream/types.d.ts.map +1 -0
- package/dist/client/threads/index.cjs +271 -0
- package/dist/client/threads/index.cjs.map +1 -0
- package/dist/client/threads/index.d.cts +235 -0
- package/dist/client/threads/index.d.cts.map +1 -0
- package/dist/client/threads/index.d.ts +235 -0
- package/dist/client/threads/index.d.ts.map +1 -0
- package/dist/client/threads/index.js +270 -0
- package/dist/client/threads/index.js.map +1 -0
- package/dist/client/ui-internal/index.cjs +29 -0
- package/dist/client/ui-internal/index.cjs.map +1 -0
- package/dist/client/ui-internal/index.d.cts +11 -0
- package/dist/client/ui-internal/index.d.cts.map +1 -0
- package/dist/client/ui-internal/index.d.ts +11 -0
- package/dist/client/ui-internal/index.d.ts.map +1 -0
- package/dist/client/ui-internal/index.js +29 -0
- package/dist/client/ui-internal/index.js.map +1 -0
- package/dist/client.cjs +35 -1308
- package/dist/client.d.cts +19 -857
- package/dist/client.d.ts +19 -857
- package/dist/client.js +16 -1301
- package/dist/index.cjs +25 -4
- package/dist/index.d.cts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +14 -3
- package/dist/react/stream.cjs.map +1 -1
- package/dist/react/stream.custom.cjs +1 -1
- package/dist/react/stream.custom.js +1 -1
- package/dist/react/stream.d.cts +2 -1
- package/dist/react/stream.d.cts.map +1 -1
- package/dist/react/stream.d.ts +2 -1
- package/dist/react/stream.d.ts.map +1 -1
- package/dist/react/stream.js.map +1 -1
- package/dist/react/stream.lgp.cjs +8 -6
- package/dist/react/stream.lgp.cjs.map +1 -1
- package/dist/react/stream.lgp.js +6 -4
- package/dist/react/stream.lgp.js.map +1 -1
- package/dist/react/types.d.cts +1 -1
- package/dist/react/types.d.ts +1 -1
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react-ui/server/server.cjs +1 -1
- package/dist/react-ui/server/server.cjs.map +1 -1
- package/dist/react-ui/server/server.js +1 -1
- package/dist/react-ui/server/server.js.map +1 -1
- package/dist/react-ui/types.cjs.map +1 -1
- package/dist/react-ui/types.d.cts +1 -1
- package/dist/react-ui/types.d.cts.map +1 -1
- package/dist/react-ui/types.d.ts +1 -1
- package/dist/react-ui/types.d.ts.map +1 -1
- package/dist/react-ui/types.js.map +1 -1
- package/dist/stream/assembled-to-message.cjs +121 -0
- package/dist/stream/assembled-to-message.cjs.map +1 -0
- package/dist/stream/assembled-to-message.d.cts +35 -0
- package/dist/stream/assembled-to-message.d.cts.map +1 -0
- package/dist/stream/assembled-to-message.d.ts +35 -0
- package/dist/stream/assembled-to-message.d.ts.map +1 -0
- package/dist/stream/assembled-to-message.js +119 -0
- package/dist/stream/assembled-to-message.js.map +1 -0
- package/dist/stream/channel-registry.cjs +224 -0
- package/dist/stream/channel-registry.cjs.map +1 -0
- package/dist/stream/channel-registry.d.cts +102 -0
- package/dist/stream/channel-registry.d.cts.map +1 -0
- package/dist/stream/channel-registry.d.ts +102 -0
- package/dist/stream/channel-registry.d.ts.map +1 -0
- package/dist/stream/channel-registry.js +224 -0
- package/dist/stream/channel-registry.js.map +1 -0
- package/dist/stream/constants.cjs +11 -0
- package/dist/stream/constants.cjs.map +1 -0
- package/dist/stream/constants.d.cts +10 -0
- package/dist/stream/constants.d.cts.map +1 -0
- package/dist/stream/constants.d.ts +10 -0
- package/dist/stream/constants.d.ts.map +1 -0
- package/dist/stream/constants.js +11 -0
- package/dist/stream/constants.js.map +1 -0
- package/dist/stream/controller.cjs +933 -0
- package/dist/stream/controller.cjs.map +1 -0
- package/dist/stream/controller.d.cts +135 -0
- package/dist/stream/controller.d.cts.map +1 -0
- package/dist/stream/controller.d.ts +135 -0
- package/dist/stream/controller.d.ts.map +1 -0
- package/dist/stream/controller.js +910 -0
- package/dist/stream/controller.js.map +1 -0
- package/dist/stream/discovery/index.d.ts +2 -0
- package/dist/stream/discovery/subagents.cjs +235 -0
- package/dist/stream/discovery/subagents.cjs.map +1 -0
- package/dist/stream/discovery/subagents.d.cts +18 -0
- package/dist/stream/discovery/subagents.d.cts.map +1 -0
- package/dist/stream/discovery/subagents.d.ts +18 -0
- package/dist/stream/discovery/subagents.d.ts.map +1 -0
- package/dist/stream/discovery/subagents.js +235 -0
- package/dist/stream/discovery/subagents.js.map +1 -0
- package/dist/stream/discovery/subgraphs.cjs +153 -0
- package/dist/stream/discovery/subgraphs.cjs.map +1 -0
- package/dist/stream/discovery/subgraphs.d.cts +19 -0
- package/dist/stream/discovery/subgraphs.d.cts.map +1 -0
- package/dist/stream/discovery/subgraphs.d.ts +19 -0
- package/dist/stream/discovery/subgraphs.d.ts.map +1 -0
- package/dist/stream/discovery/subgraphs.js +153 -0
- package/dist/stream/discovery/subgraphs.js.map +1 -0
- package/dist/stream/index.cjs +36 -0
- package/dist/stream/index.d.cts +25 -0
- package/dist/stream/index.d.ts +25 -0
- package/dist/stream/index.js +16 -0
- package/dist/stream/lifecycle-loading-tracker.cjs +83 -0
- package/dist/stream/lifecycle-loading-tracker.cjs.map +1 -0
- package/dist/stream/lifecycle-loading-tracker.js +83 -0
- package/dist/stream/lifecycle-loading-tracker.js.map +1 -0
- package/dist/stream/message-metadata-tracker.cjs +165 -0
- package/dist/stream/message-metadata-tracker.cjs.map +1 -0
- package/dist/stream/message-metadata-tracker.d.cts +24 -0
- package/dist/stream/message-metadata-tracker.d.cts.map +1 -0
- package/dist/stream/message-metadata-tracker.d.ts +24 -0
- package/dist/stream/message-metadata-tracker.d.ts.map +1 -0
- package/dist/stream/message-metadata-tracker.js +165 -0
- package/dist/stream/message-metadata-tracker.js.map +1 -0
- package/dist/stream/message-reconciliation.cjs +118 -0
- package/dist/stream/message-reconciliation.cjs.map +1 -0
- package/dist/stream/message-reconciliation.js +115 -0
- package/dist/stream/message-reconciliation.js.map +1 -0
- package/dist/stream/namespace.cjs +54 -0
- package/dist/stream/namespace.cjs.map +1 -0
- package/dist/stream/namespace.js +49 -0
- package/dist/stream/namespace.js.map +1 -0
- package/dist/stream/projections/channel.cjs +53 -0
- package/dist/stream/projections/channel.cjs.map +1 -0
- package/dist/stream/projections/channel.d.cts +22 -0
- package/dist/stream/projections/channel.d.cts.map +1 -0
- package/dist/stream/projections/channel.d.ts +22 -0
- package/dist/stream/projections/channel.d.ts.map +1 -0
- package/dist/stream/projections/channel.js +53 -0
- package/dist/stream/projections/channel.js.map +1 -0
- package/dist/stream/projections/extension.cjs +29 -0
- package/dist/stream/projections/extension.cjs.map +1 -0
- package/dist/stream/projections/extension.d.cts +7 -0
- package/dist/stream/projections/extension.d.cts.map +1 -0
- package/dist/stream/projections/extension.d.ts +7 -0
- package/dist/stream/projections/extension.d.ts.map +1 -0
- package/dist/stream/projections/extension.js +29 -0
- package/dist/stream/projections/extension.js.map +1 -0
- package/dist/stream/projections/index.cjs +6 -0
- package/dist/stream/projections/index.d.ts +6 -0
- package/dist/stream/projections/index.js +7 -0
- package/dist/stream/projections/media.cjs +81 -0
- package/dist/stream/projections/media.cjs.map +1 -0
- package/dist/stream/projections/media.d.cts +18 -0
- package/dist/stream/projections/media.d.cts.map +1 -0
- package/dist/stream/projections/media.d.ts +18 -0
- package/dist/stream/projections/media.d.ts.map +1 -0
- package/dist/stream/projections/media.js +78 -0
- package/dist/stream/projections/media.js.map +1 -0
- package/dist/stream/projections/messages.cjs +121 -0
- package/dist/stream/projections/messages.cjs.map +1 -0
- package/dist/stream/projections/messages.d.cts +8 -0
- package/dist/stream/projections/messages.d.cts.map +1 -0
- package/dist/stream/projections/messages.d.ts +8 -0
- package/dist/stream/projections/messages.d.ts.map +1 -0
- package/dist/stream/projections/messages.js +121 -0
- package/dist/stream/projections/messages.js.map +1 -0
- package/dist/stream/projections/runtime.cjs +44 -0
- package/dist/stream/projections/runtime.cjs.map +1 -0
- package/dist/stream/projections/runtime.js +44 -0
- package/dist/stream/projections/runtime.js.map +1 -0
- package/dist/stream/projections/tool-calls.cjs +50 -0
- package/dist/stream/projections/tool-calls.cjs.map +1 -0
- package/dist/stream/projections/tool-calls.d.cts +8 -0
- package/dist/stream/projections/tool-calls.d.cts.map +1 -0
- package/dist/stream/projections/tool-calls.d.ts +8 -0
- package/dist/stream/projections/tool-calls.d.ts.map +1 -0
- package/dist/stream/projections/tool-calls.js +50 -0
- package/dist/stream/projections/tool-calls.js.map +1 -0
- package/dist/stream/projections/values.cjs +52 -0
- package/dist/stream/projections/values.cjs.map +1 -0
- package/dist/stream/projections/values.d.cts +7 -0
- package/dist/stream/projections/values.d.cts.map +1 -0
- package/dist/stream/projections/values.d.ts +6 -0
- package/dist/stream/projections/values.d.ts.map +1 -0
- package/dist/stream/projections/values.js +52 -0
- package/dist/stream/projections/values.js.map +1 -0
- package/dist/stream/root-message-projection.cjs +256 -0
- package/dist/stream/root-message-projection.cjs.map +1 -0
- package/dist/stream/root-message-projection.js +256 -0
- package/dist/stream/root-message-projection.js.map +1 -0
- package/dist/stream/store.cjs +32 -0
- package/dist/stream/store.cjs.map +1 -0
- package/dist/stream/store.d.cts +37 -0
- package/dist/stream/store.d.cts.map +1 -0
- package/dist/stream/store.d.ts +37 -0
- package/dist/stream/store.d.ts.map +1 -0
- package/dist/stream/store.js +32 -0
- package/dist/stream/store.js.map +1 -0
- package/dist/stream/submit-coordinator.cjs +399 -0
- package/dist/stream/submit-coordinator.cjs.map +1 -0
- package/dist/stream/submit-coordinator.d.cts +27 -0
- package/dist/stream/submit-coordinator.d.cts.map +1 -0
- package/dist/stream/submit-coordinator.d.ts +27 -0
- package/dist/stream/submit-coordinator.d.ts.map +1 -0
- package/dist/stream/submit-coordinator.js +397 -0
- package/dist/stream/submit-coordinator.js.map +1 -0
- package/dist/stream/tool-calls.cjs +15 -0
- package/dist/stream/tool-calls.cjs.map +1 -0
- package/dist/stream/tool-calls.js +15 -0
- package/dist/stream/tool-calls.js.map +1 -0
- package/dist/stream/types-inference.d.cts +43 -0
- package/dist/stream/types-inference.d.cts.map +1 -0
- package/dist/stream/types-inference.d.ts +43 -0
- package/dist/stream/types-inference.d.ts.map +1 -0
- package/dist/stream/types.d.cts +354 -0
- package/dist/stream/types.d.cts.map +1 -0
- package/dist/stream/types.d.ts +354 -0
- package/dist/stream/types.d.ts.map +1 -0
- package/dist/types.d.cts +2 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/index.cjs +1 -1
- package/dist/ui/index.d.cts +3 -3
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +1 -1
- package/dist/ui/manager.cjs +2 -2
- package/dist/ui/manager.cjs.map +1 -1
- package/dist/ui/manager.d.cts +1 -0
- package/dist/ui/manager.d.cts.map +1 -1
- package/dist/ui/manager.d.ts +1 -0
- package/dist/ui/manager.d.ts.map +1 -1
- package/dist/ui/manager.js +2 -2
- package/dist/ui/manager.js.map +1 -1
- package/dist/ui/messages.cjs +50 -7
- package/dist/ui/messages.cjs.map +1 -1
- package/dist/ui/messages.d.cts.map +1 -1
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +51 -9
- package/dist/ui/messages.js.map +1 -1
- package/dist/ui/orchestrator-custom.cjs +1 -1
- package/dist/ui/orchestrator-custom.js +1 -1
- package/dist/ui/orchestrator.cjs +4 -3
- package/dist/ui/orchestrator.cjs.map +1 -1
- package/dist/ui/orchestrator.d.cts +1 -1
- package/dist/ui/orchestrator.d.cts.map +1 -1
- package/dist/ui/orchestrator.d.ts +1 -1
- package/dist/ui/orchestrator.d.ts.map +1 -1
- package/dist/ui/orchestrator.js +4 -3
- package/dist/ui/orchestrator.js.map +1 -1
- package/dist/ui/stream/agent.d.cts +1 -1
- package/dist/ui/stream/agent.d.cts.map +1 -1
- package/dist/ui/stream/agent.d.ts +1 -1
- package/dist/ui/stream/agent.d.ts.map +1 -1
- package/dist/ui/stream/base.d.cts +7 -6
- package/dist/ui/stream/base.d.cts.map +1 -1
- package/dist/ui/stream/base.d.ts +7 -6
- package/dist/ui/stream/base.d.ts.map +1 -1
- package/dist/ui/stream/deep-agent.d.cts +1 -1
- package/dist/ui/stream/deep-agent.d.cts.map +1 -1
- package/dist/ui/stream/deep-agent.d.ts +1 -1
- package/dist/ui/stream/deep-agent.d.ts.map +1 -1
- package/dist/ui/stream/index.d.cts +4 -4
- package/dist/ui/stream/index.d.cts.map +1 -1
- package/dist/ui/stream/index.d.ts +4 -4
- package/dist/ui/stream/index.d.ts.map +1 -1
- package/dist/ui/types.d.cts +3 -2
- package/dist/ui/types.d.cts.map +1 -1
- package/dist/ui/types.d.ts +2 -1
- package/dist/ui/types.d.ts.map +1 -1
- package/package.json +18 -8
- package/dist/client.cjs.map +0 -1
- package/dist/client.d.cts.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { AIMessage, AIMessageChunk, HumanMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
|
|
2
|
+
//#region src/stream/assembled-to-message.ts
|
|
3
|
+
/**
|
|
4
|
+
* Convert the protocol-native {@link AssembledMessage} (a namespace +
|
|
5
|
+
* content-block bag) into a class instance from
|
|
6
|
+
* `@langchain/core/messages`.
|
|
7
|
+
*
|
|
8
|
+
* The v2 messages channel carries `role` on the `message-start`
|
|
9
|
+
* event, but `MessageAssembler` drops it by the time it produces an
|
|
10
|
+
* `AssembledMessage`. The selector-side caller therefore captures the
|
|
11
|
+
* role separately (via the assembler's per-update hook, see
|
|
12
|
+
* `projections/messages.ts`) and passes it in.
|
|
13
|
+
*
|
|
14
|
+
* The conversion is intentionally lossless in the limit: when the
|
|
15
|
+
* message finishes we emit the same BaseMessage shape that a
|
|
16
|
+
* `values.messages` entry would round-trip to via
|
|
17
|
+
* {@link tryCoerceMessageLikeToMessage}. Mid-stream, partial blocks
|
|
18
|
+
* (e.g. tool_call_chunk) are folded into the same shape so the UI
|
|
19
|
+
* can render an incrementally-completing message.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Produce a `BaseMessage` class instance from an in-progress or
|
|
23
|
+
* finished assembled message. Safe to call repeatedly across deltas —
|
|
24
|
+
* each call returns a new instance whose content reflects the
|
|
25
|
+
* currently-observed blocks.
|
|
26
|
+
*/
|
|
27
|
+
function assembledToBaseMessage(input) {
|
|
28
|
+
const { id, role, blocks, toolCallId, usage } = input;
|
|
29
|
+
const content = extractContentString(blocks);
|
|
30
|
+
const toolCalls = extractToolCalls(blocks);
|
|
31
|
+
const toolCallChunks = extractToolCallChunks(blocks);
|
|
32
|
+
const additionalKwargs = usage != null ? { usage } : void 0;
|
|
33
|
+
switch (role) {
|
|
34
|
+
case "human": return new HumanMessage({
|
|
35
|
+
...id != null ? { id } : {},
|
|
36
|
+
content,
|
|
37
|
+
...additionalKwargs != null ? { additional_kwargs: additionalKwargs } : {}
|
|
38
|
+
});
|
|
39
|
+
case "system": return new SystemMessage({
|
|
40
|
+
...id != null ? { id } : {},
|
|
41
|
+
content,
|
|
42
|
+
...additionalKwargs != null ? { additional_kwargs: additionalKwargs } : {}
|
|
43
|
+
});
|
|
44
|
+
case "tool": return new ToolMessage({
|
|
45
|
+
...id != null ? { id } : {},
|
|
46
|
+
content,
|
|
47
|
+
tool_call_id: toolCallId ?? ""
|
|
48
|
+
});
|
|
49
|
+
default: {
|
|
50
|
+
const payload = {
|
|
51
|
+
...id != null ? { id } : {},
|
|
52
|
+
content,
|
|
53
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
|
|
54
|
+
...toolCallChunks.length > 0 ? { tool_call_chunks: toolCallChunks } : {},
|
|
55
|
+
...additionalKwargs != null ? { additional_kwargs: additionalKwargs } : {}
|
|
56
|
+
};
|
|
57
|
+
return toolCallChunks.length > 0 ? new AIMessageChunk(payload) : new AIMessage(payload);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Convenience: given the raw assembled message + the role captured
|
|
63
|
+
* from `message-start`, produce a `BaseMessage` with the same id.
|
|
64
|
+
*/
|
|
65
|
+
function assembledMessageToBaseMessage(assembled, role, extras = {}) {
|
|
66
|
+
return assembledToBaseMessage({
|
|
67
|
+
id: assembled.id,
|
|
68
|
+
role,
|
|
69
|
+
blocks: assembled.blocks,
|
|
70
|
+
toolCallId: extras.toolCallId,
|
|
71
|
+
usage: assembled.usage
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function extractContentString(blocks) {
|
|
75
|
+
let out = "";
|
|
76
|
+
for (const block of blocks) if (block.type === "text" && typeof block.text === "string") out += block.text;
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
function extractToolCalls(blocks) {
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const block of blocks) {
|
|
82
|
+
if (block.type !== "tool_call" && block.type !== "tool_use") continue;
|
|
83
|
+
const tc = block;
|
|
84
|
+
out.push({
|
|
85
|
+
id: tc.id ?? "",
|
|
86
|
+
name: tc.name ?? "",
|
|
87
|
+
args: normalizeToolCallArgs(tc.args ?? tc.input),
|
|
88
|
+
type: "tool_call"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function extractToolCallChunks(blocks) {
|
|
94
|
+
const out = [];
|
|
95
|
+
for (const block of blocks) {
|
|
96
|
+
if (block.type !== "tool_call_chunk") continue;
|
|
97
|
+
const tc = block;
|
|
98
|
+
out.push({
|
|
99
|
+
id: tc.id,
|
|
100
|
+
name: tc.name,
|
|
101
|
+
args: tc.args,
|
|
102
|
+
index: tc.index,
|
|
103
|
+
type: "tool_call_chunk"
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
function normalizeToolCallArgs(value) {
|
|
109
|
+
if (value != null && typeof value === "object" && !Array.isArray(value)) return value;
|
|
110
|
+
if (typeof value === "string" && value.length > 0) try {
|
|
111
|
+
const parsed = JSON.parse(value);
|
|
112
|
+
if (parsed != null && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
113
|
+
} catch {}
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { assembledMessageToBaseMessage, assembledToBaseMessage };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=assembled-to-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assembled-to-message.js","names":[],"sources":["../../src/stream/assembled-to-message.ts"],"sourcesContent":["/**\n * Convert the protocol-native {@link AssembledMessage} (a namespace +\n * content-block bag) into a class instance from\n * `@langchain/core/messages`.\n *\n * The v2 messages channel carries `role` on the `message-start`\n * event, but `MessageAssembler` drops it by the time it produces an\n * `AssembledMessage`. The selector-side caller therefore captures the\n * role separately (via the assembler's per-update hook, see\n * `projections/messages.ts`) and passes it in.\n *\n * The conversion is intentionally lossless in the limit: when the\n * message finishes we emit the same BaseMessage shape that a\n * `values.messages` entry would round-trip to via\n * {@link tryCoerceMessageLikeToMessage}. Mid-stream, partial blocks\n * (e.g. tool_call_chunk) are folded into the same shape so the UI\n * can render an incrementally-completing message.\n */\nimport {\n AIMessage,\n AIMessageChunk,\n HumanMessage,\n SystemMessage,\n ToolMessage,\n type BaseMessage,\n} from \"@langchain/core/messages\";\nimport type { ContentBlock, MessageRole, UsageInfo } from \"@langchain/protocol\";\nimport type { AssembledMessage } from \"../client/stream/messages.js\";\n\nexport type ExtendedMessageRole = MessageRole | \"tool\";\n\nexport interface AssembledToMessageInput {\n /** Stable message id (from `MessageStartData.id`). */\n id?: string;\n /** Author role captured from the `message-start` event. */\n role: ExtendedMessageRole;\n /** Content blocks assembled so far. */\n blocks: ContentBlock[];\n /** Tool-call id a `role: \"tool\"` message is responding to, if any. */\n toolCallId?: string;\n /** Final-token usage (populated on `message-finish`). */\n usage?: UsageInfo;\n}\n\n/**\n * Produce a `BaseMessage` class instance from an in-progress or\n * finished assembled message. Safe to call repeatedly across deltas —\n * each call returns a new instance whose content reflects the\n * currently-observed blocks.\n */\nexport function assembledToBaseMessage(\n input: AssembledToMessageInput\n): BaseMessage {\n const { id, role, blocks, toolCallId, usage } = input;\n const content = extractContentString(blocks);\n const toolCalls = extractToolCalls(blocks);\n const toolCallChunks = extractToolCallChunks(blocks);\n const additionalKwargs =\n usage != null ? ({ usage } as Record<string, unknown>) : undefined;\n\n switch (role) {\n case \"human\":\n return new HumanMessage({\n ...(id != null ? { id } : {}),\n content,\n ...(additionalKwargs != null\n ? { additional_kwargs: additionalKwargs }\n : {}),\n });\n case \"system\":\n return new SystemMessage({\n ...(id != null ? { id } : {}),\n content,\n ...(additionalKwargs != null\n ? { additional_kwargs: additionalKwargs }\n : {}),\n });\n case \"tool\":\n return new ToolMessage({\n ...(id != null ? { id } : {}),\n content,\n tool_call_id: toolCallId ?? \"\",\n });\n case \"ai\":\n default: {\n // Use `AIMessageChunk` whenever tool_call_chunks are present —\n // the concrete `AIMessage` class silently DROPS the\n // `tool_call_chunks` field, which would leave mid-stream tool\n // calls invisible to the UI (it sees an AI message with empty\n // content and no tool calls, rendering a blank bubble until\n // `content-block-finish` finally promotes the chunks to\n // finalized `tool_calls`). The chunk class is assignment-compatible\n // with `BaseMessage` and round-trips through the merge logic.\n const payload = {\n ...(id != null ? { id } : {}),\n content,\n ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),\n ...(toolCallChunks.length > 0\n ? { tool_call_chunks: toolCallChunks }\n : {}),\n ...(additionalKwargs != null\n ? { additional_kwargs: additionalKwargs }\n : {}),\n };\n return toolCallChunks.length > 0\n ? new AIMessageChunk(payload)\n : new AIMessage(payload);\n }\n }\n}\n\n/**\n * Convenience: given the raw assembled message + the role captured\n * from `message-start`, produce a `BaseMessage` with the same id.\n */\nexport function assembledMessageToBaseMessage(\n assembled: AssembledMessage,\n role: ExtendedMessageRole,\n extras: { toolCallId?: string } = {}\n): BaseMessage {\n return assembledToBaseMessage({\n id: assembled.id,\n role,\n blocks: assembled.blocks,\n toolCallId: extras.toolCallId,\n usage: assembled.usage,\n });\n}\n\n// ---------- helpers ----------\n\nfunction extractContentString(blocks: ContentBlock[]): string {\n let out = \"\";\n for (const block of blocks) {\n if (\n block.type === \"text\" &&\n typeof (block as { text?: unknown }).text === \"string\"\n ) {\n out += (block as { text: string }).text;\n }\n }\n return out;\n}\n\ninterface LooseToolCall {\n id: string;\n name: string;\n args: Record<string, unknown>;\n type: \"tool_call\";\n}\n\nfunction extractToolCalls(blocks: ContentBlock[]): LooseToolCall[] {\n const out: LooseToolCall[] = [];\n for (const block of blocks) {\n if (block.type !== \"tool_call\" && block.type !== \"tool_use\") continue;\n const tc = block as ToolCallLikeBlock;\n out.push({\n id: tc.id ?? \"\",\n name: tc.name ?? \"\",\n args: normalizeToolCallArgs(tc.args ?? tc.input),\n type: \"tool_call\",\n });\n }\n return out;\n}\n\ninterface ToolCallLikeBlock {\n id?: string;\n name?: string;\n args?: unknown;\n input?: unknown;\n}\n\ninterface LooseToolCallChunk {\n id?: string;\n name?: string;\n args?: string;\n index?: number;\n type: \"tool_call_chunk\";\n}\n\nfunction extractToolCallChunks(blocks: ContentBlock[]): LooseToolCallChunk[] {\n const out: LooseToolCallChunk[] = [];\n for (const block of blocks) {\n if (block.type !== \"tool_call_chunk\") continue;\n const tc = block as {\n id?: string;\n name?: string;\n args?: string;\n index?: number;\n };\n out.push({\n id: tc.id,\n name: tc.name,\n args: tc.args,\n index: tc.index,\n type: \"tool_call_chunk\",\n });\n }\n return out;\n}\n\nfunction normalizeToolCallArgs(value: unknown): Record<string, unknown> {\n if (value != null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n if (typeof value === \"string\" && value.length > 0) {\n try {\n const parsed = JSON.parse(value);\n if (\n parsed != null &&\n typeof parsed === \"object\" &&\n !Array.isArray(parsed)\n ) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // Partial streaming input is represented via tool_call_chunks.\n }\n }\n return {};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAgB,uBACd,OACa;CACb,MAAM,EAAE,IAAI,MAAM,QAAQ,YAAY,UAAU;CAChD,MAAM,UAAU,qBAAqB,OAAO;CAC5C,MAAM,YAAY,iBAAiB,OAAO;CAC1C,MAAM,iBAAiB,sBAAsB,OAAO;CACpD,MAAM,mBACJ,SAAS,OAAQ,EAAE,OAAO,GAA+B,KAAA;AAE3D,SAAQ,MAAR;EACE,KAAK,QACH,QAAO,IAAI,aAAa;GACtB,GAAI,MAAM,OAAO,EAAE,IAAI,GAAG,EAAE;GAC5B;GACA,GAAI,oBAAoB,OACpB,EAAE,mBAAmB,kBAAkB,GACvC,EAAE;GACP,CAAC;EACJ,KAAK,SACH,QAAO,IAAI,cAAc;GACvB,GAAI,MAAM,OAAO,EAAE,IAAI,GAAG,EAAE;GAC5B;GACA,GAAI,oBAAoB,OACpB,EAAE,mBAAmB,kBAAkB,GACvC,EAAE;GACP,CAAC;EACJ,KAAK,OACH,QAAO,IAAI,YAAY;GACrB,GAAI,MAAM,OAAO,EAAE,IAAI,GAAG,EAAE;GAC5B;GACA,cAAc,cAAc;GAC7B,CAAC;EAEJ,SAAS;GASP,MAAM,UAAU;IACd,GAAI,MAAM,OAAO,EAAE,IAAI,GAAG,EAAE;IAC5B;IACA,GAAI,UAAU,SAAS,IAAI,EAAE,YAAY,WAAW,GAAG,EAAE;IACzD,GAAI,eAAe,SAAS,IACxB,EAAE,kBAAkB,gBAAgB,GACpC,EAAE;IACN,GAAI,oBAAoB,OACpB,EAAE,mBAAmB,kBAAkB,GACvC,EAAE;IACP;AACD,UAAO,eAAe,SAAS,IAC3B,IAAI,eAAe,QAAQ,GAC3B,IAAI,UAAU,QAAQ;;;;;;;;AAShC,SAAgB,8BACd,WACA,MACA,SAAkC,EAAE,EACvB;AACb,QAAO,uBAAuB;EAC5B,IAAI,UAAU;EACd;EACA,QAAQ,UAAU;EAClB,YAAY,OAAO;EACnB,OAAO,UAAU;EAClB,CAAC;;AAKJ,SAAS,qBAAqB,QAAgC;CAC5D,IAAI,MAAM;AACV,MAAK,MAAM,SAAS,OAClB,KACE,MAAM,SAAS,UACf,OAAQ,MAA6B,SAAS,SAE9C,QAAQ,MAA2B;AAGvC,QAAO;;AAUT,SAAS,iBAAiB,QAAyC;CACjE,MAAM,MAAuB,EAAE;AAC/B,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,MAAM,SAAS,eAAe,MAAM,SAAS,WAAY;EAC7D,MAAM,KAAK;AACX,MAAI,KAAK;GACP,IAAI,GAAG,MAAM;GACb,MAAM,GAAG,QAAQ;GACjB,MAAM,sBAAsB,GAAG,QAAQ,GAAG,MAAM;GAChD,MAAM;GACP,CAAC;;AAEJ,QAAO;;AAkBT,SAAS,sBAAsB,QAA8C;CAC3E,MAAM,MAA4B,EAAE;AACpC,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,MAAM,SAAS,kBAAmB;EACtC,MAAM,KAAK;AAMX,MAAI,KAAK;GACP,IAAI,GAAG;GACP,MAAM,GAAG;GACT,MAAM,GAAG;GACT,OAAO,GAAG;GACV,MAAM;GACP,CAAC;;AAEJ,QAAO;;AAGT,SAAS,sBAAsB,OAAyC;AACtE,KAAI,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,CACrE,QAAO;AAET,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAC9C,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,MAAM;AAChC,MACE,UAAU,QACV,OAAO,WAAW,YAClB,CAAC,MAAM,QAAQ,OAAO,CAEtB,QAAO;SAEH;AAIV,QAAO,EAAE"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
const require_store = require("./store.cjs");
|
|
2
|
+
//#region src/stream/channel-registry.ts
|
|
3
|
+
/**
|
|
4
|
+
* Framework-agnostic ref-counted subscription cache.
|
|
5
|
+
*
|
|
6
|
+
* # What this module is
|
|
7
|
+
*
|
|
8
|
+
* Every framework binding (React, Vue, Svelte, Angular) owns one
|
|
9
|
+
* {@link ChannelRegistry} per {@link StreamController}. The registry
|
|
10
|
+
* is the single layer that:
|
|
11
|
+
*
|
|
12
|
+
* 1. Deduplicates server-side subscriptions across components — N
|
|
13
|
+
* hooks reading the same projection share one
|
|
14
|
+
* `thread.subscribe(...)` call and one {@link StreamStore}.
|
|
15
|
+
* 2. Lazily opens / tears down subscriptions in step with mounting
|
|
16
|
+
* and unmounting consumers (ref counting on `spec.key`).
|
|
17
|
+
* 3. Survives thread swaps — `controller.hydrate(newThreadId)`
|
|
18
|
+
* rebinds every live entry against the new thread without
|
|
19
|
+
* changing store identity, so React's
|
|
20
|
+
* `useSyncExternalStore` (and equivalents in other frameworks)
|
|
21
|
+
* keep working.
|
|
22
|
+
*
|
|
23
|
+
* # Why ref counting matters
|
|
24
|
+
*
|
|
25
|
+
* Most projections back at least one server subscription. Without
|
|
26
|
+
* deduplication, every additional consumer of e.g. `useMessages(sub)`
|
|
27
|
+
* would open its own SSE/WebSocket subscription, paying the same
|
|
28
|
+
* payload N times. The registry guarantees we only ever pay once per
|
|
29
|
+
* `spec.key`, regardless of how many consumers attach.
|
|
30
|
+
*
|
|
31
|
+
* # Why store identity is preserved on rebind
|
|
32
|
+
*
|
|
33
|
+
* Framework reactivity primitives subscribe to a store *instance* and
|
|
34
|
+
* memoise their last seen snapshot. If we minted a new store on every
|
|
35
|
+
* thread swap, every bound component would silently lose its
|
|
36
|
+
* subscription. Instead, the registry keeps the same {@link StreamStore}
|
|
37
|
+
* but resets its value to `spec.initial` and re-runs `spec.open()` —
|
|
38
|
+
* consumers observe a clean slate without re-subscribing.
|
|
39
|
+
*
|
|
40
|
+
* @see ProjectionSpec - The contract every projection implements.
|
|
41
|
+
* @see StreamStore - The observable store handed to consumers.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* Ref-counted, thread-aware projection registry.
|
|
45
|
+
*
|
|
46
|
+
* Owns the `spec.key → (store, runtime)` mapping for one
|
|
47
|
+
* {@link StreamController}. Lifecycle:
|
|
48
|
+
*
|
|
49
|
+
* - `acquire(spec)` → +1 ref, returns `{ store, release }`. The
|
|
50
|
+
* first acquire opens the projection's runtime; subsequent
|
|
51
|
+
* acquires for the same key share both the store and the
|
|
52
|
+
* runtime.
|
|
53
|
+
* - `release()` → -1 ref. When the last consumer releases,
|
|
54
|
+
* the entry is removed and its runtime disposed.
|
|
55
|
+
* - `bind(thread)` → swap or detach the underlying thread; every
|
|
56
|
+
* live entry's runtime is recreated against the new thread,
|
|
57
|
+
* keeping the same store identity.
|
|
58
|
+
* - `dispose()` → tear everything down (idempotent). Safe to
|
|
59
|
+
* call multiple times.
|
|
60
|
+
*
|
|
61
|
+
* The registry is intentionally not generic over a state shape —
|
|
62
|
+
* different consumers can hold projections producing different
|
|
63
|
+
* snapshot types, so the registry keys everything as `unknown` and
|
|
64
|
+
* lets {@link acquire} reapply the caller's `T` at the boundary.
|
|
65
|
+
*/
|
|
66
|
+
var ChannelRegistry = class {
|
|
67
|
+
/** Currently bound thread, or `undefined` while detached. */
|
|
68
|
+
#thread;
|
|
69
|
+
/** Read-only fan-out of the controller's root subscription. */
|
|
70
|
+
#rootBus;
|
|
71
|
+
/** All live entries, keyed by `spec.key`. */
|
|
72
|
+
#entries = /* @__PURE__ */ new Map();
|
|
73
|
+
/**
|
|
74
|
+
* Construct a registry bound to the controller's root event bus.
|
|
75
|
+
*
|
|
76
|
+
* The bus is forwarded to every projection's `open()` so root-scoped
|
|
77
|
+
* projections can avoid opening a second server subscription when
|
|
78
|
+
* their channel set is already covered by the root pump.
|
|
79
|
+
*
|
|
80
|
+
* @param rootBus - Read-only fan-out of the root subscription.
|
|
81
|
+
*/
|
|
82
|
+
constructor(rootBus) {
|
|
83
|
+
this.#rootBus = rootBus;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Rebind every live entry to a new {@link ThreadStream} (or detach
|
|
87
|
+
* when `thread == null`).
|
|
88
|
+
*
|
|
89
|
+
* Each live entry has its current runtime disposed (best-effort)
|
|
90
|
+
* and its store reset to `entry.initial` so consumers see a clean
|
|
91
|
+
* slate during the swap. When `thread != null`, a fresh runtime is
|
|
92
|
+
* opened against the new thread.
|
|
93
|
+
*
|
|
94
|
+
* Critically the {@link StreamStore} *instance* is preserved across
|
|
95
|
+
* the rebind: framework subscribers (e.g. React's
|
|
96
|
+
* `useSyncExternalStore`) keep observing the same store reference,
|
|
97
|
+
* so their subscriptions survive the swap.
|
|
98
|
+
*
|
|
99
|
+
* No-op when called with the currently bound thread.
|
|
100
|
+
*
|
|
101
|
+
* @param thread - The thread stream to bind, or `undefined` to detach.
|
|
102
|
+
*/
|
|
103
|
+
bind(thread) {
|
|
104
|
+
if (this.#thread === thread) return;
|
|
105
|
+
const previous = this.#thread;
|
|
106
|
+
this.#thread = thread;
|
|
107
|
+
for (const entry of this.#entries.values()) {
|
|
108
|
+
if (entry.runtime != null && previous != null) tryDispose(entry.runtime);
|
|
109
|
+
entry.runtime = void 0;
|
|
110
|
+
entry.store.setValue(entry.initial);
|
|
111
|
+
if (thread != null) entry.runtime = entry.open({
|
|
112
|
+
thread,
|
|
113
|
+
store: entry.store,
|
|
114
|
+
rootBus: this.#rootBus
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Currently bound thread (may be `undefined` pre-mount). */
|
|
119
|
+
get thread() {
|
|
120
|
+
return this.#thread;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Acquire a ref-counted projection.
|
|
124
|
+
*
|
|
125
|
+
* If no entry exists for `spec.key`, one is created (allocating a
|
|
126
|
+
* {@link StreamStore} seeded with `spec.initial`) and — when a
|
|
127
|
+
* thread is currently bound — its runtime is opened immediately.
|
|
128
|
+
* If an entry already exists, its ref count is incremented and the
|
|
129
|
+
* existing store is returned.
|
|
130
|
+
*
|
|
131
|
+
* The returned `release()` is idempotent: calling it more than once
|
|
132
|
+
* is a no-op. When the ref count drops to zero, the entry is removed
|
|
133
|
+
* and its runtime disposed (best-effort, never throws into callers).
|
|
134
|
+
*
|
|
135
|
+
* Safe to call from any framework lifecycle hook. Subsequent calls
|
|
136
|
+
* for the same `spec.key` always return the same `store` reference
|
|
137
|
+
* for the lifetime of the controller, so consumers can rely on store
|
|
138
|
+
* identity.
|
|
139
|
+
*
|
|
140
|
+
* @typeParam T - Snapshot type produced by this projection.
|
|
141
|
+
* @param spec - Projection contract; the registry keys off `spec.key`.
|
|
142
|
+
* @returns A `{ store, release }` handle.
|
|
143
|
+
*/
|
|
144
|
+
acquire(spec) {
|
|
145
|
+
let entry = this.#entries.get(spec.key);
|
|
146
|
+
if (entry == null) {
|
|
147
|
+
const store = new require_store.StreamStore(spec.initial);
|
|
148
|
+
const newEntry = {
|
|
149
|
+
key: spec.key,
|
|
150
|
+
store,
|
|
151
|
+
initial: spec.initial,
|
|
152
|
+
open: spec.open,
|
|
153
|
+
refCount: 0,
|
|
154
|
+
runtime: void 0
|
|
155
|
+
};
|
|
156
|
+
if (this.#thread != null) newEntry.runtime = spec.open({
|
|
157
|
+
thread: this.#thread,
|
|
158
|
+
store,
|
|
159
|
+
rootBus: this.#rootBus
|
|
160
|
+
});
|
|
161
|
+
this.#entries.set(spec.key, newEntry);
|
|
162
|
+
entry = newEntry;
|
|
163
|
+
}
|
|
164
|
+
entry.refCount += 1;
|
|
165
|
+
let released = false;
|
|
166
|
+
return {
|
|
167
|
+
store: entry.store,
|
|
168
|
+
release: () => {
|
|
169
|
+
if (released) return;
|
|
170
|
+
released = true;
|
|
171
|
+
const current = this.#entries.get(spec.key);
|
|
172
|
+
if (current == null) return;
|
|
173
|
+
current.refCount -= 1;
|
|
174
|
+
if (current.refCount <= 0) {
|
|
175
|
+
this.#entries.delete(spec.key);
|
|
176
|
+
if (current.runtime != null) tryDispose(current.runtime);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Tear everything down.
|
|
183
|
+
*
|
|
184
|
+
* Detaches the bound thread (so no further `bind()` calls reopen
|
|
185
|
+
* runtimes) and disposes every live runtime in parallel. Safe to
|
|
186
|
+
* call multiple times — subsequent calls find an empty registry
|
|
187
|
+
* and resolve immediately.
|
|
188
|
+
*/
|
|
189
|
+
async dispose() {
|
|
190
|
+
this.#thread = void 0;
|
|
191
|
+
const entries = [...this.#entries.values()];
|
|
192
|
+
this.#entries.clear();
|
|
193
|
+
await Promise.all(entries.map(async (entry) => {
|
|
194
|
+
if (entry.runtime != null) await tryDispose(entry.runtime);
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Number of live entries. Diagnostic-only — callers should not
|
|
199
|
+
* branch on this value at runtime; it exists for tests asserting
|
|
200
|
+
* that consumers properly release their projections.
|
|
201
|
+
*/
|
|
202
|
+
get size() {
|
|
203
|
+
return this.#entries.size;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Best-effort runtime disposal.
|
|
208
|
+
*
|
|
209
|
+
* `dispose()` should never throw, but a misbehaving projection should
|
|
210
|
+
* not be able to wedge the entire registry. We swallow disposal
|
|
211
|
+
* errors so the surrounding `bind()` / `release()` / `dispose()`
|
|
212
|
+
* paths always make progress.
|
|
213
|
+
*
|
|
214
|
+
* @param runtime - Runtime returned by {@link ProjectionSpec.open}.
|
|
215
|
+
*/
|
|
216
|
+
async function tryDispose(runtime) {
|
|
217
|
+
try {
|
|
218
|
+
await runtime.dispose();
|
|
219
|
+
} catch {}
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
exports.ChannelRegistry = ChannelRegistry;
|
|
223
|
+
|
|
224
|
+
//# sourceMappingURL=channel-registry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-registry.cjs","names":["#rootBus","#entries","#thread","StreamStore"],"sources":["../../src/stream/channel-registry.ts"],"sourcesContent":["/**\n * Framework-agnostic ref-counted subscription cache.\n *\n * # What this module is\n *\n * Every framework binding (React, Vue, Svelte, Angular) owns one\n * {@link ChannelRegistry} per {@link StreamController}. The registry\n * is the single layer that:\n *\n * 1. Deduplicates server-side subscriptions across components — N\n * hooks reading the same projection share one\n * `thread.subscribe(...)` call and one {@link StreamStore}.\n * 2. Lazily opens / tears down subscriptions in step with mounting\n * and unmounting consumers (ref counting on `spec.key`).\n * 3. Survives thread swaps — `controller.hydrate(newThreadId)`\n * rebinds every live entry against the new thread without\n * changing store identity, so React's\n * `useSyncExternalStore` (and equivalents in other frameworks)\n * keep working.\n *\n * # Why ref counting matters\n *\n * Most projections back at least one server subscription. Without\n * deduplication, every additional consumer of e.g. `useMessages(sub)`\n * would open its own SSE/WebSocket subscription, paying the same\n * payload N times. The registry guarantees we only ever pay once per\n * `spec.key`, regardless of how many consumers attach.\n *\n * # Why store identity is preserved on rebind\n *\n * Framework reactivity primitives subscribe to a store *instance* and\n * memoise their last seen snapshot. If we minted a new store on every\n * thread swap, every bound component would silently lose its\n * subscription. Instead, the registry keeps the same {@link StreamStore}\n * but resets its value to `spec.initial` and re-runs `spec.open()` —\n * consumers observe a clean slate without re-subscribing.\n *\n * @see ProjectionSpec - The contract every projection implements.\n * @see StreamStore - The observable store handed to consumers.\n */\nimport { StreamStore } from \"./store.js\";\nimport type {\n AcquiredProjection,\n ProjectionRuntime,\n ProjectionSpec,\n RootEventBus,\n ThreadStream,\n} from \"./types.js\";\n\n/**\n * Internal record kept for each unique `spec.key` actively held by at\n * least one consumer.\n *\n * We intentionally store `initial` and `open` separately from `spec`\n * so the registry never depends on the spec object's identity — two\n * specs sharing the same `key` but produced from different factory\n * calls (e.g. fresh objects on each render) still collapse onto the\n * same entry.\n */\ninterface Entry {\n /** Stable identity used for deduplication. */\n readonly key: string;\n /** Observable store handed back to every consumer of this key. */\n readonly store: StreamStore<unknown>;\n /** Initial snapshot reapplied on dispose / thread rebind. */\n readonly initial: unknown;\n /** Factory that opens the underlying subscription against a thread. */\n readonly open: ProjectionSpec<unknown>[\"open\"];\n /** Live consumers of this entry. Drops to 0 → entry is torn down. */\n refCount: number;\n /**\n * Active runtime returned by `open()`. Undefined while detached\n * (no thread bound yet, or a rebind is in progress).\n */\n runtime: ProjectionRuntime | undefined;\n}\n\n/**\n * Ref-counted, thread-aware projection registry.\n *\n * Owns the `spec.key → (store, runtime)` mapping for one\n * {@link StreamController}. Lifecycle:\n *\n * - `acquire(spec)` → +1 ref, returns `{ store, release }`. The\n * first acquire opens the projection's runtime; subsequent\n * acquires for the same key share both the store and the\n * runtime.\n * - `release()` → -1 ref. When the last consumer releases,\n * the entry is removed and its runtime disposed.\n * - `bind(thread)` → swap or detach the underlying thread; every\n * live entry's runtime is recreated against the new thread,\n * keeping the same store identity.\n * - `dispose()` → tear everything down (idempotent). Safe to\n * call multiple times.\n *\n * The registry is intentionally not generic over a state shape —\n * different consumers can hold projections producing different\n * snapshot types, so the registry keys everything as `unknown` and\n * lets {@link acquire} reapply the caller's `T` at the boundary.\n */\nexport class ChannelRegistry {\n /** Currently bound thread, or `undefined` while detached. */\n #thread: ThreadStream | undefined;\n\n /** Read-only fan-out of the controller's root subscription. */\n readonly #rootBus: RootEventBus;\n\n /** All live entries, keyed by `spec.key`. */\n readonly #entries = new Map<string, Entry>();\n\n /**\n * Construct a registry bound to the controller's root event bus.\n *\n * The bus is forwarded to every projection's `open()` so root-scoped\n * projections can avoid opening a second server subscription when\n * their channel set is already covered by the root pump.\n *\n * @param rootBus - Read-only fan-out of the root subscription.\n */\n constructor(rootBus: RootEventBus) {\n this.#rootBus = rootBus;\n }\n\n /**\n * Rebind every live entry to a new {@link ThreadStream} (or detach\n * when `thread == null`).\n *\n * Each live entry has its current runtime disposed (best-effort)\n * and its store reset to `entry.initial` so consumers see a clean\n * slate during the swap. When `thread != null`, a fresh runtime is\n * opened against the new thread.\n *\n * Critically the {@link StreamStore} *instance* is preserved across\n * the rebind: framework subscribers (e.g. React's\n * `useSyncExternalStore`) keep observing the same store reference,\n * so their subscriptions survive the swap.\n *\n * No-op when called with the currently bound thread.\n *\n * @param thread - The thread stream to bind, or `undefined` to detach.\n */\n bind(thread: ThreadStream | undefined): void {\n if (this.#thread === thread) return;\n const previous = this.#thread;\n this.#thread = thread;\n for (const entry of this.#entries.values()) {\n // Tear down any active runtime from the previous thread.\n if (entry.runtime != null && previous != null) {\n void tryDispose(entry.runtime);\n }\n entry.runtime = undefined;\n entry.store.setValue(entry.initial);\n if (thread != null) {\n entry.runtime = entry.open({\n thread,\n store: entry.store,\n rootBus: this.#rootBus,\n });\n }\n }\n }\n\n /** Currently bound thread (may be `undefined` pre-mount). */\n get thread(): ThreadStream | undefined {\n return this.#thread;\n }\n\n /**\n * Acquire a ref-counted projection.\n *\n * If no entry exists for `spec.key`, one is created (allocating a\n * {@link StreamStore} seeded with `spec.initial`) and — when a\n * thread is currently bound — its runtime is opened immediately.\n * If an entry already exists, its ref count is incremented and the\n * existing store is returned.\n *\n * The returned `release()` is idempotent: calling it more than once\n * is a no-op. When the ref count drops to zero, the entry is removed\n * and its runtime disposed (best-effort, never throws into callers).\n *\n * Safe to call from any framework lifecycle hook. Subsequent calls\n * for the same `spec.key` always return the same `store` reference\n * for the lifetime of the controller, so consumers can rely on store\n * identity.\n *\n * @typeParam T - Snapshot type produced by this projection.\n * @param spec - Projection contract; the registry keys off `spec.key`.\n * @returns A `{ store, release }` handle.\n */\n acquire<T>(spec: ProjectionSpec<T>): AcquiredProjection<T> {\n let entry = this.#entries.get(spec.key);\n if (entry == null) {\n const store = new StreamStore<T>(spec.initial);\n const newEntry: Entry = {\n key: spec.key,\n store: store as StreamStore<unknown>,\n initial: spec.initial as unknown,\n open: spec.open as ProjectionSpec<unknown>[\"open\"],\n refCount: 0,\n runtime: undefined,\n };\n // Open the runtime immediately when a thread is already bound.\n // Otherwise it will be opened lazily by the next `bind()` call.\n if (this.#thread != null) {\n newEntry.runtime = spec.open({\n thread: this.#thread,\n store,\n rootBus: this.#rootBus,\n });\n }\n this.#entries.set(spec.key, newEntry);\n entry = newEntry;\n }\n entry.refCount += 1;\n\n let released = false;\n return {\n store: entry.store as StreamStore<T>,\n release: () => {\n if (released) return;\n released = true;\n const current = this.#entries.get(spec.key);\n if (current == null) return;\n current.refCount -= 1;\n if (current.refCount <= 0) {\n this.#entries.delete(spec.key);\n if (current.runtime != null) void tryDispose(current.runtime);\n }\n },\n };\n }\n\n /**\n * Tear everything down.\n *\n * Detaches the bound thread (so no further `bind()` calls reopen\n * runtimes) and disposes every live runtime in parallel. Safe to\n * call multiple times — subsequent calls find an empty registry\n * and resolve immediately.\n */\n async dispose(): Promise<void> {\n this.#thread = undefined;\n const entries = [...this.#entries.values()];\n this.#entries.clear();\n await Promise.all(\n entries.map(async (entry) => {\n if (entry.runtime != null) await tryDispose(entry.runtime);\n })\n );\n }\n\n /**\n * Number of live entries. Diagnostic-only — callers should not\n * branch on this value at runtime; it exists for tests asserting\n * that consumers properly release their projections.\n */\n get size(): number {\n return this.#entries.size;\n }\n}\n\n/**\n * Best-effort runtime disposal.\n *\n * `dispose()` should never throw, but a misbehaving projection should\n * not be able to wedge the entire registry. We swallow disposal\n * errors so the surrounding `bind()` / `release()` / `dispose()`\n * paths always make progress.\n *\n * @param runtime - Runtime returned by {@link ProjectionSpec.open}.\n */\nasync function tryDispose(runtime: ProjectionRuntime): Promise<void> {\n try {\n await runtime.dispose();\n } catch {\n // Best-effort — dispose should never throw, but we don't want a\n // bad projection to wedge the registry.\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoGA,IAAa,kBAAb,MAA6B;;CAE3B;;CAGA;;CAGA,2BAAoB,IAAI,KAAoB;;;;;;;;;;CAW5C,YAAY,SAAuB;AACjC,QAAA,UAAgB;;;;;;;;;;;;;;;;;;;;CAqBlB,KAAK,QAAwC;AAC3C,MAAI,MAAA,WAAiB,OAAQ;EAC7B,MAAM,WAAW,MAAA;AACjB,QAAA,SAAe;AACf,OAAK,MAAM,SAAS,MAAA,QAAc,QAAQ,EAAE;AAE1C,OAAI,MAAM,WAAW,QAAQ,YAAY,KAClC,YAAW,MAAM,QAAQ;AAEhC,SAAM,UAAU,KAAA;AAChB,SAAM,MAAM,SAAS,MAAM,QAAQ;AACnC,OAAI,UAAU,KACZ,OAAM,UAAU,MAAM,KAAK;IACzB;IACA,OAAO,MAAM;IACb,SAAS,MAAA;IACV,CAAC;;;;CAMR,IAAI,SAAmC;AACrC,SAAO,MAAA;;;;;;;;;;;;;;;;;;;;;;;;CAyBT,QAAW,MAAgD;EACzD,IAAI,QAAQ,MAAA,QAAc,IAAI,KAAK,IAAI;AACvC,MAAI,SAAS,MAAM;GACjB,MAAM,QAAQ,IAAIG,cAAAA,YAAe,KAAK,QAAQ;GAC9C,MAAM,WAAkB;IACtB,KAAK,KAAK;IACH;IACP,SAAS,KAAK;IACd,MAAM,KAAK;IACX,UAAU;IACV,SAAS,KAAA;IACV;AAGD,OAAI,MAAA,UAAgB,KAClB,UAAS,UAAU,KAAK,KAAK;IAC3B,QAAQ,MAAA;IACR;IACA,SAAS,MAAA;IACV,CAAC;AAEJ,SAAA,QAAc,IAAI,KAAK,KAAK,SAAS;AACrC,WAAQ;;AAEV,QAAM,YAAY;EAElB,IAAI,WAAW;AACf,SAAO;GACL,OAAO,MAAM;GACb,eAAe;AACb,QAAI,SAAU;AACd,eAAW;IACX,MAAM,UAAU,MAAA,QAAc,IAAI,KAAK,IAAI;AAC3C,QAAI,WAAW,KAAM;AACrB,YAAQ,YAAY;AACpB,QAAI,QAAQ,YAAY,GAAG;AACzB,WAAA,QAAc,OAAO,KAAK,IAAI;AAC9B,SAAI,QAAQ,WAAW,KAAW,YAAW,QAAQ,QAAQ;;;GAGlE;;;;;;;;;;CAWH,MAAM,UAAyB;AAC7B,QAAA,SAAe,KAAA;EACf,MAAM,UAAU,CAAC,GAAG,MAAA,QAAc,QAAQ,CAAC;AAC3C,QAAA,QAAc,OAAO;AACrB,QAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,UAAU;AAC3B,OAAI,MAAM,WAAW,KAAM,OAAM,WAAW,MAAM,QAAQ;IAC1D,CACH;;;;;;;CAQH,IAAI,OAAe;AACjB,SAAO,MAAA,QAAc;;;;;;;;;;;;;AAczB,eAAe,WAAW,SAA2C;AACnE,KAAI;AACF,QAAM,QAAQ,SAAS;SACjB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ThreadStream } from "../client/stream/index.cjs";
|
|
2
|
+
import { AcquiredProjection, ProjectionSpec, RootEventBus } from "./types.cjs";
|
|
3
|
+
|
|
4
|
+
//#region src/stream/channel-registry.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Ref-counted, thread-aware projection registry.
|
|
7
|
+
*
|
|
8
|
+
* Owns the `spec.key → (store, runtime)` mapping for one
|
|
9
|
+
* {@link StreamController}. Lifecycle:
|
|
10
|
+
*
|
|
11
|
+
* - `acquire(spec)` → +1 ref, returns `{ store, release }`. The
|
|
12
|
+
* first acquire opens the projection's runtime; subsequent
|
|
13
|
+
* acquires for the same key share both the store and the
|
|
14
|
+
* runtime.
|
|
15
|
+
* - `release()` → -1 ref. When the last consumer releases,
|
|
16
|
+
* the entry is removed and its runtime disposed.
|
|
17
|
+
* - `bind(thread)` → swap or detach the underlying thread; every
|
|
18
|
+
* live entry's runtime is recreated against the new thread,
|
|
19
|
+
* keeping the same store identity.
|
|
20
|
+
* - `dispose()` → tear everything down (idempotent). Safe to
|
|
21
|
+
* call multiple times.
|
|
22
|
+
*
|
|
23
|
+
* The registry is intentionally not generic over a state shape —
|
|
24
|
+
* different consumers can hold projections producing different
|
|
25
|
+
* snapshot types, so the registry keys everything as `unknown` and
|
|
26
|
+
* lets {@link acquire} reapply the caller's `T` at the boundary.
|
|
27
|
+
*/
|
|
28
|
+
declare class ChannelRegistry {
|
|
29
|
+
#private;
|
|
30
|
+
/**
|
|
31
|
+
* Construct a registry bound to the controller's root event bus.
|
|
32
|
+
*
|
|
33
|
+
* The bus is forwarded to every projection's `open()` so root-scoped
|
|
34
|
+
* projections can avoid opening a second server subscription when
|
|
35
|
+
* their channel set is already covered by the root pump.
|
|
36
|
+
*
|
|
37
|
+
* @param rootBus - Read-only fan-out of the root subscription.
|
|
38
|
+
*/
|
|
39
|
+
constructor(rootBus: RootEventBus);
|
|
40
|
+
/**
|
|
41
|
+
* Rebind every live entry to a new {@link ThreadStream} (or detach
|
|
42
|
+
* when `thread == null`).
|
|
43
|
+
*
|
|
44
|
+
* Each live entry has its current runtime disposed (best-effort)
|
|
45
|
+
* and its store reset to `entry.initial` so consumers see a clean
|
|
46
|
+
* slate during the swap. When `thread != null`, a fresh runtime is
|
|
47
|
+
* opened against the new thread.
|
|
48
|
+
*
|
|
49
|
+
* Critically the {@link StreamStore} *instance* is preserved across
|
|
50
|
+
* the rebind: framework subscribers (e.g. React's
|
|
51
|
+
* `useSyncExternalStore`) keep observing the same store reference,
|
|
52
|
+
* so their subscriptions survive the swap.
|
|
53
|
+
*
|
|
54
|
+
* No-op when called with the currently bound thread.
|
|
55
|
+
*
|
|
56
|
+
* @param thread - The thread stream to bind, or `undefined` to detach.
|
|
57
|
+
*/
|
|
58
|
+
bind(thread: ThreadStream | undefined): void;
|
|
59
|
+
/** Currently bound thread (may be `undefined` pre-mount). */
|
|
60
|
+
get thread(): ThreadStream | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Acquire a ref-counted projection.
|
|
63
|
+
*
|
|
64
|
+
* If no entry exists for `spec.key`, one is created (allocating a
|
|
65
|
+
* {@link StreamStore} seeded with `spec.initial`) and — when a
|
|
66
|
+
* thread is currently bound — its runtime is opened immediately.
|
|
67
|
+
* If an entry already exists, its ref count is incremented and the
|
|
68
|
+
* existing store is returned.
|
|
69
|
+
*
|
|
70
|
+
* The returned `release()` is idempotent: calling it more than once
|
|
71
|
+
* is a no-op. When the ref count drops to zero, the entry is removed
|
|
72
|
+
* and its runtime disposed (best-effort, never throws into callers).
|
|
73
|
+
*
|
|
74
|
+
* Safe to call from any framework lifecycle hook. Subsequent calls
|
|
75
|
+
* for the same `spec.key` always return the same `store` reference
|
|
76
|
+
* for the lifetime of the controller, so consumers can rely on store
|
|
77
|
+
* identity.
|
|
78
|
+
*
|
|
79
|
+
* @typeParam T - Snapshot type produced by this projection.
|
|
80
|
+
* @param spec - Projection contract; the registry keys off `spec.key`.
|
|
81
|
+
* @returns A `{ store, release }` handle.
|
|
82
|
+
*/
|
|
83
|
+
acquire<T>(spec: ProjectionSpec<T>): AcquiredProjection<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Tear everything down.
|
|
86
|
+
*
|
|
87
|
+
* Detaches the bound thread (so no further `bind()` calls reopen
|
|
88
|
+
* runtimes) and disposes every live runtime in parallel. Safe to
|
|
89
|
+
* call multiple times — subsequent calls find an empty registry
|
|
90
|
+
* and resolve immediately.
|
|
91
|
+
*/
|
|
92
|
+
dispose(): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Number of live entries. Diagnostic-only — callers should not
|
|
95
|
+
* branch on this value at runtime; it exists for tests asserting
|
|
96
|
+
* that consumers properly release their projections.
|
|
97
|
+
*/
|
|
98
|
+
get size(): number;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { ChannelRegistry };
|
|
102
|
+
//# sourceMappingURL=channel-registry.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-registry.d.cts","names":[],"sources":["../../src/stream/channel-registry.ts"],"mappings":";;;;;;;AAoGA;;;;;;;;;;;;;;;;;;;;cAAa,eAAA;EAAA;EAyFX;;;;;;;;;EAtEA,WAAA,CAAY,OAAA,EAAS,YAAA;EAyIb;;;;;;;;;;;;;;;;;;EAnHR,IAAA,CAAK,MAAA,EAAQ,YAAA;;MAsBT,MAAA,CAAA,GAAU,YAAA;;;;;;;;;;;;;;;;;;;;;;;EA0Bd,OAAA,GAAA,CAAW,IAAA,EAAM,cAAA,CAAe,CAAA,IAAK,kBAAA,CAAmB,CAAA;;;;;;;;;EAmDlD,OAAA,CAAA,GAAW,OAAA;;;;;;MAgBb,IAAA,CAAA;AAAA"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ThreadStream } from "../client/stream/index.js";
|
|
2
|
+
import { AcquiredProjection, ProjectionSpec, RootEventBus } from "./types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/stream/channel-registry.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Ref-counted, thread-aware projection registry.
|
|
7
|
+
*
|
|
8
|
+
* Owns the `spec.key → (store, runtime)` mapping for one
|
|
9
|
+
* {@link StreamController}. Lifecycle:
|
|
10
|
+
*
|
|
11
|
+
* - `acquire(spec)` → +1 ref, returns `{ store, release }`. The
|
|
12
|
+
* first acquire opens the projection's runtime; subsequent
|
|
13
|
+
* acquires for the same key share both the store and the
|
|
14
|
+
* runtime.
|
|
15
|
+
* - `release()` → -1 ref. When the last consumer releases,
|
|
16
|
+
* the entry is removed and its runtime disposed.
|
|
17
|
+
* - `bind(thread)` → swap or detach the underlying thread; every
|
|
18
|
+
* live entry's runtime is recreated against the new thread,
|
|
19
|
+
* keeping the same store identity.
|
|
20
|
+
* - `dispose()` → tear everything down (idempotent). Safe to
|
|
21
|
+
* call multiple times.
|
|
22
|
+
*
|
|
23
|
+
* The registry is intentionally not generic over a state shape —
|
|
24
|
+
* different consumers can hold projections producing different
|
|
25
|
+
* snapshot types, so the registry keys everything as `unknown` and
|
|
26
|
+
* lets {@link acquire} reapply the caller's `T` at the boundary.
|
|
27
|
+
*/
|
|
28
|
+
declare class ChannelRegistry {
|
|
29
|
+
#private;
|
|
30
|
+
/**
|
|
31
|
+
* Construct a registry bound to the controller's root event bus.
|
|
32
|
+
*
|
|
33
|
+
* The bus is forwarded to every projection's `open()` so root-scoped
|
|
34
|
+
* projections can avoid opening a second server subscription when
|
|
35
|
+
* their channel set is already covered by the root pump.
|
|
36
|
+
*
|
|
37
|
+
* @param rootBus - Read-only fan-out of the root subscription.
|
|
38
|
+
*/
|
|
39
|
+
constructor(rootBus: RootEventBus);
|
|
40
|
+
/**
|
|
41
|
+
* Rebind every live entry to a new {@link ThreadStream} (or detach
|
|
42
|
+
* when `thread == null`).
|
|
43
|
+
*
|
|
44
|
+
* Each live entry has its current runtime disposed (best-effort)
|
|
45
|
+
* and its store reset to `entry.initial` so consumers see a clean
|
|
46
|
+
* slate during the swap. When `thread != null`, a fresh runtime is
|
|
47
|
+
* opened against the new thread.
|
|
48
|
+
*
|
|
49
|
+
* Critically the {@link StreamStore} *instance* is preserved across
|
|
50
|
+
* the rebind: framework subscribers (e.g. React's
|
|
51
|
+
* `useSyncExternalStore`) keep observing the same store reference,
|
|
52
|
+
* so their subscriptions survive the swap.
|
|
53
|
+
*
|
|
54
|
+
* No-op when called with the currently bound thread.
|
|
55
|
+
*
|
|
56
|
+
* @param thread - The thread stream to bind, or `undefined` to detach.
|
|
57
|
+
*/
|
|
58
|
+
bind(thread: ThreadStream | undefined): void;
|
|
59
|
+
/** Currently bound thread (may be `undefined` pre-mount). */
|
|
60
|
+
get thread(): ThreadStream | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Acquire a ref-counted projection.
|
|
63
|
+
*
|
|
64
|
+
* If no entry exists for `spec.key`, one is created (allocating a
|
|
65
|
+
* {@link StreamStore} seeded with `spec.initial`) and — when a
|
|
66
|
+
* thread is currently bound — its runtime is opened immediately.
|
|
67
|
+
* If an entry already exists, its ref count is incremented and the
|
|
68
|
+
* existing store is returned.
|
|
69
|
+
*
|
|
70
|
+
* The returned `release()` is idempotent: calling it more than once
|
|
71
|
+
* is a no-op. When the ref count drops to zero, the entry is removed
|
|
72
|
+
* and its runtime disposed (best-effort, never throws into callers).
|
|
73
|
+
*
|
|
74
|
+
* Safe to call from any framework lifecycle hook. Subsequent calls
|
|
75
|
+
* for the same `spec.key` always return the same `store` reference
|
|
76
|
+
* for the lifetime of the controller, so consumers can rely on store
|
|
77
|
+
* identity.
|
|
78
|
+
*
|
|
79
|
+
* @typeParam T - Snapshot type produced by this projection.
|
|
80
|
+
* @param spec - Projection contract; the registry keys off `spec.key`.
|
|
81
|
+
* @returns A `{ store, release }` handle.
|
|
82
|
+
*/
|
|
83
|
+
acquire<T>(spec: ProjectionSpec<T>): AcquiredProjection<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Tear everything down.
|
|
86
|
+
*
|
|
87
|
+
* Detaches the bound thread (so no further `bind()` calls reopen
|
|
88
|
+
* runtimes) and disposes every live runtime in parallel. Safe to
|
|
89
|
+
* call multiple times — subsequent calls find an empty registry
|
|
90
|
+
* and resolve immediately.
|
|
91
|
+
*/
|
|
92
|
+
dispose(): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Number of live entries. Diagnostic-only — callers should not
|
|
95
|
+
* branch on this value at runtime; it exists for tests asserting
|
|
96
|
+
* that consumers properly release their projections.
|
|
97
|
+
*/
|
|
98
|
+
get size(): number;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { ChannelRegistry };
|
|
102
|
+
//# sourceMappingURL=channel-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-registry.d.ts","names":[],"sources":["../../src/stream/channel-registry.ts"],"mappings":";;;;;;;AAoGA;;;;;;;;;;;;;;;;;;;;cAAa,eAAA;EAAA;EAyFX;;;;;;;;;EAtEA,WAAA,CAAY,OAAA,EAAS,YAAA;EAyIb;;;;;;;;;;;;;;;;;;EAnHR,IAAA,CAAK,MAAA,EAAQ,YAAA;;MAsBT,MAAA,CAAA,GAAU,YAAA;;;;;;;;;;;;;;;;;;;;;;;EA0Bd,OAAA,GAAA,CAAW,IAAA,EAAM,cAAA,CAAe,CAAA,IAAK,kBAAA,CAAmB,CAAA;;;;;;;;;EAmDlD,OAAA,CAAA,GAAW,OAAA;;;;;;MAgBb,IAAA,CAAA;AAAA"}
|