@nice-code/action 0.24.0 → 0.25.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 (36) hide show
  1. package/README.md +54 -6
  2. package/build/{AcceptorHandler-11-QMdx2.d.mts → AcceptorHandler-BizUtq4u.d.mts} +118 -15
  3. package/build/{AcceptorHandler-CxD0c1BE.d.cts → AcceptorHandler-CxPfZtIl.d.cts} +118 -15
  4. package/build/{ActionDevtoolsCore-37JP4bOG.d.cts → ActionDevtoolsCore-D9KBBI2V.d.cts} +2 -2
  5. package/build/{ActionDevtoolsCore-Cgq-go1R.d.mts → ActionDevtoolsCore-xZjAtB4H.d.mts} +2 -2
  6. package/build/advanced/index.cjs +1 -1
  7. package/build/advanced/index.d.cts +1 -96
  8. package/build/advanced/index.d.mts +1 -96
  9. package/build/advanced/index.mjs +1 -1
  10. package/build/{createHibernatableWsServerAdapter-C07RfUTH.mjs → createHibernatableWsServerAdapter-BkjESd01.mjs} +11 -9
  11. package/build/createHibernatableWsServerAdapter-BkjESd01.mjs.map +1 -0
  12. package/build/{createHibernatableWsServerAdapter-BNi4k9j3.cjs → createHibernatableWsServerAdapter-FSDWrxoF.cjs} +11 -9
  13. package/build/createHibernatableWsServerAdapter-FSDWrxoF.cjs.map +1 -0
  14. package/build/devtools/browser/index.d.cts +1 -1
  15. package/build/devtools/browser/index.d.mts +1 -1
  16. package/build/devtools/server/index.d.cts +1 -1
  17. package/build/devtools/server/index.d.mts +1 -1
  18. package/build/{httpAcceptorCarrier-C3S_bDkL.cjs → httpAcceptorCarrier-BQYaXI9j.cjs} +2 -2
  19. package/build/{httpAcceptorCarrier-C3S_bDkL.cjs.map → httpAcceptorCarrier-BQYaXI9j.cjs.map} +1 -1
  20. package/build/{httpAcceptorCarrier-DPBEuewS.mjs → httpAcceptorCarrier-DWqsCz3h.mjs} +2 -2
  21. package/build/{httpAcceptorCarrier-DPBEuewS.mjs.map → httpAcceptorCarrier-DWqsCz3h.mjs.map} +1 -1
  22. package/build/index.cjs +6 -2
  23. package/build/index.d.cts +2 -2
  24. package/build/index.d.mts +2 -2
  25. package/build/index.mjs +3 -3
  26. package/build/platform/cloudflare/index.cjs +45 -1
  27. package/build/platform/cloudflare/index.cjs.map +1 -1
  28. package/build/platform/cloudflare/index.d.cts +40 -2
  29. package/build/platform/cloudflare/index.d.mts +40 -2
  30. package/build/platform/cloudflare/index.mjs +45 -2
  31. package/build/platform/cloudflare/index.mjs.map +1 -1
  32. package/build/react-query/index.d.cts +1 -1
  33. package/build/react-query/index.d.mts +1 -1
  34. package/package.json +4 -4
  35. package/build/createHibernatableWsServerAdapter-BNi4k9j3.cjs.map +0 -1
  36. package/build/createHibernatableWsServerAdapter-C07RfUTH.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"httpAcceptorCarrier-DPBEuewS.mjs","names":[],"sources":["../src/ActionRuntime/Transport/Carrier/Carrier.types.ts","../src/ActionRuntime/Transport/createTransport.ts","../src/ActionRuntime/Channel/ActionChannel.ts","../src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.ts","../src/ActionRuntime/Channel/serveChannel.ts","../src/ActionRuntime/Channel/serveHost.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts","../src/ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier.ts"],"sourcesContent":["import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../Transport.types\";\r\n\r\n/**\r\n * Carrier shapes — the only transport-specific surface a new protocol must implement. The secure\r\n * session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;\r\n * a carrier just moves frames. Two shapes capture every carrier:\r\n *\r\n * - {@link IDuplexCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,\r\n * Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client\r\n * pushes (the return path + broadcast).\r\n * - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP, and anything request/response-shaped). The reply rides the response to its own request.\r\n *\r\n * Frames are `string` (text — handshake control messages and JSON action frames) or binary\r\n * (`Uint8Array`/`ArrayBuffer` — the optimized binary wire / encrypted frames).\r\n */\r\nexport type TFrame = string | Uint8Array | ArrayBuffer;\r\n\r\n/**\r\n * A bidirectional, push-capable byte stream. Reduces every duplex carrier to \"open, send bytes, receive\r\n * bytes, close\" — a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe\r\n * all satisfy this, so the identical secure session runs over each.\r\n */\r\nexport interface IDuplexCarrier {\r\n /** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */\r\n readonly ready: Promise<void>;\r\n /** Whether the carrier is currently open (a synchronous guard before `send`). */\r\n isOpen(): boolean;\r\n /** Write one frame to the peer. */\r\n send(frame: TFrame): void;\r\n /**\r\n * Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`\r\n * receives every inbound frame; `onClose` fires when the carrier goes away.\r\n */\r\n attach(handlers: {\r\n onMessage: (frame: TFrame) => void;\r\n onClose: () => void;\r\n onError?: (error: unknown) => void;\r\n }): void;\r\n /** Close the carrier deliberately (a teardown). */\r\n close(): void;\r\n /** Optional human-readable endpoint for the devtools route chip. */\r\n readonly label?: string;\r\n}\r\n\r\n/**\r\n * A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a\r\n * frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the\r\n * HTTP transaction), so no correlation id is needed on the wire.\r\n */\r\nexport interface IExchangeCarrier {\r\n /** Send one frame, await the single correlated reply frame. */\r\n exchange(frame: TFrame, opts?: { signal?: AbortSignal }): Promise<TFrame>;\r\n /** Optional human-readable endpoint for the devtools route chip. */\r\n readonly label?: string;\r\n}\r\n\r\nexport type TCarrier = IDuplexCarrier | IExchangeCarrier;\r\n\r\n/**\r\n * A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.\r\n * Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and passed as a\r\n * `carrier` to `connectChannel`'s transports (the internal `transport()` factory drives it) — so adding a\r\n * new carrier is \"write one of these\", nothing else.\r\n */\r\nexport interface IDuplexCarrierSource {\r\n /** Open (or reuse) the carrier for an action. */\r\n open: (input: ITransportRouteActionParams) => IDuplexCarrier;\r\n /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\r\n getCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Devtools route info for an action routed over this carrier. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`, `\"memory\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an\r\n * {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by\r\n * `httpCarrier` and passed as a `carrier` to `connectChannel`'s transports — adding a new request/reply\r\n * protocol is \"write one of these\". The `shape` tag lets the internal `transport()` factory pick the\r\n * duplex vs exchange transport.\r\n */\r\nexport interface IExchangeCarrierSource {\r\n /** Discriminant so a generic factory can tell an exchange source from a duplex one. */\r\n readonly shape: \"exchange\";\r\n /** Open (or reuse) the carrier for an action. */\r\n open: (input: ITransportRouteActionParams) => IExchangeCarrier;\r\n /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\r\n getCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Devtools route info for an action routed over this carrier. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * Narrow a carrier source to the exchange shape via its `shape` discriminant — the one branch the\r\n * internal `transport()` factory uses to pick the duplex vs exchange transport. A duplex source carries\r\n * no `shape`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeCarrierSource(\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource,\r\n): carrier is IExchangeCarrierSource {\r\n return \"shape\" in carrier && carrier.shape === \"exchange\";\r\n}\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\nimport type { ActionRuntime } from \"../ActionRuntime\";\nimport type { IActionChannel } from \"../Channel/ActionChannel\";\nimport {\n type IDuplexCarrierSource,\n type IExchangeCarrierSource,\n isExchangeCarrierSource,\n} from \"./Carrier/Carrier.types\";\nimport { ESecurityLevel } from \"./crypto/actionHandshake\";\nimport { ExchangeTransport } from \"./Exchange/ExchangeTransport\";\nimport { LinkTransport } from \"./Link/LinkTransport\";\nimport type { ITransportRouteActionParams } from \"./Transport.types\";\n\nexport interface ITransportOptions {\n /**\n * How to reach the peer. A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`,\n * `inMemoryCarrier().carrier`) builds a push-capable {@link LinkTransport}; an exchange carrier\n * (`httpCarrier(...)`) builds a request/reply {@link ExchangeTransport}.\n */\n carrier: IDuplexCarrierSource | IExchangeCarrierSource;\n /** The shared channel identity (per-connection codec + dictionary version) — same one both ends use. */\n channel: IActionChannel;\n /** This peer's runtime — its coordinate is the authenticated identity sent in the handshake. */\n runtime: ActionRuntime;\n /**\n * Run the authenticated/encrypted handshake over this carrier (`true`), or carry the bare action wire\n * with no crypto (`false`). A secure transport needs a crypto identity (`link` or `storage`); a plain\n * one needs neither.\n */\n secure: boolean;\n /**\n * The crypto identity for a secure transport. Defaults to a fresh {@link ClientCryptoKeyLink} over\n * `storage`. Pass an existing link to share one identity across several secure transports to the same\n * peer (e.g. a secure WS preferred + secure HTTP fallback), so they present the same verify/exchange keys.\n */\n link?: ClientCryptoKeyLink;\n /** Backing store for the crypto identity when no `link` is given (secure transports only). */\n storage?: StorageAdapter;\n /** The level a secure transport requests; the peer must allow it. Defaults to `authenticated`. */\n securityLevel?: ESecurityLevel;\n /**\n * Optional availability gate. When it returns `false`, this transport reports as `unsupported` for that\n * action and the manager falls through to the next transport in preference order — without opening the\n * carrier or computing its cache key. Re-evaluated per dispatch. Omit = always available.\n */\n available?: (input: ITransportRouteActionParams) => boolean;\n /** Override the devtools chip label (defaults to the carrier's `carrierLabel`). */\n label?: string;\n}\n\n/**\n * The one transport factory — swap the carrier to change protocol, flip `secure` to add or drop the\n * crypto layer. A {@link IDuplexCarrierSource} builds a push-capable {@link LinkTransport} (WS is just\n * `carrier: wsCarrier(() => ({ url }))`), an {@link IExchangeCarrierSource} builds a request/reply\n * {@link ExchangeTransport} (HTTP). When `secure`, it folds in the handshake `security` block (the\n * {@link ClientCryptoKeyLink} from `storage`, the runtime coordinate, the channel's dictionary version);\n * when not, it carries the bare wire. The internal building block both `connectChannel` and `serveChannel`\n * drive — consumers connect via those single entry points, not this directly.\n */\nexport function transport(\n options: ITransportOptions & { carrier: IDuplexCarrierSource },\n): LinkTransport;\nexport function transport(\n options: ITransportOptions & { carrier: IExchangeCarrierSource },\n): ExchangeTransport;\nexport function transport(options: ITransportOptions): LinkTransport | ExchangeTransport;\nexport function transport(options: ITransportOptions): LinkTransport | ExchangeTransport {\n const { carrier, channel, available, label } = options;\n const labelFor = label ?? carrier.carrierLabel;\n\n // The handshake `security` block, assembled once for a secure transport (omitted for a plain one).\n let security: Parameters<typeof LinkTransport.create>[0][\"security\"];\n if (options.secure) {\n const link =\n options.link ??\n (options.storage != null\n ? new ClientCryptoKeyLink({ storageAdapter: options.storage })\n : undefined);\n if (link == null) {\n throw new Error(\n \"transport: a secure transport requires `link` or `storage` for the crypto identity.\",\n );\n }\n security = {\n securityLevel: options.securityLevel ?? ESecurityLevel.authenticated,\n link,\n localCoordinate: options.runtime.coordinate.toJsonObject(),\n dictionaryVersion: channel.dictionaryVersion,\n };\n }\n\n if (isExchangeCarrierSource(carrier)) {\n // An exchange carrier JSON-encodes the action wire in its envelope, so it needs no channel codec.\n return ExchangeTransport.create({\n openCarrier: carrier.open,\n getTransportCacheKey: carrier.getCacheKey,\n available,\n getRouteInfo: carrier.getRouteInfo,\n label: labelFor,\n security,\n });\n }\n\n // A duplex link frames the action wire itself, so it always needs the channel's codec.\n return LinkTransport.create({\n openChannel: carrier.open,\n createFormatMessage: channel.createCodec,\n getTransportCacheKey: carrier.getCacheKey,\n available,\n getRouteInfo: carrier.getRouteInfo,\n label: labelFor,\n security,\n });\n}\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { TWrappableDomainActionHandler } from \"../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport type { ActionRuntime } from \"../ActionRuntime\";\r\nimport type { ActionLocalHandler } from \"../Handler/Local/ActionLocalHandler\";\r\nimport type {\r\n AcceptorHandler,\r\n TAcceptorCaseFn,\r\n} from \"../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport {\r\n createSecureAcceptorHandler,\r\n type ISecureAcceptorHandlerOptions,\r\n} from \"../Handler/PeerLink/Acceptor/createSecureActionServer\";\r\nimport type { ConnectorHandler } from \"../Handler/PeerLink/Connector/ConnectorHandler\";\r\nimport type { RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\nimport type {\r\n IDuplexCarrierSource,\r\n IExchangeCarrierSource,\r\n} from \"../Transport/Carrier/Carrier.types\";\r\nimport {\r\n buildActionRouteDictionary,\r\n type IActionWireFormat,\r\n} from \"../Transport/codec/actionWireCodec\";\r\nimport {\r\n createBinaryWireSessionFactory,\r\n type IBinaryWireSessionOptions,\r\n} from \"../Transport/codec/createBinaryWireSessionFactory\";\r\nimport { transport } from \"../Transport/createTransport\";\r\nimport { ESecurityLevel } from \"../Transport/crypto/actionHandshake\";\r\nimport type { Transport } from \"../Transport/Transport\";\r\nimport type { ITransportRouteActionParams } from \"../Transport/Transport.types\";\r\n\r\n/**\r\n * A transport-agnostic routing contract between two runtimes, declared *by role* rather than by\r\n * \"client\"/\"server\". The two ends are named for the only asymmetry that survives every carrier (WS,\r\n * WebRTC, BLE, raw TCP): which side dials and which side accepts.\r\n *\r\n * - The **connector** dials out and opens the link ({@link connectChannel}).\r\n * - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}).\r\n *\r\n * `toAcceptor` domains flow connector→acceptor (the classic \"request\"); `toConnector` domains flow\r\n * acceptor→connector (the classic \"push\"). Both ends derive their routing from the same channel instead\r\n * of restating domain lists — and because the contract is independent of how bytes move, the very same\r\n * channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback).\r\n *\r\n * Beyond the routing, a channel also carries its *wire identity* — the per-connection binary codec both\r\n * ends build from the same domain list, plus the `dictionaryVersion` the handshake checks for drift.\r\n * Whether a given transport runs encrypted is a per-transport choice (see {@link IConnectTransport.secure}\r\n * and the acceptor's `securityLevel`), not a property of the channel — so one `defineChannel` definition\r\n * serves both plain and secure transports.\r\n */\r\nexport interface IActionChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n> {\r\n /**\r\n * Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards\r\n * them over its transport(s); the acceptor executes them.\r\n */\r\n toAcceptorDomains: TO_ACCEPTOR;\r\n /**\r\n * Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local\r\n * handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a\r\n * bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply\r\n * never flow.\r\n */\r\n toConnectorDomains: TO_CONNECTOR;\r\n /** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */\r\n dictionaryVersion: string;\r\n /** Per-connection session codec factory (call once per live connection). */\r\n createCodec: () => IActionWireFormat;\r\n}\r\n\r\n/**\r\n * Derive a stable wire-dictionary version from the ordered route list (FNV-1a over `domain:id,…`), so\r\n * the version moves automatically whenever the transported domains change — a stale peer is then\r\n * rejected by the handshake instead of silently misrouting a positionally-packed frame.\r\n */\r\nfunction deriveDictionaryVersion(domains: ActionDomain<any>[]): string {\r\n const { intToRoute } = buildActionRouteDictionary(domains);\r\n const signature = intToRoute.map((route) => `${route.domain}:${route.id}`).join(\",\");\r\n\r\n let hash = 0x811c9dc5;\r\n for (let i = 0; i < signature.length; i++) {\r\n hash ^= signature.charCodeAt(i);\r\n hash = Math.imul(hash, 0x01000193);\r\n }\r\n return `auto:${(hash >>> 0).toString(16).padStart(8, \"0\")}`;\r\n}\r\n\r\n/**\r\n * Declare a transport-agnostic channel by role — the single source of truth both peers share. Each end\r\n * MUST call this with the same domains in the same order (the binary wire dictionary is positional); the\r\n * `dictionaryVersion` is derived from those domains unless you pin an explicit one. The wire dictionary\r\n * spans `[...toAcceptor, ...toConnector]` in that order, so add new domains to the end of their list to\r\n * keep older peers compatible.\r\n *\r\n * Declare the domains *by role* — `toAcceptor` (connector→acceptor requests) and `toConnector`\r\n * (acceptor→connector pushes) — so the routing for both ends is derived from the channel (see\r\n * {@link connectChannel} and {@link serveChannel}) instead of being restated at each end. Security is a\r\n * per-transport concern, not a channel one, so this same definition is used whether a transport runs\r\n * plain or encrypted.\r\n */\r\nexport function defineChannel<\r\n const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [],\r\n const TO_CONNECTOR extends readonly ActionDomain<any>[] = [],\r\n>(options: {\r\n /** Domains the connector sends to the acceptor (connector→acceptor requests), in a stable order. */\r\n toAcceptor: TO_ACCEPTOR;\r\n /** Domains the acceptor pushes to the connector (acceptor→connector), in a stable order. */\r\n toConnector: TO_CONNECTOR;\r\n /** Pin a human-readable version instead of the derived hash (must match on both ends). */\r\n dictionaryVersion?: string;\r\n /** Tuning for the per-connection binary session (e.g. correlation TTL). */\r\n sessionOptions?: IBinaryWireSessionOptions;\r\n}): IActionChannel<TO_ACCEPTOR, TO_CONNECTOR> {\r\n // Dictionary order is positional and must match on both ends: connector→acceptor routes first, then\r\n // acceptor→connector pushes.\r\n const allDomains: ActionDomain<any>[] = [...options.toAcceptor, ...options.toConnector];\r\n\r\n return {\r\n toAcceptorDomains: options.toAcceptor as TO_ACCEPTOR,\r\n toConnectorDomains: options.toConnector as TO_CONNECTOR,\r\n dictionaryVersion: options.dictionaryVersion ?? deriveDictionaryVersion(allDomains),\r\n createCodec: createBinaryWireSessionFactory(allDomains, options.sessionOptions),\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Channel-derived wiring (connector + acceptor)\r\n// ---------------------------------------------------------------------------\r\n\r\ntype TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void\r\n ? I\r\n : never;\r\n\r\ntype TDomainPushHandlers<D> =\r\n D extends ActionDomain<infer DEF> ? Partial<TWrappableDomainActionHandler<DEF>> : never;\r\n\r\n/**\r\n * The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action\r\n * handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so\r\n * the keys and input types follow the channel definition.\r\n */\r\nexport type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> =\r\n TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;\r\n\r\n/**\r\n * One transport to the peer, declared by *carrier* — the dial-out dual of `serveChannel`'s acceptor\r\n * carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto\r\n * identity) into each one, so a descriptor only carries what differs between transports: the carrier and\r\n * whether it runs the secure handshake.\r\n *\r\n * A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier\r\n * (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection\r\n * prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback).\r\n */\r\nexport interface IConnectTransport {\r\n /** How to reach the peer — a duplex carrier (push-capable) or an exchange carrier (request/reply). */\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource;\r\n /**\r\n * Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport\r\n * draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport\r\n * (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`.\r\n */\r\n secure?: boolean;\r\n /** Security level for this secure transport; defaults to the connection-level `securityLevel`. */\r\n securityLevel?: ESecurityLevel;\r\n /**\r\n * Optional availability gate — when it returns `false` this transport is skipped and the connection\r\n * falls through to the next in preference order, re-evaluated per dispatch. Omit = always available.\r\n */\r\n available?: (input: ITransportRouteActionParams) => boolean;\r\n /** Override the devtools chip label (defaults to the carrier's own label). */\r\n label?: string;\r\n}\r\n\r\nexport interface IConnectChannelOptions<TO_CONNECTOR extends readonly ActionDomain<any>[]> {\r\n /** The peer's runtime coordinate — the acceptor this connection dials. */\r\n peer: RuntimeCoordinate;\r\n /**\r\n * The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback).\r\n * They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and\r\n * falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into\r\n * each — the dial-out dual of `serveChannel`'s `carriers`.\r\n */\r\n transports: readonly IConnectTransport[];\r\n /**\r\n * One backing store for this connection's crypto identity, fanned across every *secure* transport so\r\n * they present the same verify/exchange keys. Required when any transport is secure (the default); a\r\n * fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an\r\n * existing identity.\r\n */\r\n storage?: StorageAdapter;\r\n /** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */\r\n link?: ClientCryptoKeyLink;\r\n /** Default security level for secure transports; defaults to `authenticated`. */\r\n securityLevel?: ESecurityLevel;\r\n /** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */\r\n onPush?: TChannelPushHandlers<TO_CONNECTOR>;\r\n /** Default per-action timeout for this connection. */\r\n defaultTimeout?: number;\r\n}\r\n\r\n/**\r\n * Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is\r\n * the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer,\r\n * `toConnector` pushes handled locally from `onPush`); the call binds the shared facts — the channel's\r\n * codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over\r\n * `storage`) — into every transport in `transports`, so none of them restate the channel or runtime.\r\n * List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback):\r\n * ```ts\r\n * const handler = connectChannel(runtime, lobbyChannel, {\r\n * peer: runtime_coordinate_lobby_do,\r\n * storage,\r\n * transports: [{ carrier: wsCarrier(() => ({ url })) }, { carrier: httpCarrier(...), secure: false }],\r\n * onPush: { player_joined: (p) => { … } },\r\n * });\r\n * ```\r\n * Returns the {@link ConnectorHandler} so the caller can later `clearTransportCache()` it.\r\n */\r\nexport function connectChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IConnectChannelOptions<TO_CONNECTOR>,\r\n): ConnectorHandler {\r\n const securityLevel = options.securityLevel ?? ESecurityLevel.authenticated;\r\n\r\n // One crypto identity for the whole connection, fanned across every secure transport so they present\r\n // the same keys — the dial-out mirror of `serveChannel` sharing one link across its carriers. Built\r\n // only when something needs it; a fully-plain connection uses no storage.\r\n const anySecure = options.transports.some((transport) => transport.secure ?? true);\r\n let link: ClientCryptoKeyLink | undefined = options.link;\r\n if (anySecure && link == null) {\r\n if (options.storage == null) {\r\n throw new Error(\r\n \"connectChannel: a secure transport requires `storage` (or `link`). Pass it, or set `secure: false` on the transport for a plain connection.\",\r\n );\r\n }\r\n link = new ClientCryptoKeyLink({ storageAdapter: options.storage });\r\n }\r\n\r\n const transports: Transport[] = options.transports.map((descriptor) =>\r\n transport({\r\n carrier: descriptor.carrier,\r\n channel,\r\n runtime,\r\n secure: descriptor.secure ?? true,\r\n link,\r\n securityLevel: descriptor.securityLevel ?? securityLevel,\r\n available: descriptor.available,\r\n label: descriptor.label,\r\n }),\r\n );\r\n\r\n // Each push domain self-filters `onPush` to its own action ids, so a channel with several push\r\n // domains splits one merged map across the right handlers.\r\n const pushHandlers =\r\n options.onPush != null\r\n ? channel.toConnectorDomains.map((domain) =>\r\n domain.wrapAsPartialLocalHandler(\r\n options.onPush as Parameters<typeof domain.wrapAsPartialLocalHandler>[0],\r\n ),\r\n )\r\n : [];\r\n\r\n return runtime.connectTo(options.peer, {\r\n transports,\r\n domains: [...channel.toAcceptorDomains],\r\n localHandlers: pushHandlers,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n}\r\n\r\ntype TDomainAcceptorCases<D, TCtx> =\r\n D extends ActionDomain<infer DEF>\r\n ? { [ID in keyof DEF[\"actionSchema\"] & string]?: TAcceptorCaseFn<DEF, ID, TCtx> }\r\n : never;\r\n\r\n/**\r\n * The connection-aware case map for a channel's acceptor side: the merged set of every\r\n * connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus a per-action\r\n * `context`. `TCtx` is whatever the wiring supplies as that second argument — the raw connection\r\n * (`TConn | undefined`) for the low-level `acceptChannelConnections`, or an enriched `IConnectionContext`\r\n * for `serveChannel`'s `channelCases`. Derived from the channel's `toAcceptorDomains`, so the keys and\r\n * input/output types follow the channel.\r\n */\r\nexport type TChannelAcceptorCases<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TCtx,\r\n> = TUnionToIntersection<TDomainAcceptorCases<TO_ACCEPTOR[number], TCtx>>;\r\n\r\n/**\r\n * Register an acceptor handler's execution for a channel straight from its definition: the channel's\r\n * `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets\r\n * the primed request + the originating connection, as with\r\n * {@link AcceptorHandler.forConnectionDomainCases}). The domain list is taken from the channel,\r\n * never restated. Add the returned handler to the runtime alongside the acceptor handler:\r\n * ```ts\r\n * runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]);\r\n * ```\r\n *\r\n * The case's second argument is the raw connection (`TConn | undefined`). For the richer state +\r\n * broadcast + pushBack context, serve the channel through `serveChannel`'s `channelCases` instead.\r\n */\r\nexport function acceptChannelConnections<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn>(\r\n serverHandler: AcceptorHandler<TConn>,\r\n channel: IActionChannel<TO_ACCEPTOR, any>,\r\n cases: TChannelAcceptorCases<TO_ACCEPTOR, TConn | undefined>,\r\n): ActionLocalHandler {\r\n return serverHandler.forConnectionDomainCasesMulti(\r\n channel.toAcceptorDomains,\r\n cases as Record<string, TAcceptorCaseFn<any, any, TConn | undefined> | undefined>,\r\n (connection) => connection,\r\n );\r\n}\r\n\r\n/**\r\n * {@link acceptChannel}'s options — the secure-acceptor builder options minus the `channel` and `runtime`\r\n * it already takes positionally. One option bag, shared with the underlying {@link createSecureAcceptorHandler}.\r\n */\r\nexport type IAcceptChannelOptions<TConn> = Omit<\r\n ISecureAcceptorHandlerOptions<TConn>,\r\n \"channel\" | \"runtime\"\r\n>;\r\n\r\n/**\r\n * Build the secure {@link AcceptorHandler} for a channel — the accept-in counterpart to\r\n * {@link connectChannel}. It folds in the boilerplate of {@link createSecureAcceptorHandler} (the\r\n * `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storage`, the channel's codec +\r\n * dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel,\r\n * options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution:\r\n * ```ts\r\n * const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storage, send });\r\n * runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]);\r\n * ```\r\n */\r\nexport function acceptChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TConn = unknown,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IAcceptChannelOptions<TConn>,\r\n): AcceptorHandler<TConn> {\r\n return createSecureAcceptorHandler<TConn>({ ...options, channel, runtime });\r\n}\r\n","import type { IDuplexConnectionRouter } from \"../../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { ETransportShape } from \"../Transport.types\";\r\nimport type { TFrame } from \"./Carrier.types\";\r\n\r\n/**\r\n * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}\r\n * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an\r\n * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral\r\n * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block\r\n * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates\r\n * it.\r\n *\r\n * Two shapes mirror the connector side:\r\n *\r\n * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can\r\n * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step\r\n * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.\r\n * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to\r\n * upgrade.\r\n */\r\n\r\n/**\r\n * Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections\r\n * outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional — omit for a\r\n * transport that never hibernates (per-connection state is then in-memory only).\r\n *\r\n * `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when\r\n * `connectionState` is requested) per-connection app state as one composite in this single slot, so both\r\n * survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections.\r\n */\r\nexport interface IAcceptorAttachmentStore<TConn> {\r\n /** All currently-live connections — enumerated on build to replay binding + app state after a wake. */\r\n getConnections: () => TConn[];\r\n /** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */\r\n read: (connection: TConn) => unknown;\r\n /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */\r\n write: (connection: TConn, value: unknown) => void;\r\n}\r\n\r\n/**\r\n * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each\r\n * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a\r\n * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the\r\n * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if\r\n * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)\r\n */\r\nexport interface IDuplexCarrierLifecycle<TConn> {\r\n /** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */\r\n receive(connection: TConn, frame: TFrame): void;\r\n /** Forget a connection on close/error. No-op until the carrier is served. */\r\n drop(connection: TConn): void;\r\n /** @internal `serveChannel` binds this carrier's live connection router here. */\r\n _activate(router: IDuplexConnectionRouter<TConn>): void;\r\n}\r\n\r\n/**\r\n * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw\r\n * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex\r\n * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the\r\n * stateful-handle wiring lives in exactly one place.\r\n */\r\nexport function createDuplexCarrierLifecycle<TConn>(): IDuplexCarrierLifecycle<TConn> {\r\n let router: IDuplexConnectionRouter<TConn> | undefined;\r\n return {\r\n receive(connection, frame) {\r\n if (router == null) {\r\n throw new Error(\r\n \"acceptor carrier not served yet — pass it to serveChannel() before feeding frames\",\r\n );\r\n }\r\n router.receive(connection, frame);\r\n },\r\n drop(connection) {\r\n router?.drop(connection);\r\n },\r\n _activate(liveRouter) {\r\n router = liveRouter;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live\r\n * connection, how to perform the transport-specific upgrade that admits one, which requests are such\r\n * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and\r\n * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see\r\n * {@link IDuplexCarrierLifecycle}).\r\n */\r\nexport interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {\r\n /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */\r\n readonly shape: ETransportShape.duplex;\r\n /**\r\n * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex\r\n * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no\r\n * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain\r\n * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a\r\n * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that\r\n * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /**\r\n * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.\r\n * Only consulted when {@link upgrade} is present.\r\n */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /**\r\n * Optional attachment read/write for connections that survive eviction (Durable Object hibernation).\r\n * Present → `serveChannel` persists the routing binding (and any `connectionState`) here and replays it\r\n * on wake. Absent → per-connection state is in-memory only.\r\n */\r\n attachmentStore?: IAcceptorAttachmentStore<TConn>;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By\r\n * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose\r\n * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint\r\n * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's\r\n * plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). So a server can pair a secure\r\n * duplex (WebSocket) with a plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.\r\n */\r\nexport interface IExchangeAcceptorCarrier {\r\n /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */\r\n readonly shape: ETransportShape.exchange;\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain\r\n * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,\r\n * or encryption. A plain endpoint ignores the central crypto identity entirely.\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\nexport type TAcceptorCarrier<TConn = unknown> =\r\n | IDuplexAcceptorCarrier<TConn>\r\n | IExchangeAcceptorCarrier;\r\n\r\n/**\r\n * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch\r\n * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex\r\n * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeAcceptorCarrier<TConn>(\r\n carrier: TAcceptorCarrier<TConn>,\r\n): carrier is IExchangeAcceptorCarrier {\r\n return carrier.shape === ETransportShape.exchange;\r\n}\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\r\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { IActionDomain } from \"../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport type { ActionRuntime } from \"../ActionRuntime\";\r\nimport type { TActionRuntimeHandler } from \"../ActionRuntime.types\";\r\nimport {\r\n type AcceptorHandler,\r\n createAcceptorHandler,\r\n type TAcceptorCaseFn,\r\n} from \"../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport { createActionFetchHandler } from \"../Handler/PeerLink/Acceptor/createActionFetchHandler\";\r\nimport {\r\n type ConnectionStateStore,\r\n createConnectionStateStore,\r\n type IConnectionAttachment,\r\n} from \"../Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\r\nimport {\r\n createHibernatableWsServerAdapter,\r\n type IDuplexConnectionRouter,\r\n} from \"../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\nimport {\r\n type IDuplexAcceptorCarrier,\r\n type IExchangeAcceptorCarrier,\r\n isExchangeAcceptorCarrier,\r\n type TAcceptorCarrier,\r\n} from \"../Transport/Carrier/AcceptorCarrier.types\";\r\nimport {\r\n createStorageTofuVerifyKeyResolver,\r\n ESecurityLevel,\r\n type IClientVerifyKeyResolver,\r\n} from \"../Transport/crypto/actionHandshake\";\r\nimport type { IExchangeAcceptorSecurity } from \"../Transport/SecureSession/exchangeAcceptor\";\r\nimport { acceptChannel, type IActionChannel, type TChannelAcceptorCases } from \"./ActionChannel\";\r\n\r\n/** Default accepted set, shared by every carrier: negotiate per connection to whatever the client picks. */\r\nconst DEFAULT_SERVER_SECURITY_LEVELS = [\r\n ESecurityLevel.none,\r\n ESecurityLevel.authenticated,\r\n ESecurityLevel.encrypted,\r\n] as const;\r\n\r\n/** Per-connection app-state config for {@link serveChannel}'s `connectionState`. */\r\nexport interface IServeConnectionStateOptions<TApp> {\r\n /**\r\n * Optional Standard Schema (valibot, zod, …) validating the app state on read — a value that fails\r\n * validation reads back as `null`. Omit to store the app state untyped.\r\n */\r\n schema?: StandardSchemaV1<unknown, TApp>;\r\n}\r\n\r\n/**\r\n * The per-action handle a `serveChannel` `channelCases` case receives as its second argument — the\r\n * originating connection enriched with everything a case typically reaches for, so it never threads\r\n * `ws` through `this.connections` / `this.server` by hand:\r\n *\r\n * - {@link state} / {@link setState} / {@link clearState} — the connection's typed app state (when\r\n * `connectionState` is configured), co-stored with the routing binding so it survives hibernation.\r\n * - {@link broadcast} — fan a server push to every other connection (skip self with `exceptSelf`).\r\n * - {@link pushBack} — push a server-initiated action down *this* same connection.\r\n *\r\n * Over the HTTP-exchange path there is no live socket: {@link connection} is `null`, {@link state} reads\r\n * `null`, {@link setState}/{@link clearState} are no-ops, and {@link pushBack} throws (an exchange reply\r\n * rides its own request — it can't carry an unsolicited push).\r\n */\r\nexport interface IConnectionContext<TConn, TApp = unknown> {\r\n /** The originating live connection, or `null` on the HTTP-exchange path (escape hatch). */\r\n connection: TConn | null;\r\n /** The originating client's coordinate (`= action.context.originClient`). */\r\n origin: RuntimeCoordinate;\r\n /** This connection's app state, or `null` if unset / no live socket. */\r\n state: TApp | null;\r\n /** Set this connection's app state (no-op without a live socket). Preserves the routing binding. */\r\n setState: (value: TApp) => void;\r\n /** Clear this connection's app state but keep the routing binding (no-op without a live socket). */\r\n clearState: () => void;\r\n /** Fan a server-initiated action out to every connection; `exceptSelf` skips this one. */\r\n broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n options?: {\r\n exceptSelf?: boolean;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ) => void;\r\n /** Push a server-initiated action down this same connection. Throws if there is no live socket. */\r\n pushBack: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n request: ActionPayload_Request<DOM, ID>,\r\n options?: { timeout?: number },\r\n ) => RunningAction<DOM, ID>;\r\n}\r\n\r\nexport interface IServeChannelOptions<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TConn,\r\n TApp = unknown,\r\n> {\r\n /**\r\n * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env(\"web_app\")`),\r\n * used to score return-path dispatch back to the right connection.\r\n */\r\n clientEnv: RuntimeCoordinate;\r\n /**\r\n * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins\r\n * (their keys don't collide). It is built once and shared across every carrier, so the WebSocket and the\r\n * secure-HTTP endpoint present the exact same verify/exchange keys and trust the same pinned clients.\r\n * Back it with persistent storage (e.g. a Durable Object's storage) so identity + pins survive eviction.\r\n *\r\n * Required only when at least one carrier is secure (the default). A fully-plain server (every carrier\r\n * `secure: false`) needs no storage and may omit it.\r\n */\r\n storage?: StorageAdapter;\r\n /**\r\n * The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`.\r\n * Build them with `wsAcceptorCarrier` / `httpAcceptorCarrier`. Any number of duplex (push-capable)\r\n * carriers are supported (e.g. WebSocket + WebRTC), plus at most one exchange (request/reply) carrier;\r\n * all share one crypto identity and one runtime, and each result/push routes back over the carrier its\r\n * client connected on.\r\n */\r\n carriers: readonly TAcceptorCarrier<TConn>[];\r\n /** Your execution handlers (e.g. the local handler holding the action cases). Registered for you. */\r\n handlers?: TActionRuntimeHandler[];\r\n /**\r\n * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. Pass an\r\n * existing link only to share identity with acceptors built outside this call.\r\n */\r\n link?: ClientCryptoKeyLink;\r\n /** Accepted level(s) for every carrier; defaults to negotiating any of none/authenticated/encrypted. */\r\n securityLevel?: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n /** Timeout (ms) applied to server-initiated actions awaiting a client response. */\r\n defaultTimeout?: number;\r\n /**\r\n * Co-store per-connection app state alongside the routing binding in the sole duplex carrier's connection\r\n * attachment, so both survive a wake from eviction. Reach the typed store back on `server.connections`.\r\n * Requires the carrier to expose an attachment store (the Cloudflare `durableObjectWsCarrier` does) and\r\n * exactly one duplex carrier.\r\n */\r\n connectionState?: IServeConnectionStateOptions<TApp>;\r\n /**\r\n * Connection-aware action cases for the channel's acceptor (`toAcceptor`) domains — each case receives the\r\n * primed request *and* an {@link IConnectionContext} (the connection plus its typed `state` and\r\n * `broadcast`/`pushBack`). The connection-aware dual of `handlers`, registered on the runtime for you.\r\n * Requires exactly one duplex carrier.\r\n */\r\n channelCases?: TChannelAcceptorCases<TO_ACCEPTOR, IConnectionContext<TConn, TApp>>;\r\n}\r\n\r\n/**\r\n * One server serving a secure channel over several carriers — the accept-in dual of `connectChannel`,\r\n * returned by {@link serveChannel}. Wire its surface straight to the host's request/socket events.\r\n */\r\nexport interface IChannelServer<TConn, TApp = unknown> {\r\n /**\r\n * The duplex acceptor handlers — one per duplex carrier, in carrier order (empty if none). For pushing,\r\n * prefer {@link pushToClient} (it resolves the owning handler); reach for these for cross-carrier work\r\n * like a per-handler `broadcast`.\r\n */\r\n handlers: AcceptorHandler<TConn>[];\r\n /**\r\n * Unified request handler: answers the CORS preflight, performs the duplex upgrade for an upgrade\r\n * request, serves a secure-exchange action `POST`, else `404`. Forward the host's `fetch` straight to it.\r\n */\r\n fetch: (request: Request) => Promise<Response>;\r\n /**\r\n * Feed one inbound frame from a live connection into the server — forward your host's \"message\" event\r\n * here (a Durable Object's `webSocketMessage`, a Bun `websocket.message`, a Node `ws.on(\"message\")`).\r\n * Routes to the sole duplex carrier; throws with a clear message when there are zero or several duplex\r\n * carriers (for the multi-carrier case feed each `handlers[i]` / carrier handle directly).\r\n */\r\n receive: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\r\n /**\r\n * Forget a connection on close/error — forward your host's \"close\"/\"error\" event here. Routes to the\r\n * sole duplex carrier; a no-op when there are none (an HTTP-only server has no sockets to drop).\r\n */\r\n drop: (connection: TConn) => void;\r\n /**\r\n * Push a server-initiated action to a connected client (the runtime is bound in, so unlike\r\n * {@link AcceptorHandler.pushToClient} you pass only the target + request). It routes through the duplex\r\n * carrier the target connected on. Throws if no duplex carrier currently holds the target.\r\n */\r\n pushToClient: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n target: TConn | RuntimeCoordinate,\r\n request: ActionPayload_Request<DOM, ID>,\r\n options?: { timeout?: number },\r\n ) => RunningAction<DOM, ID>;\r\n /**\r\n * Fan a server-initiated action out to every connection on the sole duplex carrier (skip the origin with\r\n * `except`, filter with `where`). The push-to-many counterpart of {@link pushToClient}. Throws if there\r\n * isn't exactly one duplex carrier (with several, broadcast over a specific `handlers[i]`).\r\n */\r\n broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n options?: {\r\n except?: TConn | null;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ) => void;\r\n /**\r\n * The per-connection app-state store co-stored with the routing binding in the connection attachment —\r\n * present only when `connectionState` was passed. `get`/`set`/`clearApp`/`entries` it directly; it\r\n * survives hibernation alongside the binding.\r\n */\r\n connections?: ConnectionStateStore<TConn, TApp>;\r\n}\r\n\r\n/**\r\n * Serve a secure channel over one or more carriers from a single call — the accept-in dual of\r\n * `connectChannel`. It builds the crypto identity (a {@link ClientCryptoKeyLink} + a storage-backed TOFU\r\n * resolver) and the security block (coordinate, dictionary version, accepted levels) *once* from\r\n * `(runtime, channel)` and fans them across every carrier, so the WebSocket and the secure-HTTP endpoint\r\n * can never drift apart. It registers your handlers (plus the duplex acceptor it builds) on the runtime,\r\n * wires hibernation when the duplex carrier exposes an attachment store, and returns a single\r\n * {@link IChannelServer} whose `fetch` / `receive` / `drop` / `pushToClient` / `broadcast` you forward\r\n * straight to the host:\r\n * ```ts\r\n * const server = serveChannel(runtime, channel, {\r\n * clientEnv, storage,\r\n * carriers: [wsAcceptorCarrier({ send, upgrade, attachmentStore }), httpAcceptorCarrier()],\r\n * connectionState: { schema: vs_player }, // optional: co-store per-connection app state (survives hibernation)\r\n * channelCases: { join: (action, conn) => { conn.setState(action.input); … } }, // connection-aware cases\r\n * });\r\n * // fetch(req) => server.fetch(req)\r\n * // webSocketMessage(conn, m) => server.receive(conn, m)\r\n * // webSocketClose/Error(conn) => server.drop(conn)\r\n * // server.connections.get(conn) / server.broadcast(() => push.request(…), { except: conn })\r\n * ```\r\n *\r\n * On Cloudflare, `serveDurableObject` folds the whole DO transport stack (carriers + storage + keepalive)\r\n * into this — reach for it instead of assembling the carriers by hand.\r\n *\r\n * `TConn` (the live-connection token a duplex carrier hands back through `send`/`receive`/`drop`) is\r\n * inferred from the carriers — `WebSocket` for `wsAcceptorCarrier`, the data-channel type for a WebRTC\r\n * carrier, and so on — so it stays carrier-agnostic. Passing `connectionState` narrows the return so\r\n * `server.connections` is non-optional.\r\n */\r\nexport function serveChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TConn,\r\n TApp,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp> & {\r\n connectionState: IServeConnectionStateOptions<TApp>;\r\n },\r\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\r\nexport function serveChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TConn = unknown,\r\n TApp = unknown,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>,\r\n): IChannelServer<TConn, TApp>;\r\nexport function serveChannel(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\r\n options: IServeChannelOptions<readonly ActionDomain<any>[], any, any>,\r\n): IChannelServer<any, any> {\r\n type TConn = any;\r\n const duplexCarriers = options.carriers.filter(\r\n (carrier): carrier is IDuplexAcceptorCarrier<TConn> => !isExchangeAcceptorCarrier(carrier),\r\n );\r\n const exchangeCarriers = options.carriers.filter(isExchangeAcceptorCarrier);\r\n\r\n if (exchangeCarriers.length > 1) {\r\n throw new Error(\"serveChannel: at most one exchange carrier is supported\");\r\n }\r\n const exchangeCarrier: IExchangeAcceptorCarrier | undefined = exchangeCarriers[0];\r\n\r\n const singleDuplex = duplexCarriers.length === 1;\r\n if (options.connectionState != null && !singleDuplex) {\r\n throw new Error(\"serveChannel: `connectionState` requires exactly one duplex carrier\");\r\n }\r\n if (options.channelCases != null && !singleDuplex) {\r\n throw new Error(\"serveChannel: `channelCases` requires exactly one duplex carrier\");\r\n }\r\n\r\n const exchangeSecure = exchangeCarrier != null && (exchangeCarrier.secure ?? true);\r\n const anyDuplexSecure = duplexCarriers.some((carrier) => carrier.secure ?? true);\r\n const securityLevel = options.securityLevel ?? DEFAULT_SERVER_SECURITY_LEVELS;\r\n\r\n // The shared crypto identity (link + TOFU resolver) — built once and fanned across every *secure*\r\n // carrier. Only constructed when something needs it; a fully-plain server uses no storage.\r\n let secure:\r\n | {\r\n storage: StorageAdapter;\r\n link: ClientCryptoKeyLink;\r\n verifyKeyResolver: IClientVerifyKeyResolver;\r\n }\r\n | undefined;\r\n if (anyDuplexSecure || exchangeSecure) {\r\n const storage = options.storage;\r\n if (storage == null) {\r\n throw new Error(\r\n \"serveChannel: a secure carrier requires `storage`. Pass it, or set `secure: false` on the carrier for a plain endpoint.\",\r\n );\r\n }\r\n secure = {\r\n storage,\r\n link: options.link ?? new ClientCryptoKeyLink({ storageAdapter: storage }),\r\n verifyKeyResolver: options.verifyKeyResolver ?? createStorageTofuVerifyKeyResolver(storage),\r\n };\r\n }\r\n\r\n // One acceptor handler per duplex carrier, each activated as its own lifecycle handle. Secure carriers →\r\n // `acceptChannel` (handshake + the shared identity, so they present the same keys as the exchange\r\n // endpoint); plain carriers → the base `createAcceptorHandler` over the channel's wire codec, no crypto.\r\n // Phase-A connection-aware return routing then sends each result/push back over the carrier its client\r\n // connected on.\r\n // The plain (in-memory only) router for a connection — no persistence across eviction.\r\n const plainRouter = (handler: AcceptorHandler<TConn>): IDuplexConnectionRouter<TConn> => ({\r\n receive: (connection, frame) => handler.receive(connection, frame),\r\n drop: (connection) => handler.dropConnection(connection),\r\n });\r\n const asObject = (value: unknown): object =>\r\n typeof value === \"object\" && value != null ? value : {};\r\n\r\n const handlers: AcceptorHandler<TConn>[] = [];\r\n // The per-connection app-state store, built once over the sole duplex carrier when `connectionState` is set.\r\n let connections: ConnectionStateStore<TConn, any> | undefined;\r\n for (const carrier of duplexCarriers) {\r\n const handler =\r\n (carrier.secure ?? true) && secure != null\r\n ? acceptChannel<any, any, TConn>(runtime, channel, {\r\n clientEnv: options.clientEnv,\r\n storage: secure.storage,\r\n link: secure.link,\r\n verifyKeyResolver: secure.verifyKeyResolver,\r\n securityLevel,\r\n send: carrier.send,\r\n defaultTimeout: options.defaultTimeout,\r\n })\r\n : createAcceptorHandler<TConn>({\r\n clientEnv: options.clientEnv,\r\n createFormatMessage: channel.createCodec,\r\n send: carrier.send,\r\n runtime,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n\r\n // The attachment is one composite slot `{ app?, binding? }` per connection: serveChannel owns its\r\n // layout so binding *and* app state survive a wake from a single slot.\r\n // - no attachment store → in-memory only (no replay).\r\n // - `connectionState` requested → the connection store owns binding + app persistence and wake-replay.\r\n // - otherwise → the bare hibernation adapter persists/replays just the `.binding` sub-field.\r\n const attach = carrier.attachmentStore;\r\n let router: IDuplexConnectionRouter<TConn>;\r\n if (attach == null) {\r\n router = plainRouter(handler);\r\n } else if (options.connectionState != null) {\r\n connections = createConnectionStateStore<TConn, any>(handler, {\r\n schema: options.connectionState.schema,\r\n getConnections: attach.getConnections,\r\n read: attach.read,\r\n write: attach.write,\r\n });\r\n router = plainRouter(handler); // the store already replayed surviving bindings\r\n } else {\r\n router = createHibernatableWsServerAdapter<TConn>({\r\n handler,\r\n getConnections: attach.getConnections,\r\n getAttachment: (connection) =>\r\n (attach.read(connection) as IConnectionAttachment<unknown> | null | undefined)?.binding,\r\n setAttachment: (connection, binding) =>\r\n attach.write(connection, { ...asObject(attach.read(connection)), binding }),\r\n });\r\n }\r\n carrier._activate(router);\r\n handlers.push(handler);\r\n }\r\n\r\n runtime.addHandlers([...(options.handlers ?? []), ...handlers]);\r\n\r\n // --- Connection lifecycle + server-push surface ---\r\n // `receive`/`drop` are the universal forwarding seam every host environment feeds (a Durable Object's\r\n // `webSocketMessage`, a Bun `websocket.message`, …). They route to the sole duplex carrier (the common\r\n // case); with several, each carrier is its own handle, so callers feed those directly.\r\n const soleDuplex: IDuplexConnectionRouter<TConn> | undefined = singleDuplex\r\n ? duplexCarriers[0]\r\n : undefined;\r\n const receive = (connection: TConn, frame: string | Uint8Array | ArrayBuffer): void => {\r\n if (soleDuplex == null) {\r\n throw new Error(\r\n duplexCarriers.length === 0\r\n ? \"serveChannel: no duplex carrier to receive on (this server has no socket transport)\"\r\n : \"serveChannel: several duplex carriers — feed each carrier handle's receive() directly\",\r\n );\r\n }\r\n soleDuplex.receive(connection, frame);\r\n };\r\n const drop = (connection: TConn): void => {\r\n soleDuplex?.drop(connection);\r\n };\r\n\r\n const pushToClient = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n target: TConn | RuntimeCoordinate,\r\n request: ActionPayload_Request<DOM, ID>,\r\n pushOptions?: { timeout?: number },\r\n ): RunningAction<DOM, ID> => {\r\n // Route the push through the handler that actually holds the target's live connection.\r\n const owner =\r\n target instanceof RuntimeCoordinate\r\n ? handlers.find((handler) => handler.ownsLiveConnectionFor(target))\r\n : handlers.find((handler) => handler.hasConnection(target));\r\n if (owner == null) {\r\n throw new Error(\"serveChannel: no duplex carrier holds a connection for the push target\");\r\n }\r\n return owner.pushToClient(runtime, target, request, pushOptions);\r\n };\r\n\r\n const broadcast = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n broadcastOptions?: {\r\n except?: TConn | null;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ): void => {\r\n if (!singleDuplex) {\r\n throw new Error(\r\n \"serveChannel: broadcast requires exactly one duplex carrier — broadcast over a specific handlers[i] instead\",\r\n );\r\n }\r\n handlers[0].broadcast(makeRequest, { runtime, ...broadcastOptions });\r\n };\r\n\r\n // The per-action context handed to `channelCases`: the resolved connection enriched with typed `state`\r\n // plus `broadcast`/`pushBack`, so a case never reaches back into `connections`/the server by hand.\r\n const makeConnectionContext = (\r\n connection: TConn | undefined,\r\n request: ActionPayload_Request<any, any>,\r\n ): IConnectionContext<TConn, any> => ({\r\n connection: connection ?? null,\r\n origin: request.context.originClient,\r\n get state() {\r\n return connection != null && connections != null ? connections.get(connection) : null;\r\n },\r\n setState(value) {\r\n if (connection != null && connections != null) connections.set(connection, value);\r\n },\r\n clearState() {\r\n if (connection != null && connections != null) connections.clearApp(connection);\r\n },\r\n broadcast(makeRequest, contextOptions) {\r\n broadcast(makeRequest, {\r\n except: contextOptions?.exceptSelf ? (connection ?? null) : null,\r\n where: contextOptions?.where,\r\n timeout: contextOptions?.timeout,\r\n onError: contextOptions?.onError,\r\n });\r\n },\r\n pushBack(pushRequest, pushOptions) {\r\n if (connection == null) {\r\n throw new Error(\r\n \"serveChannel: connection context has no live socket to push back to (HTTP-exchange path)\",\r\n );\r\n }\r\n return pushToClient(connection, pushRequest, pushOptions);\r\n },\r\n });\r\n\r\n // Connection-aware action cases for the channel's acceptor domains, registered on the runtime alongside\r\n // the (sole) duplex acceptor — the connection-aware dual of `handlers`. Each case gets the enriched\r\n // `IConnectionContext`, built per inbound action.\r\n if (options.channelCases != null) {\r\n runtime.addHandlers([\r\n handlers[0].forConnectionDomainCasesMulti(\r\n channel.toAcceptorDomains,\r\n options.channelCases as Record<\r\n string,\r\n TAcceptorCaseFn<any, any, IConnectionContext<TConn, any>> | undefined\r\n >,\r\n makeConnectionContext,\r\n ),\r\n ]);\r\n }\r\n\r\n // The exchange (request/reply) security block — derived from the same identity + the channel/runtime\r\n // facts, so it never restates what the duplex side already knows. Omitted for a plain exchange carrier\r\n // (`secure: false`), which then POSTs the raw wire — letting a plain HTTP fallback sit beside a secure WS.\r\n const exchangeSecurity: IExchangeAcceptorSecurity | undefined =\r\n exchangeSecure && secure != null\r\n ? {\r\n link: secure.link,\r\n verifyKeyResolver: secure.verifyKeyResolver,\r\n localCoordinate: runtime.coordinate.toJsonObject(),\r\n dictionaryVersion: channel.dictionaryVersion,\r\n securityLevel,\r\n }\r\n : undefined;\r\n\r\n // Compose the upgrade across the duplex carriers that can be reached via an HTTP upgrade (typically just\r\n // one WebSocket carrier; a WebRTC carrier is signalled out of band and has no `upgrade`). The first\r\n // carrier whose `isUpgrade` matches handles the request.\r\n const defaultIsUpgrade = (request: Request) => request.headers.get(\"Upgrade\") === \"websocket\";\r\n const upgraders: {\r\n isUpgrade: (request: Request, url: URL) => boolean;\r\n upgrade: (request: Request, url: URL) => Response | Promise<Response>;\r\n }[] = [];\r\n for (const carrier of duplexCarriers) {\r\n if (carrier.upgrade == null) continue;\r\n upgraders.push({ isUpgrade: carrier.isUpgrade ?? defaultIsUpgrade, upgrade: carrier.upgrade });\r\n }\r\n\r\n const fetch = createActionFetchHandler(runtime, {\r\n cors: exchangeCarrier?.cors,\r\n onWebSocketUpgrade:\r\n upgraders.length === 0\r\n ? undefined\r\n : (request, url) =>\r\n (upgraders.find((u) => u.isUpgrade(request, url)) ?? upgraders[0]).upgrade(\r\n request,\r\n url,\r\n ),\r\n isWebSocketUpgrade:\r\n upgraders.length === 0\r\n ? undefined\r\n : (request, url) => upgraders.some((u) => u.isUpgrade(request, url)),\r\n // A single forwarding route means the path is already matched: any POST that isn't an upgrade is an\r\n // action exchange. With no exchange carrier there is no HTTP action endpoint, so POSTs fall through.\r\n isActionPath:\r\n exchangeCarrier != null ? (exchangeCarrier.isActionPath ?? (() => true)) : () => false,\r\n security: exchangeSecurity,\r\n useErrorStatus: exchangeCarrier?.useErrorStatus,\r\n });\r\n\r\n return { handlers, fetch, receive, drop, pushToClient, broadcast, connections };\r\n}\r\n","import type { StorageAdapter } from \"@nice-code/util\";\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\nimport type { ActionRuntime } from \"../ActionRuntime\";\nimport type { ConnectionStateStore } from \"../Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\nimport type { TAcceptorCarrier } from \"../Transport/Carrier/AcceptorCarrier.types\";\nimport type { IActionChannel } from \"./ActionChannel\";\nimport {\n type IChannelServer,\n type IServeChannelOptions,\n type IServeConnectionStateOptions,\n serveChannel,\n} from \"./serveChannel\";\n\n/**\n * An environment-neutral description of *where* a channel is served — the accept-in dual of a connector's\n * transport stack, factored out so a platform adapter (a Cloudflare Durable Object, a Bun/Node WebSocket\n * server, …) supplies only what differs per environment while the channel + case wiring stays identical.\n * A host bundles:\n *\n * - the {@link carriers} the channel is served over (e.g. a WebSocket + an HTTP fallback),\n * - the {@link storage} backing the server's crypto identity, and\n * - an {@link onServed} hook run once the server exists (e.g. registering a keepalive auto-response).\n *\n * Build one with a platform helper (`cloudflareDurableObjectHost`) and hand it to {@link serveHost}.\n */\nexport interface IChannelHostAdapter<TConn> {\n /** The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`. */\n carriers: readonly TAcceptorCarrier<TConn>[];\n /** Backing store for the server's crypto identity + TOFU pins. Required when any carrier is secure. */\n storage?: StorageAdapter;\n /** Run once after the server is built — e.g. register a transport keepalive. */\n onServed?: (server: IChannelServer<TConn, unknown>) => void;\n}\n\n/** {@link serveChannel}'s options minus what the host adapter supplies (`carriers`, `storage`). */\nexport type TServeHostOptions<\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\n TConn,\n TApp = unknown,\n> = Omit<IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>, \"carriers\" | \"storage\">;\n\n/**\n * Serve a channel over a {@link IChannelHostAdapter} — the environment-neutral core every platform helper\n * (e.g. `serveDurableObject`) composes. It folds the host's carriers + storage into `serveChannel`, then\n * runs the host's `onServed` hook. Everything else (`clientEnv`, `channelCases`, `connectionState`,\n * `handlers`, …) is the same `serveChannel` surface, so moving a server between environments is swapping\n * the host adapter and nothing else. Passing `connectionState` narrows the return so `server.connections`\n * is non-optional, exactly as with `serveChannel`.\n */\nexport function serveHost<\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\n TO_CONNECTOR extends readonly ActionDomain<any>[],\n TConn,\n TApp,\n>(\n runtime: ActionRuntime,\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n host: IChannelHostAdapter<TConn>,\n options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp> & {\n connectionState: IServeConnectionStateOptions<TApp>;\n },\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\nexport function serveHost<\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n TConn = unknown,\n TApp = unknown,\n>(\n runtime: ActionRuntime,\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n host: IChannelHostAdapter<TConn>,\n options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp>,\n): IChannelServer<TConn, TApp>;\nexport function serveHost(\n runtime: ActionRuntime,\n channel: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\n host: IChannelHostAdapter<any>,\n options: TServeHostOptions<readonly ActionDomain<any>[], any, any>,\n): IChannelServer<any, any> {\n const server = serveChannel(runtime, channel, {\n ...options,\n carriers: host.carriers,\n storage: host.storage,\n });\n host.onServed?.(server);\n return server;\n}\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport {\r\n createDuplexCarrierLifecycle,\r\n type IAcceptorAttachmentStore,\r\n type IDuplexAcceptorCarrier,\r\n} from \"../../AcceptorCarrier.types\";\r\nimport type { TFrame } from \"../../Carrier.types\";\r\n\r\nexport interface IWsAcceptorCarrierOptions<TConn> {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint\r\n * — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption\r\n * (and `serveChannel` then needs no `storage` for this carrier).\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's\r\n * `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band.\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /** Attachment read/write for hibernatable sockets (e.g. a Durable Object); `serveChannel` persists here. */\r\n attachmentStore?: IAcceptorAttachmentStore<TConn>;\r\n /** Override the devtools carrier-kind label (defaults to `\"ws\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to\r\n * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to\r\n * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,\r\n * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.\r\n */\r\nexport function wsAcceptorCarrier<TConn = WebSocket>(\r\n options: IWsAcceptorCarrierOptions<TConn>,\r\n): IDuplexAcceptorCarrier<TConn> {\r\n return {\r\n ...createDuplexCarrierLifecycle<TConn>(),\r\n shape: ETransportShape.duplex,\r\n carrierLabel: options.carrierLabel ?? \"ws\",\r\n secure: options.secure,\r\n send: options.send,\r\n upgrade: options.upgrade,\r\n isUpgrade: options.isUpgrade ?? ((request) => request.headers.get(\"Upgrade\") === \"websocket\"),\r\n attachmentStore: options.attachmentStore,\r\n };\r\n}\r\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport type { IExchangeAcceptorCarrier } from \"../../AcceptorCarrier.types\";\r\n\r\nexport interface IHttpAcceptorCarrierOptions {\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). Pass `false` for a plain\r\n * endpoint — the body is the raw action wire and the result is the response body, the request/reply dual\r\n * of a connector's plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). A plain\r\n * endpoint ignores the crypto identity, so it can sit alongside a secure WebSocket on the same server\r\n * (e.g. a secure WS preferred, plain HTTP fallback).\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Override the devtools carrier-kind label (defaults to `\"http\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * An HTTP {@link IExchangeAcceptorCarrier}: the accept-in dual of {@link httpCarrier}. It serves the\r\n * secure exchange protocol (handshake → token session → encrypted frames) over web-standard\r\n * `Request`/`Response`. The crypto identity, runtime coordinate, dictionary version, and accepted security\r\n * levels are all supplied centrally by `serveChannel`, so this only needs to say which requests carry an\r\n * action envelope and how to answer CORS.\r\n */\r\nexport function httpAcceptorCarrier(\r\n options: IHttpAcceptorCarrierOptions = {},\r\n): IExchangeAcceptorCarrier {\r\n return {\r\n shape: ETransportShape.exchange,\r\n carrierLabel: options.carrierLabel ?? \"http\",\r\n secure: options.secure,\r\n isActionPath: options.isActionPath,\r\n cors: options.cors,\r\n useErrorStatus: options.useErrorStatus,\r\n };\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"httpAcceptorCarrier-DWqsCz3h.mjs","names":[],"sources":["../src/ActionRuntime/Transport/Carrier/Carrier.types.ts","../src/ActionRuntime/Transport/createTransport.ts","../src/ActionRuntime/Channel/ActionChannel.ts","../src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.ts","../src/ActionRuntime/Channel/serveChannel.ts","../src/ActionRuntime/Channel/serveHost.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts","../src/ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier.ts"],"sourcesContent":["import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../Transport.types\";\r\n\r\n/**\r\n * Carrier shapes — the only transport-specific surface a new protocol must implement. The secure\r\n * session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;\r\n * a carrier just moves frames. Two shapes capture every carrier:\r\n *\r\n * - {@link IDuplexCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,\r\n * Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client\r\n * pushes (the return path + broadcast).\r\n * - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP, and anything request/response-shaped). The reply rides the response to its own request.\r\n *\r\n * Frames are `string` (text — handshake control messages and JSON action frames) or binary\r\n * (`Uint8Array`/`ArrayBuffer` — the optimized binary wire / encrypted frames).\r\n */\r\nexport type TFrame = string | Uint8Array | ArrayBuffer;\r\n\r\n/**\r\n * A bidirectional, push-capable byte stream. Reduces every duplex carrier to \"open, send bytes, receive\r\n * bytes, close\" — a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe\r\n * all satisfy this, so the identical secure session runs over each.\r\n */\r\nexport interface IDuplexCarrier {\r\n /** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */\r\n readonly ready: Promise<void>;\r\n /** Whether the carrier is currently open (a synchronous guard before `send`). */\r\n isOpen(): boolean;\r\n /** Write one frame to the peer. */\r\n send(frame: TFrame): void;\r\n /**\r\n * Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`\r\n * receives every inbound frame; `onClose` fires when the carrier goes away.\r\n */\r\n attach(handlers: {\r\n onMessage: (frame: TFrame) => void;\r\n onClose: () => void;\r\n onError?: (error: unknown) => void;\r\n }): void;\r\n /** Close the carrier deliberately (a teardown). */\r\n close(): void;\r\n /** Optional human-readable endpoint for the devtools route chip. */\r\n readonly label?: string;\r\n}\r\n\r\n/**\r\n * A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a\r\n * frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the\r\n * HTTP transaction), so no correlation id is needed on the wire.\r\n */\r\nexport interface IExchangeCarrier {\r\n /** Send one frame, await the single correlated reply frame. */\r\n exchange(frame: TFrame, opts?: { signal?: AbortSignal }): Promise<TFrame>;\r\n /** Optional human-readable endpoint for the devtools route chip. */\r\n readonly label?: string;\r\n}\r\n\r\nexport type TCarrier = IDuplexCarrier | IExchangeCarrier;\r\n\r\n/**\r\n * A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.\r\n * Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and passed as a\r\n * `carrier` to `connectChannel`'s transports (the internal `transport()` factory drives it) — so adding a\r\n * new carrier is \"write one of these\", nothing else.\r\n */\r\nexport interface IDuplexCarrierSource {\r\n /** Open (or reuse) the carrier for an action. */\r\n open: (input: ITransportRouteActionParams) => IDuplexCarrier;\r\n /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\r\n getCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Devtools route info for an action routed over this carrier. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`, `\"memory\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an\r\n * {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by\r\n * `httpCarrier` and passed as a `carrier` to `connectChannel`'s transports — adding a new request/reply\r\n * protocol is \"write one of these\". The `shape` tag lets the internal `transport()` factory pick the\r\n * duplex vs exchange transport.\r\n */\r\nexport interface IExchangeCarrierSource {\r\n /** Discriminant so a generic factory can tell an exchange source from a duplex one. */\r\n readonly shape: \"exchange\";\r\n /** Open (or reuse) the carrier for an action. */\r\n open: (input: ITransportRouteActionParams) => IExchangeCarrier;\r\n /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\r\n getCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Devtools route info for an action routed over this carrier. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * Narrow a carrier source to the exchange shape via its `shape` discriminant — the one branch the\r\n * internal `transport()` factory uses to pick the duplex vs exchange transport. A duplex source carries\r\n * no `shape`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeCarrierSource(\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource,\r\n): carrier is IExchangeCarrierSource {\r\n return \"shape\" in carrier && carrier.shape === \"exchange\";\r\n}\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\nimport type { ActionRuntime } from \"../ActionRuntime\";\nimport type { IActionChannel } from \"../Channel/ActionChannel\";\nimport {\n type IDuplexCarrierSource,\n type IExchangeCarrierSource,\n isExchangeCarrierSource,\n} from \"./Carrier/Carrier.types\";\nimport { ESecurityLevel } from \"./crypto/actionHandshake\";\nimport { ExchangeTransport } from \"./Exchange/ExchangeTransport\";\nimport { LinkTransport } from \"./Link/LinkTransport\";\nimport type { ITransportRouteActionParams } from \"./Transport.types\";\n\nexport interface ITransportOptions {\n /**\n * How to reach the peer. A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`,\n * `inMemoryCarrier().carrier`) builds a push-capable {@link LinkTransport}; an exchange carrier\n * (`httpCarrier(...)`) builds a request/reply {@link ExchangeTransport}.\n */\n carrier: IDuplexCarrierSource | IExchangeCarrierSource;\n /** The shared channel identity (per-connection codec + dictionary version) — same one both ends use. */\n channel: IActionChannel;\n /** This peer's runtime — its coordinate is the authenticated identity sent in the handshake. */\n runtime: ActionRuntime;\n /**\n * Run the authenticated/encrypted handshake over this carrier (`true`), or carry the bare action wire\n * with no crypto (`false`). A secure transport needs a crypto identity (`link` or `storage`); a plain\n * one needs neither.\n */\n secure: boolean;\n /**\n * The crypto identity for a secure transport. Defaults to a fresh {@link ClientCryptoKeyLink} over\n * `storage`. Pass an existing link to share one identity across several secure transports to the same\n * peer (e.g. a secure WS preferred + secure HTTP fallback), so they present the same verify/exchange keys.\n */\n link?: ClientCryptoKeyLink;\n /** Backing store for the crypto identity when no `link` is given (secure transports only). */\n storage?: StorageAdapter;\n /** The level a secure transport requests; the peer must allow it. Defaults to `authenticated`. */\n securityLevel?: ESecurityLevel;\n /**\n * Optional availability gate. When it returns `false`, this transport reports as `unsupported` for that\n * action and the manager falls through to the next transport in preference order — without opening the\n * carrier or computing its cache key. Re-evaluated per dispatch. Omit = always available.\n */\n available?: (input: ITransportRouteActionParams) => boolean;\n /** Override the devtools chip label (defaults to the carrier's `carrierLabel`). */\n label?: string;\n}\n\n/**\n * The one transport factory — swap the carrier to change protocol, flip `secure` to add or drop the\n * crypto layer. A {@link IDuplexCarrierSource} builds a push-capable {@link LinkTransport} (WS is just\n * `carrier: wsCarrier(() => ({ url }))`), an {@link IExchangeCarrierSource} builds a request/reply\n * {@link ExchangeTransport} (HTTP). When `secure`, it folds in the handshake `security` block (the\n * {@link ClientCryptoKeyLink} from `storage`, the runtime coordinate, the channel's dictionary version);\n * when not, it carries the bare wire. The internal building block both `connectChannel` and `serveChannel`\n * drive — consumers connect via those single entry points, not this directly.\n */\nexport function transport(\n options: ITransportOptions & { carrier: IDuplexCarrierSource },\n): LinkTransport;\nexport function transport(\n options: ITransportOptions & { carrier: IExchangeCarrierSource },\n): ExchangeTransport;\nexport function transport(options: ITransportOptions): LinkTransport | ExchangeTransport;\nexport function transport(options: ITransportOptions): LinkTransport | ExchangeTransport {\n const { carrier, channel, available, label } = options;\n const labelFor = label ?? carrier.carrierLabel;\n\n // The handshake `security` block, assembled once for a secure transport (omitted for a plain one).\n let security: Parameters<typeof LinkTransport.create>[0][\"security\"];\n if (options.secure) {\n const link =\n options.link ??\n (options.storage != null\n ? new ClientCryptoKeyLink({ storageAdapter: options.storage })\n : undefined);\n if (link == null) {\n throw new Error(\n \"transport: a secure transport requires `link` or `storage` for the crypto identity.\",\n );\n }\n security = {\n securityLevel: options.securityLevel ?? ESecurityLevel.authenticated,\n link,\n localCoordinate: options.runtime.coordinate.toJsonObject(),\n dictionaryVersion: channel.dictionaryVersion,\n };\n }\n\n if (isExchangeCarrierSource(carrier)) {\n // An exchange carrier JSON-encodes the action wire in its envelope, so it needs no channel codec.\n return ExchangeTransport.create({\n openCarrier: carrier.open,\n getTransportCacheKey: carrier.getCacheKey,\n available,\n getRouteInfo: carrier.getRouteInfo,\n label: labelFor,\n security,\n });\n }\n\n // A duplex link frames the action wire itself, so it always needs the channel's codec.\n return LinkTransport.create({\n openChannel: carrier.open,\n createFormatMessage: channel.createCodec,\n getTransportCacheKey: carrier.getCacheKey,\n available,\n getRouteInfo: carrier.getRouteInfo,\n label: labelFor,\n security,\n });\n}\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { TWrappableDomainActionHandler } from \"../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport type { ActionRuntime } from \"../ActionRuntime\";\r\nimport type { ActionLocalHandler } from \"../Handler/Local/ActionLocalHandler\";\r\nimport type {\r\n AcceptorHandler,\r\n TAcceptorCaseFn,\r\n} from \"../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport {\r\n createSecureAcceptorHandler,\r\n type ISecureAcceptorHandlerOptions,\r\n} from \"../Handler/PeerLink/Acceptor/createSecureActionServer\";\r\nimport type { ConnectorHandler } from \"../Handler/PeerLink/Connector/ConnectorHandler\";\r\nimport type { RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\nimport type {\r\n IDuplexCarrierSource,\r\n IExchangeCarrierSource,\r\n} from \"../Transport/Carrier/Carrier.types\";\r\nimport {\r\n buildActionRouteDictionary,\r\n type IActionWireFormat,\r\n} from \"../Transport/codec/actionWireCodec\";\r\nimport {\r\n createBinaryWireSessionFactory,\r\n type IBinaryWireSessionOptions,\r\n} from \"../Transport/codec/createBinaryWireSessionFactory\";\r\nimport { transport } from \"../Transport/createTransport\";\r\nimport { ESecurityLevel } from \"../Transport/crypto/actionHandshake\";\r\nimport type { Transport } from \"../Transport/Transport\";\r\nimport type { ITransportRouteActionParams } from \"../Transport/Transport.types\";\r\n\r\n/**\r\n * A transport-agnostic routing contract between two runtimes, declared *by role* rather than by\r\n * \"client\"/\"server\". The two ends are named for the only asymmetry that survives every carrier (WS,\r\n * WebRTC, BLE, raw TCP): which side dials and which side accepts.\r\n *\r\n * - The **connector** dials out and opens the link ({@link connectChannel}).\r\n * - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}).\r\n *\r\n * `toAcceptor` domains flow connector→acceptor (the classic \"request\"); `toConnector` domains flow\r\n * acceptor→connector (the classic \"push\"). Both ends derive their routing from the same channel instead\r\n * of restating domain lists — and because the contract is independent of how bytes move, the very same\r\n * channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback).\r\n *\r\n * Beyond the routing, a channel also carries its *wire identity* — the per-connection binary codec both\r\n * ends build from the same domain list, plus the `dictionaryVersion` the handshake checks for drift.\r\n * Whether a given transport runs encrypted is a per-transport choice (see {@link IConnectTransport.secure}\r\n * and the acceptor's `securityLevel`), not a property of the channel — so one `defineChannel` definition\r\n * serves both plain and secure transports.\r\n */\r\nexport interface IActionChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n> {\r\n /**\r\n * Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards\r\n * them over its transport(s); the acceptor executes them.\r\n */\r\n toAcceptorDomains: TO_ACCEPTOR;\r\n /**\r\n * Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local\r\n * handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a\r\n * bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply\r\n * never flow.\r\n */\r\n toConnectorDomains: TO_CONNECTOR;\r\n /** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */\r\n dictionaryVersion: string;\r\n /** Per-connection session codec factory (call once per live connection). */\r\n createCodec: () => IActionWireFormat;\r\n}\r\n\r\n/**\r\n * Derive a stable wire-dictionary version from the ordered route list (FNV-1a over `domain:id,…`), so\r\n * the version moves automatically whenever the transported domains change — a stale peer is then\r\n * rejected by the handshake instead of silently misrouting a positionally-packed frame.\r\n */\r\nfunction deriveDictionaryVersion(domains: ActionDomain<any>[]): string {\r\n const { intToRoute } = buildActionRouteDictionary(domains);\r\n const signature = intToRoute.map((route) => `${route.domain}:${route.id}`).join(\",\");\r\n\r\n let hash = 0x811c9dc5;\r\n for (let i = 0; i < signature.length; i++) {\r\n hash ^= signature.charCodeAt(i);\r\n hash = Math.imul(hash, 0x01000193);\r\n }\r\n return `auto:${(hash >>> 0).toString(16).padStart(8, \"0\")}`;\r\n}\r\n\r\n/**\r\n * Declare a transport-agnostic channel by role — the single source of truth both peers share. Each end\r\n * MUST call this with the same domains in the same order (the binary wire dictionary is positional); the\r\n * `dictionaryVersion` is derived from those domains unless you pin an explicit one. The wire dictionary\r\n * spans `[...toAcceptor, ...toConnector]` in that order, so add new domains to the end of their list to\r\n * keep older peers compatible.\r\n *\r\n * Declare the domains *by role* — `toAcceptor` (connector→acceptor requests) and `toConnector`\r\n * (acceptor→connector pushes) — so the routing for both ends is derived from the channel (see\r\n * {@link connectChannel} and {@link serveChannel}) instead of being restated at each end. Security is a\r\n * per-transport concern, not a channel one, so this same definition is used whether a transport runs\r\n * plain or encrypted.\r\n */\r\nexport function defineChannel<\r\n const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [],\r\n const TO_CONNECTOR extends readonly ActionDomain<any>[] = [],\r\n>(options: {\r\n /** Domains the connector sends to the acceptor (connector→acceptor requests), in a stable order. */\r\n toAcceptor: TO_ACCEPTOR;\r\n /** Domains the acceptor pushes to the connector (acceptor→connector), in a stable order. */\r\n toConnector: TO_CONNECTOR;\r\n /** Pin a human-readable version instead of the derived hash (must match on both ends). */\r\n dictionaryVersion?: string;\r\n /** Tuning for the per-connection binary session (e.g. correlation TTL). */\r\n sessionOptions?: IBinaryWireSessionOptions;\r\n}): IActionChannel<TO_ACCEPTOR, TO_CONNECTOR> {\r\n // Dictionary order is positional and must match on both ends: connector→acceptor routes first, then\r\n // acceptor→connector pushes.\r\n const allDomains: ActionDomain<any>[] = [...options.toAcceptor, ...options.toConnector];\r\n\r\n return {\r\n toAcceptorDomains: options.toAcceptor as TO_ACCEPTOR,\r\n toConnectorDomains: options.toConnector as TO_CONNECTOR,\r\n dictionaryVersion: options.dictionaryVersion ?? deriveDictionaryVersion(allDomains),\r\n createCodec: createBinaryWireSessionFactory(allDomains, options.sessionOptions),\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Channel-derived wiring (connector + acceptor)\r\n// ---------------------------------------------------------------------------\r\n\r\ntype TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void\r\n ? I\r\n : never;\r\n\r\ntype TDomainPushHandlers<D> =\r\n D extends ActionDomain<infer DEF> ? Partial<TWrappableDomainActionHandler<DEF>> : never;\r\n\r\n/**\r\n * The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action\r\n * handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so\r\n * the keys and input types follow the channel definition.\r\n */\r\nexport type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> =\r\n TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;\r\n\r\n/**\r\n * One transport to the peer, declared by *carrier* — the dial-out dual of `serveChannel`'s acceptor\r\n * carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto\r\n * identity) into each one, so a descriptor only carries what differs between transports: the carrier and\r\n * whether it runs the secure handshake.\r\n *\r\n * A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier\r\n * (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection\r\n * prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback).\r\n */\r\nexport interface IConnectTransport {\r\n /** How to reach the peer — a duplex carrier (push-capable) or an exchange carrier (request/reply). */\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource;\r\n /**\r\n * Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport\r\n * draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport\r\n * (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`.\r\n */\r\n secure?: boolean;\r\n /** Security level for this secure transport; defaults to the connection-level `securityLevel`. */\r\n securityLevel?: ESecurityLevel;\r\n /**\r\n * Optional availability gate — when it returns `false` this transport is skipped and the connection\r\n * falls through to the next in preference order, re-evaluated per dispatch. Omit = always available.\r\n */\r\n available?: (input: ITransportRouteActionParams) => boolean;\r\n /** Override the devtools chip label (defaults to the carrier's own label). */\r\n label?: string;\r\n}\r\n\r\nexport interface IConnectChannelOptions<TO_CONNECTOR extends readonly ActionDomain<any>[]> {\r\n /** The peer's runtime coordinate — the acceptor this connection dials. */\r\n peer: RuntimeCoordinate;\r\n /**\r\n * The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback).\r\n * They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and\r\n * falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into\r\n * each — the dial-out dual of `serveChannel`'s `carriers`.\r\n */\r\n transports: readonly IConnectTransport[];\r\n /**\r\n * One backing store for this connection's crypto identity, fanned across every *secure* transport so\r\n * they present the same verify/exchange keys. Required when any transport is secure (the default); a\r\n * fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an\r\n * existing identity.\r\n */\r\n storage?: StorageAdapter;\r\n /** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */\r\n link?: ClientCryptoKeyLink;\r\n /** Default security level for secure transports; defaults to `authenticated`. */\r\n securityLevel?: ESecurityLevel;\r\n /** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */\r\n onPush?: TChannelPushHandlers<TO_CONNECTOR>;\r\n /** Default per-action timeout for this connection. */\r\n defaultTimeout?: number;\r\n}\r\n\r\n/**\r\n * Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is\r\n * the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer,\r\n * `toConnector` pushes handled locally from `onPush`); the call binds the shared facts — the channel's\r\n * codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over\r\n * `storage`) — into every transport in `transports`, so none of them restate the channel or runtime.\r\n * List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback):\r\n * ```ts\r\n * const handler = connectChannel(runtime, lobbyChannel, {\r\n * peer: runtime_coordinate_lobby_do,\r\n * storage,\r\n * transports: [{ carrier: wsCarrier(() => ({ url })) }, { carrier: httpCarrier(...), secure: false }],\r\n * onPush: { player_joined: (p) => { … } },\r\n * });\r\n * ```\r\n * Returns the {@link ConnectorHandler} so the caller can later `clearTransportCache()` it.\r\n */\r\nexport function connectChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IConnectChannelOptions<TO_CONNECTOR>,\r\n): ConnectorHandler {\r\n const securityLevel = options.securityLevel ?? ESecurityLevel.authenticated;\r\n\r\n // One crypto identity for the whole connection, fanned across every secure transport so they present\r\n // the same keys — the dial-out mirror of `serveChannel` sharing one link across its carriers. Built\r\n // only when something needs it; a fully-plain connection uses no storage.\r\n const anySecure = options.transports.some((transport) => transport.secure ?? true);\r\n let link: ClientCryptoKeyLink | undefined = options.link;\r\n if (anySecure && link == null) {\r\n if (options.storage == null) {\r\n throw new Error(\r\n \"connectChannel: a secure transport requires `storage` (or `link`). Pass it, or set `secure: false` on the transport for a plain connection.\",\r\n );\r\n }\r\n link = new ClientCryptoKeyLink({ storageAdapter: options.storage });\r\n }\r\n\r\n const transports: Transport[] = options.transports.map((descriptor) =>\r\n transport({\r\n carrier: descriptor.carrier,\r\n channel,\r\n runtime,\r\n secure: descriptor.secure ?? true,\r\n link,\r\n securityLevel: descriptor.securityLevel ?? securityLevel,\r\n available: descriptor.available,\r\n label: descriptor.label,\r\n }),\r\n );\r\n\r\n // Each push domain self-filters `onPush` to its own action ids, so a channel with several push\r\n // domains splits one merged map across the right handlers.\r\n const pushHandlers =\r\n options.onPush != null\r\n ? channel.toConnectorDomains.map((domain) =>\r\n domain.wrapAsPartialLocalHandler(\r\n options.onPush as Parameters<typeof domain.wrapAsPartialLocalHandler>[0],\r\n ),\r\n )\r\n : [];\r\n\r\n return runtime.connectTo(options.peer, {\r\n transports,\r\n domains: [...channel.toAcceptorDomains],\r\n localHandlers: pushHandlers,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n}\r\n\r\ntype TDomainAcceptorCases<D, TCtx> =\r\n D extends ActionDomain<infer DEF>\r\n ? { [ID in keyof DEF[\"actionSchema\"] & string]?: TAcceptorCaseFn<DEF, ID, TCtx> }\r\n : never;\r\n\r\n/**\r\n * The connection-aware case map for a channel's acceptor side: the merged set of every\r\n * connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus a per-action\r\n * `context`. `TCtx` is whatever the wiring supplies as that second argument — the raw connection\r\n * (`TConn | undefined`) for the low-level `acceptChannelConnections`, or an enriched `IConnectionContext`\r\n * for `serveChannel`'s `channelCases`. Derived from the channel's `toAcceptorDomains`, so the keys and\r\n * input/output types follow the channel.\r\n */\r\nexport type TChannelAcceptorCases<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TCtx,\r\n> = TUnionToIntersection<TDomainAcceptorCases<TO_ACCEPTOR[number], TCtx>>;\r\n\r\n/**\r\n * Register an acceptor handler's execution for a channel straight from its definition: the channel's\r\n * `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets\r\n * the primed request + the originating connection, as with\r\n * {@link AcceptorHandler.forConnectionDomainCases}). The domain list is taken from the channel,\r\n * never restated. Add the returned handler to the runtime alongside the acceptor handler:\r\n * ```ts\r\n * runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]);\r\n * ```\r\n *\r\n * The case's second argument is the raw connection (`TConn | undefined`). For the richer state +\r\n * broadcast + pushBack context, serve the channel through `serveChannel`'s `channelCases` instead.\r\n */\r\nexport function acceptChannelConnections<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn>(\r\n serverHandler: AcceptorHandler<TConn>,\r\n channel: IActionChannel<TO_ACCEPTOR, any>,\r\n cases: TChannelAcceptorCases<TO_ACCEPTOR, TConn | undefined>,\r\n): ActionLocalHandler {\r\n return serverHandler.forConnectionDomainCasesMulti(\r\n channel.toAcceptorDomains,\r\n cases as Record<string, TAcceptorCaseFn<any, any, TConn | undefined> | undefined>,\r\n (connection) => connection,\r\n );\r\n}\r\n\r\n/**\r\n * {@link acceptChannel}'s options — the secure-acceptor builder options minus the `channel` and `runtime`\r\n * it already takes positionally. One option bag, shared with the underlying {@link createSecureAcceptorHandler}.\r\n */\r\nexport type IAcceptChannelOptions<TConn> = Omit<\r\n ISecureAcceptorHandlerOptions<TConn>,\r\n \"channel\" | \"runtime\"\r\n>;\r\n\r\n/**\r\n * Build the secure {@link AcceptorHandler} for a channel — the accept-in counterpart to\r\n * {@link connectChannel}. It folds in the boilerplate of {@link createSecureAcceptorHandler} (the\r\n * `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storage`, the channel's codec +\r\n * dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel,\r\n * options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution:\r\n * ```ts\r\n * const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storage, send });\r\n * runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]);\r\n * ```\r\n */\r\nexport function acceptChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TConn = unknown,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IAcceptChannelOptions<TConn>,\r\n): AcceptorHandler<TConn> {\r\n return createSecureAcceptorHandler<TConn>({ ...options, channel, runtime });\r\n}\r\n","import type { IDuplexConnectionRouter } from \"../../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { ETransportShape } from \"../Transport.types\";\r\nimport type { TFrame } from \"./Carrier.types\";\r\n\r\n/**\r\n * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}\r\n * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an\r\n * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral\r\n * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block\r\n * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates\r\n * it.\r\n *\r\n * Two shapes mirror the connector side:\r\n *\r\n * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can\r\n * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step\r\n * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.\r\n * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to\r\n * upgrade.\r\n */\r\n\r\n/**\r\n * Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections\r\n * outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional — omit for a\r\n * transport that never hibernates (per-connection state is then in-memory only).\r\n *\r\n * `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when\r\n * `connectionState` is requested) per-connection app state as one composite in this single slot, so both\r\n * survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections.\r\n */\r\nexport interface IAcceptorAttachmentStore<TConn> {\r\n /** All currently-live connections — enumerated on build to replay binding + app state after a wake. */\r\n getConnections: () => TConn[];\r\n /** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */\r\n read: (connection: TConn) => unknown;\r\n /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */\r\n write: (connection: TConn, value: unknown) => void;\r\n}\r\n\r\n/**\r\n * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each\r\n * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a\r\n * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the\r\n * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if\r\n * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)\r\n */\r\nexport interface IDuplexCarrierLifecycle<TConn> {\r\n /** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */\r\n receive(connection: TConn, frame: TFrame): void;\r\n /** Forget a connection on close/error. No-op until the carrier is served. */\r\n drop(connection: TConn): void;\r\n /** @internal `serveChannel` binds this carrier's live connection router here. */\r\n _activate(router: IDuplexConnectionRouter<TConn>): void;\r\n}\r\n\r\n/**\r\n * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw\r\n * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex\r\n * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the\r\n * stateful-handle wiring lives in exactly one place.\r\n */\r\nexport function createDuplexCarrierLifecycle<TConn>(): IDuplexCarrierLifecycle<TConn> {\r\n let router: IDuplexConnectionRouter<TConn> | undefined;\r\n return {\r\n receive(connection, frame) {\r\n if (router == null) {\r\n throw new Error(\r\n \"acceptor carrier not served yet — pass it to serveChannel() before feeding frames\",\r\n );\r\n }\r\n router.receive(connection, frame);\r\n },\r\n drop(connection) {\r\n router?.drop(connection);\r\n },\r\n _activate(liveRouter) {\r\n router = liveRouter;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live\r\n * connection, how to perform the transport-specific upgrade that admits one, which requests are such\r\n * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and\r\n * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see\r\n * {@link IDuplexCarrierLifecycle}).\r\n */\r\nexport interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {\r\n /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */\r\n readonly shape: ETransportShape.duplex;\r\n /**\r\n * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex\r\n * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no\r\n * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain\r\n * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a\r\n * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that\r\n * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /**\r\n * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.\r\n * Only consulted when {@link upgrade} is present.\r\n */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /**\r\n * Optional attachment read/write for connections that survive eviction (Durable Object hibernation).\r\n * Present → `serveChannel` persists the routing binding (and any `connectionState`) here and replays it\r\n * on wake. Absent → per-connection state is in-memory only.\r\n */\r\n attachmentStore?: IAcceptorAttachmentStore<TConn>;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By\r\n * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose\r\n * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint\r\n * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's\r\n * plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). So a server can pair a secure\r\n * duplex (WebSocket) with a plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.\r\n */\r\nexport interface IExchangeAcceptorCarrier {\r\n /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */\r\n readonly shape: ETransportShape.exchange;\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain\r\n * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,\r\n * or encryption. A plain endpoint ignores the central crypto identity entirely.\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\nexport type TAcceptorCarrier<TConn = unknown> =\r\n | IDuplexAcceptorCarrier<TConn>\r\n | IExchangeAcceptorCarrier;\r\n\r\n/**\r\n * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch\r\n * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex\r\n * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeAcceptorCarrier<TConn>(\r\n carrier: TAcceptorCarrier<TConn>,\r\n): carrier is IExchangeAcceptorCarrier {\r\n return carrier.shape === ETransportShape.exchange;\r\n}\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\r\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { IActionDomain } from \"../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport type { ActionRuntime } from \"../ActionRuntime\";\r\nimport type { TActionRuntimeHandler } from \"../ActionRuntime.types\";\r\nimport {\r\n type AcceptorHandler,\r\n createAcceptorHandler,\r\n type TAcceptorCaseFn,\r\n} from \"../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport { createActionFetchHandler } from \"../Handler/PeerLink/Acceptor/createActionFetchHandler\";\r\nimport {\r\n type ConnectionStateStore,\r\n createConnectionStateStore,\r\n type IConnectionAttachment,\r\n} from \"../Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\r\nimport {\r\n createHibernatableWsServerAdapter,\r\n type IDuplexConnectionRouter,\r\n} from \"../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\nimport {\r\n type IDuplexAcceptorCarrier,\r\n type IExchangeAcceptorCarrier,\r\n isExchangeAcceptorCarrier,\r\n type TAcceptorCarrier,\r\n} from \"../Transport/Carrier/AcceptorCarrier.types\";\r\nimport {\r\n createStorageTofuVerifyKeyResolver,\r\n ESecurityLevel,\r\n type IClientVerifyKeyResolver,\r\n} from \"../Transport/crypto/actionHandshake\";\r\nimport type { IExchangeAcceptorSecurity } from \"../Transport/SecureSession/exchangeAcceptor\";\r\nimport { acceptChannel, type IActionChannel, type TChannelAcceptorCases } from \"./ActionChannel\";\r\n\r\n/** Default accepted set, shared by every carrier: negotiate per connection to whatever the client picks. */\r\nconst DEFAULT_SERVER_SECURITY_LEVELS = [\r\n ESecurityLevel.none,\r\n ESecurityLevel.authenticated,\r\n ESecurityLevel.encrypted,\r\n] as const;\r\n\r\n/** Per-connection app-state config for {@link serveChannel}'s `connectionState`. */\r\nexport interface IServeConnectionStateOptions<TApp> {\r\n /**\r\n * Optional Standard Schema (valibot, zod, …) validating the app state on read — a value that fails\r\n * validation reads back as `null`. Omit to store the app state untyped.\r\n */\r\n schema?: StandardSchemaV1<unknown, TApp>;\r\n}\r\n\r\n/**\r\n * The per-action handle a `serveChannel` `channelCases` case receives as its second argument — the\r\n * originating connection enriched with everything a case typically reaches for, so it never threads\r\n * `ws` through `this.connections` / `this.server` by hand:\r\n *\r\n * - {@link state} / {@link setState} / {@link clearState} — the connection's typed app state (when\r\n * `connectionState` is configured), co-stored with the routing binding so it survives hibernation.\r\n * - {@link broadcast} — fan a server push to every other connection (skip self with `exceptSelf`).\r\n * - {@link pushBack} — push a server-initiated action down *this* same connection.\r\n *\r\n * Over the HTTP-exchange path there is no live socket: {@link connection} is `null`, {@link state} reads\r\n * `null`, {@link setState}/{@link clearState} are no-ops, and {@link pushBack} throws (an exchange reply\r\n * rides its own request — it can't carry an unsolicited push).\r\n */\r\nexport interface IConnectionContext<TConn, TApp = unknown> {\r\n /** The originating live connection, or `null` on the HTTP-exchange path (escape hatch). */\r\n connection: TConn | null;\r\n /** The originating client's coordinate (`= action.context.originClient`). */\r\n origin: RuntimeCoordinate;\r\n /** This connection's app state, or `null` if unset / no live socket. */\r\n state: TApp | null;\r\n /** Set this connection's app state (no-op without a live socket). Preserves the routing binding. */\r\n setState: (value: TApp) => void;\r\n /** Clear this connection's app state but keep the routing binding (no-op without a live socket). */\r\n clearState: () => void;\r\n /** Fan a server-initiated action out to every connection; `exceptSelf` skips this one. */\r\n broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n options?: {\r\n exceptSelf?: boolean;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ) => void;\r\n /** Push a server-initiated action down this same connection. Throws if there is no live socket. */\r\n pushBack: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n request: ActionPayload_Request<DOM, ID>,\r\n options?: { timeout?: number },\r\n ) => RunningAction<DOM, ID>;\r\n}\r\n\r\nexport interface IServeChannelOptions<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TConn,\r\n TApp = unknown,\r\n> {\r\n /**\r\n * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env(\"web_app\")`),\r\n * used only as the offline-return scoring fallback — a result/push to a live client always routes over\r\n * the carrier it connected on regardless of this. Optional: omit it for a multi-role server that accepts\r\n * clients of several envs over one acceptor (it then scores 0 against every client, so the live\r\n * connection always decides).\r\n */\r\n clientEnv?: RuntimeCoordinate;\r\n /**\r\n * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins\r\n * (their keys don't collide). It is built once and shared across every carrier, so the WebSocket and the\r\n * secure-HTTP endpoint present the exact same verify/exchange keys and trust the same pinned clients.\r\n * Back it with persistent storage (e.g. a Durable Object's storage) so identity + pins survive eviction.\r\n *\r\n * Required only when at least one carrier is secure (the default). A fully-plain server (every carrier\r\n * `secure: false`) needs no storage and may omit it.\r\n */\r\n storage?: StorageAdapter;\r\n /**\r\n * The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`.\r\n * Build them with `wsAcceptorCarrier` / `httpAcceptorCarrier`. Any number of duplex (push-capable)\r\n * carriers are supported (e.g. WebSocket + WebRTC), plus at most one exchange (request/reply) carrier;\r\n * all share one crypto identity and one runtime, and each result/push routes back over the carrier its\r\n * client connected on.\r\n */\r\n carriers: readonly TAcceptorCarrier<TConn>[];\r\n /** Your execution handlers (e.g. the local handler holding the action cases). Registered for you. */\r\n handlers?: TActionRuntimeHandler[];\r\n /**\r\n * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. Pass an\r\n * existing link only to share identity with acceptors built outside this call.\r\n */\r\n link?: ClientCryptoKeyLink;\r\n /** Accepted level(s) for every carrier; defaults to negotiating any of none/authenticated/encrypted. */\r\n securityLevel?: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n /** Timeout (ms) applied to server-initiated actions awaiting a client response. */\r\n defaultTimeout?: number;\r\n /**\r\n * Co-store per-connection app state alongside the routing binding in the sole duplex carrier's connection\r\n * attachment, so both survive a wake from eviction. Reach the typed store back on `server.connections`.\r\n * Requires the carrier to expose an attachment store (the Cloudflare `durableObjectWsCarrier` does) and\r\n * exactly one duplex carrier.\r\n */\r\n connectionState?: IServeConnectionStateOptions<TApp>;\r\n /**\r\n * Connection-aware action cases for the channel's acceptor (`toAcceptor`) domains — each case receives the\r\n * primed request *and* an {@link IConnectionContext} (the connection plus its typed `state` and\r\n * `broadcast`/`pushBack`). The connection-aware dual of `handlers`, registered on the runtime for you.\r\n * Requires exactly one duplex carrier.\r\n */\r\n channelCases?: TChannelAcceptorCases<TO_ACCEPTOR, IConnectionContext<TConn, TApp>>;\r\n}\r\n\r\n/**\r\n * One server serving a secure channel over several carriers — the accept-in dual of `connectChannel`,\r\n * returned by {@link serveChannel}. Wire its surface straight to the host's request/socket events.\r\n */\r\nexport interface IChannelServer<TConn, TApp = unknown> {\r\n /**\r\n * The duplex acceptor handlers — one per duplex carrier, in carrier order (empty if none). For pushing,\r\n * prefer {@link pushToClient} (it resolves the owning handler); reach for these for cross-carrier work\r\n * like a per-handler `broadcast`.\r\n */\r\n handlers: AcceptorHandler<TConn>[];\r\n /**\r\n * Unified request handler: answers the CORS preflight, performs the duplex upgrade for an upgrade\r\n * request, serves a secure-exchange action `POST`, else `404`. Forward the host's `fetch` straight to it.\r\n */\r\n fetch: (request: Request) => Promise<Response>;\r\n /**\r\n * Feed one inbound frame from a live connection into the server — forward your host's \"message\" event\r\n * here (a Durable Object's `webSocketMessage`, a Bun `websocket.message`, a Node `ws.on(\"message\")`).\r\n * Routes to the sole duplex carrier; throws with a clear message when there are zero or several duplex\r\n * carriers (for the multi-carrier case feed each `handlers[i]` / carrier handle directly).\r\n */\r\n receive: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\r\n /**\r\n * Forget a connection on close/error — forward your host's \"close\"/\"error\" event here. Routes to the\r\n * sole duplex carrier; a no-op when there are none (an HTTP-only server has no sockets to drop).\r\n */\r\n drop: (connection: TConn) => void;\r\n /**\r\n * Push a server-initiated action to a connected client (the runtime is bound in, so unlike\r\n * {@link AcceptorHandler.pushToClient} you pass only the target + request). It routes through the duplex\r\n * carrier the target connected on. Throws if no duplex carrier currently holds the target.\r\n */\r\n pushToClient: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n target: TConn | RuntimeCoordinate,\r\n request: ActionPayload_Request<DOM, ID>,\r\n options?: { timeout?: number },\r\n ) => RunningAction<DOM, ID>;\r\n /**\r\n * Fan a server-initiated action out to every connection on the sole duplex carrier (skip the origin with\r\n * `except`, filter with `where`). The push-to-many counterpart of {@link pushToClient}. Throws if there\r\n * isn't exactly one duplex carrier (with several, broadcast over a specific `handlers[i]`).\r\n */\r\n broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n options?: {\r\n except?: TConn | null;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ) => void;\r\n /**\r\n * The per-connection app-state store co-stored with the routing binding in the connection attachment —\r\n * present only when `connectionState` was passed. `get`/`set`/`clearApp`/`entries` it directly; it\r\n * survives hibernation alongside the binding.\r\n */\r\n connections?: ConnectionStateStore<TConn, TApp>;\r\n}\r\n\r\n/**\r\n * Serve a secure channel over one or more carriers from a single call — the accept-in dual of\r\n * `connectChannel`. It builds the crypto identity (a {@link ClientCryptoKeyLink} + a storage-backed TOFU\r\n * resolver) and the security block (coordinate, dictionary version, accepted levels) *once* from\r\n * `(runtime, channel)` and fans them across every carrier, so the WebSocket and the secure-HTTP endpoint\r\n * can never drift apart. It registers your handlers (plus the duplex acceptor it builds) on the runtime,\r\n * wires hibernation when the duplex carrier exposes an attachment store, and returns a single\r\n * {@link IChannelServer} whose `fetch` / `receive` / `drop` / `pushToClient` / `broadcast` you forward\r\n * straight to the host:\r\n * ```ts\r\n * const server = serveChannel(runtime, channel, {\r\n * clientEnv, storage,\r\n * carriers: [wsAcceptorCarrier({ send, upgrade, attachmentStore }), httpAcceptorCarrier()],\r\n * connectionState: { schema: vs_player }, // optional: co-store per-connection app state (survives hibernation)\r\n * channelCases: { join: (action, conn) => { conn.setState(action.input); … } }, // connection-aware cases\r\n * });\r\n * // fetch(req) => server.fetch(req)\r\n * // webSocketMessage(conn, m) => server.receive(conn, m)\r\n * // webSocketClose/Error(conn) => server.drop(conn)\r\n * // server.connections.get(conn) / server.broadcast(() => push.request(…), { except: conn })\r\n * ```\r\n *\r\n * On Cloudflare, `serveDurableObject` folds the whole DO transport stack (carriers + storage + keepalive)\r\n * into this — reach for it instead of assembling the carriers by hand.\r\n *\r\n * `TConn` (the live-connection token a duplex carrier hands back through `send`/`receive`/`drop`) is\r\n * inferred from the carriers — `WebSocket` for `wsAcceptorCarrier`, the data-channel type for a WebRTC\r\n * carrier, and so on — so it stays carrier-agnostic. Passing `connectionState` narrows the return so\r\n * `server.connections` is non-optional.\r\n */\r\nexport function serveChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TConn,\r\n TApp,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp> & {\r\n connectionState: IServeConnectionStateOptions<TApp>;\r\n },\r\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\r\nexport function serveChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TConn = unknown,\r\n TApp = unknown,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>,\r\n): IChannelServer<TConn, TApp>;\r\nexport function serveChannel(\r\n runtime: ActionRuntime,\r\n channel: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\r\n options: IServeChannelOptions<readonly ActionDomain<any>[], any, any>,\r\n): IChannelServer<any, any> {\r\n type TConn = any;\r\n const duplexCarriers = options.carriers.filter(\r\n (carrier): carrier is IDuplexAcceptorCarrier<TConn> => !isExchangeAcceptorCarrier(carrier),\r\n );\r\n const exchangeCarriers = options.carriers.filter(isExchangeAcceptorCarrier);\r\n\r\n if (exchangeCarriers.length > 1) {\r\n throw new Error(\"serveChannel: at most one exchange carrier is supported\");\r\n }\r\n const exchangeCarrier: IExchangeAcceptorCarrier | undefined = exchangeCarriers[0];\r\n\r\n const singleDuplex = duplexCarriers.length === 1;\r\n if (options.connectionState != null && !singleDuplex) {\r\n throw new Error(\"serveChannel: `connectionState` requires exactly one duplex carrier\");\r\n }\r\n if (options.channelCases != null && !singleDuplex) {\r\n throw new Error(\"serveChannel: `channelCases` requires exactly one duplex carrier\");\r\n }\r\n\r\n const exchangeSecure = exchangeCarrier != null && (exchangeCarrier.secure ?? true);\r\n const anyDuplexSecure = duplexCarriers.some((carrier) => carrier.secure ?? true);\r\n const securityLevel = options.securityLevel ?? DEFAULT_SERVER_SECURITY_LEVELS;\r\n\r\n // The shared crypto identity (link + TOFU resolver) — built once and fanned across every *secure*\r\n // carrier. Only constructed when something needs it; a fully-plain server uses no storage.\r\n let secure:\r\n | {\r\n storage: StorageAdapter;\r\n link: ClientCryptoKeyLink;\r\n verifyKeyResolver: IClientVerifyKeyResolver;\r\n }\r\n | undefined;\r\n if (anyDuplexSecure || exchangeSecure) {\r\n const storage = options.storage;\r\n if (storage == null) {\r\n throw new Error(\r\n \"serveChannel: a secure carrier requires `storage`. Pass it, or set `secure: false` on the carrier for a plain endpoint.\",\r\n );\r\n }\r\n secure = {\r\n storage,\r\n link: options.link ?? new ClientCryptoKeyLink({ storageAdapter: storage }),\r\n verifyKeyResolver: options.verifyKeyResolver ?? createStorageTofuVerifyKeyResolver(storage),\r\n };\r\n }\r\n\r\n // One acceptor handler per duplex carrier, each activated as its own lifecycle handle. Secure carriers →\r\n // `acceptChannel` (handshake + the shared identity, so they present the same keys as the exchange\r\n // endpoint); plain carriers → the base `createAcceptorHandler` over the channel's wire codec, no crypto.\r\n // Phase-A connection-aware return routing then sends each result/push back over the carrier its client\r\n // connected on.\r\n // The plain (in-memory only) router for a connection — no persistence across eviction.\r\n const plainRouter = (handler: AcceptorHandler<TConn>): IDuplexConnectionRouter<TConn> => ({\r\n receive: (connection, frame) => handler.receive(connection, frame),\r\n drop: (connection) => handler.dropConnection(connection),\r\n });\r\n const asObject = (value: unknown): object =>\r\n typeof value === \"object\" && value != null ? value : {};\r\n\r\n const handlers: AcceptorHandler<TConn>[] = [];\r\n // The per-connection app-state store, built once over the sole duplex carrier when `connectionState` is set.\r\n let connections: ConnectionStateStore<TConn, any> | undefined;\r\n for (const carrier of duplexCarriers) {\r\n const handler =\r\n (carrier.secure ?? true) && secure != null\r\n ? acceptChannel<any, any, TConn>(runtime, channel, {\r\n clientEnv: options.clientEnv,\r\n storage: secure.storage,\r\n link: secure.link,\r\n verifyKeyResolver: secure.verifyKeyResolver,\r\n securityLevel,\r\n send: carrier.send,\r\n defaultTimeout: options.defaultTimeout,\r\n })\r\n : createAcceptorHandler<TConn>({\r\n clientEnv: options.clientEnv,\r\n createFormatMessage: channel.createCodec,\r\n send: carrier.send,\r\n runtime,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n\r\n // The attachment is one composite slot `{ app?, binding? }` per connection: serveChannel owns its\r\n // layout so binding *and* app state survive a wake from a single slot.\r\n // - no attachment store → in-memory only (no replay).\r\n // - `connectionState` requested → the connection store owns binding + app persistence and wake-replay.\r\n // - otherwise → the bare hibernation adapter persists/replays just the `.binding` sub-field.\r\n const attach = carrier.attachmentStore;\r\n let router: IDuplexConnectionRouter<TConn>;\r\n if (attach == null) {\r\n router = plainRouter(handler);\r\n } else if (options.connectionState != null) {\r\n connections = createConnectionStateStore<TConn, any>(handler, {\r\n schema: options.connectionState.schema,\r\n getConnections: attach.getConnections,\r\n read: attach.read,\r\n write: attach.write,\r\n });\r\n router = plainRouter(handler); // the store already replayed surviving bindings\r\n } else {\r\n router = createHibernatableWsServerAdapter<TConn>({\r\n handler,\r\n getConnections: attach.getConnections,\r\n getAttachment: (connection) =>\r\n (attach.read(connection) as IConnectionAttachment<unknown> | null | undefined)?.binding,\r\n setAttachment: (connection, binding) =>\r\n attach.write(connection, { ...asObject(attach.read(connection)), binding }),\r\n });\r\n }\r\n carrier._activate(router);\r\n handlers.push(handler);\r\n }\r\n\r\n runtime.addHandlers([...(options.handlers ?? []), ...handlers]);\r\n\r\n // --- Connection lifecycle + server-push surface ---\r\n // `receive`/`drop` are the universal forwarding seam every host environment feeds (a Durable Object's\r\n // `webSocketMessage`, a Bun `websocket.message`, …). They route to the sole duplex carrier (the common\r\n // case); with several, each carrier is its own handle, so callers feed those directly.\r\n const soleDuplex: IDuplexConnectionRouter<TConn> | undefined = singleDuplex\r\n ? duplexCarriers[0]\r\n : undefined;\r\n const receive = (connection: TConn, frame: string | Uint8Array | ArrayBuffer): void => {\r\n if (soleDuplex == null) {\r\n throw new Error(\r\n duplexCarriers.length === 0\r\n ? \"serveChannel: no duplex carrier to receive on (this server has no socket transport)\"\r\n : \"serveChannel: several duplex carriers — feed each carrier handle's receive() directly\",\r\n );\r\n }\r\n soleDuplex.receive(connection, frame);\r\n };\r\n const drop = (connection: TConn): void => {\r\n soleDuplex?.drop(connection);\r\n };\r\n\r\n const pushToClient = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n target: TConn | RuntimeCoordinate,\r\n request: ActionPayload_Request<DOM, ID>,\r\n pushOptions?: { timeout?: number },\r\n ): RunningAction<DOM, ID> => {\r\n // Route the push through the handler that actually holds the target's live connection.\r\n const owner =\r\n target instanceof RuntimeCoordinate\r\n ? handlers.find((handler) => handler.ownsLiveConnectionFor(target))\r\n : handlers.find((handler) => handler.hasConnection(target));\r\n if (owner == null) {\r\n throw new Error(\"serveChannel: no duplex carrier holds a connection for the push target\");\r\n }\r\n return owner.pushToClient(runtime, target, request, pushOptions);\r\n };\r\n\r\n const broadcast = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n broadcastOptions?: {\r\n except?: TConn | null;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ): void => {\r\n if (!singleDuplex) {\r\n throw new Error(\r\n \"serveChannel: broadcast requires exactly one duplex carrier — broadcast over a specific handlers[i] instead\",\r\n );\r\n }\r\n handlers[0].broadcast(makeRequest, { runtime, ...broadcastOptions });\r\n };\r\n\r\n // The per-action context handed to `channelCases`: the resolved connection enriched with typed `state`\r\n // plus `broadcast`/`pushBack`, so a case never reaches back into `connections`/the server by hand.\r\n const makeConnectionContext = (\r\n connection: TConn | undefined,\r\n request: ActionPayload_Request<any, any>,\r\n ): IConnectionContext<TConn, any> => ({\r\n connection: connection ?? null,\r\n origin: request.context.originClient,\r\n get state() {\r\n return connection != null && connections != null ? connections.get(connection) : null;\r\n },\r\n setState(value) {\r\n if (connection != null && connections != null) connections.set(connection, value);\r\n },\r\n clearState() {\r\n if (connection != null && connections != null) connections.clearApp(connection);\r\n },\r\n broadcast(makeRequest, contextOptions) {\r\n broadcast(makeRequest, {\r\n except: contextOptions?.exceptSelf ? (connection ?? null) : null,\r\n where: contextOptions?.where,\r\n timeout: contextOptions?.timeout,\r\n onError: contextOptions?.onError,\r\n });\r\n },\r\n pushBack(pushRequest, pushOptions) {\r\n if (connection == null) {\r\n throw new Error(\r\n \"serveChannel: connection context has no live socket to push back to (HTTP-exchange path)\",\r\n );\r\n }\r\n return pushToClient(connection, pushRequest, pushOptions);\r\n },\r\n });\r\n\r\n // Connection-aware action cases for the channel's acceptor domains, registered on the runtime alongside\r\n // the (sole) duplex acceptor — the connection-aware dual of `handlers`. Each case gets the enriched\r\n // `IConnectionContext`, built per inbound action.\r\n if (options.channelCases != null) {\r\n runtime.addHandlers([\r\n handlers[0].forConnectionDomainCasesMulti(\r\n channel.toAcceptorDomains,\r\n options.channelCases as Record<\r\n string,\r\n TAcceptorCaseFn<any, any, IConnectionContext<TConn, any>> | undefined\r\n >,\r\n makeConnectionContext,\r\n ),\r\n ]);\r\n }\r\n\r\n // The exchange (request/reply) security block — derived from the same identity + the channel/runtime\r\n // facts, so it never restates what the duplex side already knows. Omitted for a plain exchange carrier\r\n // (`secure: false`), which then POSTs the raw wire — letting a plain HTTP fallback sit beside a secure WS.\r\n const exchangeSecurity: IExchangeAcceptorSecurity | undefined =\r\n exchangeSecure && secure != null\r\n ? {\r\n link: secure.link,\r\n verifyKeyResolver: secure.verifyKeyResolver,\r\n localCoordinate: runtime.coordinate.toJsonObject(),\r\n dictionaryVersion: channel.dictionaryVersion,\r\n securityLevel,\r\n }\r\n : undefined;\r\n\r\n // Compose the upgrade across the duplex carriers that can be reached via an HTTP upgrade (typically just\r\n // one WebSocket carrier; a WebRTC carrier is signalled out of band and has no `upgrade`). The first\r\n // carrier whose `isUpgrade` matches handles the request.\r\n const defaultIsUpgrade = (request: Request) => request.headers.get(\"Upgrade\") === \"websocket\";\r\n const upgraders: {\r\n isUpgrade: (request: Request, url: URL) => boolean;\r\n upgrade: (request: Request, url: URL) => Response | Promise<Response>;\r\n }[] = [];\r\n for (const carrier of duplexCarriers) {\r\n if (carrier.upgrade == null) continue;\r\n upgraders.push({ isUpgrade: carrier.isUpgrade ?? defaultIsUpgrade, upgrade: carrier.upgrade });\r\n }\r\n\r\n const fetch = createActionFetchHandler(runtime, {\r\n cors: exchangeCarrier?.cors,\r\n onWebSocketUpgrade:\r\n upgraders.length === 0\r\n ? undefined\r\n : (request, url) =>\r\n (upgraders.find((u) => u.isUpgrade(request, url)) ?? upgraders[0]).upgrade(\r\n request,\r\n url,\r\n ),\r\n isWebSocketUpgrade:\r\n upgraders.length === 0\r\n ? undefined\r\n : (request, url) => upgraders.some((u) => u.isUpgrade(request, url)),\r\n // A single forwarding route means the path is already matched: any POST that isn't an upgrade is an\r\n // action exchange. With no exchange carrier there is no HTTP action endpoint, so POSTs fall through.\r\n isActionPath:\r\n exchangeCarrier != null ? (exchangeCarrier.isActionPath ?? (() => true)) : () => false,\r\n security: exchangeSecurity,\r\n useErrorStatus: exchangeCarrier?.useErrorStatus,\r\n });\r\n\r\n return { handlers, fetch, receive, drop, pushToClient, broadcast, connections };\r\n}\r\n","import type { StorageAdapter } from \"@nice-code/util\";\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\nimport type { ActionRuntime } from \"../ActionRuntime\";\nimport type { ConnectionStateStore } from \"../Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\nimport type { TAcceptorCarrier } from \"../Transport/Carrier/AcceptorCarrier.types\";\nimport type { IActionChannel } from \"./ActionChannel\";\nimport {\n type IChannelServer,\n type IServeChannelOptions,\n type IServeConnectionStateOptions,\n serveChannel,\n} from \"./serveChannel\";\n\n/**\n * An environment-neutral description of *where* a channel is served — the accept-in dual of a connector's\n * transport stack, factored out so a platform adapter (a Cloudflare Durable Object, a Bun/Node WebSocket\n * server, …) supplies only what differs per environment while the channel + case wiring stays identical.\n * A host bundles:\n *\n * - the {@link carriers} the channel is served over (e.g. a WebSocket + an HTTP fallback),\n * - the {@link storage} backing the server's crypto identity, and\n * - an {@link onServed} hook run once the server exists (e.g. registering a keepalive auto-response).\n *\n * Build one with a platform helper (`cloudflareDurableObjectHost`) and hand it to {@link serveHost}.\n */\nexport interface IChannelHostAdapter<TConn> {\n /** The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`. */\n carriers: readonly TAcceptorCarrier<TConn>[];\n /** Backing store for the server's crypto identity + TOFU pins. Required when any carrier is secure. */\n storage?: StorageAdapter;\n /** Run once after the server is built — e.g. register a transport keepalive. */\n onServed?: (server: IChannelServer<TConn, unknown>) => void;\n}\n\n/** {@link serveChannel}'s options minus what the host adapter supplies (`carriers`, `storage`). */\nexport type TServeHostOptions<\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\n TConn,\n TApp = unknown,\n> = Omit<IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>, \"carriers\" | \"storage\">;\n\n/**\n * Serve a channel over a {@link IChannelHostAdapter} — the environment-neutral core every platform helper\n * (e.g. `serveDurableObject`) composes. It folds the host's carriers + storage into `serveChannel`, then\n * runs the host's `onServed` hook. Everything else (`clientEnv`, `channelCases`, `connectionState`,\n * `handlers`, …) is the same `serveChannel` surface, so moving a server between environments is swapping\n * the host adapter and nothing else. Passing `connectionState` narrows the return so `server.connections`\n * is non-optional, exactly as with `serveChannel`.\n */\nexport function serveHost<\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\n TO_CONNECTOR extends readonly ActionDomain<any>[],\n TConn,\n TApp,\n>(\n runtime: ActionRuntime,\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n host: IChannelHostAdapter<TConn>,\n options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp> & {\n connectionState: IServeConnectionStateOptions<TApp>;\n },\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\nexport function serveHost<\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n TConn = unknown,\n TApp = unknown,\n>(\n runtime: ActionRuntime,\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n host: IChannelHostAdapter<TConn>,\n options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp>,\n): IChannelServer<TConn, TApp>;\nexport function serveHost(\n runtime: ActionRuntime,\n channel: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\n host: IChannelHostAdapter<any>,\n options: TServeHostOptions<readonly ActionDomain<any>[], any, any>,\n): IChannelServer<any, any> {\n const server = serveChannel(runtime, channel, {\n ...options,\n carriers: host.carriers,\n storage: host.storage,\n });\n host.onServed?.(server);\n return server;\n}\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport {\r\n createDuplexCarrierLifecycle,\r\n type IAcceptorAttachmentStore,\r\n type IDuplexAcceptorCarrier,\r\n} from \"../../AcceptorCarrier.types\";\r\nimport type { TFrame } from \"../../Carrier.types\";\r\n\r\nexport interface IWsAcceptorCarrierOptions<TConn> {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint\r\n * — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption\r\n * (and `serveChannel` then needs no `storage` for this carrier).\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's\r\n * `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band.\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /** Attachment read/write for hibernatable sockets (e.g. a Durable Object); `serveChannel` persists here. */\r\n attachmentStore?: IAcceptorAttachmentStore<TConn>;\r\n /** Override the devtools carrier-kind label (defaults to `\"ws\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to\r\n * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to\r\n * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,\r\n * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.\r\n */\r\nexport function wsAcceptorCarrier<TConn = WebSocket>(\r\n options: IWsAcceptorCarrierOptions<TConn>,\r\n): IDuplexAcceptorCarrier<TConn> {\r\n return {\r\n ...createDuplexCarrierLifecycle<TConn>(),\r\n shape: ETransportShape.duplex,\r\n carrierLabel: options.carrierLabel ?? \"ws\",\r\n secure: options.secure,\r\n send: options.send,\r\n upgrade: options.upgrade,\r\n isUpgrade: options.isUpgrade ?? ((request) => request.headers.get(\"Upgrade\") === \"websocket\"),\r\n attachmentStore: options.attachmentStore,\r\n };\r\n}\r\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport type { IExchangeAcceptorCarrier } from \"../../AcceptorCarrier.types\";\r\n\r\nexport interface IHttpAcceptorCarrierOptions {\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). Pass `false` for a plain\r\n * endpoint — the body is the raw action wire and the result is the response body, the request/reply dual\r\n * of a connector's plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). A plain\r\n * endpoint ignores the crypto identity, so it can sit alongside a secure WebSocket on the same server\r\n * (e.g. a secure WS preferred, plain HTTP fallback).\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Override the devtools carrier-kind label (defaults to `\"http\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * An HTTP {@link IExchangeAcceptorCarrier}: the accept-in dual of {@link httpCarrier}. It serves the\r\n * secure exchange protocol (handshake → token session → encrypted frames) over web-standard\r\n * `Request`/`Response`. The crypto identity, runtime coordinate, dictionary version, and accepted security\r\n * levels are all supplied centrally by `serveChannel`, so this only needs to say which requests carry an\r\n * action envelope and how to answer CORS.\r\n */\r\nexport function httpAcceptorCarrier(\r\n options: IHttpAcceptorCarrierOptions = {},\r\n): IExchangeAcceptorCarrier {\r\n return {\r\n shape: ETransportShape.exchange,\r\n carrierLabel: options.carrierLabel ?? \"http\",\r\n secure: options.secure,\r\n isActionPath: options.isActionPath,\r\n cors: options.cors,\r\n useErrorStatus: options.useErrorStatus,\r\n };\r\n}\r\n"],"mappings":"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"}
