{"version":3,"file":"httpAcceptorCarrier-D9iaPX4-.mjs","names":["ETransportShape","ETransportShape","ETransportShape"],"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/Gateway/forwardTo.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts","../src/ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier.ts"],"sourcesContent":["import type { IDuplexCarrier, IExchangeCarrier } from \"@nice-code/wire\";\nimport type { ITransportRouteInfo, TTransportRouteParams } from \"../Transport.types\";\n\n/**\n * The carrier *shapes* ({@link TFrame}, {@link IDuplexCarrier}, {@link IExchangeCarrier},\n * {@link TCarrier}) now live in `@nice-code/wire` (plan M0a) — they are protocol-agnostic byte-moving\n * contracts shared with `@nice-code/realm`. Re-exported here so existing imports keep working.\n *\n * What stays action-side are the carrier **sources**: the per-action openers + routing metadata that\n * bind a wire carrier to the action transport (`TTransportRouteParams` is action routing).\n */\nexport type { IDuplexCarrier, IExchangeCarrier, TCarrier, TFrame } from \"@nice-code/wire\";\n\n/**\n * A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.\n * Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and passed as a\n * `carrier` to `connectChannel`'s transports (the internal `transport()` factory drives it) — so adding a\n * new carrier is \"write one of these\", nothing else.\n */\nexport interface IDuplexCarrierSource {\n  /** Open (or reuse) the carrier for an action. */\n  open: (input: TTransportRouteParams) => IDuplexCarrier;\n  /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\n  getCacheKey?: (input: TTransportRouteParams) => string[];\n  /** Devtools route info for an action routed over this carrier. */\n  getRouteInfo?: (input: TTransportRouteParams) => ITransportRouteInfo;\n  /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`, `\"memory\"`). */\n  readonly carrierLabel: string;\n}\n\n/**\n * The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an\n * {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by\n * `httpCarrier` and passed as a `carrier` to `connectChannel`'s transports — adding a new request/reply\n * protocol is \"write one of these\". The `shape` tag lets the internal `transport()` factory pick the\n * duplex vs exchange transport.\n */\nexport interface IExchangeCarrierSource {\n  /** Discriminant so a generic factory can tell an exchange source from a duplex one. */\n  readonly shape: \"exchange\";\n  /** Open (or reuse) the carrier for an action. */\n  open: (input: TTransportRouteParams) => IExchangeCarrier;\n  /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\n  getCacheKey?: (input: TTransportRouteParams) => string[];\n  /** Devtools route info for an action routed over this carrier. */\n  getRouteInfo?: (input: TTransportRouteParams) => ITransportRouteInfo;\n  /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\n  readonly carrierLabel: string;\n}\n\n/**\n * Narrow a carrier source to the exchange shape via its `shape` discriminant — the one branch the\n * internal `transport()` factory uses to pick the duplex vs exchange transport. A duplex source carries\n * no `shape`, so the `else` branch is the duplex one.\n */\nexport function isExchangeCarrierSource(\n  carrier: IDuplexCarrierSource | IExchangeCarrierSource,\n): carrier is IExchangeCarrierSource {\n  return \"shape\" in carrier && carrier.shape === \"exchange\";\n}\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\nimport { ESecurityLevel } from \"@nice-code/wire\";\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 { ExchangeTransport } from \"./Exchange/ExchangeTransport\";\nimport { LinkTransport } from \"./Link/LinkTransport\";\nimport type { TTransportRouteParams } 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: TTransportRouteParams) => boolean;\n  /** Override the devtools chip label (defaults to the carrier's `carrierLabel`). */\n  label?: string;\n  /** Optional frame-protocol mux threaded into the secure config (see `ISecureClientConfig.mux`). */\n  mux?: import(\"@nice-code/wire\").WireProtocolMux;\n  /**\n   * Whether *every* transport on this connection is exchange-shaped (computed by `connectChannel`).\n   * Protocols need a duplex transport (plan D-11) — an exchange transport built with this set\n   * throws `protocol_on_exchange_only` at bring-up when {@link mux} has registered protocols.\n   */\n  exchangeOnlyConnection?: boolean;\n  /**\n   * Wire keepalive (8b.3) resolver, threaded by `connectChannel` — resolved per dial (see\n   * `ILinkTransportOptions.linkKeepalive`). Duplex transports only; an exchange transport has no\n   * long-lived link to keep alive.\n   */\n  linkKeepalive?: () => import(\"@nice-code/wire\").IWireLinkKeepalive | undefined;\n  /**\n   * Wire-traffic observation (devtools): every frame this transport actually puts on / takes off\n   * the carrier is reported with its true wire byte size and lane (`action`, `realm`, `handshake`,\n   * `keepalive`, `http`). Threaded by `connectChannel({ wireTap })`.\n   */\n  wireTap?: import(\"@nice-code/wire\").TWireTapFn;\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      channelTags: channel.tags,\n      mux: options.mux,\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      mux: options.mux,\n      exchangeOnlyConnection: options.exchangeOnlyConnection,\n      wireTap: options.wireTap,\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    linkKeepalive: options.linkKeepalive,\n    wireTap: options.wireTap,\n  });\n}\n","import type { ClientCryptoKeyLink, StorageAdapter } from \"@nice-code/util\";\nimport type { RuntimeCoordinate } from \"@nice-code/wire\";\nimport { assembleWireConnectionParts, ESecurityLevel, securityLevelMeets } from \"@nice-code/wire\";\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\nimport type { TWrappableDomainActionHandler } from \"../../ActionDefinition/Domain/ActionDomain.types\";\nimport type { ActionRuntime } from \"../ActionRuntime\";\nimport type { ActionLocalHandler } from \"../Handler/Local/ActionLocalHandler\";\nimport type {\n  ChannelAcceptor,\n  TAcceptorCaseFn,\n} from \"../Handler/PeerLink/Acceptor/ChannelAcceptor\";\nimport {\n  createSecureChannelAcceptor,\n  type ISecureChannelAcceptorOptions,\n} from \"../Handler/PeerLink/Acceptor/createSecureChannelAcceptor\";\nimport type { ChannelConnector } from \"../Handler/PeerLink/Connector/ChannelConnector\";\nimport type {\n  TLinkEvent,\n  TReliableStreamEvent,\n} from \"../Handler/PeerLink/Connector/ChannelConnector.types\";\nimport {\n  type IDuplexCarrierSource,\n  type IExchangeCarrierSource,\n  isExchangeCarrierSource,\n} from \"../Transport/Carrier/Carrier.types\";\nimport {\n  buildActionRouteDictionary,\n  type IActionWireFormat,\n} from \"../Transport/codec/actionWireCodec\";\nimport {\n  createBinaryWireSessionFactory,\n  type IBinaryWireSessionOptions,\n} from \"../Transport/codec/createBinaryWireSessionFactory\";\nimport { transport } from \"../Transport/createTransport\";\nimport type { Transport } from \"../Transport/Transport\";\nimport type { TTransportRouteParams } from \"../Transport/Transport.types\";\n\n/**\n * A transport-agnostic routing contract between two runtimes, declared *by role* rather than by\n * \"client\"/\"server\". The two ends are named for the only asymmetry that survives every carrier (WS,\n * WebRTC, BLE, raw TCP): which side dials and which side accepts.\n *\n * - The **connector** dials out and opens the link ({@link connectChannel}).\n * - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}).\n *\n * `toAcceptor` domains flow connector→acceptor (the classic \"request\"); `toConnector` domains flow\n * acceptor→connector (the classic \"push\"). Both ends derive their routing from the same channel instead\n * of restating domain lists — and because the contract is independent of how bytes move, the very same\n * channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback).\n *\n * Beyond the routing, a channel also carries its *wire identity* — the per-connection binary codec both\n * ends build from the same domain list, plus the `dictionaryVersion` the handshake checks for drift.\n * Whether a given transport runs encrypted is a per-transport choice (see {@link IConnectTransport.secure}\n * and the acceptor's `securityLevel`), not a property of the channel — so one `defineChannel` definition\n * serves both plain and secure transports.\n */\nexport interface IActionChannel<\n  TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n  TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n> {\n  /**\n   * Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards\n   * them over its transport(s); the acceptor executes them.\n   */\n  toAcceptorDomains: TO_ACCEPTOR;\n  /**\n   * Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local\n   * handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a\n   * bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply\n   * never flow.\n   */\n  toConnectorDomains: TO_CONNECTOR;\n  /** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */\n  dictionaryVersion: string;\n  /** Per-connection session codec factory (call once per live connection). */\n  createCodec: () => IActionWireFormat;\n  /**\n   * Stable channel id, auto-derived from the channel's domain *names* (so it survives action-level\n   * evolution — adding an action changes `dictionaryVersion`, not `tag`). A connecting client advertises\n   * it in the handshake so a multi-channel acceptor can select/compose the right codec per connection.\n   */\n  tag: string;\n  /**\n   * The constituent channel tags this channel carries: `[tag]` for a plain channel, the parts in order for\n   * a {@link combineChannels} result. This is what the connector advertises to the acceptor (`hello.channels`).\n   */\n  tags: readonly string[];\n}\n\n/**\n * Derive a stable channel tag from the domain *names* (not the action list), so the tag identifies the\n * channel across action-level changes — those move `dictionaryVersion` (the drift check), never the tag.\n */\nfunction deriveChannelTag(domains: readonly ActionDomain<any>[]): string {\n  return domains.map((domain) => domain.domain).join(\"+\");\n}\n\n/**\n * Derive a stable wire-dictionary version from the ordered route list (FNV-1a over `domain:id,…`), so\n * the version moves automatically whenever the transported domains change — a stale peer is then\n * rejected by the handshake instead of silently misrouting a positionally-packed frame.\n */\nfunction deriveDictionaryVersion(domains: ActionDomain<any>[]): string {\n  const { intToRoute } = buildActionRouteDictionary(domains);\n  const signature = intToRoute.map((route) => `${route.domain}:${route.id}`).join(\",\");\n\n  let hash = 0x811c9dc5;\n  for (let i = 0; i < signature.length; i++) {\n    hash ^= signature.charCodeAt(i);\n    hash = Math.imul(hash, 0x01000193);\n  }\n  return `auto:${(hash >>> 0).toString(16).padStart(8, \"0\")}`;\n}\n\n/**\n * Declare a transport-agnostic channel by role — the single source of truth both peers share. Each end\n * MUST call this with the same domains in the same order (the binary wire dictionary is positional); the\n * `dictionaryVersion` is derived from those domains unless you pin an explicit one. The wire dictionary\n * spans `[...toAcceptor, ...toConnector]` in that order, so add new domains to the end of their list to\n * keep older peers compatible.\n *\n * Declare the domains *by role* — `toAcceptor` (connector→acceptor requests) and `toConnector`\n * (acceptor→connector pushes) — so the routing for both ends is derived from the channel (see\n * {@link connectChannel} and {@link serveChannel}) instead of being restated at each end. Security is a\n * per-transport concern, not a channel one, so this same definition is used whether a transport runs\n * plain or encrypted.\n */\nexport function defineChannel<\n  const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [],\n  const TO_CONNECTOR extends readonly ActionDomain<any>[] = [],\n>(\n  options: {\n    /** Domains the connector sends to the acceptor (connector→acceptor requests), in a stable order. Omit for a connection-only channel (e.g. a realm-only app). */\n    toAcceptor?: TO_ACCEPTOR;\n    /** Domains the acceptor pushes to the connector (acceptor→connector), in a stable order. Omit for a connection-only channel. */\n    toConnector?: TO_CONNECTOR;\n    /** Pin a human-readable version instead of the derived hash (must match on both ends). */\n    dictionaryVersion?: string;\n    /** Pin the channel's selection tag instead of the domain-name-derived default (must match on both ends). */\n    tag?: string;\n    /** Tuning for the per-connection binary session (e.g. correlation TTL). */\n    sessionOptions?: IBinaryWireSessionOptions;\n  } = {},\n): IActionChannel<TO_ACCEPTOR, TO_CONNECTOR> {\n  // Omitted lists mean a connection-only channel: it still negotiates its (empty) codec + dictionary\n  // version, establishes identity + security, and can carry frame protocols (e.g. a realm) — there are\n  // simply no actions routed over it.\n  const toAcceptor = (options.toAcceptor ?? []) as TO_ACCEPTOR;\n  const toConnector = (options.toConnector ?? []) as TO_CONNECTOR;\n  // Dictionary order is positional and must match on both ends: connector→acceptor routes first, then\n  // acceptor→connector pushes.\n  const allDomains: ActionDomain<any>[] = [...toAcceptor, ...toConnector];\n  const tag = options.tag ?? deriveChannelTag(allDomains);\n\n  return {\n    toAcceptorDomains: toAcceptor,\n    toConnectorDomains: toConnector,\n    dictionaryVersion: options.dictionaryVersion ?? deriveDictionaryVersion(allDomains),\n    createCodec: createBinaryWireSessionFactory(allDomains, options.sessionOptions),\n    tag,\n    tags: [tag],\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Channel-derived wiring (connector + acceptor)\n// ---------------------------------------------------------------------------\n\ntype TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void\n  ? I\n  : never;\n\ntype TDomainPushHandlers<D> =\n  D extends ActionDomain<infer DEF> ? Partial<TWrappableDomainActionHandler<DEF>> : never;\n\n/**\n * The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action\n * handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so\n * the keys and input types follow the channel definition.\n */\nexport type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> =\n  TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;\n\n/**\n * One transport to the peer, declared by *carrier* — the dial-out dual of `serveChannel`'s acceptor\n * carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto\n * identity) into each one, so a descriptor only carries what differs between transports: the carrier and\n * whether it runs the secure handshake.\n *\n * A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier\n * (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection\n * prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback).\n */\nexport interface IConnectTransport {\n  /** How to reach the peer — a duplex carrier (push-capable) or an exchange carrier (request/reply). */\n  carrier: IDuplexCarrierSource | IExchangeCarrierSource;\n  /**\n   * Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport\n   * draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport\n   * (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`.\n   */\n  secure?: boolean;\n  /** Security level for this secure transport; defaults to the connection-level `securityLevel`. */\n  securityLevel?: ESecurityLevel;\n  /**\n   * Optional availability gate — when it returns `false` this transport is skipped and the connection\n   * falls through to the next in preference order, re-evaluated per dispatch. Omit = always available.\n   */\n  available?: (input: TTransportRouteParams) => boolean;\n  /** Override the devtools chip label (defaults to the carrier's own label). */\n  label?: string;\n}\n\nexport interface IConnectChannelOptions<TO_CONNECTOR extends readonly ActionDomain<any>[]> {\n  /** The peer's runtime coordinate — the acceptor this connection dials. */\n  peer: RuntimeCoordinate;\n  /**\n   * The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback).\n   * They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and\n   * falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into\n   * each — the dial-out dual of `serveChannel`'s `carriers`.\n   */\n  transports: readonly IConnectTransport[];\n  /**\n   * One backing store for this connection's crypto identity, fanned across every *secure* transport so\n   * they present the same verify/exchange keys. Required when any transport is secure (the default); a\n   * fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an\n   * existing identity.\n   *\n   * **Must be durable, not just present**: the peer pins this identity's verify key on first contact\n   * (trust-on-first-use, keyed by the runtime coordinate's `envId::perId`). A store that forgets across\n   * reloads (a memory adapter) regenerates the key, and every load after the first is rejected with\n   * `identity_pin_mismatch`. In a browser use `createWebLocalStorageAdapter`; memory adapters are for\n   * tests. Pairing a non-durable store with `withPersistentId` logs a warning for exactly this reason.\n   */\n  storage?: StorageAdapter;\n  /** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */\n  link?: ClientCryptoKeyLink;\n  /**\n   * Declare this connection's identity **ephemeral by construction**: the coordinate's persistent id\n   * is minted fresh every session (e.g. `perId: crypto.randomUUID()` at startup), so the peer's\n   * trust-on-first-use pin intentionally lives and dies with the session and a non-durable `storage`\n   * is correct — this flag suppresses the durability warning for exactly that pairing. Do NOT set it\n   * for an id that survives reloads (a stored visitor/user id): that pairing genuinely breaks on the\n   * second load (`identity_pin_mismatch`), which is what the warning exists to catch.\n   */\n  ephemeralIdentity?: boolean;\n  /** Default security level for secure transports; defaults to `authenticated`. */\n  securityLevel?: ESecurityLevel;\n  /** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */\n  onPush?: TChannelPushHandlers<TO_CONNECTOR>;\n  /**\n   * Frame protocols riding this connection beside the action lane (shared-base-connect plan,\n   * Phase 3): registered on the connection's mux **at construction**, before any dispatch or\n   * `connect()` can run — so the handshake always advertises them and the caps ordering trap\n   * (review A.2) is unrepresentable on this path. Protocol modules that manage their own\n   * registration (a realm client via `realmConnection(connector)`) don't need this — register them\n   * before calling `connect()` instead.\n   */\n  protocols?: readonly import(\"@nice-code/wire\").IWireFrameProtocol[];\n  /**\n   * Bring your own frame-protocol mux — effectively a pre-populated alias of {@link protocols}\n   * (plan Phase 5): `connectChannel` creates one per connection by default (reachable as\n   * `connector.wireMux`) and registers `protocols` onto it. Pass one only to share it across\n   * connections or drive it from tests; prefer `protocols` everywhere else.\n   */\n  wireMux?: import(\"@nice-code/wire\").WireProtocolMux;\n  /** Default per-action timeout for this connection. */\n  defaultTimeout?: number;\n  /**\n   * Delivery deadline (ms) for `.reliable()` sends — how long an unacked frame retries across reconnects\n   * before it's abandoned (a pending action aborts with `reliable_delivery_abandoned`; the stream skips\n   * past it and continues). Default 60s.\n   *\n   * Not the same dial as `@nice-code/realm`'s `pendingExpiryMs` (default 10s): this is a\n   * *redelivery* deadline for at-least-once actions; that is an *optimistic-settle* expiry for\n   * realm writes. They differ on purpose — a realm write past its window rolls back UI, while a\n   * reliable action keeps retrying toward delivery (review A.7).\n   */\n  reliableActionTimeout?: number;\n  /**\n   * Observe stream-level reliable-delivery events without holding per-send handles: `abandoned` (frames\n   * `fromSeq..toSeq` of a stream dropped undelivered — deadline, abort sweep, or stream close; the\n   * stream skipped past them and continues) and `overflow` (a send rejected at the unacked-window cap).\n   * The handle-less complement of `RunningAction.waitForAck()` — use it to re-push lost ranges from your\n   * own records, surface \"receiver may be behind\", or shed load ahead of the overflow cliff.\n   */\n  onReliableEvent?: (event: TReliableStreamEvent) => void;\n  /**\n   * Observe transport link-state (resilience-surface §3): `link_down`, `redial_scheduled`\n   * (attempt + delay — truthful \"reconnecting, retrying in N s\" UX and flaky-link telemetry), and\n   * `link_up` (with `downForMs`). A drop that {@link keepLinkAlive} heals cleanly is *silent* at\n   * the realm's sync layer (`onDiagnostic`) — this hook is where transport churn is visible.\n   * Post-hoc subscription: `connector.addLinkEventListener`.\n   */\n  onLinkEvent?: (event: TLinkEvent) => void;\n  /**\n   * Keep a realm-carrying duplex link alive by auto-redialing it on an unexpected drop, with\n   * exponential backoff + jitter (DESYNC F6 / Phase 8b). Default: on when this connection carries a\n   * frame protocol (a realm rides the mux), off for a pure-action connection. Set `false` to opt\n   * out (e.g. an app that manages its own connection lifecycle). Independent of a realm's own\n   * probe-escalation `requestReconnect`, which also routes here.\n   *\n   * Teardown: `connector.releaseLink()` stops the auto-redial and releases the link (the\n   * intention-revealing counterpart of this option); `clearTransportCache()` also suppresses the\n   * redial until the next explicit `connect()`/dispatch. Observe drops/redials via\n   * {@link onLinkEvent}.\n   */\n  keepLinkAlive?: boolean;\n  /**\n   * Wire-level half-open detection (8b.3 / resilience-surface §5): after `idleMs` with no inbound\n   * frame the link sends a raw `\"ping\"`; nothing back within `pongTimeoutMs` ⇒ the socket is\n   * closed locally, converting a *half-open* link (browser offline, NAT death — `isOpen()` true,\n   * sends into the void) into the ordinary close → {@link keepLinkAlive} redial, for **every**\n   * protocol on the link at once. Worst-case detection ≈ `idleMs + pongTimeoutMs` (default\n   * 15 s + 5 s ≈ 20 s — vs ~35 s for the realm staleness probe alone, which stays as\n   * belt-and-braces and can now be relaxed rather than carrying the whole burden).\n   *\n   * Default: on whenever {@link keepLinkAlive} is active (protocol-carrying links); pass a config\n   * object to force it on for a pure-action connection too; `false` disables. A busy link never\n   * pings. Requires a peer that answers the wire `\"ping\"` — every `@nice-code` acceptor (and the\n   * Cloudflare DO auto-response) does; disable against older third-party peers.\n   */\n  linkKeepalive?: false | { idleMs?: number; pongTimeoutMs?: number };\n  /**\n   * Observe this connection's wire traffic (devtools-consolidation plan §3): called once per\n   * frame that actually crosses a carrier, with its **true wire byte size** — measured\n   * post-encryption, exactly what a platform's per-message billing / egress metering sees — and\n   * its lane (`action`, `realm`/protocol id, `handshake`, `keepalive`, `http`). Feed it a\n   * `TrafficMetricsCore` (`wireTap: traffic.wireTap`) and render the devtools Traffic tab, or\n   * point it at your own telemetry. Applies to every transport in {@link transports} and\n   * survives redials; sizes only — payload contents are never captured. Omit = zero overhead.\n   *\n   * Every event is stamped with `linkId` = {@link peer}'s `stringId`, so several connectors feeding\n   * ONE core still split per backend (\"which backend costs what\") — the mirror of `serveChannel`'s\n   * tap, which stamps the bound *client* per frame.\n   */\n  wireTap?: import(\"@nice-code/wire\").TWireTapFn;\n}\n\n/**\n * Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is\n * the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer,\n * `toConnector` pushes handled locally from `onPush`); the call binds the shared facts — the channel's\n * codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over\n * `storage`) — into every transport in `transports`, so none of them restate the channel or runtime.\n * List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback):\n * ```ts\n * const connector = connectChannel(runtime, lobbyChannel, {\n *   peer: runtime_coordinate_lobby_do,\n *   storage,\n *   transports: [{ carrier: wsCarrier(() => ({ url })) }, { carrier: httpCarrier(...), secure: false }],\n *   onPush: { player_joined: (p) => { … } },\n * });\n * ```\n * Returns the {@link ChannelConnector} so the caller can later `clearTransportCache()` it.\n */\nexport function connectChannel<\n  TO_ACCEPTOR extends readonly ActionDomain<any>[],\n  TO_CONNECTOR extends readonly ActionDomain<any>[],\n>(\n  runtime: ActionRuntime,\n  channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n  options: IConnectChannelOptions<TO_CONNECTOR>,\n): ChannelConnector {\n  const securityLevel = options.securityLevel ?? ESecurityLevel.authenticated;\n  const anySecure = options.transports.some((transport) => transport.secure ?? true);\n\n  // The connection facts shared with `createWireClient` — the mux (created up-front so protocol\n  // registration precedes the handshake), one crypto identity fanned across every secure transport,\n  // the keepalive resolver, and the per-link traffic tap (stamped with the peer this connector dials,\n  // so several connectors into ONE `TrafficMetricsCore` still split per backend). One implementation\n  // for both dial-out entry points (plan Phase 1.6); `connectChannel` then builds action `Transport`s\n  // (with the codec lane) around the result, a realm-only `createWireClient` builds mux-only\n  // `WireLinkConnection`s.\n  const {\n    mux: wireMux,\n    link,\n    resolveLinkKeepalive,\n    wireTap,\n  } = assembleWireConnectionParts({\n    callerName: \"connectChannel\",\n    peer: options.peer,\n    identity: runtime.coordinate,\n    mux: options.wireMux,\n    protocols: options.protocols,\n    anySecure,\n    storage: options.storage,\n    link: options.link,\n    ephemeralIdentity: options.ephemeralIdentity,\n    keepLinkAlive: options.keepLinkAlive,\n    linkKeepalive: options.linkKeepalive,\n    wireTap: options.wireTap,\n  });\n\n  // Protocols need a duplex transport (plan D-11): when the whole chain is exchange-shaped, an\n  // exchange transport built with this flag surfaces `protocol_on_exchange_only` at bring-up if\n  // protocols were registered on the mux (a mixed chain rides them on its duplex transport).\n  const exchangeOnlyConnection = options.transports.every((descriptor) =>\n    isExchangeCarrierSource(descriptor.carrier),\n  );\n\n  const transports: Transport[] = options.transports.map((descriptor) =>\n    transport({\n      carrier: descriptor.carrier,\n      channel,\n      runtime,\n      secure: descriptor.secure ?? true,\n      link,\n      securityLevel: descriptor.securityLevel ?? securityLevel,\n      available: descriptor.available,\n      label: descriptor.label,\n      mux: wireMux,\n      exchangeOnlyConnection,\n      linkKeepalive: resolveLinkKeepalive,\n      wireTap,\n    }),\n  );\n\n  // Each push domain self-filters `onPush` to its own action ids, so a channel with several push\n  // domains splits one merged map across the right handlers.\n  const pushHandlers =\n    options.onPush != null\n      ? channel.toConnectorDomains.map((domain) =>\n          domain.wrapAsPartialLocalHandler(\n            options.onPush as Parameters<typeof domain.wrapAsPartialLocalHandler>[0],\n          ),\n        )\n      : [];\n\n  // The level the mux's frame protocols (a realm) actually ride at. Protocols ride the mux over a\n  // *duplex* transport (an exchange transport can't push them), so the connector reports the **floor**\n  // across the duplex transports — a plain duplex fallback drags it to `none`, and a per-transport\n  // override below the connection default can't hide behind that default. A realm client asserts this\n  // before its first `hello` (PLAN-security Phase 4): it must never over-report, or avatar data could\n  // leave as app-layer plaintext. No duplex transport ⇒ `none` (a realm can't ride an exchange-only\n  // connection anyway — it needs push).\n  const duplexSecurityLevels = options.transports\n    .filter((descriptor) => !isExchangeCarrierSource(descriptor.carrier))\n    .map((descriptor) =>\n      (descriptor.secure ?? true)\n        ? (descriptor.securityLevel ?? securityLevel)\n        : ESecurityLevel.none,\n    );\n  const connectionSecurityLevel =\n    duplexSecurityLevels.length === 0\n      ? ESecurityLevel.none\n      : duplexSecurityLevels.reduce((floor, level) =>\n          securityLevelMeets(level, floor) ? floor : level,\n        );\n\n  const handler = runtime.connectTo(options.peer, {\n    transports,\n    domains: [...channel.toAcceptorDomains],\n    localHandlers: pushHandlers,\n    defaultTimeout: options.defaultTimeout,\n    reliableActionTimeout: options.reliableActionTimeout,\n    wireMux,\n    securityLevel: connectionSecurityLevel,\n    keepLinkAlive: options.keepLinkAlive,\n  });\n  if (options.onReliableEvent != null) {\n    handler.addReliableEventListener(options.onReliableEvent);\n  }\n  if (options.onLinkEvent != null) {\n    handler.addLinkEventListener(options.onLinkEvent);\n  }\n  return handler;\n}\n\n// ---------------------------------------------------------------------------\n// Multi-channel: combine several channels into one, to share a single connection / endpoint\n// ---------------------------------------------------------------------------\n\n/** The acceptor domains of one channel (`toAcceptor`). */\ntype TChannelAcceptorDomains<C> = C extends IActionChannel<infer A, any> ? A : never;\n/** The connector (push) domains of one channel (`toConnector`). */\ntype TChannelConnectorDomains<C> = C extends IActionChannel<any, infer B> ? B : never;\n/** The union of every channel's acceptor domains (distributes over the channel tuple). */\nexport type TCombinedAcceptorDomains<CHANNELS extends readonly IActionChannel<any, any>[]> =\n  TChannelAcceptorDomains<CHANNELS[number]>;\n/** The union of every channel's connector (push) domains. */\nexport type TCombinedConnectorDomains<CHANNELS extends readonly IActionChannel<any, any>[]> =\n  TChannelConnectorDomains<CHANNELS[number]>;\n\n/**\n * Combine several channels into one whose domains are the **union** of theirs, in list order — so a set of\n * channels can ride a *single* connection / endpoint (one handshake, one crypto identity) yet stay\n * independent contracts (the runtime still routes each action by its domain). The positional wire\n * dictionary spans `[...each toAcceptor, ...each toConnector]`, and the derived `dictionaryVersion` covers\n * the whole union, so the handshake's drift check validates the exact combination with no new wire fields.\n *\n * Both ends MUST combine the **same channels in the same order** (exactly the per-channel `defineChannel`\n * contract, lifted to the set) — which is why {@link connectChannels} and {@link serveChannels} are exact\n * duals. A single-element list returns that channel unchanged.\n */\nexport function combineChannels<const CHANNELS extends readonly IActionChannel<any, any>[]>(\n  channels: CHANNELS,\n): IActionChannel<TCombinedAcceptorDomains<CHANNELS>, TCombinedConnectorDomains<CHANNELS>> {\n  if (channels.length === 1) {\n    return channels[0] as IActionChannel<\n      TCombinedAcceptorDomains<CHANNELS>,\n      TCombinedConnectorDomains<CHANNELS>\n    >;\n  }\n  const toAcceptor = channels.flatMap((channel) => [...channel.toAcceptorDomains]);\n  const toConnector = channels.flatMap((channel) => [...channel.toConnectorDomains]);\n  // The union flatten can't be expressed as a single value-level tuple; defineChannel derives the right\n  // codec + version from the concrete domain arrays, and we re-assert the precise union element types.\n  // Carry the constituent tags (in order) so the connector can advertise the exact subset it uses and a\n  // multi-channel acceptor composes the matching codec; `tag` is the combined, human-readable id.\n  const tags = channels.flatMap((channel) => [...channel.tags]);\n  const combined: IActionChannel = {\n    ...defineChannel({ toAcceptor, toConnector }),\n    tag: tags.join(\"+\"),\n    tags,\n  };\n  return combined as IActionChannel<\n    TCombinedAcceptorDomains<CHANNELS>,\n    TCombinedConnectorDomains<CHANNELS>\n  >;\n}\n\n/**\n * Connect several channels to a single backend over **one shared connection** — the connect-side dual of\n * {@link serveChannels} and the multi-channel form of {@link connectChannel}. One handshake, one crypto\n * identity, and one transport stack carry the union of the channels' domains; the runtime still dispatches\n * each action by its domain, so the channels stay independent. `onPush` is the merged set of every\n * channel's `toConnector` handlers.\n * ```ts\n * connectChannels(runtime, [mainChannel, notificationsChannel], {\n *   peer: backendCoord, storage,\n *   transports: [{ carrier: httpCarrier(() => ({ url })) }],\n *   onPush: { notified: (p) => { … } },\n * });\n * ```\n * For per-channel identities/transports (e.g. an ephemeral per-resource connection), keep using separate\n * {@link connectChannel} calls — `connectChannels` is purely additive for the \"several channels, one\n * backend\" case.\n */\nexport function connectChannels<const CHANNELS extends readonly IActionChannel<any, any>[]>(\n  runtime: ActionRuntime,\n  channels: CHANNELS,\n  options: IConnectChannelOptions<TCombinedConnectorDomains<CHANNELS>>,\n): ChannelConnector {\n  return connectChannel(runtime, combineChannels(channels), options);\n}\n\ntype TDomainAcceptorCases<D, TCtx> =\n  D extends ActionDomain<infer DEF>\n    ? { [ID in keyof DEF[\"actionSchema\"] & string]?: TAcceptorCaseFn<DEF, ID, TCtx> }\n    : never;\n\n/**\n * The connection-aware case map for a channel's acceptor side: the merged set of every\n * connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus a per-action\n * `context`. `TCtx` is whatever the wiring supplies as that second argument — the raw connection\n * (`TConn | undefined`) for the low-level `acceptChannelConnections`, or an enriched `IConnectionContext`\n * for `serveChannel`'s `channelCases`. Derived from the channel's `toAcceptorDomains`, so the keys and\n * input/output types follow the channel.\n */\nexport type TChannelAcceptorCases<\n  TO_ACCEPTOR extends readonly ActionDomain<any>[],\n  TCtx,\n> = TUnionToIntersection<TDomainAcceptorCases<TO_ACCEPTOR[number], TCtx>>;\n\n/**\n * Register an acceptor handler's execution for a channel straight from its definition: the channel's\n * `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets\n * the primed request + the originating connection, as with\n * {@link ChannelAcceptor.forConnectionDomainCases}). The domain list is taken from the channel,\n * never restated. Add the returned handler to the runtime alongside the acceptor handler:\n * ```ts\n * runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]);\n * ```\n *\n * The case's second argument is the raw connection (`TConn | undefined`). For the richer state +\n * broadcast + pushBack context, serve the channel through `serveChannel`'s `channelCases` instead.\n */\nexport function acceptChannelConnections<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn>(\n  serverHandler: ChannelAcceptor<TConn>,\n  channel: IActionChannel<TO_ACCEPTOR, any>,\n  cases: TChannelAcceptorCases<TO_ACCEPTOR, TConn | undefined>,\n): ActionLocalHandler {\n  return serverHandler.forConnectionDomainCasesMulti(\n    channel.toAcceptorDomains,\n    cases as Record<string, TAcceptorCaseFn<any, any, TConn | undefined> | undefined>,\n    (connection) => connection,\n  );\n}\n\n/**\n * {@link acceptChannel}'s options — the secure-acceptor builder options minus the `channel` and `runtime`\n * it already takes positionally. One option bag, shared with the underlying {@link createSecureChannelAcceptor}.\n */\nexport type IAcceptChannelOptions<TConn> = Omit<\n  ISecureChannelAcceptorOptions<TConn>,\n  \"channel\" | \"runtime\"\n>;\n\n/**\n * Build the secure {@link ChannelAcceptor} for a channel — the accept-in counterpart to\n * {@link connectChannel}. It folds in the boilerplate of {@link createSecureChannelAcceptor} (the\n * `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storage`, the channel's codec +\n * dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel,\n * options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution:\n * ```ts\n * const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storage, send });\n * runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]);\n * ```\n */\nexport function acceptChannel<\n  TO_ACCEPTOR extends readonly ActionDomain<any>[],\n  TO_CONNECTOR extends readonly ActionDomain<any>[],\n  TConn = unknown,\n>(\n  runtime: ActionRuntime,\n  channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n  options: IAcceptChannelOptions<TConn>,\n): ChannelAcceptor<TConn> {\n  return createSecureChannelAcceptor<TConn>({ ...options, channel, runtime });\n}\n","import type { IDuplexConnectionRouter } from \"../../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\nimport { ETransportShape } from \"../Transport.types\";\nimport type { TFrame } from \"./Carrier.types\";\n\n/**\n * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}\n * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an\n * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral\n * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block\n * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates\n * it.\n *\n * Two shapes mirror the connector side:\n *\n * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can\n *   push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step\n *   (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.\n * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push\n *   (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to\n *   upgrade.\n */\n\n/**\n * Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections\n * outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional — omit for a\n * transport that never hibernates (per-connection state is then in-memory only).\n *\n * `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when\n * `connectionState` is requested) per-connection app state as one composite in this single slot, so both\n * survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections.\n */\nexport interface IAcceptorAttachmentStore<TConn> {\n  /** All currently-live connections — enumerated on build to replay binding + app state after a wake. */\n  getConnections: () => TConn[];\n  /** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */\n  read: (connection: TConn) => unknown;\n  /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */\n  write: (connection: TConn, value: unknown) => void;\n}\n\n/**\n * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each\n * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a\n * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the\n * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if\n * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)\n */\nexport interface IDuplexCarrierLifecycle<TConn> {\n  /** Feed a live frame into the server. Throws until served; permanently ignores frames after disposal. */\n  receive(connection: TConn, frame: TFrame): void;\n  /** Forget a connection on close/error. No-op before serving and after terminal disposal. */\n  drop(connection: TConn): void;\n  /** @internal `serveChannel` binds this carrier's live connection router here. */\n  _activate(router: IDuplexConnectionRouter<TConn>): void;\n  /** @internal Permanently deactivate the carrier and release its per-connection bookkeeping. */\n  _dispose?(): void;\n}\n\nexport type TInboundFrameLimitReason = \"frame_bytes\" | \"message_rate\";\n\nexport interface IInboundFrameLimits<TConn> {\n  /** Exact UTF-8/binary bytes allowed for one inbound carrier frame. */\n  maxFrameBytes?: number;\n  /** Fixed-window message budget. Boundary double-bursts are part of the declared semantics. */\n  rate?: { maxMessages: number; windowMs: number };\n  /** Called for each dropped frame so the host can close/quarantine the connection. */\n  onExceeded: (connection: TConn, reason: TInboundFrameLimitReason) => void;\n}\n\n/**\n * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw\n * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex\n * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the\n * stateful-handle wiring lives in exactly one place.\n */\nexport function createDuplexCarrierLifecycle<TConn>(\n  limits?: IInboundFrameLimits<TConn>,\n): IDuplexCarrierLifecycle<TConn> {\n  let router: IDuplexConnectionRouter<TConn> | undefined;\n  let disposed = false;\n  const windows = new Map<TConn, { count: number; startedAt: number }>();\n  const maxFrameBytes = limits?.maxFrameBytes;\n  const rate = limits?.rate;\n  if (maxFrameBytes != null && (!Number.isSafeInteger(maxFrameBytes) || maxFrameBytes <= 0)) {\n    throw new Error(\"maxFrameBytes must be a positive safe integer\");\n  }\n  if (\n    rate != null &&\n    (!Number.isSafeInteger(rate.maxMessages) ||\n      rate.maxMessages <= 0 ||\n      !Number.isSafeInteger(rate.windowMs) ||\n      rate.windowMs <= 0)\n  ) {\n    throw new Error(\"inbound rate limits must be positive safe integers\");\n  }\n\n  const frameBytes = (frame: TFrame): number =>\n    typeof frame === \"string\"\n      ? new TextEncoder().encode(frame).byteLength\n      : frame instanceof ArrayBuffer\n        ? frame.byteLength\n        : frame.byteLength;\n\n  return {\n    receive(connection, frame) {\n      if (disposed) return;\n      if (router == null) {\n        throw new Error(\n          \"acceptor carrier not served yet — pass it to serveChannel() before feeding frames\",\n        );\n      }\n      if (maxFrameBytes != null && frameBytes(frame) > maxFrameBytes) {\n        limits?.onExceeded(connection, \"frame_bytes\");\n        return;\n      }\n      if (rate != null) {\n        const now = Date.now();\n        let window = windows.get(connection);\n        if (window == null || now - window.startedAt >= rate.windowMs) {\n          window = { count: 0, startedAt: now };\n          windows.set(connection, window);\n        }\n        window.count++;\n        if (window.count > rate.maxMessages) {\n          limits?.onExceeded(connection, \"message_rate\");\n          return;\n        }\n      }\n      router.receive(connection, frame);\n    },\n    drop(connection) {\n      if (disposed) return;\n      windows.delete(connection);\n      router?.drop(connection);\n    },\n    _activate(liveRouter) {\n      if (disposed) throw new Error(\"acceptor carrier already disposed\");\n      router = liveRouter;\n    },\n    _dispose() {\n      if (disposed) return;\n      disposed = true;\n      router = undefined;\n      windows.clear();\n    },\n  };\n}\n\n/**\n * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live\n * connection, how to perform the transport-specific upgrade that admits one, which requests are such\n * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and\n * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see\n * {@link IDuplexCarrierLifecycle}).\n */\nexport interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {\n  /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */\n  readonly shape: ETransportShape.duplex;\n  /**\n   * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex\n   * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no\n   * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain\n   * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.\n   */\n  secure?: boolean;\n  /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\n  send: (connection: TConn, frame: TFrame) => void;\n  /**\n   * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a\n   * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that\n   * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).\n   */\n  upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\n  /**\n   * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.\n   * Only consulted when {@link upgrade} is present.\n   */\n  isUpgrade?: (request: Request, url: URL) => boolean;\n  /**\n   * Optional attachment read/write for connections that survive eviction (Durable Object hibernation).\n   * Present → `serveChannel` persists the routing binding (and any `connectionState`) here and replays it\n   * on wake. Absent → per-connection state is in-memory only.\n   */\n  attachmentStore?: IAcceptorAttachmentStore<TConn>;\n  /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`). */\n  readonly carrierLabel: string;\n}\n\n/**\n * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By\n * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose\n * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint\n * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's\n * plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). So a server can pair a secure\n * duplex (WebSocket) with a plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.\n */\nexport interface IExchangeAcceptorCarrier {\n  /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */\n  readonly shape: ETransportShape.exchange;\n  /**\n   * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain\n   * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,\n   * or encryption. A plain endpoint ignores the central crypto identity entirely.\n   */\n  secure?: boolean;\n  /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\n  isActionPath?: (url: URL) => boolean;\n  /**\n   * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\n   * permissive `*` set; pass `false` to attach no CORS headers at all.\n   */\n  cors?: Record<string, string> | false;\n  /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\n  useErrorStatus?: boolean;\n  /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\n  readonly carrierLabel: string;\n}\n\nexport type TAcceptorCarrier<TConn = unknown> =\n  | IDuplexAcceptorCarrier<TConn>\n  | IExchangeAcceptorCarrier;\n\n/**\n * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch\n * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex\n * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.\n */\nexport function isExchangeAcceptorCarrier<TConn>(\n  carrier: TAcceptorCarrier<TConn>,\n): carrier is IExchangeAcceptorCarrier {\n  return carrier.shape === ETransportShape.exchange;\n}\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\nimport type { IReliableReceiver } from \"@nice-code/wire\";\nimport {\n  createStorageTofuVerifyKeyResolver,\n  ESecurityLevel,\n  type IClientVerifyKeyResolver,\n  RuntimeCoordinate,\n} from \"@nice-code/wire\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { IHandledReliability } from \"../../ActionDefinition/Action/Context/ActionContext.types\";\nimport type { TActionPayload_Any_JsonObject } from \"../../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\nimport type { IActionDomain } from \"../../ActionDefinition/Domain/ActionDomain.types\";\nimport type { ActionRuntime } from \"../ActionRuntime\";\nimport type { TActionRuntimeHandler } from \"../ActionRuntime.types\";\nimport {\n  type ChannelAcceptor,\n  createChannelAcceptor,\n  type IAcceptorFrameProtocol,\n  type TAcceptorCaseFn,\n} from \"../Handler/PeerLink/Acceptor/ChannelAcceptor\";\nimport { createActionFetchHandler } from \"../Handler/PeerLink/Acceptor/createActionFetchHandler\";\nimport {\n  type ConnectionStateStore,\n  createConnectionStateStore,\n  type IConnectionAttachment,\n} from \"../Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\nimport {\n  createHibernatableWsServerAdapter,\n  type IDuplexConnectionRouter,\n} from \"../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\nimport {\n  type IDuplexAcceptorCarrier,\n  type IExchangeAcceptorCarrier,\n  isExchangeAcceptorCarrier,\n  type TAcceptorCarrier,\n} from \"../Transport/Carrier/AcceptorCarrier.types\";\nimport type { IExchangeAcceptorSecurity } from \"../Transport/SecureSession/exchangeAcceptor\";\nimport {\n  acceptChannel,\n  combineChannels,\n  type IActionChannel,\n  type TChannelAcceptorCases,\n  type TCombinedAcceptorDomains,\n} from \"./ActionChannel\";\nimport type { IActionServeLogger } from \"./serveLogger\";\n\n/**\n * Build the per-connection subset resolver a multi-channel acceptor uses: advertised `hello.channels`\n * tags → the channel that connection speaks (its own codec + dictionary version for a single tag, the\n * composed union for several, the combined `channel` for none). Returns `null` — the handshake then\n * rejects — for an unknown tag, and for a repeated tag: a repeat would compose the same channel twice\n * into the positional wire dictionary and desync every later route index.\n *\n * Throws immediately (setup time, not per connection) when two served channels share a tag — a\n * last-write-wins registry could never route that deterministically.\n */\nexport function buildChannelSubsetResolver(\n  combined: IActionChannel<any, any>,\n  registry: readonly IActionChannel<any, any>[],\n): (tags: readonly string[] | undefined) => IActionChannel | null {\n  const byTag = new Map<string, IActionChannel<any, any>>();\n  for (const entry of registry) {\n    if (byTag.has(entry.tag)) {\n      throw new Error(\n        `serveChannel: duplicate channel tag \"${entry.tag}\" in \\`channels\\` — every served channel needs a distinct tag, or subset selection cannot route deterministically.`,\n      );\n    }\n    byTag.set(entry.tag, entry);\n  }\n  return (tags) => {\n    if (tags == null || tags.length === 0) return combined;\n    const selected: IActionChannel<any, any>[] = [];\n    const seen = new Set<string>();\n    for (const tag of tags) {\n      const found = byTag.get(tag);\n      if (found == null || seen.has(tag)) return null;\n      seen.add(tag);\n      selected.push(found);\n    }\n    return selected.length === 1 ? selected[0] : combineChannels(selected);\n  };\n}\n\n/** Default accepted set, shared by every carrier: negotiate per connection to whatever the client picks. */\nconst DEFAULT_SERVER_SECURITY_LEVELS = [\n  ESecurityLevel.none,\n  ESecurityLevel.authenticated,\n  ESecurityLevel.encrypted,\n] as const;\n\n/** Per-connection app-state config for {@link serveChannel}'s `connectionState`. */\nexport interface IServeConnectionStateOptions<TApp> {\n  /**\n   * Optional Standard Schema (valibot, zod, …) validating the app state on read — a value that fails\n   * validation reads back as `null`. Omit to store the app state untyped.\n   */\n  schema?: StandardSchemaV1<unknown, TApp>;\n}\n\n/**\n * The per-action handle a `serveChannel` `channelCases` case receives as its second argument — the\n * originating connection enriched with everything a case typically reaches for, so it never threads\n * `ws` through `this.connections` / `this.server` by hand:\n *\n * - {@link state} / {@link setState} / {@link clearState} — the connection's typed app state (when\n *   `connectionState` is configured), co-stored with the routing binding so it survives hibernation.\n * - {@link broadcast} — fan a server push to every other connection (skip self with `exceptSelf`).\n * - {@link pushBack} — push a server-initiated action down *this* same connection.\n *\n * Over the HTTP-exchange path there is no live socket: {@link connection} is `null`, {@link state} reads\n * `null`, {@link setState}/{@link clearState} are no-ops, and {@link pushBack} throws (an exchange reply\n * rides its own request — it can't carry an unsolicited push).\n */\nexport interface IConnectionContext<TConn, TApp = unknown> {\n  /** The originating live connection, or `null` on the HTTP-exchange path (escape hatch). */\n  connection: TConn | null;\n  /** The originating client's coordinate (`= action.context.originClient`). */\n  origin: RuntimeCoordinate;\n  /**\n   * Receiver-side reliable-delivery facts for the frame being handled — `(seq, streamKey, redelivered)`,\n   * the same value as `action.context.reliability` (see {@link IHandledReliability}). `undefined` for a\n   * best-effort action.\n   */\n  reliability?: IHandledReliability;\n  /** This connection's app state, or `null` if unset / no live socket. */\n  state: TApp | null;\n  /** Set this connection's app state (no-op without a live socket). Preserves the routing binding. */\n  setState: (value: TApp) => void;\n  /** Clear this connection's app state but keep the routing binding (no-op without a live socket). */\n  clearState: () => void;\n  /** Fan a server-initiated action out to every connection; `exceptSelf` skips this one. */\n  broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\n    makeRequest: () => ActionPayload_Request<DOM, ID>,\n    options?: {\n      exceptSelf?: boolean;\n      where?: (connection: TConn) => boolean;\n      timeout?: number;\n      onError?: (error: unknown, connection: TConn) => void;\n    },\n  ) => void;\n  /** Push a server-initiated action down this same connection. Throws if there is no live socket. */\n  pushBack: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\n    request: ActionPayload_Request<DOM, ID>,\n    options?: { timeout?: number },\n  ) => RunningAction<DOM, ID>;\n}\n\nexport interface IServeChannelOptions<\n  TO_ACCEPTOR extends readonly ActionDomain<any>[],\n  TConn,\n  TApp = unknown,\n> {\n  /**\n   * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env(\"web_app\")`),\n   * used only as the offline-return scoring fallback — a result/push to a live client always routes over\n   * the carrier it connected on regardless of this. Optional: omit it for a multi-role server that accepts\n   * clients of several envs over one acceptor (it then scores 0 against every client, so the live\n   * connection always decides).\n   */\n  clientEnv?: RuntimeCoordinate;\n  /**\n   * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins\n   * (their keys don't collide). It is built once and shared across every carrier, so the WebSocket and the\n   * secure-HTTP endpoint present the exact same verify/exchange keys and trust the same pinned clients.\n   * Back it with persistent storage (e.g. a Durable Object's storage) so identity + pins survive eviction.\n   *\n   * Required only when at least one carrier is secure (the default). A fully-plain server (every carrier\n   * `secure: false`) needs no storage and may omit it.\n   *\n   * The secure HTTP exchange is stateless — its handshake + session ride sealed tokens, so it touches\n   * this store only for the (read-mostly) identity, never per session. That lets a single secure-exchange\n   * server (`carriers: [httpAcceptorCarrier()]`) run on a stateless Worker/Node backend with no Durable\n   * Object. On a *strongly-consistent* store (DO storage, D1, Node memory) the default lazy identity is\n   * fork-safe. On an *eventually-consistent* store (Cloudflare KV) pass an explicit {@link link} built\n   * with `identityMode: \"required\"` and `provisionIdentity()` it once out-of-band, so a transient read\n   * miss can never fork a second identity (which pinned clients would then permanently reject).\n   */\n  storage?: StorageAdapter;\n  /**\n   * The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`.\n   * Build them with `wsAcceptorCarrier` / `httpAcceptorCarrier`. Any number of duplex (push-capable)\n   * carriers are supported (e.g. WebSocket + WebRTC), plus at most one exchange (request/reply) carrier;\n   * all share one crypto identity and one runtime, and each result/push routes back over the carrier its\n   * client connected on.\n   */\n  carriers: readonly TAcceptorCarrier<TConn>[];\n  /** Your execution handlers (e.g. the local handler holding the action cases). Registered for you. */\n  handlers?: TActionRuntimeHandler[];\n  /**\n   * The individual channels this acceptor serves, for **subset selection** — set by `serveChannels` so a\n   * client connecting any subset (advertised as `hello.channels` tags) gets the matching composed codec +\n   * dictionary version. Omit for a single channel (then `channel` is used as-is). When two or more are\n   * given, a connection with no advertised tags falls back to the combined `channel`.\n   */\n  channels?: readonly IActionChannel<any, any>[];\n  /**\n   * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. Pass an\n   * existing link only to share identity with acceptors built outside this call.\n   */\n  link?: ClientCryptoKeyLink;\n  /** Accepted level(s) for every carrier; defaults to negotiating any of none/authenticated/encrypted. */\n  securityLevel?: ESecurityLevel | readonly ESecurityLevel[];\n  /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */\n  verifyKeyResolver?: IClientVerifyKeyResolver;\n  /** Timeout (ms) applied to server-initiated actions awaiting a client response. */\n  defaultTimeout?: number;\n  /**\n   * Co-store per-connection app state alongside the routing binding in the sole duplex carrier's connection\n   * attachment, so both survive a wake from eviction. Reach the typed store back on `server.connections`.\n   * Requires the carrier to expose an attachment store (the Cloudflare `durableObjectWsCarrier` does) and\n   * exactly one duplex carrier.\n   */\n  connectionState?: IServeConnectionStateOptions<TApp>;\n  /**\n   * Connection-aware action cases for the channel's acceptor (`toAcceptor`) domains — each case receives the\n   * primed request *and* an {@link IConnectionContext} (the connection plus its typed `state` and\n   * `broadcast`/`pushBack`). The connection-aware dual of `handlers`, registered on the runtime for you.\n   * Requires exactly one duplex carrier.\n   */\n  channelCases?: TChannelAcceptorCases<TO_ACCEPTOR, IConnectionContext<TConn, TApp>>;\n  /**\n   * Optional pluggable logger for served requests — `onRequest` fires when an inbound action is accepted\n   * (before it executes) and returns a reporter called with the outcome, so each request produces a\n   * request line and a result line. Fanned across every carrier (each request is tagged with its carrier's\n   * transport label, e.g. `\"http\"` / `\"ws\"`). Use {@link createDefaultServeLogger} for a ready-made console\n   * logger, or implement {@link IActionServeLogger} to forward to your own logging stack:\n   * ```ts\n   * serveChannel(runtime, channel, { storage, carriers, logger: createDefaultServeLogger() });\n   * ```\n   */\n  logger?: IActionServeLogger;\n  /**\n   * The server's wire-observation seam — the exact mirror of `connectChannel({ wireTap })`. Every frame\n   * that crosses a carrier is reported with its **true wire byte size** (post-encryption on the way out,\n   * pre-decryption on the way in: what the platform actually transmits and bills) and its lane\n   * (`\"handshake\"` / `\"keepalive\"` / `\"action\"` / a frame-protocol id such as `\"realm\"`), tagged with the\n   * bound client's coordinate as `linkId` so a multi-client server attributes traffic per connection.\n   * Sizes only — payload contents are never captured.\n   *\n   * Feed it a `TrafficMetricsCore` to give a backend the same traffic panel a frontend has:\n   * ```ts\n   * const traffic = new TrafficMetricsCore();\n   * serveChannel(runtime, channel, { storage, carriers, wireTap: traffic.wireTap });\n   * createServerDevtoolsHost({ name: \"api\" }).contributeSample(trafficSampleScope(\"traffic\", traffic));\n   * ```\n   * Fanned across every duplex carrier. With no tap the cost is one null check per frame.\n   */\n  wireTap?: import(\"@nice-code/wire\").TWireTapFn;\n  /**\n   * Persisted receive store for the **persisted** reliability tier (`.reliable({ persist: true })`). When set,\n   * a persisted-tier stream dedups through this store (shared across all duplex carriers) instead of the\n   * in-memory inbox, so its high-water survives eviction and a replayed stream dedups rather than\n   * redelivering. Build it with the platform helper (Cloudflare: `cloudflareReliableLog(ctx)`). Omit it and\n   * persisted-tier streams degrade gracefully to the session-tier (in-memory) behavior.\n   *\n   * Same store as the lower-level acceptor's\n   * {@link IChannelAcceptorBaseOptions.persistedReceiver | `persistedReceiver`} — this serve-level\n   * option simply feeds that field, so type-surface searches for either name land on the same thing.\n   */\n  reliableStore?: IReliableReceiver<TActionPayload_Any_JsonObject<any>>;\n  /**\n   * Cap on distinct **keyed** (`streamKey`) reliable streams tracked per client (default 256). Keys are\n   * client-chosen strings, so this bounds the receiver state one client can allocate; past the cap a new\n   * key's frames are served best-effort with a one-time warning.\n   */\n  maxKeyedStreamsPerClient?: number;\n  /**\n   * Protocol modules to register on every duplex acceptor handler — e.g. a realm server's acceptor\n   * protocol (`serveRealmDurableObject(...).protocol`). The accept-in mirror of\n   * `connectChannel({ protocols })`. Registered **before** the hibernation adapter replays surviving\n   * bindings, so each protocol's `onAttach` fires for rehydrated connections on a Durable Object wake.\n   */\n  protocols?: readonly IAcceptorFrameProtocol<TConn>[];\n}\n\n/**\n * One server serving a secure channel over several carriers — the accept-in dual of `connectChannel`,\n * returned by {@link serveChannel}. Wire its surface straight to the host's request/socket events.\n */\nexport interface IChannelServer<TConn, TApp = unknown> {\n  /**\n   * The duplex channel acceptors — one per duplex carrier, in carrier order (empty if none). For pushing,\n   * prefer {@link pushToClient} (it resolves the owning acceptor); reach for these for cross-carrier work\n   * like a per-acceptor `broadcast`.\n   */\n  acceptors: ChannelAcceptor<TConn>[];\n  /**\n   * Unified request handler: answers the CORS preflight, performs the duplex upgrade for an upgrade\n   * request, serves a secure-exchange action `POST`, else `404`. Forward the host's `fetch` straight to it.\n   */\n  fetch: (request: Request) => Promise<Response>;\n  /**\n   * Feed one inbound frame from a live connection into the server — forward your host's \"message\" event\n   * here (a Durable Object's `webSocketMessage`, a Bun `websocket.message`, a Node `ws.on(\"message\")`).\n   * Routes to the sole duplex carrier; throws with a clear message when there are zero or several duplex\n   * carriers (for the multi-carrier case feed each `acceptors[i]` / carrier handle directly).\n   */\n  receive: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\n  /**\n   * Forget a connection on close/error — forward your host's \"close\"/\"error\" event here. Routes to the\n   * sole duplex carrier; a no-op when there are none (an HTTP-only server has no sockets to drop).\n   */\n  drop: (connection: TConn) => void;\n  /** Permanently quiesce the host: detach connections and reject/ignore all late entry points. */\n  dispose: () => void;\n  /**\n   * Push a server-initiated action to a connected client (the runtime is bound in, so unlike\n   * {@link ChannelAcceptor.pushToClient} you pass only the target + request). It routes through the duplex\n   * carrier the target connected on. Throws if no duplex carrier currently holds the target.\n   */\n  pushToClient: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\n    target: TConn | RuntimeCoordinate,\n    request: ActionPayload_Request<DOM, ID>,\n    options?: { timeout?: number },\n  ) => RunningAction<DOM, ID>;\n  /**\n   * Fan a server-initiated action out to every connection on the sole duplex carrier (skip the origin with\n   * `except`, filter with `where`). The push-to-many counterpart of {@link pushToClient}. Throws if there\n   * isn't exactly one duplex carrier (with several, broadcast over a specific `acceptors[i]`).\n   */\n  broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\n    makeRequest: () => ActionPayload_Request<DOM, ID>,\n    options?: {\n      except?: TConn | null;\n      where?: (connection: TConn) => boolean;\n      timeout?: number;\n      onError?: (error: unknown, connection: TConn) => void;\n    },\n  ) => void;\n  /**\n   * The per-connection app-state store co-stored with the routing binding in the connection attachment —\n   * present only when `connectionState` was passed. `get`/`set`/`clearApp`/`entries` it directly; it\n   * survives hibernation alongside the binding.\n   */\n  connections?: ConnectionStateStore<TConn, TApp>;\n}\n\n/**\n * Serve a secure channel over one or more carriers from a single call — the accept-in dual of\n * `connectChannel`. It builds the crypto identity (a {@link ClientCryptoKeyLink} + a storage-backed TOFU\n * resolver) and the security block (coordinate, dictionary version, accepted levels) *once* from\n * `(runtime, channel)` and fans them across every carrier, so the WebSocket and the secure-HTTP endpoint\n * can never drift apart. It registers your handlers (plus the duplex acceptor it builds) on the runtime,\n * wires hibernation when the duplex carrier exposes an attachment store, and returns a single\n * {@link IChannelServer} whose `fetch` / `receive` / `drop` / `pushToClient` / `broadcast` you forward\n * straight to the host:\n * ```ts\n * const server = serveChannel(runtime, channel, {\n *   clientEnv, storage,\n *   carriers: [wsAcceptorCarrier({ send, upgrade, attachmentStore }), httpAcceptorCarrier()],\n *   connectionState: { schema: vs_player }, // optional: co-store per-connection app state (survives hibernation)\n *   channelCases: { join: (action, conn) => { conn.setState(action.input); … } }, // connection-aware cases\n * });\n * // fetch(req) => server.fetch(req)\n * // webSocketMessage(conn, m) => server.receive(conn, m)\n * // webSocketClose/Error(conn) => server.drop(conn)\n * // server.connections.get(conn) / server.broadcast(() => push.request(…), { except: conn })\n * ```\n *\n * On Cloudflare, `serveDurableObject` folds the whole DO transport stack (carriers + storage + keepalive)\n * into this — reach for it instead of assembling the carriers by hand.\n *\n * `TConn` (the live-connection token a duplex carrier hands back through `send`/`receive`/`drop`) is\n * inferred from the carriers — `WebSocket` for `wsAcceptorCarrier`, the data-channel type for a WebRTC\n * carrier, and so on — so it stays carrier-agnostic. Passing `connectionState` narrows the return so\n * `server.connections` is non-optional.\n */\nexport function serveChannel<\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  options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp> & {\n    connectionState: IServeConnectionStateOptions<TApp>;\n  },\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\nexport function serveChannel<\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  options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>,\n): IChannelServer<TConn, TApp>;\nexport function serveChannel(\n  runtime: ActionRuntime,\n  channel: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\n  options: IServeChannelOptions<readonly ActionDomain<any>[], any, any>,\n): IChannelServer<any, any> {\n  type TConn = any;\n  const duplexCarriers = options.carriers.filter(\n    (carrier): carrier is IDuplexAcceptorCarrier<TConn> => !isExchangeAcceptorCarrier(carrier),\n  );\n  const exchangeCarriers = options.carriers.filter(isExchangeAcceptorCarrier);\n\n  if (exchangeCarriers.length > 1) {\n    throw new Error(\"serveChannel: at most one exchange carrier is supported\");\n  }\n  const exchangeCarrier: IExchangeAcceptorCarrier | undefined = exchangeCarriers[0];\n\n  const singleDuplex = duplexCarriers.length === 1;\n  if (options.connectionState != null && !singleDuplex) {\n    throw new Error(\"serveChannel: `connectionState` requires exactly one duplex carrier\");\n  }\n  if (options.channelCases != null && !singleDuplex) {\n    throw new Error(\"serveChannel: `channelCases` requires exactly one duplex carrier\");\n  }\n\n  const exchangeSecure = exchangeCarrier != null && (exchangeCarrier.secure ?? true);\n  const anyDuplexSecure = duplexCarriers.some((carrier) => carrier.secure ?? true);\n  const securityLevel = options.securityLevel ?? DEFAULT_SERVER_SECURITY_LEVELS;\n\n  // Multi-channel subset selection: when serving several channels, resolve a connection's advertised tags\n  // (`hello.channels`) into the channel it should use. Omitted for a single channel (unchanged path).\n  const registry = options.channels;\n  const resolveChannel =\n    registry != null && registry.length > 1\n      ? buildChannelSubsetResolver(channel, registry)\n      : undefined;\n\n  // The shared crypto identity (link + TOFU resolver) — built once and fanned across every *secure*\n  // carrier. Only constructed when something needs it; a fully-plain server uses no storage.\n  let secure:\n    | {\n        storage: StorageAdapter;\n        link: ClientCryptoKeyLink;\n        verifyKeyResolver: IClientVerifyKeyResolver;\n      }\n    | undefined;\n  if (anyDuplexSecure || exchangeSecure) {\n    const storage = options.storage;\n    if (storage == null) {\n      throw new Error(\n        \"serveChannel: a secure carrier requires `storage`. Pass it, or set `secure: false` on the carrier for a plain endpoint.\",\n      );\n    }\n    secure = {\n      storage,\n      link: options.link ?? new ClientCryptoKeyLink({ storageAdapter: storage }),\n      verifyKeyResolver: options.verifyKeyResolver ?? createStorageTofuVerifyKeyResolver(storage),\n    };\n  }\n\n  // One acceptor handler per duplex carrier, each activated as its own lifecycle handle. Secure carriers →\n  // `acceptChannel` (handshake + the shared identity, so they present the same keys as the exchange\n  // endpoint); plain carriers → the base `createChannelAcceptor` over the channel's wire codec, no crypto.\n  // Phase-A connection-aware return routing then sends each result/push back over the carrier its client\n  // connected on.\n  // The plain (in-memory only) router for a connection — no persistence across eviction.\n  const plainRouter = (handler: ChannelAcceptor<TConn>): IDuplexConnectionRouter<TConn> => ({\n    receive: (connection, frame) => handler.receive(connection, frame),\n    drop: (connection) => handler.drop(connection),\n  });\n  const asObject = (value: unknown): object =>\n    typeof value === \"object\" && value != null ? value : {};\n\n  const acceptors: ChannelAcceptor<TConn>[] = [];\n  // The per-connection app-state store, built once over the sole duplex carrier when `connectionState` is set.\n  let connections: ConnectionStateStore<TConn, any> | undefined;\n  for (const carrier of duplexCarriers) {\n    const handler =\n      (carrier.secure ?? true) && secure != null\n        ? acceptChannel<any, any, TConn>(runtime, channel, {\n            clientEnv: options.clientEnv,\n            storage: secure.storage,\n            link: secure.link,\n            verifyKeyResolver: secure.verifyKeyResolver,\n            securityLevel,\n            send: carrier.send,\n            defaultTimeout: options.defaultTimeout,\n            resolveChannel,\n            logger: options.logger,\n            wireTap: options.wireTap,\n            transportLabel: carrier.carrierLabel,\n            persistedReceiver: options.reliableStore,\n            maxKeyedStreamsPerClient: options.maxKeyedStreamsPerClient,\n          })\n        : createChannelAcceptor<TConn>({\n            clientEnv: options.clientEnv,\n            createFormatMessage: channel.createCodec,\n            send: carrier.send,\n            runtime,\n            defaultTimeout: options.defaultTimeout,\n            logger: options.logger,\n            wireTap: options.wireTap,\n            transportLabel: carrier.carrierLabel,\n            persistedReceiver: options.reliableStore,\n            maxKeyedStreamsPerClient: options.maxKeyedStreamsPerClient,\n          });\n\n    // Frame protocols first (M1 seam): they must exist before the hibernation adapter below replays\n    // surviving bindings, so protocol `onAttach` hooks fire for rehydrated connections.\n    for (const protocol of options.protocols ?? []) {\n      handler.registerFrameProtocol(protocol);\n    }\n\n    // The attachment is one composite slot `{ app?, binding? }` per connection: serveChannel owns its\n    // layout so binding *and* app state survive a wake from a single slot.\n    // - no attachment store → in-memory only (no replay).\n    // - `connectionState` requested → the connection store owns binding + app persistence and wake-replay.\n    // - otherwise → the bare hibernation adapter persists/replays just the `.binding` sub-field.\n    const attach = carrier.attachmentStore;\n    let router: IDuplexConnectionRouter<TConn>;\n    if (attach == null) {\n      router = plainRouter(handler);\n    } else if (options.connectionState != null) {\n      connections = createConnectionStateStore<TConn, any>(handler, {\n        schema: options.connectionState.schema,\n        getConnections: attach.getConnections,\n        read: attach.read,\n        write: attach.write,\n      });\n      router = plainRouter(handler); // the store already replayed surviving bindings\n    } else {\n      router = createHibernatableWsServerAdapter<TConn>({\n        handler,\n        getConnections: attach.getConnections,\n        getAttachment: (connection) =>\n          (attach.read(connection) as IConnectionAttachment<unknown> | null | undefined)?.binding,\n        setAttachment: (connection, binding) =>\n          attach.write(connection, { ...asObject(attach.read(connection)), binding }),\n      });\n    }\n    carrier._activate(router);\n    acceptors.push(handler);\n  }\n\n  runtime.addHandlers([...(options.handlers ?? []), ...acceptors]);\n\n  // --- Connection lifecycle + server-push surface ---\n  // `receive`/`drop` are the universal forwarding seam every host environment feeds (a Durable Object's\n  // `webSocketMessage`, a Bun `websocket.message`, …). They route to the sole duplex carrier (the common\n  // case); with several, each carrier is its own handle, so callers feed those directly.\n  const soleDuplex: IDuplexConnectionRouter<TConn> | undefined = singleDuplex\n    ? duplexCarriers[0]\n    : undefined;\n  let disposed = false;\n  const receive = (connection: TConn, frame: string | Uint8Array | ArrayBuffer): void => {\n    if (disposed) return;\n    if (soleDuplex == null) {\n      throw new Error(\n        duplexCarriers.length === 0\n          ? \"serveChannel: no duplex carrier to receive on (this server has no socket transport)\"\n          : \"serveChannel: several duplex carriers — feed each carrier handle's receive() directly\",\n      );\n    }\n    soleDuplex.receive(connection, frame);\n  };\n  const drop = (connection: TConn): void => {\n    if (disposed) return;\n    soleDuplex?.drop(connection);\n  };\n\n  const pushToClient = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\n    target: TConn | RuntimeCoordinate,\n    request: ActionPayload_Request<DOM, ID>,\n    pushOptions?: { timeout?: number },\n  ): RunningAction<DOM, ID> => {\n    if (disposed) throw new Error(\"serveChannel: server disposed\");\n    // Route the push through the acceptor that actually holds the target's live connection.\n    const owner =\n      target instanceof RuntimeCoordinate\n        ? acceptors.find((acceptor) => acceptor.ownsLiveConnectionFor(target))\n        : acceptors.find((acceptor) => acceptor.hasConnection(target));\n    if (owner == null) {\n      throw new Error(\"serveChannel: no duplex carrier holds a connection for the push target\");\n    }\n    return owner.pushToClient(runtime, target, request, pushOptions);\n  };\n\n  const broadcast = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\n    makeRequest: () => ActionPayload_Request<DOM, ID>,\n    broadcastOptions?: {\n      except?: TConn | null;\n      where?: (connection: TConn) => boolean;\n      timeout?: number;\n      onError?: (error: unknown, connection: TConn) => void;\n    },\n  ): void => {\n    if (disposed) throw new Error(\"serveChannel: server disposed\");\n    if (!singleDuplex) {\n      throw new Error(\n        \"serveChannel: broadcast requires exactly one duplex carrier — broadcast over a specific acceptors[i] instead\",\n      );\n    }\n    acceptors[0].broadcast(makeRequest, { runtime, ...broadcastOptions });\n  };\n\n  // The per-action context handed to `channelCases`: the resolved connection enriched with typed `state`\n  // plus `broadcast`/`pushBack`, so a case never reaches back into `connections`/the server by hand.\n  const makeConnectionContext = (\n    connection: TConn | undefined,\n    request: ActionPayload_Request<any, any>,\n  ): IConnectionContext<TConn, any> => ({\n    connection: connection ?? null,\n    origin: request.context.originClient,\n    reliability: request.context.reliability,\n    get state() {\n      return connection != null && connections != null ? connections.get(connection) : null;\n    },\n    setState(value) {\n      if (connection != null && connections != null) connections.set(connection, value);\n    },\n    clearState() {\n      if (connection != null && connections != null) connections.clearApp(connection);\n    },\n    broadcast(makeRequest, contextOptions) {\n      broadcast(makeRequest, {\n        except: contextOptions?.exceptSelf ? (connection ?? null) : null,\n        where: contextOptions?.where,\n        timeout: contextOptions?.timeout,\n        onError: contextOptions?.onError,\n      });\n    },\n    pushBack(pushRequest, pushOptions) {\n      if (connection == null) {\n        throw new Error(\n          \"serveChannel: connection context has no live socket to push back to (HTTP-exchange path)\",\n        );\n      }\n      return pushToClient(connection, pushRequest, pushOptions);\n    },\n  });\n\n  // Connection-aware action cases for the channel's acceptor domains, registered on the runtime alongside\n  // the (sole) duplex acceptor — the connection-aware dual of `handlers`. Each case gets the enriched\n  // `IConnectionContext`, built per inbound action.\n  if (options.channelCases != null) {\n    runtime.addHandlers([\n      acceptors[0].forConnectionDomainCasesMulti(\n        channel.toAcceptorDomains,\n        options.channelCases as Record<\n          string,\n          TAcceptorCaseFn<any, any, IConnectionContext<TConn, any>> | undefined\n        >,\n        makeConnectionContext,\n      ),\n    ]);\n  }\n\n  // The exchange (request/reply) security block — derived from the same identity + the channel/runtime\n  // facts, so it never restates what the duplex side already knows. Omitted for a plain exchange carrier\n  // (`secure: false`), which then POSTs the raw wire — letting a plain HTTP fallback sit beside a secure WS.\n  const exchangeSecurity: IExchangeAcceptorSecurity | undefined =\n    exchangeSecure && secure != null\n      ? {\n          link: secure.link,\n          verifyKeyResolver: secure.verifyKeyResolver,\n          localCoordinate: runtime.coordinate.toJsonObject(),\n          // The exchange carries the JSON wire (no positional codec), so it only needs the right dictionary\n          // version for the handshake — composed per connection from the advertised tags when multi-channel.\n          dictionaryVersion:\n            resolveChannel != null\n              ? (hello) => resolveChannel(hello.channels)?.dictionaryVersion ?? null\n              : channel.dictionaryVersion,\n          securityLevel,\n        }\n      : undefined;\n\n  // Compose the upgrade across the duplex carriers that can be reached via an HTTP upgrade (typically just\n  // one WebSocket carrier; a WebRTC carrier is signalled out of band and has no `upgrade`). The first\n  // carrier whose `isUpgrade` matches handles the request.\n  const defaultIsUpgrade = (request: Request) => request.headers.get(\"Upgrade\") === \"websocket\";\n  const upgraders: {\n    isUpgrade: (request: Request, url: URL) => boolean;\n    upgrade: (request: Request, url: URL) => Response | Promise<Response>;\n  }[] = [];\n  for (const carrier of duplexCarriers) {\n    if (carrier.upgrade == null) continue;\n    upgraders.push({ isUpgrade: carrier.isUpgrade ?? defaultIsUpgrade, upgrade: carrier.upgrade });\n  }\n\n  const activeFetch = createActionFetchHandler(runtime, {\n    cors: exchangeCarrier?.cors,\n    onWebSocketUpgrade:\n      upgraders.length === 0\n        ? undefined\n        : (request, url) =>\n            (upgraders.find((u) => u.isUpgrade(request, url)) ?? upgraders[0]).upgrade(\n              request,\n              url,\n            ),\n    isWebSocketUpgrade:\n      upgraders.length === 0\n        ? undefined\n        : (request, url) => upgraders.some((u) => u.isUpgrade(request, url)),\n    // A single forwarding route means the path is already matched: any POST that isn't an upgrade is an\n    // action exchange. With no exchange carrier there is no HTTP action endpoint, so POSTs fall through.\n    isActionPath:\n      exchangeCarrier != null ? (exchangeCarrier.isActionPath ?? (() => true)) : () => false,\n    security: exchangeSecurity,\n    useErrorStatus: exchangeCarrier?.useErrorStatus,\n    logger: options.logger,\n    transportLabel: exchangeCarrier?.carrierLabel,\n  });\n\n  const fetch = (request: Request): Promise<Response> =>\n    disposed\n      ? Promise.resolve(\n          new Response(\"serveChannel: server disposed\", {\n            status: 503,\n            headers: { \"cache-control\": \"no-store\" },\n          }),\n        )\n      : activeFetch(request);\n\n  const dispose = (): void => {\n    if (disposed) return;\n    disposed = true;\n    // Close every public transport entry point before detach hooks run: a hook that synchronously\n    // touches its carrier must still observe a terminal server, and rate windows can be released now.\n    for (const carrier of duplexCarriers) carrier._dispose?.();\n    for (const acceptor of acceptors) acceptor.dispose();\n  };\n\n  return { acceptors, fetch, receive, drop, dispose, pushToClient, broadcast, connections };\n}\n\n/**\n * Serve a **set** of channels over one acceptor / endpoint — the accept-in dual of `connectChannels` and\n * the multi-channel form of {@link serveChannel}. The channels are combined into one (their domains\n * unioned in list order, see {@link combineChannels}) and served over a single set of carriers + one crypto\n * identity; the runtime routes each inbound action to the right handler by its domain, exactly as for a\n * single channel. `handlers` cover every channel's actions; `channelCases` is one merged, connection-aware\n * map typed against the union of all the channels' acceptor domains.\n * ```ts\n * const server = serveChannels(runtime, [mainChannel, notificationsChannel], {\n *   clientEnv, storage,\n *   carriers: [wsAcceptorCarrier(...), httpAcceptorCarrier()],\n *   handlers: [mainHandler, notificationsHandler],\n * });\n * ```\n * Both ends must list the **same channels in the same order** (the `combineChannels` contract), which is\n * what makes `serveChannels` ↔ `connectChannels` exact duals. Passing `connectionState` narrows the return\n * so `server.connections` is non-optional, exactly as with `serveChannel`.\n */\nexport function serveChannels<\n  const CHANNELS extends readonly IActionChannel<any, any>[],\n  TConn,\n  TApp,\n>(\n  runtime: ActionRuntime,\n  channels: CHANNELS,\n  options: IServeChannelOptions<TCombinedAcceptorDomains<CHANNELS>, TConn, TApp> & {\n    connectionState: IServeConnectionStateOptions<TApp>;\n  },\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\nexport function serveChannels<\n  const CHANNELS extends readonly IActionChannel<any, any>[],\n  TConn = unknown,\n  TApp = unknown,\n>(\n  runtime: ActionRuntime,\n  channels: CHANNELS,\n  options: IServeChannelOptions<TCombinedAcceptorDomains<CHANNELS>, TConn, TApp>,\n): IChannelServer<TConn, TApp>;\nexport function serveChannels(\n  runtime: ActionRuntime,\n  channels: readonly IActionChannel<any, any>[],\n  options: IServeChannelOptions<readonly ActionDomain<any>[], any, any>,\n): IChannelServer<any, any> {\n  const combined: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]> =\n    combineChannels(channels as readonly [IActionChannel<any, any>, ...IActionChannel<any, any>[]]);\n  // Pass the individual channels as the registry so a client connecting any *subset* (advertised as\n  // `hello.channels` tags) gets the matching composed codec + dictionary version.\n  return serveChannel(runtime, combined, { ...options, channels });\n}\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","/**\n * Opaque action forwarding — the platform-neutral half of the front-door routing surface.\n *\n * Security in `@nice-code/action` terminates at the *final runtime* (the acceptor that runs the secure\n * handshake). A forwarder therefore never reads, decrypts, or re-signs the body: it only picks a\n * destination and passes the request through. That keeps the channel end-to-end encrypted between the\n * origin client and whatever final runtime ultimately serves it — a Durable Object, a service binding, or\n * an entirely different HTTP server — while the forwarder in the middle stays a dumb, stateless, identity-\n * less relay. Because the body is opaque, the routing key must ride the URL/headers (which is why callers\n * encode it in the path, e.g. `/bridge/:id/...`).\n *\n * A `forwardTo(...)` is just an {@link IFetchHandler}, so it drops straight into any framework (Hono, raw\n * Workers) or the bundled {@link actionRouter}.\n */\n\n/** Permissive CORS, matching the rest of the action HTTP surface. Override or disable per forwarder. */\nconst DEFAULT_FORWARD_CORS: Record<string, string> = {\n  \"Access-Control-Allow-Origin\": \"*\",\n  \"Access-Control-Allow-Methods\": \"GET, POST, OPTIONS\",\n  \"Access-Control-Allow-Headers\": \"Content-Type\",\n  \"Access-Control-Max-Age\": \"86400\",\n};\n\n/**\n * The matched-route context the {@link actionRouter} threads into a handler — the parsed URL and any path\n * params (`:id` → `{ id }`). Passed as `fetch`'s optional second argument so every `{ fetch }` (a\n * forwarder, a served channel-set, a whole sub-app) shares one call shape; sub-apps simply ignore it.\n */\nexport interface IRouteContext {\n  url: URL;\n  params: Record<string, string>;\n}\n\n/**\n * The universal front-door unit: anything that answers a `Request`. A served channel-set\n * (`serveChannels`/`serveWorker`), an opaque forwarder, or a nested router/sub-app all satisfy it, so they\n * compose by nesting and mount into any framework. The optional {@link IRouteContext} is supplied when a\n * router dispatches (carrying matched path params); a direct caller may omit it.\n */\nexport interface IFetchHandler {\n  fetch(request: Request, route?: IRouteContext): Promise<Response> | Response;\n}\n\n/** A forward destination — just something with a `fetch` (a DO stub, a service binding, a proxy). */\nexport interface IForwardTarget {\n  fetch(request: Request): Promise<Response>;\n}\n\n/** What {@link forwardTo}'s `pickTarget` receives — the request plus the matched-route context. */\nexport interface IForwardContext {\n  request: Request;\n  url: URL;\n  /** Matched path params when forwarded through {@link actionRouter}; `{}` for a direct call. */\n  params: Record<string, string>;\n}\n\nexport interface IForwardToOptions {\n  /**\n   * Edge-answer the CORS `OPTIONS` preflight here (default: the permissive `*` set) so a per-id Durable\n   * Object is never woken just to reply to a preflight. `false` forwards the preflight to the target too.\n   */\n  cors?: Record<string, string> | false;\n  /**\n   * Remap the URL before forwarding — e.g. strip a path prefix when proxying to another server. Durable\n   * Objects need none (their `serveChannels` matches the `/ws` · `/secure` · `/action` suffix). Return a\n   * `URL` or a string; the forwarded request is rebuilt against it.\n   */\n  rewrite?: (url: URL, ctx: IForwardContext) => URL | string;\n}\n\n/**\n * Build an opaque forwarder: pick a destination `{ fetch }` from the request (and matched params), then\n * pass the request straight through. `pickTarget` may be async (e.g. to look up which instance/region owns\n * a resource first). The CORS `OPTIONS` preflight is answered at the edge by default.\n *\n * ```ts\n * // to a per-id Durable Object (E2E client ↔ DO):\n * forwardTo(({ params }) => env.DO_BRIDGE.get(env.DO_BRIDGE.idFromString(params.id)))\n * // to a service binding:\n * forwardTo(() => env.OTHER_WORKER)\n * // to any HTTP server, stripping a prefix:\n * forwardTo(() => ({ fetch: (req) => fetch(UPSTREAM, req) }), { rewrite: (u) => u.pathname.replace(\"/up\", \"\") })\n * ```\n */\nexport function forwardTo(\n  pickTarget: (ctx: IForwardContext) => IForwardTarget | Promise<IForwardTarget>,\n  options: IForwardToOptions = {},\n): IFetchHandler {\n  return {\n    async fetch(request: Request, route?: IRouteContext): Promise<Response> {\n      if (request.method === \"OPTIONS\" && options.cors !== false) {\n        return new Response(null, { status: 204, headers: options.cors ?? DEFAULT_FORWARD_CORS });\n      }\n\n      const url = route?.url ?? new URL(request.url);\n      const ctx: IForwardContext = { request, url, params: route?.params ?? {} };\n      const target = await pickTarget(ctx);\n\n      if (options.rewrite == null) return target.fetch(request);\n\n      // Rebuild the request against the rewritten URL (method/headers/body preserved).\n      const rewritten = options.rewrite(url, ctx);\n      const nextUrl = typeof rewritten === \"string\" ? rewritten : rewritten.toString();\n      return target.fetch(new Request(nextUrl, request));\n    },\n  };\n}\n","import { ETransportShape } from \"../../../Transport.types\";\nimport {\n  createDuplexCarrierLifecycle,\n  type IAcceptorAttachmentStore,\n  type IDuplexAcceptorCarrier,\n  type IInboundFrameLimits,\n} from \"../../AcceptorCarrier.types\";\nimport type { TFrame } from \"../../Carrier.types\";\n\nexport interface IWsAcceptorCarrierOptions<TConn> {\n  /**\n   * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint\n   * — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption\n   * (and `serveChannel` then needs no `storage` for this carrier).\n   */\n  secure?: boolean;\n  /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\n  send: (connection: TConn, frame: TFrame) => void;\n  /**\n   * Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's\n   * `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band.\n   */\n  upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\n  /** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */\n  isUpgrade?: (request: Request, url: URL) => boolean;\n  /** Attachment read/write for hibernatable sockets (e.g. a Durable Object); `serveChannel` persists here. */\n  attachmentStore?: IAcceptorAttachmentStore<TConn>;\n  /** Override the devtools carrier-kind label (defaults to `\"ws\"`). */\n  carrierLabel?: string;\n  /** Drop oversized/rate-excess inbound frames before handshake, decoding, or action dispatch. */\n  inboundLimits?: IInboundFrameLimits<TConn>;\n}\n\n/**\n * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to\n * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to\n * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,\n * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.\n */\nexport function wsAcceptorCarrier<TConn = WebSocket>(\n  options: IWsAcceptorCarrierOptions<TConn>,\n): IDuplexAcceptorCarrier<TConn> {\n  return {\n    ...createDuplexCarrierLifecycle<TConn>(options.inboundLimits),\n    shape: ETransportShape.duplex,\n    carrierLabel: options.carrierLabel ?? \"ws\",\n    secure: options.secure,\n    send: options.send,\n    upgrade: options.upgrade,\n    isUpgrade: options.isUpgrade ?? ((request) => request.headers.get(\"Upgrade\") === \"websocket\"),\n    attachmentStore: options.attachmentStore,\n  };\n}\n","import { ETransportShape } from \"../../../Transport.types\";\nimport type { IExchangeAcceptorCarrier } from \"../../AcceptorCarrier.types\";\n\nexport interface IHttpAcceptorCarrierOptions {\n  /**\n   * Whether this endpoint runs the secure exchange protocol (default `true`). Pass `false` for a plain\n   * endpoint — the body is the raw action wire and the result is the response body, the request/reply dual\n   * of a connector's plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). A plain\n   * endpoint ignores the crypto identity, so it can sit alongside a secure WebSocket on the same server\n   * (e.g. a secure WS preferred, plain HTTP fallback).\n   */\n  secure?: boolean;\n  /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\n  isActionPath?: (url: URL) => boolean;\n  /**\n   * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\n   * permissive `*` set; pass `false` to attach no CORS headers at all.\n   */\n  cors?: Record<string, string> | false;\n  /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\n  useErrorStatus?: boolean;\n  /** Override the devtools carrier-kind label (defaults to `\"http\"`). */\n  carrierLabel?: string;\n}\n\n/**\n * An HTTP {@link IExchangeAcceptorCarrier}: the accept-in dual of {@link httpCarrier}. It serves the\n * secure exchange protocol (handshake → token session → encrypted frames) over web-standard\n * `Request`/`Response`. The crypto identity, runtime coordinate, dictionary version, and accepted security\n * levels are all supplied centrally by `serveChannel`, so this only needs to say which requests carry an\n * action envelope and how to answer CORS.\n */\nexport function httpAcceptorCarrier(\n  options: IHttpAcceptorCarrierOptions = {},\n): IExchangeAcceptorCarrier {\n  return {\n    shape: ETransportShape.exchange,\n    carrierLabel: options.carrierLabel ?? \"http\",\n    secure: options.secure,\n    isActionPath: options.isActionPath,\n    cors: options.cors,\n    useErrorStatus: options.useErrorStatus,\n  };\n}\n"],"mappings":"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"}