package/build/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_createHibernatableWsServerAdapter = require("./createHibernatableWsServerAdapter-BNi4k9j3.cjs");
2
+ const require_createHibernatableWsServerAdapter = require("./createHibernatableWsServerAdapter-FSDWrxoF.cjs");
3
3
  const require_RunningAction_types = require("./RunningAction.types-DjCX1xp5.cjs");
4
- const require_httpAcceptorCarrier = require("./httpAcceptorCarrier-C3S_bDkL.cjs");
4
+ const require_httpAcceptorCarrier = require("./httpAcceptorCarrier-BQYaXI9j.cjs");
5
5
  let nanoid = require("nanoid");
6
6
  let _nice_code_error = require("@nice-code/error");
7
7
  //#region src/ActionDefinition/Action/Context/ActionContext.ts
@@ -831,6 +831,7 @@ exports.ERunningActionFinishedType = require_RunningAction_types.ERunningActionF
831
831
  exports.ERunningActionState = require_RunningAction_types.ERunningActionState;
832
832
  exports.ERunningActionUpdateType = require_RunningAction_types.ERunningActionUpdateType;
833
833
  exports.ESecurityLevel = require_createHibernatableWsServerAdapter.ESecurityLevel;
834
+ exports.ExchangeAcceptor = require_createHibernatableWsServerAdapter.ExchangeAcceptor;
834
835
  exports.RunningAction = require_createHibernatableWsServerAdapter.RunningAction;
835
836
  exports.RuntimeCoordinate = require_createHibernatableWsServerAdapter.RuntimeCoordinate;
836
837
  exports.acceptChannel = require_httpAcceptorCarrier.acceptChannel;
@@ -843,7 +844,10 @@ exports.createInMemoryTofuVerifyKeyResolver = require_createHibernatableWsServer
843
844
  exports.createLocalHandler = require_createHibernatableWsServerAdapter.createLocalHandler;
844
845
  exports.createStorageTofuVerifyKeyResolver = require_createHibernatableWsServerAdapter.createStorageTofuVerifyKeyResolver;
845
846
  exports.decodeActionFrame = require_createHibernatableWsServerAdapter.decodeActionFrame;
847
+ exports.decodeExchangeReply = require_createHibernatableWsServerAdapter.decodeExchangeReply;
848
+ exports.decodeExchangeRequest = require_createHibernatableWsServerAdapter.decodeExchangeRequest;
846
849
  exports.defineChannel = require_httpAcceptorCarrier.defineChannel;
850
+ exports.encodeExchange = require_createHibernatableWsServerAdapter.encodeExchange;
847
851
  exports.err_nice_action = require_createHibernatableWsServerAdapter.err_nice_action;
848
852
  exports.err_nice_external_client = require_createHibernatableWsServerAdapter.err_nice_external_client;
849
853
  exports.err_nice_transport = require_createHibernatableWsServerAdapter.err_nice_transport;
package/build/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as rtcCarrier, $n as MaybePromise, A as TActionResultOutcome, Ar as TInferOutputFromSchema, At as IConnectChannelOptions, B as IHttpCarrierOptions, C as IActionProgress_Custom, Cr as IActionDomain, D as TActionPayload_Any_Instance, Dr as TActionDomainSchema, E as IActionRouteItemHandler, Er as TActionDomainChildDef, F as decodeActionFrame, Fr as TInferActionError, Ft as acceptChannelConnections, G as httpAcceptorCarrier, Gn as createInMemoryTofuVerifyKeyResolver, Gt as TCarrier, H as TCarrierFetch, Ht as IDuplexCarrierSource, I as EErrId_NiceAction, In as ESecurityLevel, Ir as actionSchema, It as connectChannel, J as IWsAcceptorCarrierOptions, K as IWsCarrierOptions, Kt as TFrame, L as err_nice_action, Lr as TActionSchemaOptions, Lt as defineChannel, M as isActionPayload_Request_JsonObject, Mr as TPossibleDomainIdList, Mt as TChannelAcceptorCases, N as isActionPayload_Any_JsonObject, Nr as ActionSchema, Nt as TChannelPushHandlers, O as TActionPayload_Any_JsonObject, Or as TDomainActionId, Ot as IAcceptChannelOptions, P as IActionFrameDecoder, Pr as EActionResponseMode, Pt as acceptChannel, Q as IRtcCarrierOptions, Qn as createLocalHandler, R as EErrId_NiceTransport, Rn as IClientVerifyKeyResolveInput, Rr as TActionSerializationDefinition, S as IActionPayload_Result_JsonObject, Sr as IActionCore_JsonObject, T as IActionProgress_Percentage, Tr as IActionRootDomain, U as httpCarrier, Ut as IExchangeCarrier, V as IHttpCarrierRequest, Vt as IDuplexCarrier, W as IHttpAcceptorCarrierOptions, Wt as IExchangeCarrierSource, X as EErrId_NiceTransport_WebSocket, Xn as runtimeLinkId, Y as wsAcceptorCarrier, Z as err_nice_transport_ws, Zn as ActionLocalHandler, _ as IActionPayload_Data_Base, _r as IRuntimeFullCoordinates, _t as IDuplexAcceptorCarrier, ar as ERunningActionUpdateType, at as IInMemoryServerEndpoint, b as IActionPayload_Request_JsonObject, br as TRuntimeCoordinateStringId, bt as isExchangeAcceptorCarrier, cr as IRunningActionUpdate_Started, ct as IChannelHostAdapter, d as ActionCore, dr as TRunningActionUpdateFinished, dt as IChannelServer, er as createActionRootDomain, et as IRtcDataChannelLike, f as ActionPayload_Request, fr as TRunningActionUpdateListener, ft as IConnectionContext, g as IActionPayload_Base_JsonObject, gr as IRuntimeCoordinateSpecifics, gt as IAcceptorAttachmentStore, h as IActionPayload_Base, hr as IRuntimeCoordinate, ht as serveChannel, ir as ERunningActionState, it as IInMemoryChannelPair, j as isActionPayload_Result_JsonObject, jr as TPossibleDomainId, jt as IConnectTransport, k as TActionProgress, kr as TInferInputFromSchema, kt as IActionChannel, l as ActionDomain, lr as IRunningActionUpdate_Success, lt as TServeHostOptions, m as EActionProgressType, mr as ActionPayload_Progress, mt as IServeConnectionStateOptions, nr as RunningAction, nt as IInMemoryCarrier, or as IRunningActionUpdate_Abort, ot as createInMemoryChannelPair, p as EActionPayloadType, pr as ActionPayload_Result, pt as IServeChannelOptions, q as wsCarrier, qn as createStorageTofuVerifyKeyResolver, qt as ConnectorHandler, rr as ERunningActionFinishedType, rt as inMemoryCarrier, sr as IRunningActionUpdate_Progress, st as err_nice_external_client, t as AcceptorHandler, tr as ActionRootDomain, tt as rtcDataChannelByteChannel, u as ActionRuntime, ur as TRunningActionUpdate, ut as serveHost, v as IActionPayload_Progress, vr as RuntimeCoordinate, vt as IExchangeAcceptorCarrier, w as IActionProgress_None, wr as IActionDomainChildOptions, wt as ConnectionStateStore, x as IActionPayload_Result, xr as IActionCore, y as IActionPayload_Progress_JsonObject, yr as TRuntimeCoordinateEnvId, yt as TAcceptorCarrier, z as err_nice_transport, zn as IClientVerifyKeyResolver, zr as TTransportedValue } from "./AcceptorHandler-CxD0c1BE.cjs";
2
- export { type AcceptorHandler, ActionCore, ActionDomain, ActionLocalHandler, type ActionPayload_Progress, type ActionPayload_Request, type ActionPayload_Result, ActionRootDomain, ActionRuntime, ActionSchema, type ConnectionStateStore, type ConnectorHandler, EActionPayloadType, EActionProgressType, EActionResponseMode, EErrId_NiceAction, EErrId_NiceTransport, EErrId_NiceTransport_WebSocket, ERunningActionFinishedType, ERunningActionState, ERunningActionUpdateType, ESecurityLevel, type IAcceptChannelOptions, type IAcceptorAttachmentStore, type IActionChannel, IActionCore, IActionCore_JsonObject, type IActionDomain, type IActionDomainChildOptions, type IActionFrameDecoder, IActionPayload_Base, IActionPayload_Base_JsonObject, IActionPayload_Data_Base, IActionPayload_Progress, IActionPayload_Progress_JsonObject, IActionPayload_Request_JsonObject, IActionPayload_Result, IActionPayload_Result_JsonObject, IActionProgress_Custom, IActionProgress_None, IActionProgress_Percentage, type IActionRootDomain, IActionRouteItemHandler, type IChannelHostAdapter, type IChannelServer, type IClientVerifyKeyResolveInput, type IClientVerifyKeyResolver, type IConnectChannelOptions, type IConnectTransport, type IConnectionContext, type IDuplexAcceptorCarrier, type IDuplexCarrier, type IDuplexCarrierSource, type IExchangeAcceptorCarrier, type IExchangeCarrier, type IExchangeCarrierSource, type IHttpAcceptorCarrierOptions, type IHttpCarrierOptions, type IHttpCarrierRequest, type IInMemoryCarrier, type IInMemoryChannelPair, type IInMemoryServerEndpoint, type IRtcCarrierOptions, type IRtcDataChannelLike, type IRunningActionUpdate_Abort, type IRunningActionUpdate_Progress, type IRunningActionUpdate_Started, type IRunningActionUpdate_Success, IRuntimeCoordinate, IRuntimeCoordinateSpecifics, IRuntimeFullCoordinates, type IServeChannelOptions, type IServeConnectionStateOptions, type IWsAcceptorCarrierOptions, type IWsCarrierOptions, MaybePromise, RunningAction, RuntimeCoordinate, type TAcceptorCarrier, type TActionDomainChildDef, type TActionDomainSchema, TActionPayload_Any_Instance, TActionPayload_Any_JsonObject, TActionProgress, TActionResultOutcome, type TActionSchemaOptions, type TActionSerializationDefinition, type TCarrier, type TCarrierFetch, type TChannelAcceptorCases, type TChannelPushHandlers, type TDomainActionId, type TFrame, type TInferActionError, type TInferInputFromSchema, type TInferOutputFromSchema, type TPossibleDomainId, type TPossibleDomainIdList, type TRunningActionUpdate, type TRunningActionUpdateFinished, type TRunningActionUpdateListener, TRuntimeCoordinateEnvId, TRuntimeCoordinateStringId, type TServeHostOptions, type TTransportedValue, acceptChannel, acceptChannelConnections, actionSchema, connectChannel, createActionRootDomain, createInMemoryChannelPair, createInMemoryTofuVerifyKeyResolver, createLocalHandler, createStorageTofuVerifyKeyResolver, decodeActionFrame, defineChannel, err_nice_action, err_nice_external_client, err_nice_transport, err_nice_transport_ws, httpAcceptorCarrier, httpCarrier, inMemoryCarrier, isActionPayload_Any_JsonObject, isActionPayload_Request_JsonObject, isActionPayload_Result_JsonObject, isExchangeAcceptorCarrier, rtcCarrier, rtcDataChannelByteChannel, runtimeLinkId, serveChannel, serveHost, wsAcceptorCarrier, wsCarrier };
1
+ import { $ as httpAcceptorCarrier, $n as createInMemoryTofuVerifyKeyResolver, $t as TCarrier, A as TActionResultOutcome, Ar as IActionCore_JsonObject, B as decodeExchangeReply, Br as TPossibleDomainIdList, Bt as TChannelAcceptorCases, C as IActionProgress_Custom, Cr as IRuntimeCoordinate, Ct as serveChannel, D as TActionPayload_Any_Instance, Dr as TRuntimeCoordinateEnvId, Dt as TAcceptorCarrier, E as IActionRouteItemHandler, Er as RuntimeCoordinate, Et as IExchangeAcceptorCarrier, F as decodeActionFrame, Fr as TActionDomainSchema, G as IExchangeAcceptorSecurity, Gr as TActionSchemaOptions, Gt as defineChannel, H as encodeExchange, Hr as EActionResponseMode, Ht as acceptChannel, I as EErrId_NiceAction, Ir as TDomainActionId, It as IAcceptChannelOptions, J as IHttpCarrierOptions, K as EErrId_NiceTransport, Kn as IClientVerifyKeyResolveInput, Kr as TActionSerializationDefinition, L as err_nice_action, Lr as TInferInputFromSchema, Lt as IActionChannel, M as isActionPayload_Request_JsonObject, Mr as IActionDomainChildOptions, Mt as ConnectionStateStore, N as isActionPayload_Any_JsonObject, Nr as IActionRootDomain, O as TActionPayload_Any_JsonObject, Or as TRuntimeCoordinateStringId, Ot as isExchangeAcceptorCarrier, P as IActionFrameDecoder, Pr as TActionDomainChildDef, Q as IHttpAcceptorCarrierOptions, Qt as IExchangeCarrierSource, R as TExchangeReply, Rr as TInferOutputFromSchema, Rt as IConnectChannelOptions, S as IActionPayload_Result_JsonObject, Sr as ActionPayload_Progress, St as IServeConnectionStateOptions, T as IActionProgress_Percentage, Tr as IRuntimeFullCoordinates, Tt as IDuplexAcceptorCarrier, U as ExchangeAcceptor, Ur as TInferActionError, Ut as acceptChannelConnections, V as decodeExchangeRequest, Vr as ActionSchema, Vt as TChannelPushHandlers, W as IExchangeAcceptorConfig, Wn as ESecurityLevel, Wr as actionSchema, Wt as connectChannel, X as TCarrierFetch, Xt as IDuplexCarrierSource, Y as IHttpCarrierRequest, Yt as IDuplexCarrier, Z as httpCarrier, Zt as IExchangeCarrier, _ as IActionPayload_Data_Base, _r as IRunningActionUpdate_Success, _t as TServeHostOptions, ar as ActionLocalHandler, at as err_nice_transport_ws, b as IActionPayload_Request_JsonObject, br as TRunningActionUpdateListener, bt as IConnectionContext, cr as createActionRootDomain, ct as IRtcDataChannelLike, d as ActionCore, dr as ERunningActionFinishedType, dt as inMemoryCarrier, en as TFrame, et as IWsCarrierOptions, f as ActionPayload_Request, fr as ERunningActionState, ft as IInMemoryChannelPair, g as IActionPayload_Base_JsonObject, gr as IRunningActionUpdate_Started, gt as IChannelHostAdapter, h as IActionPayload_Base, hr as IRunningActionUpdate_Progress, ht as err_nice_external_client, ir as runtimeLinkId, it as EErrId_NiceTransport_WebSocket, j as isActionPayload_Result_JsonObject, jr as IActionDomain, k as TActionProgress, kr as IActionCore, l as ActionDomain, lr as ActionRootDomain, lt as rtcDataChannelByteChannel, m as EActionProgressType, mr as IRunningActionUpdate_Abort, mt as createInMemoryChannelPair, nt as IWsAcceptorCarrierOptions, or as createLocalHandler, ot as IRtcCarrierOptions, p as EActionPayloadType, pr as ERunningActionUpdateType, pt as IInMemoryServerEndpoint, q as err_nice_transport, qn as IClientVerifyKeyResolver, qr as TTransportedValue, rt as wsAcceptorCarrier, sr as MaybePromise, st as rtcCarrier, t as AcceptorHandler, tn as ConnectorHandler, tr as createStorageTofuVerifyKeyResolver, tt as wsCarrier, u as ActionRuntime, ur as RunningAction, ut as IInMemoryCarrier, v as IActionPayload_Progress, vr as TRunningActionUpdate, vt as serveHost, w as IActionProgress_None, wr as IRuntimeCoordinateSpecifics, wt as IAcceptorAttachmentStore, x as IActionPayload_Result, xr as ActionPayload_Result, xt as IServeChannelOptions, y as IActionPayload_Progress_JsonObject, yr as TRunningActionUpdateFinished, yt as IChannelServer, z as TExchangeRequest, zr as TPossibleDomainId, zt as IConnectTransport } from "./AcceptorHandler-CxPfZtIl.cjs";
2
+ export { type AcceptorHandler, ActionCore, ActionDomain, ActionLocalHandler, type ActionPayload_Progress, type ActionPayload_Request, type ActionPayload_Result, ActionRootDomain, ActionRuntime, ActionSchema, type ConnectionStateStore, type ConnectorHandler, EActionPayloadType, EActionProgressType, EActionResponseMode, EErrId_NiceAction, EErrId_NiceTransport, EErrId_NiceTransport_WebSocket, ERunningActionFinishedType, ERunningActionState, ERunningActionUpdateType, ESecurityLevel, ExchangeAcceptor, type IAcceptChannelOptions, type IAcceptorAttachmentStore, type IActionChannel, IActionCore, IActionCore_JsonObject, type IActionDomain, type IActionDomainChildOptions, type IActionFrameDecoder, IActionPayload_Base, IActionPayload_Base_JsonObject, IActionPayload_Data_Base, IActionPayload_Progress, IActionPayload_Progress_JsonObject, IActionPayload_Request_JsonObject, IActionPayload_Result, IActionPayload_Result_JsonObject, IActionProgress_Custom, IActionProgress_None, IActionProgress_Percentage, type IActionRootDomain, IActionRouteItemHandler, type IChannelHostAdapter, type IChannelServer, type IClientVerifyKeyResolveInput, type IClientVerifyKeyResolver, type IConnectChannelOptions, type IConnectTransport, type IConnectionContext, type IDuplexAcceptorCarrier, type IDuplexCarrier, type IDuplexCarrierSource, type IExchangeAcceptorCarrier, type IExchangeAcceptorConfig, type IExchangeAcceptorSecurity, type IExchangeCarrier, type IExchangeCarrierSource, type IHttpAcceptorCarrierOptions, type IHttpCarrierOptions, type IHttpCarrierRequest, type IInMemoryCarrier, type IInMemoryChannelPair, type IInMemoryServerEndpoint, type IRtcCarrierOptions, type IRtcDataChannelLike, type IRunningActionUpdate_Abort, type IRunningActionUpdate_Progress, type IRunningActionUpdate_Started, type IRunningActionUpdate_Success, IRuntimeCoordinate, IRuntimeCoordinateSpecifics, IRuntimeFullCoordinates, type IServeChannelOptions, type IServeConnectionStateOptions, type IWsAcceptorCarrierOptions, type IWsCarrierOptions, MaybePromise, RunningAction, RuntimeCoordinate, type TAcceptorCarrier, type TActionDomainChildDef, type TActionDomainSchema, TActionPayload_Any_Instance, TActionPayload_Any_JsonObject, TActionProgress, TActionResultOutcome, type TActionSchemaOptions, type TActionSerializationDefinition, type TCarrier, type TCarrierFetch, type TChannelAcceptorCases, type TChannelPushHandlers, type TDomainActionId, type TExchangeReply, type TExchangeRequest, type TFrame, type TInferActionError, type TInferInputFromSchema, type TInferOutputFromSchema, type TPossibleDomainId, type TPossibleDomainIdList, type TRunningActionUpdate, type TRunningActionUpdateFinished, type TRunningActionUpdateListener, TRuntimeCoordinateEnvId, TRuntimeCoordinateStringId, type TServeHostOptions, type TTransportedValue, acceptChannel, acceptChannelConnections, actionSchema, connectChannel, createActionRootDomain, createInMemoryChannelPair, createInMemoryTofuVerifyKeyResolver, createLocalHandler, createStorageTofuVerifyKeyResolver, decodeActionFrame, decodeExchangeReply, decodeExchangeRequest, defineChannel, encodeExchange, err_nice_action, err_nice_external_client, err_nice_transport, err_nice_transport_ws, httpAcceptorCarrier, httpCarrier, inMemoryCarrier, isActionPayload_Any_JsonObject, isActionPayload_Request_JsonObject, isActionPayload_Result_JsonObject, isExchangeAcceptorCarrier, rtcCarrier, rtcDataChannelByteChannel, runtimeLinkId, serveChannel, serveHost, wsAcceptorCarrier, wsCarrier };
package/build/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as rtcCarrier, $n as MaybePromise, A as TActionResultOutcome, Ar as TInferOutputFromSchema, At as IConnectChannelOptions, B as IHttpCarrierOptions, C as IActionProgress_Custom, Cr as IActionDomain, D as TActionPayload_Any_Instance, Dr as TActionDomainSchema, E as IActionRouteItemHandler, Er as TActionDomainChildDef, F as decodeActionFrame, Fr as TInferActionError, Ft as acceptChannelConnections, G as httpAcceptorCarrier, Gn as createInMemoryTofuVerifyKeyResolver, Gt as TCarrier, H as TCarrierFetch, Ht as IDuplexCarrierSource, I as EErrId_NiceAction, In as ESecurityLevel, Ir as actionSchema, It as connectChannel, J as IWsAcceptorCarrierOptions, K as IWsCarrierOptions, Kt as TFrame, L as err_nice_action, Lr as TActionSchemaOptions, Lt as defineChannel, M as isActionPayload_Request_JsonObject, Mr as TPossibleDomainIdList, Mt as TChannelAcceptorCases, N as isActionPayload_Any_JsonObject, Nr as ActionSchema, Nt as TChannelPushHandlers, O as TActionPayload_Any_JsonObject, Or as TDomainActionId, Ot as IAcceptChannelOptions, P as IActionFrameDecoder, Pr as EActionResponseMode, Pt as acceptChannel, Q as IRtcCarrierOptions, Qn as createLocalHandler, R as EErrId_NiceTransport, Rn as IClientVerifyKeyResolveInput, Rr as TActionSerializationDefinition, S as IActionPayload_Result_JsonObject, Sr as IActionCore_JsonObject, T as IActionProgress_Percentage, Tr as IActionRootDomain, U as httpCarrier, Ut as IExchangeCarrier, V as IHttpCarrierRequest, Vt as IDuplexCarrier, W as IHttpAcceptorCarrierOptions, Wt as IExchangeCarrierSource, X as EErrId_NiceTransport_WebSocket, Xn as runtimeLinkId, Y as wsAcceptorCarrier, Z as err_nice_transport_ws, Zn as ActionLocalHandler, _ as IActionPayload_Data_Base, _r as IRuntimeFullCoordinates, _t as IDuplexAcceptorCarrier, ar as ERunningActionUpdateType, at as IInMemoryServerEndpoint, b as IActionPayload_Request_JsonObject, br as TRuntimeCoordinateStringId, bt as isExchangeAcceptorCarrier, cr as IRunningActionUpdate_Started, ct as IChannelHostAdapter, d as ActionCore, dr as TRunningActionUpdateFinished, dt as IChannelServer, er as createActionRootDomain, et as IRtcDataChannelLike, f as ActionPayload_Request, fr as TRunningActionUpdateListener, ft as IConnectionContext, g as IActionPayload_Base_JsonObject, gr as IRuntimeCoordinateSpecifics, gt as IAcceptorAttachmentStore, h as IActionPayload_Base, hr as IRuntimeCoordinate, ht as serveChannel, ir as ERunningActionState, it as IInMemoryChannelPair, j as isActionPayload_Result_JsonObject, jr as TPossibleDomainId, jt as IConnectTransport, k as TActionProgress, kr as TInferInputFromSchema, kt as IActionChannel, l as ActionDomain, lr as IRunningActionUpdate_Success, lt as TServeHostOptions, m as EActionProgressType, mr as ActionPayload_Progress, mt as IServeConnectionStateOptions, nr as RunningAction, nt as IInMemoryCarrier, or as IRunningActionUpdate_Abort, ot as createInMemoryChannelPair, p as EActionPayloadType, pr as ActionPayload_Result, pt as IServeChannelOptions, q as wsCarrier, qn as createStorageTofuVerifyKeyResolver, qt as ConnectorHandler, rr as ERunningActionFinishedType, rt as inMemoryCarrier, sr as IRunningActionUpdate_Progress, st as err_nice_external_client, t as AcceptorHandler, tr as ActionRootDomain, tt as rtcDataChannelByteChannel, u as ActionRuntime, ur as TRunningActionUpdate, ut as serveHost, v as IActionPayload_Progress, vr as RuntimeCoordinate, vt as IExchangeAcceptorCarrier, w as IActionProgress_None, wr as IActionDomainChildOptions, wt as ConnectionStateStore, x as IActionPayload_Result, xr as IActionCore, y as IActionPayload_Progress_JsonObject, yr as TRuntimeCoordinateEnvId, yt as TAcceptorCarrier, z as err_nice_transport, zn as IClientVerifyKeyResolver, zr as TTransportedValue } from "./AcceptorHandler-11-QMdx2.mjs";
2
- export { type AcceptorHandler, ActionCore, ActionDomain, ActionLocalHandler, type ActionPayload_Progress, type ActionPayload_Request, type ActionPayload_Result, ActionRootDomain, ActionRuntime, ActionSchema, type ConnectionStateStore, type ConnectorHandler, EActionPayloadType, EActionProgressType, EActionResponseMode, EErrId_NiceAction, EErrId_NiceTransport, EErrId_NiceTransport_WebSocket, ERunningActionFinishedType, ERunningActionState, ERunningActionUpdateType, ESecurityLevel, type IAcceptChannelOptions, type IAcceptorAttachmentStore, type IActionChannel, IActionCore, IActionCore_JsonObject, type IActionDomain, type IActionDomainChildOptions, type IActionFrameDecoder, IActionPayload_Base, IActionPayload_Base_JsonObject, IActionPayload_Data_Base, IActionPayload_Progress, IActionPayload_Progress_JsonObject, IActionPayload_Request_JsonObject, IActionPayload_Result, IActionPayload_Result_JsonObject, IActionProgress_Custom, IActionProgress_None, IActionProgress_Percentage, type IActionRootDomain, IActionRouteItemHandler, type IChannelHostAdapter, type IChannelServer, type IClientVerifyKeyResolveInput, type IClientVerifyKeyResolver, type IConnectChannelOptions, type IConnectTransport, type IConnectionContext, type IDuplexAcceptorCarrier, type IDuplexCarrier, type IDuplexCarrierSource, type IExchangeAcceptorCarrier, type IExchangeCarrier, type IExchangeCarrierSource, type IHttpAcceptorCarrierOptions, type IHttpCarrierOptions, type IHttpCarrierRequest, type IInMemoryCarrier, type IInMemoryChannelPair, type IInMemoryServerEndpoint, type IRtcCarrierOptions, type IRtcDataChannelLike, type IRunningActionUpdate_Abort, type IRunningActionUpdate_Progress, type IRunningActionUpdate_Started, type IRunningActionUpdate_Success, IRuntimeCoordinate, IRuntimeCoordinateSpecifics, IRuntimeFullCoordinates, type IServeChannelOptions, type IServeConnectionStateOptions, type IWsAcceptorCarrierOptions, type IWsCarrierOptions, MaybePromise, RunningAction, RuntimeCoordinate, type TAcceptorCarrier, type TActionDomainChildDef, type TActionDomainSchema, TActionPayload_Any_Instance, TActionPayload_Any_JsonObject, TActionProgress, TActionResultOutcome, type TActionSchemaOptions, type TActionSerializationDefinition, type TCarrier, type TCarrierFetch, type TChannelAcceptorCases, type TChannelPushHandlers, type TDomainActionId, type TFrame, type TInferActionError, type TInferInputFromSchema, type TInferOutputFromSchema, type TPossibleDomainId, type TPossibleDomainIdList, type TRunningActionUpdate, type TRunningActionUpdateFinished, type TRunningActionUpdateListener, TRuntimeCoordinateEnvId, TRuntimeCoordinateStringId, type TServeHostOptions, type TTransportedValue, acceptChannel, acceptChannelConnections, actionSchema, connectChannel, createActionRootDomain, createInMemoryChannelPair, createInMemoryTofuVerifyKeyResolver, createLocalHandler, createStorageTofuVerifyKeyResolver, decodeActionFrame, defineChannel, err_nice_action, err_nice_external_client, err_nice_transport, err_nice_transport_ws, httpAcceptorCarrier, httpCarrier, inMemoryCarrier, isActionPayload_Any_JsonObject, isActionPayload_Request_JsonObject, isActionPayload_Result_JsonObject, isExchangeAcceptorCarrier, rtcCarrier, rtcDataChannelByteChannel, runtimeLinkId, serveChannel, serveHost, wsAcceptorCarrier, wsCarrier };
1
+ import { $ as httpAcceptorCarrier, $n as createInMemoryTofuVerifyKeyResolver, $t as TCarrier, A as TActionResultOutcome, Ar as IActionCore_JsonObject, B as decodeExchangeReply, Br as TPossibleDomainIdList, Bt as TChannelAcceptorCases, C as IActionProgress_Custom, Cr as IRuntimeCoordinate, Ct as serveChannel, D as TActionPayload_Any_Instance, Dr as TRuntimeCoordinateEnvId, Dt as TAcceptorCarrier, E as IActionRouteItemHandler, Er as RuntimeCoordinate, Et as IExchangeAcceptorCarrier, F as decodeActionFrame, Fr as TActionDomainSchema, G as IExchangeAcceptorSecurity, Gr as TActionSchemaOptions, Gt as defineChannel, H as encodeExchange, Hr as EActionResponseMode, Ht as acceptChannel, I as EErrId_NiceAction, Ir as TDomainActionId, It as IAcceptChannelOptions, J as IHttpCarrierOptions, K as EErrId_NiceTransport, Kn as IClientVerifyKeyResolveInput, Kr as TActionSerializationDefinition, L as err_nice_action, Lr as TInferInputFromSchema, Lt as IActionChannel, M as isActionPayload_Request_JsonObject, Mr as IActionDomainChildOptions, Mt as ConnectionStateStore, N as isActionPayload_Any_JsonObject, Nr as IActionRootDomain, O as TActionPayload_Any_JsonObject, Or as TRuntimeCoordinateStringId, Ot as isExchangeAcceptorCarrier, P as IActionFrameDecoder, Pr as TActionDomainChildDef, Q as IHttpAcceptorCarrierOptions, Qt as IExchangeCarrierSource, R as TExchangeReply, Rr as TInferOutputFromSchema, Rt as IConnectChannelOptions, S as IActionPayload_Result_JsonObject, Sr as ActionPayload_Progress, St as IServeConnectionStateOptions, T as IActionProgress_Percentage, Tr as IRuntimeFullCoordinates, Tt as IDuplexAcceptorCarrier, U as ExchangeAcceptor, Ur as TInferActionError, Ut as acceptChannelConnections, V as decodeExchangeRequest, Vr as ActionSchema, Vt as TChannelPushHandlers, W as IExchangeAcceptorConfig, Wn as ESecurityLevel, Wr as actionSchema, Wt as connectChannel, X as TCarrierFetch, Xt as IDuplexCarrierSource, Y as IHttpCarrierRequest, Yt as IDuplexCarrier, Z as httpCarrier, Zt as IExchangeCarrier, _ as IActionPayload_Data_Base, _r as IRunningActionUpdate_Success, _t as TServeHostOptions, ar as ActionLocalHandler, at as err_nice_transport_ws, b as IActionPayload_Request_JsonObject, br as TRunningActionUpdateListener, bt as IConnectionContext, cr as createActionRootDomain, ct as IRtcDataChannelLike, d as ActionCore, dr as ERunningActionFinishedType, dt as inMemoryCarrier, en as TFrame, et as IWsCarrierOptions, f as ActionPayload_Request, fr as ERunningActionState, ft as IInMemoryChannelPair, g as IActionPayload_Base_JsonObject, gr as IRunningActionUpdate_Started, gt as IChannelHostAdapter, h as IActionPayload_Base, hr as IRunningActionUpdate_Progress, ht as err_nice_external_client, ir as runtimeLinkId, it as EErrId_NiceTransport_WebSocket, j as isActionPayload_Result_JsonObject, jr as IActionDomain, k as TActionProgress, kr as IActionCore, l as ActionDomain, lr as ActionRootDomain, lt as rtcDataChannelByteChannel, m as EActionProgressType, mr as IRunningActionUpdate_Abort, mt as createInMemoryChannelPair, nt as IWsAcceptorCarrierOptions, or as createLocalHandler, ot as IRtcCarrierOptions, p as EActionPayloadType, pr as ERunningActionUpdateType, pt as IInMemoryServerEndpoint, q as err_nice_transport, qn as IClientVerifyKeyResolver, qr as TTransportedValue, rt as wsAcceptorCarrier, sr as MaybePromise, st as rtcCarrier, t as AcceptorHandler, tn as ConnectorHandler, tr as createStorageTofuVerifyKeyResolver, tt as wsCarrier, u as ActionRuntime, ur as RunningAction, ut as IInMemoryCarrier, v as IActionPayload_Progress, vr as TRunningActionUpdate, vt as serveHost, w as IActionProgress_None, wr as IRuntimeCoordinateSpecifics, wt as IAcceptorAttachmentStore, x as IActionPayload_Result, xr as ActionPayload_Result, xt as IServeChannelOptions, y as IActionPayload_Progress_JsonObject, yr as TRunningActionUpdateFinished, yt as IChannelServer, z as TExchangeRequest, zr as TPossibleDomainId, zt as IConnectTransport } from "./AcceptorHandler-BizUtq4u.mjs";
2
+ export { type AcceptorHandler, ActionCore, ActionDomain, ActionLocalHandler, type ActionPayload_Progress, type ActionPayload_Request, type ActionPayload_Result, ActionRootDomain, ActionRuntime, ActionSchema, type ConnectionStateStore, type ConnectorHandler, EActionPayloadType, EActionProgressType, EActionResponseMode, EErrId_NiceAction, EErrId_NiceTransport, EErrId_NiceTransport_WebSocket, ERunningActionFinishedType, ERunningActionState, ERunningActionUpdateType, ESecurityLevel, ExchangeAcceptor, type IAcceptChannelOptions, type IAcceptorAttachmentStore, type IActionChannel, IActionCore, IActionCore_JsonObject, type IActionDomain, type IActionDomainChildOptions, type IActionFrameDecoder, IActionPayload_Base, IActionPayload_Base_JsonObject, IActionPayload_Data_Base, IActionPayload_Progress, IActionPayload_Progress_JsonObject, IActionPayload_Request_JsonObject, IActionPayload_Result, IActionPayload_Result_JsonObject, IActionProgress_Custom, IActionProgress_None, IActionProgress_Percentage, type IActionRootDomain, IActionRouteItemHandler, type IChannelHostAdapter, type IChannelServer, type IClientVerifyKeyResolveInput, type IClientVerifyKeyResolver, type IConnectChannelOptions, type IConnectTransport, type IConnectionContext, type IDuplexAcceptorCarrier, type IDuplexCarrier, type IDuplexCarrierSource, type IExchangeAcceptorCarrier, type IExchangeAcceptorConfig, type IExchangeAcceptorSecurity, type IExchangeCarrier, type IExchangeCarrierSource, type IHttpAcceptorCarrierOptions, type IHttpCarrierOptions, type IHttpCarrierRequest, type IInMemoryCarrier, type IInMemoryChannelPair, type IInMemoryServerEndpoint, type IRtcCarrierOptions, type IRtcDataChannelLike, type IRunningActionUpdate_Abort, type IRunningActionUpdate_Progress, type IRunningActionUpdate_Started, type IRunningActionUpdate_Success, IRuntimeCoordinate, IRuntimeCoordinateSpecifics, IRuntimeFullCoordinates, type IServeChannelOptions, type IServeConnectionStateOptions, type IWsAcceptorCarrierOptions, type IWsCarrierOptions, MaybePromise, RunningAction, RuntimeCoordinate, type TAcceptorCarrier, type TActionDomainChildDef, type TActionDomainSchema, TActionPayload_Any_Instance, TActionPayload_Any_JsonObject, TActionProgress, TActionResultOutcome, type TActionSchemaOptions, type TActionSerializationDefinition, type TCarrier, type TCarrierFetch, type TChannelAcceptorCases, type TChannelPushHandlers, type TDomainActionId, type TExchangeReply, type TExchangeRequest, type TFrame, type TInferActionError, type TInferInputFromSchema, type TInferOutputFromSchema, type TPossibleDomainId, type TPossibleDomainIdList, type TRunningActionUpdate, type TRunningActionUpdateFinished, type TRunningActionUpdateListener, TRuntimeCoordinateEnvId, TRuntimeCoordinateStringId, type TServeHostOptions, type TTransportedValue, acceptChannel, acceptChannelConnections, actionSchema, connectChannel, createActionRootDomain, createInMemoryChannelPair, createInMemoryTofuVerifyKeyResolver, createLocalHandler, createStorageTofuVerifyKeyResolver, decodeActionFrame, decodeExchangeReply, decodeExchangeRequest, defineChannel, encodeExchange, err_nice_action, err_nice_external_client, err_nice_transport, err_nice_transport_ws, httpAcceptorCarrier, httpCarrier, inMemoryCarrier, isActionPayload_Any_JsonObject, isActionPayload_Request_JsonObject, isActionPayload_Result_JsonObject, isExchangeAcceptorCarrier, rtcCarrier, rtcDataChannelByteChannel, runtimeLinkId, serveChannel, serveHost, wsAcceptorCarrier, wsCarrier };
package/build/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as ActionPayload_Request, B as EErrId_NiceTransport, E as createInMemoryTofuVerifyKeyResolver, G as ActionSchema, H as err_nice_external_client, J as isActionPayload_Result_JsonObject, K as EActionResponseMode, M as ActionLocalHandler, N as createLocalHandler, O as createStorageTofuVerifyKeyResolver, P as ActionRuntime, Q as RunningAction, S as decodeActionFrame, U as isActionPayload_Any_JsonObject, V as err_nice_transport, W as isActionPayload_Request_JsonObject, X as EErrId_NiceAction, Y as isAction_Base_JsonObject, Z as err_nice_action, at as RuntimeCoordinate, et as ActionPayload_Result, it as ActionBase, j as runtimeLinkId, nt as EActionProgressType, ot as runtimeCoordinateToStringIds, q as actionSchema, rt as ActionPayload, tt as EActionPayloadType, w as ESecurityLevel } from "./createHibernatableWsServerAdapter-C07RfUTH.mjs";
1
+ import { $ as ActionPayload_Request, B as EErrId_NiceTransport, E as createInMemoryTofuVerifyKeyResolver, G as ActionSchema, H as err_nice_external_client, J as isActionPayload_Result_JsonObject, K as EActionResponseMode, M as ActionLocalHandler, N as createLocalHandler, O as createStorageTofuVerifyKeyResolver, P as ActionRuntime, Q as RunningAction, S as decodeActionFrame, U as isActionPayload_Any_JsonObject, V as err_nice_transport, W as isActionPayload_Request_JsonObject, X as EErrId_NiceAction, Y as isAction_Base_JsonObject, Z as err_nice_action, a as ExchangeAcceptor, at as RuntimeCoordinate, c as decodeExchangeReply, et as ActionPayload_Result, it as ActionBase, j as runtimeLinkId, l as decodeExchangeRequest, nt as EActionProgressType, ot as runtimeCoordinateToStringIds, q as actionSchema, rt as ActionPayload, tt as EActionPayloadType, u as encodeExchange, w as ESecurityLevel } from "./createHibernatableWsServerAdapter-BkjESd01.mjs";
2
2
  import { n as ERunningActionState, r as ERunningActionUpdateType, t as ERunningActionFinishedType } from "./RunningAction.types-C176rqHG.mjs";
3
- import { a as isExchangeAcceptorCarrier, c as connectChannel, i as serveChannel, l as defineChannel, n as wsAcceptorCarrier, o as acceptChannel, r as serveHost, s as acceptChannelConnections, t as httpAcceptorCarrier } from "./httpAcceptorCarrier-DPBEuewS.mjs";
3
+ import { a as isExchangeAcceptorCarrier, c as connectChannel, i as serveChannel, l as defineChannel, n as wsAcceptorCarrier, o as acceptChannel, r as serveHost, s as acceptChannelConnections, t as httpAcceptorCarrier } from "./httpAcceptorCarrier-DWqsCz3h.mjs";
4
4
  import { nanoid } from "nanoid";
5
5
  import { err } from "@nice-code/error";
6
6
  //#region src/ActionDefinition/Action/Context/ActionContext.ts
@@ -814,6 +814,6 @@ function httpCarrier(createRequest, options = {}) {
814
814
  };
815
815
  }
816
816
  //#endregion
817
- export { ActionCore, ActionDomain, ActionLocalHandler, ActionRootDomain, ActionRuntime, ActionSchema, EActionPayloadType, EActionProgressType, EActionResponseMode, EErrId_NiceAction, EErrId_NiceTransport, EErrId_NiceTransport_WebSocket, ERunningActionFinishedType, ERunningActionState, ERunningActionUpdateType, ESecurityLevel, RunningAction, RuntimeCoordinate, acceptChannel, acceptChannelConnections, actionSchema, connectChannel, createActionRootDomain, createInMemoryChannelPair, createInMemoryTofuVerifyKeyResolver, createLocalHandler, createStorageTofuVerifyKeyResolver, decodeActionFrame, defineChannel, err_nice_action, err_nice_external_client, err_nice_transport, err_nice_transport_ws, httpAcceptorCarrier, httpCarrier, inMemoryCarrier, isActionPayload_Any_JsonObject, isActionPayload_Request_JsonObject, isActionPayload_Result_JsonObject, isExchangeAcceptorCarrier, rtcCarrier, rtcDataChannelByteChannel, runtimeLinkId, serveChannel, serveHost, wsAcceptorCarrier, wsCarrier };
817
+ export { ActionCore, ActionDomain, ActionLocalHandler, ActionRootDomain, ActionRuntime, ActionSchema, EActionPayloadType, EActionProgressType, EActionResponseMode, EErrId_NiceAction, EErrId_NiceTransport, EErrId_NiceTransport_WebSocket, ERunningActionFinishedType, ERunningActionState, ERunningActionUpdateType, ESecurityLevel, ExchangeAcceptor, RunningAction, RuntimeCoordinate, acceptChannel, acceptChannelConnections, actionSchema, connectChannel, createActionRootDomain, createInMemoryChannelPair, createInMemoryTofuVerifyKeyResolver, createLocalHandler, createStorageTofuVerifyKeyResolver, decodeActionFrame, decodeExchangeReply, decodeExchangeRequest, defineChannel, encodeExchange, err_nice_action, err_nice_external_client, err_nice_transport, err_nice_transport_ws, httpAcceptorCarrier, httpCarrier, inMemoryCarrier, isActionPayload_Any_JsonObject, isActionPayload_Request_JsonObject, isActionPayload_Result_JsonObject, isExchangeAcceptorCarrier, rtcCarrier, rtcDataChannelByteChannel, runtimeLinkId, serveChannel, serveHost, wsAcceptorCarrier, wsCarrier };
818
818
 
819
819
  //# sourceMappingURL=index.mjs.map