import { ESecurityLevel, ESecurityLevel as ESecurityLevel$1, ETransportShape, ETransportShape as ETransportShape$1, ETransportStatus, EWireConnectFailureKind, EWireConnectFailureKind as EWireConnectFailureKind$1, IClientVerifyKeyResolveInput, IClientVerifyKeyResolver, IClientVerifyKeyResolver as IClientVerifyKeyResolver$1, IDuplexCarrier, IDuplexCarrier as IDuplexCarrier$1, IExchangeCarrier, IExchangeCarrier as IExchangeCarrier$1, IFrameReliability, IFrameReliability as IFrameReliability$1, IFrameReliabilityWire, IFrameReliabilityWire as IFrameReliabilityWire$1, IHttpCarrierRequest, IHttpCarrierRequest as IHttpCarrierRequest$1, IInMemoryChannelPair, IInMemoryServerEndpoint, IInMemoryServerEndpoint as IInMemoryServerEndpoint$1, IReliableReceiver, IRtcDataChannelLike, IRtcDataChannelLike as IRtcDataChannelLike$1, IRuntimeCoordinate, IRuntimeCoordinate as IRuntimeCoordinate$1, IRuntimeCoordinateSpecifics, IRuntimeCoordinateSpecifics as IRuntimeCoordinateSpecifics$1, IRuntimeFullCoordinates, ITransportStatusInfo_Base, ITransportStatusInfo_Failed, ITransportStatusInfo_Initializing, ITransportStatusInfo_Ready, ITransportStatusInfo_Unsupported, IWireAcceptorProtocol, IWireClientPair, IWireConnectionBinding, IWireRouteInfo, IWireSecureClientConfig, IWireTransportReady, IWsCarrierRequest, RuntimeCoordinate, RuntimeCoordinate as RuntimeCoordinate$1, TCarrier, TCarrierFetch, TCarrierFetch as TCarrierFetch$1, TControlMessage, TFrame, TLinkEvent, TRuntimeCoordinateEnvId, TRuntimeCoordinateStringId, TRuntimeCoordinateStringId as TRuntimeCoordinateStringId$1, TServerDictionaryVersionResolver, TTransportCache, TTransportInitializationFinishedInfo, TTransportStatusInfo, TTransportStatusInfo_GetTransport_Output, TTransportStatusInfo_GetTransport_Output as TTransportStatusInfo_GetTransport_Output$1, TransportConnection, WireAcceptor, WireProtocolMux, classifyConnectFailure, createInMemoryChannelPair as createInMemoryChannelPair$1, createInMemoryTofuVerifyKeyResolver as createInMemoryTofuVerifyKeyResolver$1, createStorageTofuVerifyKeyResolver as createStorageTofuVerifyKeyResolver$1, rtcDataChannelByteChannel, runtimeLinkId } from "@nice-code/wire"; import { INiceErrorDomainProps, INiceErrorJsonObject, InferNiceError, NiceError, NiceErrorDomain } from "@nice-code/error"; import { RuntimeName } from "std-env"; import { ClientCryptoKeyLink, StorageAdapter } from "@nice-code/util"; import { err_cast_not_nice } from "@nice-code/error/internal"; import { StandardSchemaV1 } from "@standard-schema/spec"; //#region src/ActionDefinition/Schema/ActionSchema.types.d.ts type TTransportedValue = [RAW_VAL] | [RAW_VAL, SERDE_VAL]; type TActionSerializationDefinition = { serialize: (value: RAW_VAL) => SERDE_VAL; deserialize: (value: SERDE_VAL) => RAW_VAL; }; type TActionSchemaOptions = { schema: VS; serialization?: TActionSerializationDefinition, SERDE_IN>; }; /** * One error declaration on an action schema. * `IDS` is the subset of error IDs that may be thrown. When the full * `keyof schema` union is used it means any ID from the domain can be thrown. * * Build via `action().throws(domain)` or `action().throws(domain, ids)`. */ interface IActionErrorDeclaration { readonly _domain: NiceErrorDomain; /** The specific IDs constrained for this declaration, or `undefined` meaning the full domain. */ readonly _ids: ReadonlyArray | undefined; } /** @internal Maps a single INiceActionErrorDeclaration to its NiceError type. */ type TInferErrorFromDeclaration = D extends IActionErrorDeclaration ? NiceError : never; /** * Union of all `NiceError` types that can be thrown from a tuple of error declarations. * Distributes over each declaration and unions the results. */ type TInferDeclaredErrors = TInferErrorFromDeclaration; //#endregion //#region src/ActionDefinition/Schema/ActionSchema.d.ts /** * What a sender should expect back from an action — declared on its schema so both ends agree without * any wire flag (each derives the mode from the shared `domain:id`). * * - `payload` — a typed output (the action has `.output(...)`); the sender awaits it. * - `ack` — an empty success confirming receipt (no output); the sender may await it to know the * receiver handled it (or to surface an error). This is the default for an action with no output. * - `none` — fire-and-forget: the receiver sends no reply and the sender doesn't wait. The sender's * running action completes as soon as the frame is on the wire (no pending reply, no timeout). */ declare enum EActionResponseMode { payload = "payload", ack = "ack", none = "none" } /** * The delivery guarantee for an action, declared on its schema so both ends agree without a wire flag * (each derives the tier from the shared `domain:id`, exactly like {@link EActionResponseMode}). Opt-in; * the default is `none` (today's best-effort transport, unchanged). * * - `none` — best-effort (default). A dropped frame is lost; a reconnect does not replay it. * - `session` — ordered, at-least-once, dedup-by-seq **within a resumable session**; the client resends * unacked frames on reconnect. Across a session reset (server eviction/restart) it degrades to * at-least-once *with possible redelivery*, so a reliable handler **must be idempotent**. * - `persisted` — as `session`, but the server's dedup high-water is persisted (e.g. DO storage), so dedup * and gap-free catch-up survive eviction too. Backed by the `ReliableLog` primitive. * * Orthogonal to {@link EActionResponseMode}: the reliability ack is a transport concern (it rides a * dedicated envelope slot), separate from the action's application-level reply. A reliable action with no * natural reply still gets a standalone transport ack. */ declare enum EReliabilityTier { none = "none", session = "session", persisted = "persisted" } declare class ActionSchema = never, OUTPUT extends TTransportedValue = never, ERRORS extends readonly IActionErrorDeclaration[] = readonly []> { private _errorDeclarations; private inputOptions; private outputOptions; private _responseMode; private _reliabilityTier; get inputSchema(): StandardSchemaV1 | undefined; get outputSchema(): StandardSchemaV1 | undefined; /** * The response contract for this action. Defaults are inferred — `payload` when an output schema is * declared, otherwise `ack` — and made explicit by {@link ack} / {@link fireAndForget}. */ get responseMode(): EActionResponseMode; /** * Mark this action as expecting only an acknowledgment (an empty success). Mostly for clarity — an * output-less action already acks by default — but it documents intent and reads as the deliberate * counterpart to {@link fireAndForget}. */ ack(): this; /** * Mark this action as fire-and-forget: the receiver sends no reply, and the sender's running action * completes the moment the frame is sent (no awaited reply, no timeout). Ideal for high-frequency * server→client pushes (presence, ticks) where an ack would only add wire chatter. */ fireAndForget(): this; /** * The reliability tier for this action (see {@link EReliabilityTier}). Both ends read it from the * shared schema, so no wire flag says "this is reliable" — the sender/receiver derive it from the * `domain:id`. Defaults to {@link EReliabilityTier.none} (best-effort). */ get reliabilityTier(): EReliabilityTier; /** * Opt this action into ordered, at-least-once delivery with resend-on-reconnect (see * {@link EReliabilityTier}). Pass `{ persist: true }` for the persisted tier whose dedup survives * server eviction. The `persist` flag is the *only* knob — reliability is deliberately un-parameterized * (no priorities/TTL/exactly-once-effect); those stay in app land. * * At-least-once means a reliable handler can see a redelivered frame after a session reset, so it * **must be idempotent**. The dedup-by-seq runtime makes that a non-issue within a live session. */ reliable(options?: { persist?: boolean; }): this; /** * Declare the input schema (JSON-native or with explicit SERDE type param). * For non-JSON-native inputs, prefer the 3-argument form below to avoid * needing explicit type parameters. * * The schema runs at `request()` AND again on the receiving runtime before the handler executes, * so it must accept its own validated output. Plain validators always do; a `transform` whose * output fails its own input schema will reject valid requests on arrival — type conversion * belongs in the SERDE pack/unpack arguments, not in schema transforms. */ input(options: TActionSchemaOptions): ActionSchema, SERDE_IN>, OUTPUT, ERRORS>; /** * Declare the output schema (JSON-native or with explicit SERDE type param). * For non-JSON-native outputs, prefer the 3-argument form below to avoid * needing explicit type parameters. */ output(options: TActionSchemaOptions): ActionSchema, SERDE_OUT>, ERRORS>; /** * Declare that this action may throw any error from `domain`. * `TInferActionError` will include `NiceError` in its union. */ throws(domain: NiceErrorDomain): ActionSchema]>; /** * Declare that this action may throw only the listed `ids` from `domain`. * `TInferActionError` will include `NiceError` narrowed to those IDs. */ throws>(domain: NiceErrorDomain, ids: IDS): ActionSchema]>; /** * Runtime counterpart of {@link TInferActionError}: `true` when `error` is one of * the errors this action declared via `.throws()` (exact domain + id match). A * wrapped foreign throw (`err_cast_not_nice`) is never declared, so it returns * `false` automatically. Drives the `expected` flag on the action result. */ isExpectedError(error: NiceError): boolean; /** * Serialize raw input to a JSON-serializable form. * Uses the schema's serialization.serialize if defined; otherwise the input * is already JSON-native and is returned as-is. */ serializeInput(rawInput: INPUT[0]): INPUT[1]; /** * Deserialize a JSON value back into the raw input type. * Uses serialization.deserialize if defined; otherwise the value is cast * directly (it's already in the correct shape). */ deserializeInput(serialized: INPUT[1]): INPUT[0]; /** * Validate raw input against the schema defined via `.input({ schema })`. * Throws `action_input_validation_failed` if validation fails. * Returns the validated (and possibly coerced) value on success. * If no input schema was declared, the value is passed through as-is. */ validateInput(value: unknown, meta: { domain: string; actionId: string; }): INPUT[0]; validateOutput(value: unknown, meta: { domain: string; actionId: string; }): OUTPUT[0]; /** * Serialize raw output to a JSON-serializable form. */ serializeOutput(rawOutput: OUTPUT[0]): OUTPUT[1]; /** * Deserialize a JSON value back into the raw output type. */ deserializeOutput(serialized: OUTPUT[1]): OUTPUT[0]; } /** * The union of `NiceError`s an action **declares** via `.throws()` — i.e. its * "expected" errors. An action with no `.throws()` declares none (`never`). The * generic / unhandled failures an action can also produce are intentionally *not* * here; they surface through the `expected: false` branch of the result outcome. */ type TInferActionError = SCH extends ActionSchema ? DECLS extends readonly IActionErrorDeclaration[] ? TInferDeclaredErrors : never : never; /** * The error type for the **throw / catch** surface (`runToOutput`, react-query) — * the action's declared errors *plus* the generic `err_cast_not_nice` fallback, * since a thrown error may be one we never accounted for. (On the non-throwing * `runToResult` path you instead get the typed `expected` discriminant.) */ type TActionThrownError = TInferActionError | InferNiceError; declare const actionSchema: () => ActionSchema; //#endregion //#region src/ActionDefinition/Domain/ActionDomain.types.d.ts type TPossibleDomainId = string; type TPossibleDomainIdList = [...TPossibleDomainId[], TPossibleDomainId]; type TActionDomainSchema = Record, TTransportedValue, readonly IActionErrorDeclaration[]>>; /** * Data shape for a domain — used for construction and as the type-level schema carrier. * Does NOT include class methods. */ interface IActionDomain { domain: IDS[0] & string; allDomains: IDS; actionSchema: SCH; } interface IActionRootDomain extends IActionDomain<[ID], {}> { domain: ID; allDomains: [ID]; actionSchema: {}; } interface IActionDomainChildOptions { domain: ERR_DOMAIN; actions: SCHEMA; } type TActionDomainChildDef = { domain: SUB["domain"]; allDomains: [...PARENT_DEF["allDomains"], SUB["domain"]]; actionSchema: SUB["actions"]; }; type TDomainActionId = keyof DOM["actionSchema"] & string; type TInferInputFromSchema> = SCH extends ActionSchema ? { Input: IN[0]; SerdeInput: IN[1]; } : { Input: never; SerdeInput: never; }; type TInferOutputFromSchema> = SCH extends ActionSchema ? { Output: OUT[0]; SerdeOutput: OUT[1]; } : never; type TWrappableDomainActionHandler = { [K in TDomainActionId]: (...args: [TInferInputFromSchema["Input"]] extends [never] ? [] : [input: TInferInputFromSchema["Input"]]) => [TInferOutputFromSchema["Output"]] extends [never] ? Promise | void : Promise["Output"]> }; //#endregion //#region src/ActionDefinition/Action/ActionBase.d.ts declare abstract class ActionBase
implements IActionBase { readonly form: FORM; readonly _domain: ActionDomain; readonly id: ID; readonly domain: DOM["domain"]; readonly allDomains: DOM["allDomains"]; readonly schema: DOM["actionSchema"][ID]; constructor(form: FORM, _domain: ActionDomain, id: ID); protected toJsonObject(): IActionBase_JsonObject; protected toJsonString(): string; } //#endregion //#region src/ActionDefinition/Action/Context/ActionContext.types.d.ts interface IActionRouteItem { runtime: RuntimeCoordinate; handler: IActionRouteItemHandler; time: number; } /** * **Local-only** receiver-side facts for the reliable frame being handled — stamped by the accepting * side onto the delivered frame's context immediately before dispatch, never serialized (a reply built * from this context is byte-identical with or without it). `undefined` for a best-effort action. */ interface IHandledReliability { /** This frame's per-stream sequence number, as the receiver delivered it. */ seq: number; /** The `streamKey` the frame rode in on (E3), when the stream is keyed. */ streamKey?: string; /** * True when this dispatch re-runs a frame the receiver already delivered — possible **only for * reply-carrying actions** (the re-run regenerates the reply the caller may still await). A * fire-and-forget reliable duplicate is suppressed before dispatch and never reaches the handler, so * `redelivered` is never `true` there. The at-least-once window that survives a session-tier server * reset is *not* flagged (a fresh receiver has no way to know a frame is a replay) — that window is * what the idempotent-handler contract covers. */ redelivered: boolean; } interface IActionContext_Data { cuid: string; timeCreated: number; routing: IActionRouteItem[]; originClient: RuntimeCoordinate; /** Local-only reliable-delivery facts (see {@link IHandledReliability}); never rides the wire. */ reliability?: IHandledReliability; } interface IActionContext extends IActionBase, IActionContext_Data {} /** * * JSON TYPES * */ interface IActionRouteItem_JsonObject { runtime: IRuntimeCoordinate; handler: IActionRouteItemHandler; time: number; } interface IActionContext_Data_JsonObject { cuid: string; timeCreated: number; routing: IActionRouteItem_JsonObject[]; originClient: IRuntimeCoordinate; /** * **LOCAL-ONLY — never serialized.** The accepting side stamps this onto a *decoded* reliable frame * just before dispatch (the `originClient`-overwrite pattern), so hydration can carry it to the * executing handler as `action.context.reliability`. `toJsonObject()` / the codecs never write it: a * reply produced by a reliable handler is byte-identical with and without the stamp (wire-tested). */ reliability?: IHandledReliability; } interface IActionContext_JsonObject extends IActionBase_JsonObject, IActionContext_Data_JsonObject {} //#endregion //#region src/ActionDefinition/Action/Core/ActionCore.types.d.ts interface IActionCore extends IActionBase {} type IActionCore_JsonObject = { form: EActionForm.core; domain: DOM["domain"]; allDomains: DOM["allDomains"]; id: ID; }; //#endregion //#region src/ActionDefinition/Action/Context/ActionContext.d.ts declare class ActionContext extends ActionBase implements IActionContext { readonly _domain: ActionDomain; readonly form = EActionForm.context; readonly _routing: IActionRouteItem[]; readonly timeCreated: number; readonly cuid: string; originClient: RuntimeCoordinate; /** * Local-only receiver-side reliable-delivery facts for the frame this context arrived on — * `(seq, streamKey, redelivered)`, the free idempotency key for exactly-once effects. Present only * inside the executing handler of a `.reliable()` action; `undefined` for best-effort actions and on * the sending side. Never serialized (see {@link IHandledReliability}). */ readonly reliability?: IHandledReliability; constructor(_domain: ActionDomain, id: ID, hydrationData: IActionContext_Data); _setOriginClient(client: RuntimeCoordinate): void; toJsonString(): string; toContextDataJsonObject(): IActionContext_Data_JsonObject; toJsonObject(): IActionContext_JsonObject; get routing(): IActionRouteItem[]; addRouteItem(item: IActionRouteItem): void; deserializeInput(serialized: TInferInputFromSchema["SerdeInput"]): TInferInputFromSchema["Input"]; serializeInput(raw: TInferInputFromSchema["Input"]): TInferInputFromSchema["SerdeInput"]; validateInput(input: unknown): TInferInputFromSchema["Input"]; validateOutput(output: unknown): TInferOutputFromSchema["Output"]; } //#endregion //#region src/ActionDefinition/Action/Payload/ActionPayload.d.ts declare abstract class ActionPayload
extends ActionBase implements IActionPayload_Base { readonly form: EActionForm.data; readonly type: DT; readonly context: ActionContext; readonly time: number; protected constructor(context: ActionContext, type: DT, data: IActionPayload_Data_Base); protected toBaseJsonObject(): IActionPayload_Base_JsonObject; abstract toJsonObject(): IActionPayload_Base_JsonObject; } //#endregion //#region src/ActionDefinition/Action/Payload/ActionPayload_Progress.d.ts declare class ActionPayload_Progress extends ActionPayload implements IActionPayload_Progress { readonly progress: TActionProgress; constructor(params: { context: ActionContext; } | ActionPayload_Request, progress: TActionProgress, data: IActionPayload_Data_Base); toJsonObject(): IActionPayload_Progress_JsonObject; toJsonString(): string; toHttpResponse(): Response; } //#endregion //#region src/ActionDefinition/Action/Payload/ActionPayload_Result.d.ts declare class ActionPayload_Result extends ActionPayload { readonly result: TActionResultOutcome["Output"], TInferActionError>; readonly outputHash: string; constructor(params: { context: ActionContext; } | ActionPayload_Request, result: { ok: true; output: TInferOutputFromSchema["Output"]; } | { ok: false; error: NiceError; }, data: IActionPayload_Data_Base); toJsonObject(): IActionPayload_Result_JsonObject; toJsonString(): string; toHttpResponse({ useErrorStatus }?: { useErrorStatus?: boolean; }): Response; } //#endregion //#region src/ActionDefinition/Action/RunningAction.types.d.ts declare enum ERunningActionState { running = "running", completed = "completed" } interface IRunningActionState { request: ActionPayload_Request; progress: ActionPayload_Progress[]; result?: ActionPayload_Result; } interface IRunningActionState_ConstructorParams { context: ActionContext; request: ActionPayload_Request; progress?: ActionPayload_Progress[]; result?: ActionPayload_Result; parentCuid?: string; callSite?: string; } declare enum ERunningActionUpdateType { started = "started", progress = "progress", finished = "finished", /** * A reliable action's sender-side delivery facts changed — today: the peer's cumulative ack covered * this frame (`reliability.acked` flipped to `true`). Fires *after* `finished` for a reply-less * reliable action (which settles on send while delivery continues in the background), so observers * (devtools) must not treat `finished` as the last update for those. */ reliability = "reliability" } interface IRunningActionEvent_Base { type: T; runningAction: RunningAction; time: number; } interface IRunningActionUpdate_Started extends IRunningActionEvent_Base {} interface IRunningActionUpdate_Progress extends IRunningActionEvent_Base { progress: TActionProgress; } /** * Sender-side reliable-delivery facts changed (see {@link ERunningActionUpdateType.reliability}). Read * the current facts off `runningAction.reliability` — the update carries no payload of its own so the * facts have exactly one source of truth. */ interface IRunningActionUpdate_Reliability extends IRunningActionEvent_Base {} declare enum ERunningActionFinishedType { aborted = "aborted", failed = "failed", success = "success" } interface IRunningActionUpdate_Finished extends IRunningActionEvent_Base { finishType: FT; reason?: unknown; } interface IRunningActionUpdate_Abort extends IRunningActionUpdate_Finished { reason?: unknown; } interface IRunningActionUpdate_Failed extends IRunningActionUpdate_Finished { error: TInferActionError; } interface IRunningActionUpdate_Success extends IRunningActionUpdate_Finished { response: ActionPayload_Result; } type TRunningActionUpdateFinished = IRunningActionUpdate_Abort | IRunningActionUpdate_Failed | IRunningActionUpdate_Success; type TRunningActionUpdate = IRunningActionUpdate_Started | IRunningActionUpdate_Progress | IRunningActionUpdate_Reliability | TRunningActionUpdateFinished; type TRunningActionUpdateListener = (update: TRunningActionUpdate) => void; /** * Distributes a union ID into a proper discriminated union of RunningAction update events, * so that narrowing on `update.runningAction.id` also narrows `update.runningAction`'s input/output types. */ type TDistributeRunningActionUpdate = ID extends keyof DOM["actionSchema"] & string ? TRunningActionUpdate : never; type TDistributeRunningActionUpdateListener = (update: TDistributeRunningActionUpdate) => void; interface IRunningActionUserMethods { waitForResultPayload(): Promise>; /** Await delivery settlement (ack / abandonment) — see {@link RunningAction.waitForAck}. */ waitForAck(): Promise; addUpdateListeners(listeners: TRunningActionUpdateListener[]): () => void; iterateUpdates(): AsyncIterable>; abort(reason?: unknown): void; } //#endregion //#region src/ActionDefinition/Action/RunningAction.d.ts /** Reliable-delivery facts an observer (devtools) can show for a reliable action, stamped by the connector. */ interface IRunningActionReliability { /** The reliability tier this action opted into (`session` / `persisted`). */ tier: EReliabilityTier; /** This frame's per-stream sequence number (the sender's outbox seq). */ seq?: number; /** The `streamKey` this send rode on (E3), when the stream is keyed. */ streamKey?: string; /** * Set `true` when the peer's cumulative ack covered this frame — the sender-side proof of delivery. * Stamped by the connector when its outbox prunes the send (a reply's piggyback or a standalone `rack`). * Flipping it emits an {@link ERunningActionUpdateType.reliability} update; `waitForAck()` awaits it. */ acked?: boolean; } declare class RunningAction implements IRunningActionUserMethods { protected _state: IRunningActionState; /** * Reliability facts for a `.reliable()` action (tier + this frame's seq), stamped by the connector when it * assigns the outbox seq. `undefined` for a best-effort action. Read by devtools to surface a reliable chip; * the cumulative `ack` / `redelivered` are receiver-side facts (see the server serve-logger), not the * sender's to report. */ reliability?: IRunningActionReliability; readonly context: ActionContext; readonly cuid: string; readonly id: ID; readonly _domain: ActionDomain; readonly domain: DOM["domain"]; readonly allDomains: DOM["allDomains"]; readonly parentCuid?: string; readonly callSite?: string; private readonly _resultPayloadPromise; private _resolveResult; private _rejectResult; private _isAborted; private readonly _updates; private readonly _updateListeners; /** * Delivery-settlement state for {@link waitForAck}, recorded even when nobody is waiting so a late * `waitForAck()` still settles correctly. The promise itself is created lazily on first call — * otherwise every abandoned reliable send would raise unhandled-rejection noise for the callers * (the overwhelming majority) that never ask. */ private _ackOutcome?; private _ackPromise?; private _resolveAck?; private _rejectAck?; constructor(initialState: IRunningActionState_ConstructorParams); get state(): IRunningActionState; /** Whether this action has reached a terminal state (resolved, aborted, or failed) — no more updates. */ get isSettled(): boolean; /** * Whether this action was aborted (deadline, overflow, or an explicit abort) — as opposed to having * completed successfully. A reliable **reply-less** action completes *on send* (so it's `isSettled`), yet * the outbox must keep delivering it in the background until the peer acks; only an *abort* should stop * those resends. Resend/resync gating uses this, not {@link isSettled}. */ get isAborted(): boolean; /** Stamp this action's reliability facts (tier + seq) — the connector calls it when it assigns the seq. */ _setReliability(reliability: IRunningActionReliability): void; /** * Await this send's **delivery settlement** — the sender-side proof of what became of the frame, * distinct from the action's own promise (a reply-less reliable action resolves *on send* while * delivery continues in the background; this is how you await that delivery): * * - **Reliable action** — resolves when the peer's cumulative ack covers this frame; rejects with the * abandon reason when the frame is dropped undelivered (delivery deadline, explicit `abort()`, a * newer frame's cumulative sweep, or a `closeReliableStream`). * - **Best-effort action** — there is no ack concept, so it mirrors the action itself: resolves when * the action settles successfully (for fire-and-forget: on send), rejects when it aborts/fails. * Callers compose without branching on the tier. * * Note the at-least-once caveat: a *rejected* `waitForAck` means the sender stopped trying, not that * the frame provably never arrived (the ack itself may have been lost). */ waitForAck(): Promise; /** Record a delivery-settlement outcome (first one wins) and settle {@link waitForAck} if it exists. */ private _settleAck; /** * The peer's cumulative ack covered this frame — the connector calls it when its outbox prunes the * send. Stamps `reliability.acked` and emits a {@link ERunningActionUpdateType.reliability} update so * observers (devtools) refresh, then settles {@link waitForAck}. */ _notifyAcked(): void; /** * This frame was dropped undelivered (deadline / abort / sweep / stream close) — the connector calls * it from the outbox's drop hook. Rejects {@link waitForAck} with the abandon reason. */ _notifyAckAbandoned(reason: unknown): void; abort(reason?: unknown): void; addUpdateListeners(listeners: TRunningActionUpdateListener[]): () => void; iterateUpdates(): AsyncIterable>; _sendUpdate(update: TRunningActionUpdate): void; _completeWithResult(result: ActionPayload_Result): boolean; _abort(reason?: unknown): boolean; _failWithError(error: unknown): boolean; _updateProgress(progress: ActionPayload_Progress): void; waitForResultPayload(): Promise>; _resolveFromJson(resultJson: IActionPayload_Result_JsonObject): boolean; } //#endregion //#region src/ActionDefinition/Domain/ActionDomainBase.d.ts declare abstract class ActionDomainBase implements IActionDomain { readonly domain: ACT_DOM["domain"]; readonly allDomains: ACT_DOM["allDomains"]; readonly actionSchema: ACT_DOM["actionSchema"]; protected _listeners: TRunningActionUpdateListener[]; constructor(definition: ACT_DOM); /** * Add an observer that is called after every action dispatched through this domain. * Returns an unsubscribe function — call it to remove the listener. */ addActionListener(listener: TDistributeRunningActionUpdateListener): () => void; /** * @internal * Observers registered directly on this domain via {@link addActionListener}. * Used to wire observers (e.g. devtools) onto RunningActions that aren't created * through the local-dispatch path — notably inbound actions pushed from a backend * or another client over a bidirectional transport. */ _getActionObservers(): TRunningActionUpdateListener[]; } //#endregion //#region src/ActionDefinition/Domain/ActionRootDomain.d.ts declare class ActionRootDomain extends ActionDomainBase { readonly domainDefinition: { domain: ROOT_DOM["domain"]; }; private _actionRuntimeManager; constructor(domainDefinition: { domain: ROOT_DOM["domain"]; }); createChildDomain(subDomainDef: SUB_DOM & { [K in Exclude]: never }): ActionDomain>; _registerRuntime(runtime: ActionRuntime): void; _hasRuntime(runtime: ActionRuntime): boolean; getRuntime(clientSpecifier: IRuntimeCoordinate): ActionRuntime | undefined; _runAction = ActionPayload_Request>(actionPayload: ACT, options?: IExecuteActionOptions): Promise>; } //#endregion //#region src/ActionDefinition/Domain/helpers/createRootActionDomain.d.ts declare const createActionRootDomain: (definition: { domain: ID; }) => ActionRootDomain>; //#endregion //#region src/ActionRuntime/ActionDomainManager.d.ts declare class ActionDomainManager { private _domains; addDomain(domain: ActionDomain): void; getDomains(): ActionDomain[]; verifyIsActionJson(action: INiceActionIdAndDomain): void; getActionDomain>(action: ACT): ActionDomain | undefined; getActionDomainOrThrow>(action: ACT): ActionDomain; hydrateActionPayload>(actionJson: A): TNarrowActionJsonTypeToActionInstanceType; } //#endregion //#region src/ActionRuntime/Routing/ActionRouter.types.d.ts /** * Format: `dom[${domainName}]id[${actionId | "_"}]` * The wildcard `_` matches any action ID within a domain. */ type TMatchHandlerKey = `${"dom"}[${string}]id[${string | "_"}]`; declare enum EActionRouterContextType { runtime_to_handler = "runtime_to_handler", handler_route = "handler_route" } interface IActionRouterContext_HandlerRoute { contextType: EActionRouterContextType.handler_route; handler: TActionHandler; } interface IActionRouterContext_RuntimeToHandler { contextType: EActionRouterContextType.runtime_to_handler; runtime: ActionRuntime; } type IActionRouterContext = IActionRouterContext_HandlerRoute | IActionRouterContext_RuntimeToHandler; //#endregion //#region src/ActionRuntime/Routing/ActionRouter.d.ts declare class ActionRouter$1 { readonly domainManager: ActionDomainManager; private actionRouteData; private _context; constructor(context: IActionRouterContext); /** Copy all routes from another router into this one, replacing any overlapping keys. */ mergeRouter(actionRouter: ActionRouter$1): void; addDomainsFromOther(actionRouter: ActionRouter$1): void; /** All FNs registered for an action, ID-specific entries first then domain wildcard. */ getRouteDataEntriesForAction(action: { domain: string; id: string; }): DATA[]; /** First FN registered for an action (ID-specific beats domain wildcard). */ getRouteDataForAction(action: INiceActionIdAndDomain): DATA | undefined; private throwNoHandlerForAction; getRouteDataEntriesForActionOrThrow(action: INiceActionIdAndDomain, context: IHandleActionOptions): DATA[]; getRouteDataForActionOrThrow(action: INiceActionIdAndDomain, context: IHandleActionOptions): DATA; /** All FNs stored under an exact match key. */ getForKey(key: TMatchHandlerKey): readonly DATA[]; /** Every match key that has at least one registered FN. */ getRegisteredKeys(): TMatchHandlerKey[]; getDomains(): ActionDomain[]; /** Register a handler for all actions in a domain, replacing any existing one. */ forDomain(domain: ActionDomain, routeData: DATA): this; forAction(action: ActionCore, routeData: DATA): this; /** Register a handler for a specific action, replacing any existing one. */ forActionId(domain: ActionDomain, id: ID, routeData: DATA): this; /** Register one handler for several action IDs, replacing any existing ones. */ forActionIds>(domain: ActionDomain, ids: IDS, routeData: DATA): this; /** Register per-action handlers from a cases map, replacing any existing ones. */ forDomainActionCases(domain: ActionDomain, cases: { [ID in keyof FOR_DOM["actionSchema"] & string]?: DATA }): this; /** Append a handler for all actions in a domain (accumulates alongside existing). */ addForDomain(domain: ActionDomain, routeData: DATA): this; /** Append a handler for a specific action (accumulates alongside existing). */ addForAction(domain: ActionDomain, id: ID, routeData: DATA): this; /** Append one handler for several action IDs (accumulates alongside existing). */ addForActionIds>(domain: ActionDomain, ids: IDS, routeData: DATA): this; /** Append per-action handlers from a cases map (accumulates alongside existing). */ addForDomainActionCases(domain: ActionDomain, cases: { [ID in keyof FOR_DOM["actionSchema"] & string]?: DATA }): this; /** Append a handler directly by its raw match key (used when the key is known ahead of time). */ addForKey(key: TMatchHandlerKey, routeData: DATA): this; private _push; } //#endregion //#region src/ActionRuntime/Handler/ActionHandler.d.ts declare abstract class ActionHandler implements IActionHandler_Base { abstract readonly handlerType: T; readonly cuid: string; abstract readonly actionRouter: ActionRouter$1; constructor(); getActionRouter(): ActionRouter$1; abstract handleActionRequest(action: ActionPayload_Request, config?: IHandleActionOptions): Promise>; abstract toJsonObject(): TActionHandler_Json; abstract toHandlerRouteItem(...args: any[]): IActionRouteItemHandler; } //#endregion //#region src/utils/typescript/MaybePromise.d.ts type MaybePromise = T | Promise; //#endregion //#region src/ActionRuntime/Handler/Local/ActionLocalHandler.types.d.ts type THandleActionExecutionFn = (action: TDistributeActionPayload_Request) => MaybePromise | IActionPayload_Result_JsonObject | TInferOutputFromSchema["Output"] | undefined>; //#endregion //#region src/ActionRuntime/Handler/Local/ActionLocalHandler.d.ts declare class ActionLocalHandler extends ActionHandler implements IActionHandler_Local { readonly handlerType = EActionHandlerType.local; readonly actionRouter: ActionRouter$1>; constructor(); /** * Register a handler for all actions in a domain. * Receives the full primed action — use `matchAction()` to narrow to a specific action id. * Useful for forwarding all domain actions to a remote endpoint. * Lower priority than `forAction`. */ forDomain(domain: ActionDomain, handler: THandleActionExecutionFn): this; /** * Register a handler for a base action instance. Takes priority over domain-wide handlers. * Receives the full primed action with narrowed input type. * Useful for handling specific actions locally while forwarding the rest of the domain. For example, a local "ping" action that checks connectivity without needing a round trip. */ forAction(action: ActionCore, handler: THandleActionExecutionFn): this; /** * Register a handler for multiple action IDs (first-match-wins among cases). * Receives the full primed action narrowed to the union of those IDs. * Use `act.coreAction.id` to branch on which action was dispatched. */ forActionIds>(domain: ActionDomain, ids: IDS, handler: THandleActionExecutionFn): this; /** * Register per-action handlers for a domain using a single map, without needing * separate `forAction` calls. Unregistered action IDs are unaffected. * * @example * ```ts * handler.forDomainActionCases(userDomain, { * getUser: (primed) => db.getUser(primed.input.userId), * deleteUser: (primed) => db.deleteUser(primed.input.userId), * }); * ``` */ forDomainActionCases(domain: ActionDomain, cases: { [ID in keyof FOR_DOM["actionSchema"] & string]?: THandleActionExecutionFn }): this; handleActionRequest(action: ActionPayload_Request, config?: IHandleActionOptions): Promise>; private _handleRunningAction; handlePayloadWireOrThrow(wire: unknown, config?: IHandleActionOptions): Promise>; toJsonObject(): IActionHandler_Local_Json; toHandlerRouteItem(): IActionRouteItemHandler; } declare const createLocalHandler: () => ActionLocalHandler; //#endregion //#region src/ActionRuntime/Channel/serveLogger.d.ts /** * What the server knows about an inbound action request when it accepts it — handed to a logger *before* * the action executes. Deliberately carries only routing-level facts (which action, over which transport, * from whom) plus the raw {@link input}; a logger decides for itself whether to surface the input (the * default logger hides it unless told otherwise, since inputs can hold sensitive data). */ interface IActionServeRequestInfo { /** The carrier that received the request — its short kind label, e.g. `"http"`, `"ws"`, `"webrtc"`. */ transport: string; /** Domain-qualified action id, e.g. `"demo_basic/greet"` (the {@link domain} + {@link action}). */ actionId: string; /** The action's own (unqualified) id, e.g. `"greet"`. */ action: string; /** The domain the action belongs to, e.g. `"demo_basic"`. */ domain: string; /** The originating client runtime coordinate (its `stringId`) — who sent the request. */ origin: string; /** The security level the request arrived under (`none` for a plain endpoint), when known. */ securityLevel?: ESecurityLevel; /** The action's input — always provided; a logger shows it only if configured to (default: hidden). */ input: unknown; /** * The encoded request frame's size in bytes, as the serving path decoded it — **pre-decryption**, so * this is the action payload's own cost, not the billed carrier frame. (For true wire bytes across * every lane, use `serveChannel({ wireTap })`.) Absent where the path never sees an encoded frame. */ bytes?: number; /** * Reliable-delivery metadata, present only when the request arrived on a `.reliable()` stream: the frame's * per-stream sequence number, the cumulative high-water acked back to the client, and whether it was a * **redelivered** duplicate (a resend the server already had). Absent for best-effort requests. */ reliability?: IActionServeReliabilityInfo; } /** The reliable-stream facts a logger can surface for a `.reliable()` request. See {@link IActionServeRequestInfo.reliability}. */ interface IActionServeReliabilityInfo { seq?: number; ack?: number; redelivered?: boolean; } /** * How an action request was served — handed to the reporter that {@link IActionServeLogger.onRequest} * returns, once the action has finished and its result has been handed back to the client. */ interface IActionServeResultInfo { /** Whether the action succeeded. */ ok: boolean; /** How the result went back to the client — typically the receiving transport (`"http"`, `"ws"`, …). */ returnedVia: string; /** Wall-clock milliseconds from the request being received to its result being returned. */ durationMs: number; /** * The encoded result frame's size in bytes, pre-encryption — the mirror of * {@link IActionServeRequestInfo.bytes}. Absent where the path never encodes a frame. */ bytes?: number; /** When {@link ok} is `false`: the failure's error id (when present) and message. */ error?: { id?: string; message: string; }; } /** Called once with the outcome by the reporter {@link IActionServeLogger.onRequest} returns. */ type TActionServeResultReporter = (result: IActionServeResultInfo) => void; /** * A pluggable server-side logger for {@link serveChannel}. {@link onRequest} is called when the server * accepts an inbound action request (before it executes) and returns a reporter the server then calls with * the outcome — so one logger call spans the whole request→response, letting an implementation pair the two * lines (and time the gap) however it likes. * * Pass an instance as `serveChannel(..., { logger })`. Use {@link createDefaultServeLogger} for a ready-made * console logger, or implement this interface to forward to your own logging stack (pino, a metrics sink, …). */ interface IActionServeLogger { /** * Observe an accepted inbound request. Return a reporter the server invokes once the action has been * served with its outcome + how it was returned. Returning `undefined` skips the result line for this one * request (e.g. to sample, or to ignore a noisy action). */ onRequest(info: IActionServeRequestInfo): TActionServeResultReporter | undefined; } /** Options for {@link createDefaultServeLogger}. */ interface IDefaultServeLoggerOptions { /** Where each line is written. Defaults to the global `console`. */ sink?: Pick; /** Tag prefixed to every line so server logs are greppable. Defaults to `"[nice-action]"`. */ tag?: string; /** * Include the action input on the request line. Off by default — inputs can carry sensitive data, so * opt in only when you want it (this is the "unless configured that way" switch). */ logInputs?: boolean; /** Show the negotiated security level (`[encrypted]`, …) on the request line. Defaults to `true`. */ showSecurityLevel?: boolean; } /** * A ready-made {@link IActionServeLogger} that prints one line when a request arrives and one when it has * been served, to `console` (or any `sink` you pass). Drop it straight into `serveChannel` so a "host and * forget" backend gets request/response feedback with no extra wiring: * ```ts * serveChannel(runtime, channel, { storage, carriers, logger: createDefaultServeLogger() }); * // → [nice-action] ▶ demo_basic/greet via http from envId[web_app]… [encrypted] * // ← [nice-action] ✓ demo_basic/greet ok via http 12ms * ``` * Inputs are hidden by default (`logInputs: true` to include them). */ declare function createDefaultServeLogger(options?: IDefaultServeLoggerOptions): IActionServeLogger; //#endregion //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createSecureChannelAcceptor.d.ts interface ISecureChannelAcceptorOptions { /** * The default channel identity (codec + dictionary version) — same one single-channel clients use, and * the fallback a multi-channel acceptor composes against when a client advertises no tags. */ channel: IActionChannel; /** * Multi-channel: resolve a connection's advertised channel tags (`hello.channels`) into the channel it * should use (its codec + dictionary version), or `null` for an unknown/unserved set (the handshake then * rejects). When set, this acceptor serves several channels and selects/composes per connection; when * omitted it serves the single {@link channel}. Built by `serveChannel` from its channel registry. */ resolveChannel?: (tags: readonly string[] | undefined) => IActionChannel | null; /** * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`), * used as the offline-return scoring fallback (a live connection always wins regardless). Optional — * omit it for a multi-role server accepting several client envs over one acceptor. */ clientEnv?: RuntimeCoordinate; /** This server's runtime — its coordinate is the server identity presented in the handshake. */ runtime: ActionRuntime; /** * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins. * Their keys don't collide, so a single adapter is enough; back it with persistent storage (e.g. a * Durable Object's storage) so identity and pins survive eviction. */ storage: StorageAdapter; /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */ send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void; /** * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. * Pass an existing link to share one identity across several acceptors on the same server (e.g. a * WebSocket acceptor and a secure-HTTP {@link createActionFetchHandler}), so they present the same * verify/exchange keys — avoiding a divergent-key race when two fresh links initialize concurrently. */ link?: ClientCryptoKeyLink; /** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */ securityLevel?: ESecurityLevel | readonly ESecurityLevel[]; /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */ verifyKeyResolver?: IClientVerifyKeyResolver; /** Timeout (ms) applied to server-initiated actions awaiting a client response. */ defaultTimeout?: number; /** Optional server-side logger — called per inbound action request with its served outcome. */ logger?: IActionServeLogger; /** Server-side wire tap (see {@link IChannelAcceptorBaseOptions.wireTap}). */ wireTap?: import("@nice-code/wire").TWireTapFn; /** Short carrier-kind label surfaced to the logger as the request's transport (e.g. `"ws"`). */ transportLabel?: string; /** Persisted receive store for the persisted reliability tier (see {@link IChannelAcceptorBaseOptions.persistedReceiver}). */ persistedReceiver?: IReliableReceiver>; /** Cap on distinct keyed (`streamKey`) reliable streams per client (see {@link IChannelAcceptorBaseOptions.maxKeyedStreamsPerClient}). */ maxKeyedStreamsPerClient?: number; } /** * Build an {@link ChannelAcceptor} for the secure binary channel with the boilerplate folded in: * it creates the {@link ClientCryptoKeyLink} and the storage-backed TOFU resolver from a single * `storage`, installs the channel's per-connection codec, and assembles the `security` block * from the runtime coordinate + channel version (accepting all three levels by default). * * For a hibernatable transport (e.g. a Durable Object), pair it with * {@link createHibernatableWsServerAdapter} to wire persistence + replay. */ declare function createSecureChannelAcceptor(options: ISecureChannelAcceptorOptions): ChannelAcceptor; //#endregion //#region src/ActionRuntime/Transport/TransportConnection.d.ts /** * Live, per-handler transport runtime built from a reusable {@link Transport} definition — the * action instantiation of wire's generic `TransportConnection` (shared-base-connect plan, * Phase 2): routing params pinned to {@link ITransportRouteActionParams} and the finalized * methods to {@link IActionTransportReadyData_Methods}. The machinery (status processing, cache * keys, async bring-up hooks) lives in wire; subclasses (`LinkConnection`, `ExchangeConnection`) * are unchanged. Construct these via `definition._createConnection(...)`, never directly. */ declare abstract class TransportConnection$1 = IActionTransportInitialized, DEF extends IActionTransportDef = IActionTransportDef> extends TransportConnection {} //#endregion //#region src/ActionRuntime/Transport/Transport.types.d.ts /** * Serializable, display-only description of how an action was routed through a transport. Stored on * the action's route items and shown in the devtools external-handler chips. The shape is * protocol-neutral and lives in wire's Connect layer ({@link IWireRouteInfo}, plan Phase 1); * aliased here so action code keeps its vocabulary. */ type ITransportRouteInfo = IWireRouteInfo; interface IUpdateActionRunConfig_Output { timeout?: number; } type TUpdateActionRunConfig = (input: ITransportRouteActionParams & { timeout: number; }) => IUpdateActionRunConfig_Output; interface IActionTransportReadyData_Base { updateRunConfig?: TUpdateActionRunConfig; } /** * Client-side secure-channel config for a connector link — carrier-neutral (the secure session never * cared about the carrier). When present (and `securityLevel !== none`), the connection runs the * handshake during initialization and, at the `encrypted` level, encrypts every frame. The acceptor * side adds a negotiable level set + verify-key resolver on top of this (see Phase 3's `ISecureConfig`). * * The neutral core (`securityLevel`, `link`, `localCoordinate`) is wire's * {@link IWireSecureClientConfig} (plan Phase 1); what this extension adds is exactly the * **lane's own config** (E5, formalized at plan Phase 5): the handshake payload * (`dictionaryVersion`, `channelTags`) the action lane supplies to wire via * `IWireLaneProtocol.handshakeConfig()`, plus the connection's frame-protocol mux. Wire consumes * only the neutral core — these fields cross the boundary through the lane protocol alone. */ interface ISecureClientConfig extends IWireSecureClientConfig { /** Wire dictionary version; the peer rejects the handshake on a mismatch. */ dictionaryVersion: string; /** * The channel tags this connection carries (`channel.tags`), advertised in the handshake so a * multi-channel acceptor selects/composes the matching codec + dictionary version. Omitted for a plain * single-channel peer; an acceptor then uses its sole registered channel. */ channelTags?: readonly string[]; /** * Optional frame-protocol mux (M1 multiplex seam): its registered protocols are advertised as * `proto:` handshake caps, prefixed frames (`0x01`–`0x0F`) dispatch to it before the legacy * sniffing, and it attaches to the connection's (encrypting) send path once the handshake lands. * Secure duplex connections only — a plain connection has no capability negotiation. */ mux?: WireProtocolMux; } /** * * TRANSPORT ROUTING * */ /** The two coordinates a dispatch runs between — wire's {@link IWireClientPair}, action's name. */ type ITransportRouteClientParams = IWireClientPair; /** * The dial-path params when a connection is established **without an action** — * `ChannelConnector.connect()` (review A.1, plan Phase 3). Carrier callbacks and availability * gates receive {@link TTransportRouteParams}: a callback deriving per-action dial state must * handle the actionless shape (the common closure-based carriers never read the input at all). */ interface ITransportRouteConnectParams extends ITransportRouteClientParams { action?: undefined; reliability?: undefined; } /** What the dial path (carriers, availability gates, cache keys) is invoked with. */ type TTransportRouteParams = ITransportRouteActionParams | ITransportRouteConnectParams; interface ITransportRouteActionParams extends ITransportRouteClientParams { action: TActionPayload_Any_Instance; /** * Present only for a reliable action ({@link IFrameReliability}). When set, the codec writes the * reliability slot; when absent, the frame is byte-identical to a best-effort frame. */ reliability?: IFrameReliability; } interface ITransportMethod_SendActionData_Input extends ITransportRouteActionParams { runningAction: RunningAction; timeout: number; } interface ITransportDispatchAction

extends ITransportMethod_SendActionData_Input { params: P; } type TSendActionDataMethod = (input: ITransportMethod_SendActionData_Input) => void; type TSendReturnDataMethod = (payload: TActionPayload_Any_Instance, /** * The local/external client pair this payload is being returned over. Bidirectional transports use * it to build the full route params their outgoing formatter expects (e.g. binary packing). Optional * so existing return-data implementations keep type-checking. */ clients?: ITransportRouteClientParams) => void; interface IActionTransportReadyData_Methods extends IActionTransportReadyData_Base { sendActionData: TSendActionDataMethod; /** * Optional — implement on bidirectional transports (WebSocket, Custom) to enable return-path * routing. When present, the runtime uses this to dispatch results and progress payloads directly * back to `originClient` without going through the original request transport. */ sendReturnData?: TSendReturnDataMethod; /** * Optional — implement on duplex transports to send a transport {@link TControlMessage} beside action * frames (through the connection's crypto pipe when secure). The connector uses it for * sender-originated reliable control (`rskip` — abandoning undeliverable frames so the peer's stream * continues); absent on exchange-only transports, where there is no reliable stream to control. */ sendControlData?: (message: TControlMessage) => void; addOnDisconnectListener?: (callback: () => void) => void; /** * Optional — implement on transports holding a long-lived connection (WebSocket, Custom) to close it * deliberately. Called by `ChannelConnector.clearTransportCache()` so a teardown actually * releases the underlying socket instead of leaving it open until GC. */ disconnect?: () => void; } /** Wire's ready/cache pair, pinned to the action methods + connection types. */ type IActionTransportReady = IWireTransportReady; type TTransportCache$1 = TTransportCache; type TOnResolveIncomingRequest = (request: ActionPayload_Request) => void; type TOnResolveIncomingRequestJson = (request: IActionPayload_Request_JsonObject) => void; type TOnResolveIncomingResponse = (response: ActionPayload_Result) => void; type TOnResolveIncomingResponseJson = (response: IActionPayload_Result_JsonObject) => void; type TOnResolveAnyIncomingActionData = (actionData: ActionPayload_Request | ActionPayload_Result) => void; type TOnResolveAnyIncomingActionData_Json = (actionData: TActionPayload_Any_JsonObject, /** * The reliability integers decoded off this frame's envelope slot, when present (a reliable frame). * The connector reads `ack` to prune its outbox; the acceptor reads `seq` to dedup/order. Absent for a * best-effort frame — the receive path is otherwise unchanged. */ reliability?: IFrameReliabilityWire) => void; interface IActionTransportResolvers { onIncomingActionDataJson: TOnResolveAnyIncomingActionData_Json; /** * A transport control frame arrived (see {@link TControlMessage}) — connection metadata that rides * beside action frames (reliability acks today; flow-control/presence/keepalive later). Absent for a * receiver that doesn't consume control frames. */ onControlMessage?: (message: TControlMessage) => void; } type TGetTransportFn = (input: IN) => TTransportStatusInfo_GetTransport_Output; interface IActionTransportInitialized { getTransportCacheKey?: (input: IN) => string[]; /** * Optional availability gate, consulted by {@link ConnectionTransportManager} *before* cache-key * resolution and `getTransport`. When it returns `false`, this transport is treated as `unsupported` * for that action and the manager falls through to the next transport in preference order — without * opening the carrier or computing its cache key. Re-evaluated per action dispatch, so a transport can * become available later (e.g. once a session/connection precondition is met) with no reconnect. Omit = * always available. */ isAvailable?: (input: IN) => boolean; getTransport: TGetTransportFn; } interface IActionTransportDef> { type: TYPE; initialize: () => INIT; } //#endregion //#region src/ActionRuntime/Handler/PeerLink/PeerLink.d.ts /** * Shared base for every handler that routes a domain set to/from *another runtime* (a "peer") — the * unified peer-link concept. Both specializations extend this as siblings, differing only in *who * establishes the connection*, which is a transport trait, not a routing one: * * - {@link ChannelConnector} — **dial-out**: this runtime opens connection(s) to one peer * over a transport stack (with caching + fallback). The classic "client → backend" link. * - {@link ChannelAcceptor} — **accept-in**: connections are accepted from many peers and fed in * via `receive()`; it keeps a per-connection registry and can push to any of them. * * To the runtime there is no "client" vs "server" — both are peer-link handlers (`handlerType = * external`) keyed to a peer coordinate, chosen by the return-path dispatch via {@link sendReturnPayload}. */ declare abstract class PeerLink extends ActionHandler implements IActionHandler_Peer { /** The peer runtime this handler links to (an env-only coordinate for an accept-in handler). */ readonly peerClient: RuntimeCoordinate; readonly handlerType = EActionHandlerType.peer; /** * Whether this link can deliver an *unsolicited* frame to the peer (a result/progress pushed back on * the return path, or a `broadcast`). A duplex carrier (WebSocket/WebRTC/…) can; an exchange-only * carrier (HTTP) cannot — its reply must ride the response to its own request. The runtime's * return-path dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) skips handlers that can't * push, so an exchange-only handler is never asked to deliver one. */ abstract readonly canPush: boolean; readonly actionRouter: ActionRouter$1; /** Listeners installed by the runtime (`resolveIncomingActionPayload`) for inbound peer frames. */ private readonly _incomingActionDataListeners; constructor(peerCoordinate: RuntimeCoordinate); forDomain(domain: ActionDomain): this; forAction(action: ActionCore): this; forActionIds>(domain: ActionDomain, ids: IDS): this; _setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject) => void): void; /** Hand a decoded inbound frame to the runtime (called by each specialization's receive path). */ protected _emitIncoming(json: TActionPayload_Any_JsonObject): void; /** * Dispatch a result/progress payload back to the action's origin peer over this link. The runtime's * return-path dispatch calls it on whichever peer-link handler best reaches `originClient`. Returns * `true` if it was sent, `false` if no channel was available. */ abstract sendReturnPayload(payload: TActionPayload_Any_Instance, config: { targetLocalRuntime: ActionRuntime; }): Promise; /** * Whether this handler currently holds a *live* connection bound to `origin`. The runtime's return-path * dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) prefers a handler that owns the origin's * connection over a mere coordinate match, so with several duplex acceptors a result/push routes back * over the carrier the client connected on. Defaults to `false`; an acceptor overrides it from its * connection registry. */ ownsLiveConnectionFor(_origin: RuntimeCoordinate): boolean; /** Release any long-lived connections this handler owns (a teardown). No-op by default. */ clearTransportCache(): void; } //#endregion //#region src/ActionRuntime/Transport/Transport.d.ts /** * Context handed to a {@link Transport} definition when a handler builds a live connection from it. * Only bidirectional transports (WebSocket / Custom) make use of `resolvers`. */ interface ITransportConnectionContext { resolvers?: IActionTransportResolvers; } /** * Reusable transport definition. Built by the internal `transport({ carrier, secure })` factory (which * `connectChannel` / `serveChannel` drive) and passed to a `ChannelConnector`. A single * definition can be shared across multiple handlers — each handler builds its own live * {@link TransportConnection} via {@link TransportConnection._createConnection}. */ declare abstract class Transport { abstract readonly type: T; /** Internal: build a fresh, per-handler live connection from this definition. */ abstract _createConnection(ctx: ITransportConnectionContext): TransportConnection$1; /** * Resolve human-readable info about how a specific action would be routed through this transport * (e.g. the request URL/method, or the WebSocket endpoint). Surfaced in the action devtools. */ abstract getRouteInfo(input: TTransportRouteParams): ITransportRouteInfo; } //#endregion //#region src/ActionRuntime/Handler/PeerLink/Connector/ChannelConnector.types.d.ts interface IChannelConnectorConfig { defaultTimeout?: number; /** * Overall deadline (ms) for a `.reliable()` action — it retries across reconnects until acked, but * aborts if still unacked after this, so an unreachable peer surfaces as a failure instead of a silent * hang. Defaults to a generous value that tolerates a long reconnect. */ reliableActionTimeout?: number; runtimeCoordinate: RuntimeCoordinate; transports: Transport[]; /** * The local runtime coordinate this connection dials out from — used by `connect()` (review * A.1) as the dial identity when no runtime is passed, and by protocol modules (a realm * client) to default their own identity from the connection (review A.4.1). */ localCoordinate?: RuntimeCoordinate; /** * The negotiated wire security level this connection's frame protocols (a realm) ride at — the * floor across the *duplex* transports, since protocols ride the mux over a push-capable transport * only (PLAN-security Phase 4.1). `connectChannel` computes it; `undefined` ⇒ no level known (a * bare-mux/loopback build), which a realm treats as "skip the client-side security assertion". */ securityLevel?: ESecurityLevel; /** * The frame-protocol mux riding this connection — held by the handler's `WireClient`. * `connectChannel` creates one per connection and threads it in; protocol modules (a realm client) * register on it via `realmConnection`. */ wireMux?: WireProtocolMux; /** * Keep a **protocol-carrying** (realm) duplex link alive by auto-redialing it on an unexpected * drop, with exponential backoff + jitter (DESYNC F6 / Phase 8b). `undefined` (default) = auto: * on when the mux carries a registered protocol, off for a pure-action connector (which keeps * today's dispatch-driven reconnect). `false` opts out; `true` forces it on for any duplex link. * An explicit `clearTransportCache()`/`dispose()` always stops it. */ keepLinkAlive?: boolean; } /** * A stream-level reliable-delivery event — the handle-less observation surface for apps that monitor * delivery without retaining a `RunningAction` per send (subscribe via * `ChannelConnector.addReliableEventListener`, or `connectChannel`'s `onReliableEvent`). * * - `abandoned` — the outbox dropped frames `fromSeq..toSeq` of a stream undelivered (delivery deadline, * an explicit `abort()` whose cumulative sweep took older frames with it, or a * `closeReliableStream`). The receiver is told to skip past them; the stream continues. An app can * re-push the lost range from its own records, or surface "receiver may be behind". * - `overflow` — a send was rejected because the stream hit its unacked window * (`maxUnackedPerStream`); the send-time promise also rejects with `reliable_outbox_overflow`. An app * seeing this (or watching `reliablePending` climb) can coalesce/shed before the cliff. */ type TReliableStreamEvent = { type: "abandoned"; domain: string; actionId: string; streamKey?: string; /** The abandoned seq range, inclusive — cumulative, so it may cover several sends. */ fromSeq: number; toSeq: number; /** Why the range was abandoned (the same reason each swept action rejects with). */ reason: unknown; } | { type: "overflow"; domain: string; actionId: string; streamKey?: string; }; /** Read-only pressure stats for one reliable stream — see `ChannelConnector.reliablePending`. */ interface IReliableStreamPressure { /** Sends currently held unacknowledged for this stream. */ unackedCount: number; /** Age (ms) of the oldest unacknowledged send, `undefined` when the stream is drained. */ oldestUnackedAgeMs?: number; /** The per-stream cap `unackedCount` is heading toward (`reliable_outbox_overflow` at the cliff). */ maxUnackedPerStream: number; } //#endregion //#region src/ActionRuntime/Handler/PeerLink/Connector/ChannelConnector.d.ts /** * Dial-out peer link: this runtime opens connection(s) to one peer over a transport stack (cached, with * preference-ordered fallback). The classic "client → backend" handler — but to the runtime it's just a * {@link PeerLink} like the accept-in server one. * * The connection **lifecycle** — dial, keep-alive redial ladder, link events, park, teardown — lives on * the {@link WireClient} this handler holds ({@link _wire}); the handler keeps only the action *lane*: * routing, the reliable outbox, return-path dispatch, and delegates every lifecycle verb to the client * (so `connectChannel` and a realm-only `createWireClient` are one connection implementation). */ declare class ChannelConnector extends PeerLink { private _defaultTimeout; private _reliableTimeout; /** * The wire connection this handler binds the action lane onto — it owns the transport cache, * preference-ordered selection, the mux, and the whole connection **lifecycle** (dial, keep-alive * redial, link events, park, teardown). The handler delegates every connection-level verb + * property to it; `createWireClient` wraps the same class for a realm-only app. */ private readonly _wire; /** Whether any transport can push (duplex) — a realm reads it. */ get canPush(): boolean; /** The local coordinate this connection dials out from — a realm defaults its identity from it. */ get localCoordinate(): import("@nice-code/wire").RuntimeCoordinate | undefined; /** * The negotiated wire security level this connection's frame protocols (a realm) ride at — a realm * client reads it via `realmConnection(connector)` and refuses to send below its required minimum. */ get securityLevel(): import("@nice-code/wire").ESecurityLevel; /** * The frame-protocol mux riding this connection's link — protocol modules (a realm client) * register here. Register protocols *before* {@link connect} / the first dispatch so the handshake * advertises them. */ get wireMux(): import("@nice-code/wire").WireProtocolMux | undefined; /** * The re-dial hook a realm client rides (DESYNC F6 / Phase 8a — the `IRealmWireLink` seam): * `realmConnection(connector)` hands it to the engine as `requestReconnect`, invoked on an * unanswered staleness probe. */ get requestReconnect(): () => void; /** * Chaos/QA surface — `connector.debug.dropLink(...)` forces the outages * page-level interception can't (never for production control flow). Delegated to the connection. */ get debug(): { dropLink: (opts?: { suppressRedialMs?: number; }) => Promise; }; /** Client-side send bookkeeping for `.reliable()` actions (seq/ack/resend). Inert until one is used. */ private readonly _outbox; /** Transports we've already attached a resend-on-disconnect listener to (attach once per connection). */ private readonly _resendHooked; /** * Per-send **delivery deadline** timers, keyed by each unacked send's reliability object (stable across * a re-sync, which renumbers `seq` by mutating that same object in place). Armed at `prepare`, cleared * when the send is **acked** (not when its action settles — a reply-less action settles on send while * its delivery is still pending). On expiry the frame is abandoned: its action aborts if still pending, * and the stream skips past it (see {@link _abandonReliableThrough}), so delivery is *bounded* for * reply-carrying and reply-less sends alike. */ private readonly _deliveryDeadlines; /** * The highest-abandoned-seq `rskip` per stream, kept (and re-flushed on every fresh connection) until * the peer's ack passes it — so a skip lost to a drop mid-send still lands, and the receiver never * waits forever on a seq the outbox abandoned. Cleared on an `rsync` (a fresh receiver has no gap to * skip; stale pre-reset seqs must not be replayed against the renumbered stream). */ private readonly _streamSkips; /** * Pending `rclose` per closed stream, kept until sent once on a live control channel (then dropped — * `rclose` is a loss-tolerant memory reclaim, unlike an `rskip` it needs no ack retirement: delivery * was already settled by the close's retained `rskip`, and a receiver that misses it self-heals). */ private readonly _streamCloses; /** Most recent live methods able to carry control frames — the immediate path for an `rskip`. */ private _controlMethods?; /** Stream-level reliable-delivery observers (see {@link TReliableStreamEvent}). */ private readonly _reliableEventListeners; constructor({ runtimeCoordinate: peerSpecifier, transports, defaultTimeout, reliableActionTimeout, wireMux, localCoordinate, securityLevel, keepLinkAlive }: IChannelConnectorConfig); /** * Establish the connection without dispatching an action — so a realm-only * (actionless) client can exist. Idempotent; resolves when the handshake completes, rejects (and * arms/parks the keep-alive ladder) when the transport chain is exhausted. `targetLocalRuntime` * overrides the dial identity; otherwise the connection's configured coordinate, else the ambient * runtime's default — preserving the pre-descent behaviour. * * **Do not fire-and-forget the returned promise** — it is where handshake failures surface, * including `identity_pin_mismatch` (branch on it with `err.hasId("identity_pin_mismatch")`; see * `err_wire_connect`). */ connect(config?: { targetLocalRuntime?: ActionRuntime; }): Promise; /** * Tear down the (possibly half-open) duplex link and immediately re-dial it (DESYNC F6 / Phase * 8b) — surfaced to a realm client as `requestReconnect`, called by the staleness-probe * escalation. **Awaitable** — resolves when the fresh link's handshake completes. */ reconnectLink(): Promise; /** * Permanently stop this connector: cancel any pending auto-redial and drop the connection cache * (keep-alive never re-dials again — an explicit, final teardown, distinct from * {@link clearTransportCache}). */ dispose(): void; handleActionRequest(action: ActionPayload_Request, config?: IHandleActionOptions): Promise>; private _dispatchWhenTransportReady; /** * Apply a cumulative ack for a stream: prune the outbox, clear each acked send's delivery deadline, and * retire a pending `rskip` once the ack passes it (proof the skip — or the frames it covered — landed). * The single funnel for both ack paths (reply piggyback + standalone `rack` control frame). */ private _applyAck; /** * Arm one reliable send's **delivery deadline**: if the peer hasn't acked the frame within * {@link _reliableTimeout}, the frame is abandoned — the action aborts if still pending (its abort * listener then runs the abandonment), while an already-settled reply-less action abandons directly * (with a one-time route warning, since its caller was already told "success on send"). This is what * turns "retry across reconnects" from a potential infinite hang into a bounded, eventually-failing * operation for an unreachable peer — for *every* reliable send, reply-less included. Cleared by * {@link _applyAck} when the frame is acked. */ private _armDeliveryDeadline; /** * Abandon a reliable send that will never deliver (its action aborted / its delivery deadline expired): * drop it — and, cumulatively, every older still-unacked send on its stream (they could no longer be * delivered in order; each fails loudly via its `onDrop`) — then record + send an `rskip` so the * receiver advances past the abandoned seqs and the stream **continues** instead of wedging on a * permanent gap. The skip is kept (and re-flushed on each fresh connection) until an ack passes it, so * a drop can't lose it. Idempotent: a second call for the same (or an older) seq drops nothing. */ private _abandonReliableThrough; /** * Send every pending `rskip` over the last live control-capable connection (kept until acked past), * then every pending `rclose` (dropped after one successful send — loss-tolerant reclaim). */ private _flushStreamSkips; /** * Observe stream-level reliable-delivery events — the handle-less settlement surface: `abandoned` * (frames `fromSeq..toSeq` dropped undelivered; the stream skipped past them) and `overflow` (a send * rejected at the unacked-window cap). Complements the per-send `RunningAction.waitForAck()`, which * requires holding the handle. Returns an unsubscribe. Wired by `connectChannel({ onReliableEvent })`. */ addReliableEventListener(listener: (event: TReliableStreamEvent) => void): () => void; private _emitReliableEvent; /** * Observe transport link-state: `link_down`, `redial_scheduled` * (attempt + delay — truthful "retrying in N s" UX), `link_up` (with `downForMs`). Returns an * unsubscribe. Wired by `connectChannel({ onLinkEvent })`; see {@link TLinkEvent} for the * contract (a healthy heal is silent at the realm's sync layer — this is where it's visible). */ addLinkEventListener(listener: (event: TLinkEvent) => void): () => void; /** * Close one logical reliable stream **for good** — the teardown verb for a stream whose real-world * subject is over (a finished game run, a departed room): abandon every still-unacked send on it * (each pending action aborts loudly with `reliable_stream_closed`; already-settled fire-and-forget * sends stop resending), tell the receiver to skip past them (retained `rskip` — survives drops), and * release both sides' stream state (an `rclose` reclaim frame; on a keyed stream it also frees the * key's `maxKeyedStreamsPerClient` slot). * * **Synchronous on the sender's state**: after it returns, nothing from this stream can resend — so * calling it at teardown *before* changing dial state (e.g. switching the active run whose URL a * multiplexed-peer transport derives) closes the cross-instance redelivery window entirely. * * The local seq counter is deliberately **kept** (a tiny tombstone): a reused key continues the seq * space, so a receiver that missed the `rclose` (dead-socket race) can never mistake new sends for * duplicates — and against a receiver that *did* forget, the first new send self-heals through the * normal mid-stream re-sync. Closing a stream this connection never sent on is a no-op. */ closeReliableStream(action: { domain: string; id: string; }, streamKey?: string): void; /** Total unacked reliable sends held across every stream of this connection. */ reliablePending(): number; /** One stream's pressure stats — see {@link IReliableStreamPressure}. */ reliablePending(action: { domain: string; id: string; }, streamKey?: string): IReliableStreamPressure; /** * Dispatch a result or progress payload directly back to the external client via the best * available bidirectional transport (WebSocket / Custom). Used for return-path routing when the * local runtime recognises that it has a direct channel to the action's originClient. * * Returns `true` if the payload was sent, `false` if no suitable transport was available. */ sendReturnPayload(payload: TActionPayload_Any_Instance, config: { targetLocalRuntime: ActionRuntime; }): Promise; toJsonObject(): IActionHandler_Peer_Json; toHandlerRouteItem(transport: TransportConnection$1, input: ITransportRouteActionParams): IActionRouteItemHandler; /** * Stop the keep-alive auto-redial and release the current link — the intention-revealing * teardown counterpart of `keepLinkAlive`. Call this when a session is * over (leaving a match, logging out): the link closes and stays closed until the next explicit * `connect()`/dispatch. Not terminal (unlike {@link dispose}) — the connector is reusable. * * For dynamic-endpoint carriers, pair with a `createRequest` that returns `null` once its dial * context is torn down — then even a mis-ordered teardown can't dial a garbage endpoint (the * redial loop parks on `dial_unavailable`). */ releaseLink(): void; clearTransportCache(): void; } declare const createChannelConnector: (config: IChannelConnectorConfig) => ChannelConnector; //#endregion //#region src/ActionRuntime/Transport/Carrier/Carrier.types.d.ts /** * A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs. * Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and passed as a * `carrier` to `connectChannel`'s transports (the internal `transport()` factory drives it) — so adding a * new carrier is "write one of these", nothing else. */ interface IDuplexCarrierSource { /** Open (or reuse) the carrier for an action. */ open: (input: TTransportRouteParams) => IDuplexCarrier; /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */ getCacheKey?: (input: TTransportRouteParams) => string[]; /** Devtools route info for an action routed over this carrier. */ getRouteInfo?: (input: TTransportRouteParams) => ITransportRouteInfo; /** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`, `"memory"`). */ readonly carrierLabel: string; } /** * The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an * {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by * `httpCarrier` and passed as a `carrier` to `connectChannel`'s transports — adding a new request/reply * protocol is "write one of these". The `shape` tag lets the internal `transport()` factory pick the * duplex vs exchange transport. */ interface IExchangeCarrierSource { /** Discriminant so a generic factory can tell an exchange source from a duplex one. */ readonly shape: "exchange"; /** Open (or reuse) the carrier for an action. */ open: (input: TTransportRouteParams) => IExchangeCarrier; /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */ getCacheKey?: (input: TTransportRouteParams) => string[]; /** Devtools route info for an action routed over this carrier. */ getRouteInfo?: (input: TTransportRouteParams) => ITransportRouteInfo; /** Short carrier-kind label for the devtools chip (e.g. `"http"`). */ readonly carrierLabel: string; } //#endregion //#region src/ActionRuntime/Transport/codec/actionWireCodec.d.ts /** * Shared building blocks for the binary action codecs (the stateless {@link createBinaryWireAdapter} and * the per-connection `createBinaryWireSessionFactory`). Both map a `domain:id` route to a tiny integer * and reduce the verbose JSON wire to a positional tuple — they only differ in how much context they * carry per frame, so the dictionary + payload (de)assembly live here. */ /** * The carrier-neutral codec a Link connection uses to (de)serialize action payloads on the wire — the * same shape every duplex carrier (WS/WebRTC/in-memory) shares. */ interface IActionWireFormat { /** * Pack an outgoing action payload. Return a `string` for text frames (JSON) or a binary * `Uint8Array`/`ArrayBuffer` for optimized binary frames (e.g. msgpackr). */ outgoing: (input: ITransportRouteActionParams) => string | Uint8Array | ArrayBuffer; /** * Unpack an incoming frame back into the wire JSON object the runtime hydrates + validates. Return * `undefined` to defer to the connection's built-in JSON parser — this is how binary adapters stay * backward compatible with plain-JSON clients on the same socket. */ incoming?: (input: string | ArrayBuffer | Uint8Array | Blob) => TActionPayload_Any_JsonObject | undefined; /** * Unpack just the reliability integers off a frame (the optional trailing envelope slot), or * `undefined` when the frame carries none (a best-effort frame). Separate from {@link incoming} so the * best-effort decode path stays untouched; the receive-side reliability layer reads this alongside. */ incomingReliability?: (input: string | ArrayBuffer | Uint8Array | Blob) => IFrameReliabilityWire$1 | undefined; } //#endregion //#region src/ActionRuntime/Transport/codec/createBinaryWireSessionFactory.d.ts type TFormatMessage = IActionWireFormat; interface IBinaryWireSessionOptions { /** Override how long an unresolved correlation is retained before being swept (ms). */ correlationTtlMs?: number; } /** * Builds a factory of *stateful, per-connection* codecs for {@link LinkTransport} / * `ChannelAcceptor` — the maximally compact binary wire. Call the returned factory once per live * connection (each socket on the client, each accepted connection on the server) so every channel * gets its own correlation + identity state. * * On top of everything {@link createBinaryWireAdapter} drops, a session also drops: * - **`cuid`** — replaced by a per-connection integer correlation id. The initiator maps it to its * real cuid; the responder echoes it; each side reconstructs the cuid from its own map. Correlation * only needs to be unique per socket, so a counter suffices. * - **`originClient` after the first request** — the first request each side sends carries its * identity; the peer remembers it and injects it into later frames. Replies omit it entirely (a * reply carries the initiator's own origin, which the initiator already knows). * * Both ends MUST build the factory from the same domains in the same order (positional dictionary). * Text frames still return `undefined` from `incoming`, so JSON clients remain interoperable. * * Hibernation note: after a server connection is evicted its session resets, so a still-connected * client (whose session persists) will keep omitting `originClient`. The server must therefore restore * the connection→client binding from its own store (see `ChannelAcceptor.rehydrate`) and * inject `originClient` from there — the session alone can't recover it. */ declare function createBinaryWireSessionFactory(domains: ActionDomain[], options?: IBinaryWireSessionOptions): () => TFormatMessage; //#endregion //#region src/ActionRuntime/Channel/ActionChannel.d.ts /** * A transport-agnostic routing contract between two runtimes, declared *by role* rather than by * "client"/"server". The two ends are named for the only asymmetry that survives every carrier (WS, * WebRTC, BLE, raw TCP): which side dials and which side accepts. * * - The **connector** dials out and opens the link ({@link connectChannel}). * - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}). * * `toAcceptor` domains flow connector→acceptor (the classic "request"); `toConnector` domains flow * acceptor→connector (the classic "push"). Both ends derive their routing from the same channel instead * of restating domain lists — and because the contract is independent of how bytes move, the very same * channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback). * * Beyond the routing, a channel also carries its *wire identity* — the per-connection binary codec both * ends build from the same domain list, plus the `dictionaryVersion` the handshake checks for drift. * Whether a given transport runs encrypted is a per-transport choice (see {@link IConnectTransport.secure} * and the acceptor's `securityLevel`), not a property of the channel — so one `defineChannel` definition * serves both plain and secure transports. */ interface IActionChannel[] = readonly ActionDomain[], TO_CONNECTOR extends readonly ActionDomain[] = readonly ActionDomain[]> { /** * Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards * them over its transport(s); the acceptor executes them. */ toAcceptorDomains: TO_ACCEPTOR; /** * Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local * handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a * bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply * never flow. */ toConnectorDomains: TO_CONNECTOR; /** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */ dictionaryVersion: string; /** Per-connection session codec factory (call once per live connection). */ createCodec: () => IActionWireFormat; /** * Stable channel id, auto-derived from the channel's domain *names* (so it survives action-level * evolution — adding an action changes `dictionaryVersion`, not `tag`). A connecting client advertises * it in the handshake so a multi-channel acceptor can select/compose the right codec per connection. */ tag: string; /** * The constituent channel tags this channel carries: `[tag]` for a plain channel, the parts in order for * a {@link combineChannels} result. This is what the connector advertises to the acceptor (`hello.channels`). */ tags: readonly string[]; } /** * Declare a transport-agnostic channel by role — the single source of truth both peers share. Each end * MUST call this with the same domains in the same order (the binary wire dictionary is positional); the * `dictionaryVersion` is derived from those domains unless you pin an explicit one. The wire dictionary * spans `[...toAcceptor, ...toConnector]` in that order, so add new domains to the end of their list to * keep older peers compatible. * * Declare the domains *by role* — `toAcceptor` (connector→acceptor requests) and `toConnector` * (acceptor→connector pushes) — so the routing for both ends is derived from the channel (see * {@link connectChannel} and {@link serveChannel}) instead of being restated at each end. Security is a * per-transport concern, not a channel one, so this same definition is used whether a transport runs * plain or encrypted. */ declare function defineChannel[] = [], const TO_CONNECTOR extends readonly ActionDomain[] = []>(options?: { /** 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). */toAcceptor?: TO_ACCEPTOR; /** Domains the acceptor pushes to the connector (acceptor→connector), in a stable order. Omit for a connection-only channel. */ toConnector?: TO_CONNECTOR; /** Pin a human-readable version instead of the derived hash (must match on both ends). */ dictionaryVersion?: string; /** Pin the channel's selection tag instead of the domain-name-derived default (must match on both ends). */ tag?: string; /** Tuning for the per-connection binary session (e.g. correlation TTL). */ sessionOptions?: IBinaryWireSessionOptions; }): IActionChannel; type TUnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; type TDomainPushHandlers = D extends ActionDomain ? Partial> : never; /** * The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action * handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so * the keys and input types follow the channel definition. */ type TChannelPushHandlers[]> = TUnionToIntersection>; /** * One transport to the peer, declared by *carrier* — the dial-out dual of `serveChannel`'s acceptor * carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto * identity) into each one, so a descriptor only carries what differs between transports: the carrier and * whether it runs the secure handshake. * * A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier * (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection * prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback). */ interface IConnectTransport { /** How to reach the peer — a duplex carrier (push-capable) or an exchange carrier (request/reply). */ carrier: IDuplexCarrierSource | IExchangeCarrierSource; /** * Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport * draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport * (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`. */ secure?: boolean; /** Security level for this secure transport; defaults to the connection-level `securityLevel`. */ securityLevel?: ESecurityLevel; /** * Optional availability gate — when it returns `false` this transport is skipped and the connection * falls through to the next in preference order, re-evaluated per dispatch. Omit = always available. */ available?: (input: TTransportRouteParams) => boolean; /** Override the devtools chip label (defaults to the carrier's own label). */ label?: string; } interface IConnectChannelOptions[]> { /** The peer's runtime coordinate — the acceptor this connection dials. */ peer: RuntimeCoordinate; /** * The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback). * They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and * falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into * each — the dial-out dual of `serveChannel`'s `carriers`. */ transports: readonly IConnectTransport[]; /** * One backing store for this connection's crypto identity, fanned across every *secure* transport so * they present the same verify/exchange keys. Required when any transport is secure (the default); a * fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an * existing identity. * * **Must be durable, not just present**: the peer pins this identity's verify key on first contact * (trust-on-first-use, keyed by the runtime coordinate's `envId::perId`). A store that forgets across * reloads (a memory adapter) regenerates the key, and every load after the first is rejected with * `identity_pin_mismatch`. In a browser use `createWebLocalStorageAdapter`; memory adapters are for * tests. Pairing a non-durable store with `withPersistentId` logs a warning for exactly this reason. */ storage?: StorageAdapter; /** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */ link?: ClientCryptoKeyLink; /** * Declare this connection's identity **ephemeral by construction**: the coordinate's persistent id * is minted fresh every session (e.g. `perId: crypto.randomUUID()` at startup), so the peer's * trust-on-first-use pin intentionally lives and dies with the session and a non-durable `storage` * is correct — this flag suppresses the durability warning for exactly that pairing. Do NOT set it * for an id that survives reloads (a stored visitor/user id): that pairing genuinely breaks on the * second load (`identity_pin_mismatch`), which is what the warning exists to catch. */ ephemeralIdentity?: boolean; /** Default security level for secure transports; defaults to `authenticated`. */ securityLevel?: ESecurityLevel; /** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */ onPush?: TChannelPushHandlers; /** * Frame protocols riding this connection beside the action lane (shared-base-connect plan, * Phase 3): registered on the connection's mux **at construction**, before any dispatch or * `connect()` can run — so the handshake always advertises them and the caps ordering trap * (review A.2) is unrepresentable on this path. Protocol modules that manage their own * registration (a realm client via `realmConnection(connector)`) don't need this — register them * before calling `connect()` instead. */ protocols?: readonly import("@nice-code/wire").IWireFrameProtocol[]; /** * Bring your own frame-protocol mux — effectively a pre-populated alias of {@link protocols} * (plan Phase 5): `connectChannel` creates one per connection by default (reachable as * `connector.wireMux`) and registers `protocols` onto it. Pass one only to share it across * connections or drive it from tests; prefer `protocols` everywhere else. */ wireMux?: import("@nice-code/wire").WireProtocolMux; /** Default per-action timeout for this connection. */ defaultTimeout?: number; /** * Delivery deadline (ms) for `.reliable()` sends — how long an unacked frame retries across reconnects * before it's abandoned (a pending action aborts with `reliable_delivery_abandoned`; the stream skips * past it and continues). Default 60s. * * Not the same dial as `@nice-code/realm`'s `pendingExpiryMs` (default 10s): this is a * *redelivery* deadline for at-least-once actions; that is an *optimistic-settle* expiry for * realm writes. They differ on purpose — a realm write past its window rolls back UI, while a * reliable action keeps retrying toward delivery (review A.7). */ reliableActionTimeout?: number; /** * Observe stream-level reliable-delivery events without holding per-send handles: `abandoned` (frames * `fromSeq..toSeq` of a stream dropped undelivered — deadline, abort sweep, or stream close; the * stream skipped past them and continues) and `overflow` (a send rejected at the unacked-window cap). * The handle-less complement of `RunningAction.waitForAck()` — use it to re-push lost ranges from your * own records, surface "receiver may be behind", or shed load ahead of the overflow cliff. */ onReliableEvent?: (event: TReliableStreamEvent) => void; /** * Observe transport link-state (resilience-surface §3): `link_down`, `redial_scheduled` * (attempt + delay — truthful "reconnecting, retrying in N s" UX and flaky-link telemetry), and * `link_up` (with `downForMs`). A drop that {@link keepLinkAlive} heals cleanly is *silent* at * the realm's sync layer (`onDiagnostic`) — this hook is where transport churn is visible. * Post-hoc subscription: `connector.addLinkEventListener`. */ onLinkEvent?: (event: TLinkEvent) => void; /** * Keep a realm-carrying duplex link alive by auto-redialing it on an unexpected drop, with * exponential backoff + jitter (DESYNC F6 / Phase 8b). Default: on when this connection carries a * frame protocol (a realm rides the mux), off for a pure-action connection. Set `false` to opt * out (e.g. an app that manages its own connection lifecycle). Independent of a realm's own * probe-escalation `requestReconnect`, which also routes here. * * Teardown: `connector.releaseLink()` stops the auto-redial and releases the link (the * intention-revealing counterpart of this option); `clearTransportCache()` also suppresses the * redial until the next explicit `connect()`/dispatch. Observe drops/redials via * {@link onLinkEvent}. */ keepLinkAlive?: boolean; /** * Wire-level half-open detection (8b.3 / resilience-surface §5): after `idleMs` with no inbound * frame the link sends a raw `"ping"`; nothing back within `pongTimeoutMs` ⇒ the socket is * closed locally, converting a *half-open* link (browser offline, NAT death — `isOpen()` true, * sends into the void) into the ordinary close → {@link keepLinkAlive} redial, for **every** * protocol on the link at once. Worst-case detection ≈ `idleMs + pongTimeoutMs` (default * 15 s + 5 s ≈ 20 s — vs ~35 s for the realm staleness probe alone, which stays as * belt-and-braces and can now be relaxed rather than carrying the whole burden). * * Default: on whenever {@link keepLinkAlive} is active (protocol-carrying links); pass a config * object to force it on for a pure-action connection too; `false` disables. A busy link never * pings. Requires a peer that answers the wire `"ping"` — every `@nice-code` acceptor (and the * Cloudflare DO auto-response) does; disable against older third-party peers. */ linkKeepalive?: false | { idleMs?: number; pongTimeoutMs?: number; }; /** * Observe this connection's wire traffic (devtools-consolidation plan §3): called once per * frame that actually crosses a carrier, with its **true wire byte size** — measured * post-encryption, exactly what a platform's per-message billing / egress metering sees — and * its lane (`action`, `realm`/protocol id, `handshake`, `keepalive`, `http`). Feed it a * `TrafficMetricsCore` (`wireTap: traffic.wireTap`) and render the devtools Traffic tab, or * point it at your own telemetry. Applies to every transport in {@link transports} and * survives redials; sizes only — payload contents are never captured. Omit = zero overhead. * * Every event is stamped with `linkId` = {@link peer}'s `stringId`, so several connectors feeding * ONE core still split per backend ("which backend costs what") — the mirror of `serveChannel`'s * tap, which stamps the bound *client* per frame. */ wireTap?: import("@nice-code/wire").TWireTapFn; } /** * Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is * the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer, * `toConnector` pushes handled locally from `onPush`); the call binds the shared facts — the channel's * codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over * `storage`) — into every transport in `transports`, so none of them restate the channel or runtime. * List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback): * ```ts * const connector = connectChannel(runtime, lobbyChannel, { * peer: runtime_coordinate_lobby_do, * storage, * transports: [{ carrier: wsCarrier(() => ({ url })) }, { carrier: httpCarrier(...), secure: false }], * onPush: { player_joined: (p) => { … } }, * }); * ``` * Returns the {@link ChannelConnector} so the caller can later `clearTransportCache()` it. */ declare function connectChannel[], TO_CONNECTOR extends readonly ActionDomain[]>(runtime: ActionRuntime, channel: IActionChannel, options: IConnectChannelOptions): ChannelConnector; /** The acceptor domains of one channel (`toAcceptor`). */ type TChannelAcceptorDomains = C extends IActionChannel ? A : never; /** The connector (push) domains of one channel (`toConnector`). */ type TChannelConnectorDomains = C extends IActionChannel ? B : never; /** The union of every channel's acceptor domains (distributes over the channel tuple). */ type TCombinedAcceptorDomains[]> = TChannelAcceptorDomains; /** The union of every channel's connector (push) domains. */ type TCombinedConnectorDomains[]> = TChannelConnectorDomains; /** * Combine several channels into one whose domains are the **union** of theirs, in list order — so a set of * channels can ride a *single* connection / endpoint (one handshake, one crypto identity) yet stay * independent contracts (the runtime still routes each action by its domain). The positional wire * dictionary spans `[...each toAcceptor, ...each toConnector]`, and the derived `dictionaryVersion` covers * the whole union, so the handshake's drift check validates the exact combination with no new wire fields. * * Both ends MUST combine the **same channels in the same order** (exactly the per-channel `defineChannel` * contract, lifted to the set) — which is why {@link connectChannels} and {@link serveChannels} are exact * duals. A single-element list returns that channel unchanged. */ declare function combineChannels[]>(channels: CHANNELS): IActionChannel, TCombinedConnectorDomains>; /** * Connect several channels to a single backend over **one shared connection** — the connect-side dual of * {@link serveChannels} and the multi-channel form of {@link connectChannel}. One handshake, one crypto * identity, and one transport stack carry the union of the channels' domains; the runtime still dispatches * each action by its domain, so the channels stay independent. `onPush` is the merged set of every * channel's `toConnector` handlers. * ```ts * connectChannels(runtime, [mainChannel, notificationsChannel], { * peer: backendCoord, storage, * transports: [{ carrier: httpCarrier(() => ({ url })) }], * onPush: { notified: (p) => { … } }, * }); * ``` * For per-channel identities/transports (e.g. an ephemeral per-resource connection), keep using separate * {@link connectChannel} calls — `connectChannels` is purely additive for the "several channels, one * backend" case. */ declare function connectChannels[]>(runtime: ActionRuntime, channels: CHANNELS, options: IConnectChannelOptions>): ChannelConnector; type TDomainAcceptorCases = D extends ActionDomain ? { [ID in keyof DEF["actionSchema"] & string]?: TAcceptorCaseFn } : never; /** * The connection-aware case map for a channel's acceptor side: the merged set of every * connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus a per-action * `context`. `TCtx` is whatever the wiring supplies as that second argument — the raw connection * (`TConn | undefined`) for the low-level `acceptChannelConnections`, or an enriched `IConnectionContext` * for `serveChannel`'s `channelCases`. Derived from the channel's `toAcceptorDomains`, so the keys and * input/output types follow the channel. */ type TChannelAcceptorCases[], TCtx> = TUnionToIntersection>; /** * Register an acceptor handler's execution for a channel straight from its definition: the channel's * `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets * the primed request + the originating connection, as with * {@link ChannelAcceptor.forConnectionDomainCases}). The domain list is taken from the channel, * never restated. Add the returned handler to the runtime alongside the acceptor handler: * ```ts * runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]); * ``` * * The case's second argument is the raw connection (`TConn | undefined`). For the richer state + * broadcast + pushBack context, serve the channel through `serveChannel`'s `channelCases` instead. */ declare function acceptChannelConnections[], TConn>(serverHandler: ChannelAcceptor, channel: IActionChannel, cases: TChannelAcceptorCases): ActionLocalHandler; /** * {@link acceptChannel}'s options — the secure-acceptor builder options minus the `channel` and `runtime` * it already takes positionally. One option bag, shared with the underlying {@link createSecureChannelAcceptor}. */ type IAcceptChannelOptions = Omit, "channel" | "runtime">; /** * Build the secure {@link ChannelAcceptor} for a channel — the accept-in counterpart to * {@link connectChannel}. It folds in the boilerplate of {@link createSecureChannelAcceptor} (the * `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storage`, the channel's codec + * dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel, * options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution: * ```ts * const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storage, send }); * runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]); * ``` */ declare function acceptChannel[], TO_CONNECTOR extends readonly ActionDomain[], TConn = unknown>(runtime: ActionRuntime, channel: IActionChannel, options: IAcceptChannelOptions): ChannelAcceptor; //#endregion //#region src/ActionRuntime/ActionRuntime.types.d.ts interface IRuntimeMeta { assumed: boolean; runtimeName: RuntimeName; } interface IActionRuntimeManagerContext { domain?: string; } type TActionRuntimeHandler = ActionLocalHandler | PeerLink; //#endregion //#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore.d.ts /** * The composite value persisted to a connection's attachment: the consumer's own app state plus the * {@link ChannelAcceptor} routing binding. Co-storing them in one slot means a transport whose * sockets outlive process eviction (e.g. a Durable Object's hibernatable WebSocket) recovers both the * application identity *and* the action routing from a single attachment after a wake — no storage reads. */ interface IConnectionAttachment { app?: TApp; binding?: IAcceptorConnectionBinding; } interface IConnectionStateStoreOptions { /** Read a connection's raw attachment (e.g. `(ws) => ws.deserializeAttachment()`). */ read: (connection: TConn) => unknown; /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */ write: (connection: TConn, value: IConnectionAttachment) => void; /** * All currently-live connections (e.g. `() => ctx.getWebSockets()`). Used to replay routing bindings * after a wake (via {@link createConnectionStateStore}) and to enumerate app state in * {@link ConnectionStateStore.entries}. */ getConnections: () => TConn[]; /** * Optional Standard Schema (valibot, zod, …) validating the *app* portion on read. A value that * fails validation reads back as `null` — the same lenient behavior as a hand-written safeParse * helper. The binding is the library's own shape and is never validated. */ schema?: StandardSchemaV1; } /** * A typed per-connection state store that co-owns the app state and the acceptor handler's routing * binding in one attachment, so neither the consumer nor the handler has to hand-merge the two. Create * it through {@link createConnectionStateStore} (which also wires binding persistence and replays * surviving connections after a wake), then `get`/`set`/`clearApp` the app state directly. * * The mechanism is carrier-neutral — it only needs read/write/enumerate callbacks for the connection's * attachment — but it pays off on transports whose connections outlive process eviction (e.g. a * Durable Object's hibernatable WebSockets), which is why it lives beside the hibernation adapter. * * ```ts * const players = createConnectionStateStore(serverHandler, { * schema: vs_player, * read: (ws) => ws.deserializeAttachment(), * write: (ws, v) => ws.serializeAttachment(v), * getConnections: () => ctx.getWebSockets(), * }); * players.set(ws, player); // binding is preserved automatically * const player = players.get(ws); * ``` */ declare class ConnectionStateStore { private readonly options; constructor(options: IConnectionStateStoreOptions); /** The validated app state for a connection, or `null` if unset / invalid. */ get(connection: TConn): TApp | null; /** Set the app state, preserving the runtime binding already pinned to the connection. */ set(connection: TConn, app: TApp): void; /** Clear the app state but keep the binding (e.g. a spectator that stopped watching). */ clearApp(connection: TConn): void; /** Every live connection paired with its (validated) app state — for rebuilding in-memory state after a wake. */ entries(): [TConn, TApp | null][]; /** @internal Persist a freshly-bound connection's binding, preserving any app state already stored. */ _persistBinding(connection: TConn, binding: IAcceptorConnectionBinding): void; /** @internal The persisted binding for a connection, if any (used to replay routing after a wake). */ _readBinding(connection: TConn): IAcceptorConnectionBinding | undefined; private _readAttachment; private _validateApp; } /** * Build a per-connection {@link ConnectionStateStore} bound to an {@link ChannelAcceptor}: it registers * itself as the handler's connection-bound persistence callback (so bindings are written without * overwriting app state) and immediately replays every live connection's stored binding via * {@link ChannelAcceptor.rehydrate} — so on a transport that resumes after eviction (e.g. a * Durable Object waking from hibernation) both the app identity and the action routing come back from a * single attachment, with no storage reads and no hand-rolled merge. * * Lives outside the handler so the generic {@link ChannelAcceptor} stays free of any attachment/ * hibernation concern — it exposes only the neutral `setOnConnectionBound` + `rehydrate` * hooks this builder drives. */ declare function createConnectionStateStore(handler: ChannelAcceptor, options: IConnectionStateStoreOptions): ConnectionStateStore; //#endregion //#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter.d.ts interface IHibernatableWsServerAdapterOptions { /** The handler to drive (from `createSecureChannelAcceptor` or `createChannelAcceptor`). */ handler: ChannelAcceptor; /** All currently-live connections — replayed on construction to rebuild bindings after a wake. */ getConnections: () => TConn[]; /** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */ getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined; /** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */ setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void; } /** * The neutral lifecycle surface for a duplex (push-capable) acceptor: feed it each inbound frame and tell * it when a connection goes away. Carrier-agnostic — a WebSocket, a WebRTC data channel, or any other * duplex connection drives the same two methods. */ interface IDuplexConnectionRouter { /** Feed one inbound frame from a connection into the handler. */ receive: (connection: TConn, frame: string | ArrayBuffer | Uint8Array) => void; /** Forget a connection (call on socket close/error). */ drop: (connection: TConn) => void; } /** * Wire the hibernation lifecycle for an acceptor handler on a transport whose connections outlive process * eviction (e.g. a Durable Object's hibernatable WebSockets). It owns persistence end to end: * registers `setAttachment` as the handler's connection-bound callback and immediately replays every * live connection's stored binding via `getAttachment`, so results/pushes still route after a wake. * * Layered on top of the generic {@link ChannelAcceptor} — it touches only the handler's neutral * `setOnConnectionBound` / `rehydrate` / `receive` / `drop` surface, so no * hibernation concern leaks into the handler itself. * * Construct it once when the handler is built, then forward connection events: * ```ts * const duplex = createHibernatableWsServerAdapter({ handler, getConnections, getAttachment, setAttachment }); * // webSocketMessage(ws, msg) => duplex.receive(ws, msg); * // webSocketClose/Error(ws) => duplex.drop(ws); * ``` */ declare function createHibernatableWsServerAdapter(options: IHibernatableWsServerAdapterOptions): IDuplexConnectionRouter; //#endregion //#region src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.d.ts /** * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource} * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates * it. * * Two shapes mirror the connector side: * * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence. * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to * upgrade. */ /** * Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections * outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional — omit for a * transport that never hibernates (per-connection state is then in-memory only). * * `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when * `connectionState` is requested) per-connection app state as one composite in this single slot, so both * survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections. */ interface IAcceptorAttachmentStore { /** All currently-live connections — enumerated on build to replay binding + app state after a wake. */ getConnections: () => TConn[]; /** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */ read: (connection: TConn) => unknown; /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */ write: (connection: TConn, value: unknown) => void; } /** * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.) */ interface IDuplexCarrierLifecycle { /** Feed a live frame into the server. Throws until served; permanently ignores frames after disposal. */ receive(connection: TConn, frame: TFrame): void; /** Forget a connection on close/error. No-op before serving and after terminal disposal. */ drop(connection: TConn): void; /** @internal `serveChannel` binds this carrier's live connection router here. */ _activate(router: IDuplexConnectionRouter): void; /** @internal Permanently deactivate the carrier and release its per-connection bookkeeping. */ _dispose?(): void; } type TInboundFrameLimitReason = "frame_bytes" | "message_rate"; interface IInboundFrameLimits { /** Exact UTF-8/binary bytes allowed for one inbound carrier frame. */ maxFrameBytes?: number; /** Fixed-window message budget. Boundary double-bursts are part of the declared semantics. */ rate?: { maxMessages: number; windowMs: number; }; /** Called for each dropped frame so the host can close/quarantine the connection. */ onExceeded: (connection: TConn, reason: TInboundFrameLimitReason) => void; } /** * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live * connection, how to perform the transport-specific upgrade that admits one, which requests are such * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see * {@link IDuplexCarrierLifecycle}). */ interface IDuplexAcceptorCarrier extends IDuplexCarrierLifecycle { /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */ readonly shape: ETransportShape$1.duplex; /** * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`. */ secure?: boolean; /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */ send: (connection: TConn, frame: TFrame) => void; /** * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}). */ upgrade?: (request: Request, url: URL) => Response | Promise; /** * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header. * Only consulted when {@link upgrade} is present. */ isUpgrade?: (request: Request, url: URL) => boolean; /** * Optional attachment read/write for connections that survive eviction (Durable Object hibernation). * Present → `serveChannel` persists the routing binding (and any `connectionState`) here and replays it * on wake. Absent → per-connection state is in-memory only. */ attachmentStore?: IAcceptorAttachmentStore; /** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`). */ readonly carrierLabel: string; } /** * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's * plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). So a server can pair a secure * duplex (WebSocket) with a plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`. */ interface IExchangeAcceptorCarrier { /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */ readonly shape: ETransportShape$1.exchange; /** * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain * endpoint: the body is the raw action wire and the result is the response body — no handshake, token, * or encryption. A plain endpoint ignores the central crypto identity entirely. */ secure?: boolean; /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */ isActionPath?: (url: URL) => boolean; /** * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the * permissive `*` set; pass `false` to attach no CORS headers at all. */ cors?: Record | false; /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */ useErrorStatus?: boolean; /** Short carrier-kind label for the devtools chip (e.g. `"http"`). */ readonly carrierLabel: string; } type TAcceptorCarrier = IDuplexAcceptorCarrier | IExchangeAcceptorCarrier; /** * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one. */ declare function isExchangeAcceptorCarrier(carrier: TAcceptorCarrier): carrier is IExchangeAcceptorCarrier; //#endregion //#region src/ActionRuntime/Channel/serveChannel.d.ts /** * Build the per-connection subset resolver a multi-channel acceptor uses: advertised `hello.channels` * tags → the channel that connection speaks (its own codec + dictionary version for a single tag, the * composed union for several, the combined `channel` for none). Returns `null` — the handshake then * rejects — for an unknown tag, and for a repeated tag: a repeat would compose the same channel twice * into the positional wire dictionary and desync every later route index. * * Throws immediately (setup time, not per connection) when two served channels share a tag — a * last-write-wins registry could never route that deterministically. */ declare function buildChannelSubsetResolver(combined: IActionChannel, registry: readonly IActionChannel[]): (tags: readonly string[] | undefined) => IActionChannel | null; /** Per-connection app-state config for {@link serveChannel}'s `connectionState`. */ interface IServeConnectionStateOptions { /** * Optional Standard Schema (valibot, zod, …) validating the app state on read — a value that fails * validation reads back as `null`. Omit to store the app state untyped. */ schema?: StandardSchemaV1; } /** * The per-action handle a `serveChannel` `channelCases` case receives as its second argument — the * originating connection enriched with everything a case typically reaches for, so it never threads * `ws` through `this.connections` / `this.server` by hand: * * - {@link state} / {@link setState} / {@link clearState} — the connection's typed app state (when * `connectionState` is configured), co-stored with the routing binding so it survives hibernation. * - {@link broadcast} — fan a server push to every other connection (skip self with `exceptSelf`). * - {@link pushBack} — push a server-initiated action down *this* same connection. * * Over the HTTP-exchange path there is no live socket: {@link connection} is `null`, {@link state} reads * `null`, {@link setState}/{@link clearState} are no-ops, and {@link pushBack} throws (an exchange reply * rides its own request — it can't carry an unsolicited push). */ interface IConnectionContext { /** The originating live connection, or `null` on the HTTP-exchange path (escape hatch). */ connection: TConn | null; /** The originating client's coordinate (`= action.context.originClient`). */ origin: RuntimeCoordinate; /** * Receiver-side reliable-delivery facts for the frame being handled — `(seq, streamKey, redelivered)`, * the same value as `action.context.reliability` (see {@link IHandledReliability}). `undefined` for a * best-effort action. */ reliability?: IHandledReliability; /** This connection's app state, or `null` if unset / no live socket. */ state: TApp | null; /** Set this connection's app state (no-op without a live socket). Preserves the routing binding. */ setState: (value: TApp) => void; /** Clear this connection's app state but keep the routing binding (no-op without a live socket). */ clearState: () => void; /** Fan a server-initiated action out to every connection; `exceptSelf` skips this one. */ broadcast: (makeRequest: () => ActionPayload_Request, options?: { exceptSelf?: boolean; where?: (connection: TConn) => boolean; timeout?: number; onError?: (error: unknown, connection: TConn) => void; }) => void; /** Push a server-initiated action down this same connection. Throws if there is no live socket. */ pushBack: (request: ActionPayload_Request, options?: { timeout?: number; }) => RunningAction; } interface IServeChannelOptions[], TConn, TApp = unknown> { /** * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`), * used only as the offline-return scoring fallback — a result/push to a live client always routes over * the carrier it connected on regardless of this. Optional: omit it for a multi-role server that accepts * clients of several envs over one acceptor (it then scores 0 against every client, so the live * connection always decides). */ clientEnv?: RuntimeCoordinate; /** * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins * (their keys don't collide). It is built once and shared across every carrier, so the WebSocket and the * secure-HTTP endpoint present the exact same verify/exchange keys and trust the same pinned clients. * Back it with persistent storage (e.g. a Durable Object's storage) so identity + pins survive eviction. * * Required only when at least one carrier is secure (the default). A fully-plain server (every carrier * `secure: false`) needs no storage and may omit it. * * The secure HTTP exchange is stateless — its handshake + session ride sealed tokens, so it touches * this store only for the (read-mostly) identity, never per session. That lets a single secure-exchange * server (`carriers: [httpAcceptorCarrier()]`) run on a stateless Worker/Node backend with no Durable * Object. On a *strongly-consistent* store (DO storage, D1, Node memory) the default lazy identity is * fork-safe. On an *eventually-consistent* store (Cloudflare KV) pass an explicit {@link link} built * with `identityMode: "required"` and `provisionIdentity()` it once out-of-band, so a transient read * miss can never fork a second identity (which pinned clients would then permanently reject). */ storage?: StorageAdapter; /** * The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`. * Build them with `wsAcceptorCarrier` / `httpAcceptorCarrier`. Any number of duplex (push-capable) * carriers are supported (e.g. WebSocket + WebRTC), plus at most one exchange (request/reply) carrier; * all share one crypto identity and one runtime, and each result/push routes back over the carrier its * client connected on. */ carriers: readonly TAcceptorCarrier[]; /** Your execution handlers (e.g. the local handler holding the action cases). Registered for you. */ handlers?: TActionRuntimeHandler[]; /** * The individual channels this acceptor serves, for **subset selection** — set by `serveChannels` so a * client connecting any subset (advertised as `hello.channels` tags) gets the matching composed codec + * dictionary version. Omit for a single channel (then `channel` is used as-is). When two or more are * given, a connection with no advertised tags falls back to the combined `channel`. */ channels?: readonly IActionChannel[]; /** * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. Pass an * existing link only to share identity with acceptors built outside this call. */ link?: ClientCryptoKeyLink; /** Accepted level(s) for every carrier; defaults to negotiating any of none/authenticated/encrypted. */ securityLevel?: ESecurityLevel | readonly ESecurityLevel[]; /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */ verifyKeyResolver?: IClientVerifyKeyResolver; /** Timeout (ms) applied to server-initiated actions awaiting a client response. */ defaultTimeout?: number; /** * Co-store per-connection app state alongside the routing binding in the sole duplex carrier's connection * attachment, so both survive a wake from eviction. Reach the typed store back on `server.connections`. * Requires the carrier to expose an attachment store (the Cloudflare `durableObjectWsCarrier` does) and * exactly one duplex carrier. */ connectionState?: IServeConnectionStateOptions; /** * Connection-aware action cases for the channel's acceptor (`toAcceptor`) domains — each case receives the * primed request *and* an {@link IConnectionContext} (the connection plus its typed `state` and * `broadcast`/`pushBack`). The connection-aware dual of `handlers`, registered on the runtime for you. * Requires exactly one duplex carrier. */ channelCases?: TChannelAcceptorCases>; /** * Optional pluggable logger for served requests — `onRequest` fires when an inbound action is accepted * (before it executes) and returns a reporter called with the outcome, so each request produces a * request line and a result line. Fanned across every carrier (each request is tagged with its carrier's * transport label, e.g. `"http"` / `"ws"`). Use {@link createDefaultServeLogger} for a ready-made console * logger, or implement {@link IActionServeLogger} to forward to your own logging stack: * ```ts * serveChannel(runtime, channel, { storage, carriers, logger: createDefaultServeLogger() }); * ``` */ logger?: IActionServeLogger; /** * The server's wire-observation seam — the exact mirror of `connectChannel({ wireTap })`. Every frame * that crosses a carrier is reported with its **true wire byte size** (post-encryption on the way out, * pre-decryption on the way in: what the platform actually transmits and bills) and its lane * (`"handshake"` / `"keepalive"` / `"action"` / a frame-protocol id such as `"realm"`), tagged with the * bound client's coordinate as `linkId` so a multi-client server attributes traffic per connection. * Sizes only — payload contents are never captured. * * Feed it a `TrafficMetricsCore` to give a backend the same traffic panel a frontend has: * ```ts * const traffic = new TrafficMetricsCore(); * serveChannel(runtime, channel, { storage, carriers, wireTap: traffic.wireTap }); * createServerDevtoolsHost({ name: "api" }).contributeSample(trafficSampleScope("traffic", traffic)); * ``` * Fanned across every duplex carrier. With no tap the cost is one null check per frame. */ wireTap?: import("@nice-code/wire").TWireTapFn; /** * Persisted receive store for the **persisted** reliability tier (`.reliable({ persist: true })`). When set, * a persisted-tier stream dedups through this store (shared across all duplex carriers) instead of the * in-memory inbox, so its high-water survives eviction and a replayed stream dedups rather than * redelivering. Build it with the platform helper (Cloudflare: `cloudflareReliableLog(ctx)`). Omit it and * persisted-tier streams degrade gracefully to the session-tier (in-memory) behavior. * * Same store as the lower-level acceptor's * {@link IChannelAcceptorBaseOptions.persistedReceiver | `persistedReceiver`} — this serve-level * option simply feeds that field, so type-surface searches for either name land on the same thing. */ reliableStore?: IReliableReceiver>; /** * Cap on distinct **keyed** (`streamKey`) reliable streams tracked per client (default 256). Keys are * client-chosen strings, so this bounds the receiver state one client can allocate; past the cap a new * key's frames are served best-effort with a one-time warning. */ maxKeyedStreamsPerClient?: number; /** * Protocol modules to register on every duplex acceptor handler — e.g. a realm server's acceptor * protocol (`serveRealmDurableObject(...).protocol`). The accept-in mirror of * `connectChannel({ protocols })`. Registered **before** the hibernation adapter replays surviving * bindings, so each protocol's `onAttach` fires for rehydrated connections on a Durable Object wake. */ protocols?: readonly IAcceptorFrameProtocol[]; } /** * One server serving a secure channel over several carriers — the accept-in dual of `connectChannel`, * returned by {@link serveChannel}. Wire its surface straight to the host's request/socket events. */ interface IChannelServer { /** * The duplex channel acceptors — one per duplex carrier, in carrier order (empty if none). For pushing, * prefer {@link pushToClient} (it resolves the owning acceptor); reach for these for cross-carrier work * like a per-acceptor `broadcast`. */ acceptors: ChannelAcceptor[]; /** * Unified request handler: answers the CORS preflight, performs the duplex upgrade for an upgrade * request, serves a secure-exchange action `POST`, else `404`. Forward the host's `fetch` straight to it. */ fetch: (request: Request) => Promise; /** * Feed one inbound frame from a live connection into the server — forward your host's "message" event * here (a Durable Object's `webSocketMessage`, a Bun `websocket.message`, a Node `ws.on("message")`). * Routes to the sole duplex carrier; throws with a clear message when there are zero or several duplex * carriers (for the multi-carrier case feed each `acceptors[i]` / carrier handle directly). */ receive: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void; /** * Forget a connection on close/error — forward your host's "close"/"error" event here. Routes to the * sole duplex carrier; a no-op when there are none (an HTTP-only server has no sockets to drop). */ drop: (connection: TConn) => void; /** Permanently quiesce the host: detach connections and reject/ignore all late entry points. */ dispose: () => void; /** * Push a server-initiated action to a connected client (the runtime is bound in, so unlike * {@link ChannelAcceptor.pushToClient} you pass only the target + request). It routes through the duplex * carrier the target connected on. Throws if no duplex carrier currently holds the target. */ pushToClient: (target: TConn | RuntimeCoordinate, request: ActionPayload_Request, options?: { timeout?: number; }) => RunningAction; /** * Fan a server-initiated action out to every connection on the sole duplex carrier (skip the origin with * `except`, filter with `where`). The push-to-many counterpart of {@link pushToClient}. Throws if there * isn't exactly one duplex carrier (with several, broadcast over a specific `acceptors[i]`). */ broadcast: (makeRequest: () => ActionPayload_Request, options?: { except?: TConn | null; where?: (connection: TConn) => boolean; timeout?: number; onError?: (error: unknown, connection: TConn) => void; }) => void; /** * The per-connection app-state store co-stored with the routing binding in the connection attachment — * present only when `connectionState` was passed. `get`/`set`/`clearApp`/`entries` it directly; it * survives hibernation alongside the binding. */ connections?: ConnectionStateStore; } /** * Serve a secure channel over one or more carriers from a single call — the accept-in dual of * `connectChannel`. It builds the crypto identity (a {@link ClientCryptoKeyLink} + a storage-backed TOFU * resolver) and the security block (coordinate, dictionary version, accepted levels) *once* from * `(runtime, channel)` and fans them across every carrier, so the WebSocket and the secure-HTTP endpoint * can never drift apart. It registers your handlers (plus the duplex acceptor it builds) on the runtime, * wires hibernation when the duplex carrier exposes an attachment store, and returns a single * {@link IChannelServer} whose `fetch` / `receive` / `drop` / `pushToClient` / `broadcast` you forward * straight to the host: * ```ts * const server = serveChannel(runtime, channel, { * clientEnv, storage, * carriers: [wsAcceptorCarrier({ send, upgrade, attachmentStore }), httpAcceptorCarrier()], * connectionState: { schema: vs_player }, // optional: co-store per-connection app state (survives hibernation) * channelCases: { join: (action, conn) => { conn.setState(action.input); … } }, // connection-aware cases * }); * // fetch(req) => server.fetch(req) * // webSocketMessage(conn, m) => server.receive(conn, m) * // webSocketClose/Error(conn) => server.drop(conn) * // server.connections.get(conn) / server.broadcast(() => push.request(…), { except: conn }) * ``` * * On Cloudflare, `serveDurableObject` folds the whole DO transport stack (carriers + storage + keepalive) * into this — reach for it instead of assembling the carriers by hand. * * `TConn` (the live-connection token a duplex carrier hands back through `send`/`receive`/`drop`) is * inferred from the carriers — `WebSocket` for `wsAcceptorCarrier`, the data-channel type for a WebRTC * carrier, and so on — so it stays carrier-agnostic. Passing `connectionState` narrows the return so * `server.connections` is non-optional. */ declare function serveChannel[], TO_CONNECTOR extends readonly ActionDomain[], TConn, TApp>(runtime: ActionRuntime, channel: IActionChannel, options: IServeChannelOptions & { connectionState: IServeConnectionStateOptions; }): IChannelServer & { connections: ConnectionStateStore; }; declare function serveChannel[] = readonly ActionDomain[], TO_CONNECTOR extends readonly ActionDomain[] = readonly ActionDomain[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channel: IActionChannel, options: IServeChannelOptions): IChannelServer; /** * Serve a **set** of channels over one acceptor / endpoint — the accept-in dual of `connectChannels` and * the multi-channel form of {@link serveChannel}. The channels are combined into one (their domains * unioned in list order, see {@link combineChannels}) and served over a single set of carriers + one crypto * identity; the runtime routes each inbound action to the right handler by its domain, exactly as for a * single channel. `handlers` cover every channel's actions; `channelCases` is one merged, connection-aware * map typed against the union of all the channels' acceptor domains. * ```ts * const server = serveChannels(runtime, [mainChannel, notificationsChannel], { * clientEnv, storage, * carriers: [wsAcceptorCarrier(...), httpAcceptorCarrier()], * handlers: [mainHandler, notificationsHandler], * }); * ``` * Both ends must list the **same channels in the same order** (the `combineChannels` contract), which is * what makes `serveChannels` ↔ `connectChannels` exact duals. Passing `connectionState` narrows the return * so `server.connections` is non-optional, exactly as with `serveChannel`. */ declare function serveChannels[], TConn, TApp>(runtime: ActionRuntime, channels: CHANNELS, options: IServeChannelOptions, TConn, TApp> & { connectionState: IServeConnectionStateOptions; }): IChannelServer & { connections: ConnectionStateStore; }; declare function serveChannels[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channels: CHANNELS, options: IServeChannelOptions, TConn, TApp>): IChannelServer; //#endregion //#region src/ActionRuntime/Channel/serveHost.d.ts /** * An environment-neutral description of *where* a channel is served — the accept-in dual of a connector's * transport stack, factored out so a platform adapter (a Cloudflare Durable Object, a Bun/Node WebSocket * server, …) supplies only what differs per environment while the channel + case wiring stays identical. * A host bundles: * * - the {@link carriers} the channel is served over (e.g. a WebSocket + an HTTP fallback), * - the {@link storage} backing the server's crypto identity, and * - an {@link onServed} hook run once the server exists (e.g. registering a keepalive auto-response). * * Build one with a platform helper (`cloudflareDurableObjectHost`) and hand it to {@link serveHost}. */ interface IChannelHostAdapter { /** The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`. */ carriers: readonly TAcceptorCarrier[]; /** Backing store for the server's crypto identity + TOFU pins. Required when any carrier is secure. */ storage?: StorageAdapter; /** Run once after the server is built — e.g. register a transport keepalive. */ onServed?: (server: IChannelServer) => void; } /** {@link serveChannel}'s options minus what the host adapter supplies (`carriers`, `storage`). */ type TServeHostOptions[], TConn, TApp = unknown> = Omit, "carriers" | "storage">; /** * Serve a channel over a {@link IChannelHostAdapter} — the environment-neutral core every platform helper * (e.g. `serveDurableObject`) composes. It folds the host's carriers + storage into `serveChannel`, then * runs the host's `onServed` hook. Everything else (`clientEnv`, `channelCases`, `connectionState`, * `handlers`, …) is the same `serveChannel` surface, so moving a server between environments is swapping * the host adapter and nothing else. Passing `connectionState` narrows the return so `server.connections` * is non-optional, exactly as with `serveChannel`. */ declare function serveHost[], TO_CONNECTOR extends readonly ActionDomain[], TConn, TApp>(runtime: ActionRuntime, channel: IActionChannel, host: IChannelHostAdapter, options: TServeHostOptions & { connectionState: IServeConnectionStateOptions; }): IChannelServer & { connections: ConnectionStateStore; }; declare function serveHost[] = readonly ActionDomain[], TO_CONNECTOR extends readonly ActionDomain[] = readonly ActionDomain[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channel: IActionChannel, host: IChannelHostAdapter, options: TServeHostOptions): IChannelServer; //#endregion //#region src/ActionRuntime/Gateway/forwardTo.d.ts /** * Opaque action forwarding — the platform-neutral half of the front-door routing surface. * * Security in `@nice-code/action` terminates at the *final runtime* (the acceptor that runs the secure * handshake). A forwarder therefore never reads, decrypts, or re-signs the body: it only picks a * destination and passes the request through. That keeps the channel end-to-end encrypted between the * origin client and whatever final runtime ultimately serves it — a Durable Object, a service binding, or * an entirely different HTTP server — while the forwarder in the middle stays a dumb, stateless, identity- * less relay. Because the body is opaque, the routing key must ride the URL/headers (which is why callers * encode it in the path, e.g. `/bridge/:id/...`). * * A `forwardTo(...)` is just an {@link IFetchHandler}, so it drops straight into any framework (Hono, raw * Workers) or the bundled {@link actionRouter}. */ /** * The matched-route context the {@link actionRouter} threads into a handler — the parsed URL and any path * params (`:id` → `{ id }`). Passed as `fetch`'s optional second argument so every `{ fetch }` (a * forwarder, a served channel-set, a whole sub-app) shares one call shape; sub-apps simply ignore it. */ interface IRouteContext { url: URL; params: Record; } /** * The universal front-door unit: anything that answers a `Request`. A served channel-set * (`serveChannels`/`serveWorker`), an opaque forwarder, or a nested router/sub-app all satisfy it, so they * compose by nesting and mount into any framework. The optional {@link IRouteContext} is supplied when a * router dispatches (carrying matched path params); a direct caller may omit it. */ interface IFetchHandler { fetch(request: Request, route?: IRouteContext): Promise | Response; } /** A forward destination — just something with a `fetch` (a DO stub, a service binding, a proxy). */ interface IForwardTarget { fetch(request: Request): Promise; } /** What {@link forwardTo}'s `pickTarget` receives — the request plus the matched-route context. */ interface IForwardContext { request: Request; url: URL; /** Matched path params when forwarded through {@link actionRouter}; `{}` for a direct call. */ params: Record; } interface IForwardToOptions { /** * Edge-answer the CORS `OPTIONS` preflight here (default: the permissive `*` set) so a per-id Durable * Object is never woken just to reply to a preflight. `false` forwards the preflight to the target too. */ cors?: Record | false; /** * Remap the URL before forwarding — e.g. strip a path prefix when proxying to another server. Durable * Objects need none (their `serveChannels` matches the `/ws` · `/secure` · `/action` suffix). Return a * `URL` or a string; the forwarded request is rebuilt against it. */ rewrite?: (url: URL, ctx: IForwardContext) => URL | string; } /** * Build an opaque forwarder: pick a destination `{ fetch }` from the request (and matched params), then * pass the request straight through. `pickTarget` may be async (e.g. to look up which instance/region owns * a resource first). The CORS `OPTIONS` preflight is answered at the edge by default. * * ```ts * // to a per-id Durable Object (E2E client ↔ DO): * forwardTo(({ params }) => env.DO_BRIDGE.get(env.DO_BRIDGE.idFromString(params.id))) * // to a service binding: * forwardTo(() => env.OTHER_WORKER) * // to any HTTP server, stripping a prefix: * forwardTo(() => ({ fetch: (req) => fetch(UPSTREAM, req) }), { rewrite: (u) => u.pathname.replace("/up", "") }) * ``` */ declare function forwardTo(pickTarget: (ctx: IForwardContext) => IForwardTarget | Promise, options?: IForwardToOptions): IFetchHandler; //#endregion //#region src/ActionRuntime/Gateway/actionRouter.d.ts /** A route target: a `{ fetch }` handler, or a bare function receiving the request + matched-route context. */ type TRoutable = IFetchHandler | ((request: Request, route: IRouteContext) => Promise | Response); interface IActionRouterOptions { /** * CORS headers for the edge-answered `OPTIONS` preflight on a *matched* route (default: the permissive * `*` set). `false` forwards `OPTIONS` to the matched handler instead. Unmatched requests are never * touched — they fall through to `otherwise` (which may answer its own preflight). */ cors?: Record | false; } declare class ActionRouter implements IFetchHandler { private readonly options; private readonly routes; private fallback?; constructor(options?: IActionRouterOptions); /** Add a route. Patterns support `:param` and a trailing `*`. Matched in declaration order, first wins. */ route(pattern: string, handler: TRoutable): this; /** The handler for any request no route matched (default: a `404`). */ otherwise(handler: TRoutable): this; fetch(request: Request): Promise; } /** Create an {@link ActionRouter} — the optional framework-free front-door multiplexer. */ declare function actionRouter(options?: IActionRouterOptions): ActionRouter; //#endregion //#region src/ActionRuntime/Handler/PeerLink/Connector/err_nice_external_client.d.ts declare const err_nice_external_client: import("@nice-code/error").NiceErrorDomain<{ domain: string; allDomains: [string, string, "err_nice"]; schema: {}; }>; //#endregion //#region src/ActionRuntime/Transport/Carrier/duplex/inMemory/inMemoryCarrier.d.ts interface IInMemoryCarrier { /** The connector end — pass as the `carrier` to one of `connectChannel`'s transports. */ carrier: IDuplexCarrierSource; /** The acceptor end — wire into an `ChannelAcceptor` (`send` + `receive`). */ serverEndpoint: IInMemoryServerEndpoint; } /** * A loopback duplex carrier with no socket — two cross-wired in-process ends, the action * instantiation of wire's `inMemoryCarrier` (plan Phase 2). The connector end is an * {@link IDuplexCarrierSource} for `connectChannel`; the acceptor end plugs into an * `ChannelAcceptor`. Ideal for tests and for running two runtimes in one process, or proving a * non-WS carrier end to end. */ declare function inMemoryCarrier$1(): IInMemoryCarrier; //#endregion //#region src/ActionRuntime/Transport/Carrier/duplex/rtc/rtcCarrier.d.ts interface IRtcCarrierOptions { getTransportCacheKey?: (input: TTransportRouteParams) => string[]; getRouteInfo?: (input: TTransportRouteParams) => ITransportRouteInfo; } /** * A WebRTC {@link IDuplexCarrierSource} over an already-negotiated `RTCDataChannel` (signaling is * the app's concern) — the action instantiation of wire's `rtcCarrier` (plan Phase 2 / E2). Pass * it as a `carrier` to `connectChannel` so two browsers/apps linked peer-to-peer run the identical * secure session as a WebSocket. */ declare function rtcCarrier$1(dataChannel: IRtcDataChannelLike, options?: IRtcCarrierOptions): IDuplexCarrierSource; //#endregion //#region src/ActionRuntime/Transport/Carrier/duplex/ws/err_nice_transport_ws.d.ts declare enum EErrId_NiceTransport_WebSocket { ws_disconnected = "ws_disconnected", ws_create_failed = "ws_create_failed", ws_error = "ws_error" } declare const err_nice_transport_ws: import("@nice-code/error").NiceErrorDomain<{ domain: string; allDomains: [string, string, string, string, "err_nice"]; schema: { ws_disconnected: import("@nice-code/error").INiceErrorIdMetadata, import("@nice-code/error").JSONSerializableValue>; ws_create_failed: import("@nice-code/error").INiceErrorIdMetadata<{ originalError?: Error; }, import("@nice-code/error").JSONSerializableValue>; ws_error: import("@nice-code/error").INiceErrorIdMetadata<{ originalError?: Error; }, import("@nice-code/error").JSONSerializableValue>; }; }>; //#endregion //#region src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.d.ts interface IWsAcceptorCarrierOptions { /** * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint * — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption * (and `serveChannel` then needs no `storage` for this carrier). */ secure?: boolean; /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */ send: (connection: TConn, frame: TFrame) => void; /** * Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's * `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band. */ upgrade?: (request: Request, url: URL) => Response | Promise; /** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */ isUpgrade?: (request: Request, url: URL) => boolean; /** Attachment read/write for hibernatable sockets (e.g. a Durable Object); `serveChannel` persists here. */ attachmentStore?: IAcceptorAttachmentStore; /** Override the devtools carrier-kind label (defaults to `"ws"`). */ carrierLabel?: string; /** Drop oversized/rate-excess inbound frames before handshake, decoding, or action dispatch. */ inboundLimits?: IInboundFrameLimits; } /** * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session, * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface. */ declare function wsAcceptorCarrier(options: IWsAcceptorCarrierOptions): IDuplexAcceptorCarrier; //#endregion //#region src/ActionRuntime/Transport/Carrier/duplex/ws/wsCarrier.d.ts interface IWsCarrierOptions { /** Override the reuse key (defaults to `[url]`, so one socket is shared per endpoint). */ getTransportCacheKey?: (input: TTransportRouteParams) => string[]; /** Override the devtools route info for a specific action. */ getRouteInfo?: (input: TTransportRouteParams) => ITransportRouteInfo; /** * Construct the socket for a dial — the **testability seam** (resilience-surface §2): inject a * controllable socket to simulate outage/latency/drops without touching globals. Gates every * dial, keep-alive redials included. Passed straight through to wire's `wsCarrier`. */ createWebSocket?: (url: string) => WebSocket; } /** * A WebSocket {@link IDuplexCarrierSource}: the action instantiation of wire's `wsCarrier` * (shared-base-connect plan, Phase 2 / E2) — the dial context is the per-action routing params, * so `createRequest` still derives the socket URL per action exactly as before. Pass it as a * `carrier` to `connectChannel`. * * `createRequest` may return `null` for "no valid endpoint right now" (resilience-surface §6) — * the keep-alive redial then PARKS instead of dialing garbage, so a dynamic-endpoint teardown * (`_activeMatchId = undefined`) needs no careful ordering against `releaseLink()`. */ declare function wsCarrier$1(createRequest: (input: TTransportRouteParams) => IWsCarrierRequest | null, options?: IWsCarrierOptions): IDuplexCarrierSource; //#endregion //#region src/ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier.d.ts interface IHttpAcceptorCarrierOptions { /** * Whether this endpoint runs the secure exchange protocol (default `true`). Pass `false` for a plain * endpoint — the body is the raw action wire and the result is the response body, the request/reply dual * of a connector's plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). A plain * endpoint ignores the crypto identity, so it can sit alongside a secure WebSocket on the same server * (e.g. a secure WS preferred, plain HTTP fallback). */ secure?: boolean; /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */ isActionPath?: (url: URL) => boolean; /** * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the * permissive `*` set; pass `false` to attach no CORS headers at all. */ cors?: Record | false; /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */ useErrorStatus?: boolean; /** Override the devtools carrier-kind label (defaults to `"http"`). */ carrierLabel?: string; } /** * An HTTP {@link IExchangeAcceptorCarrier}: the accept-in dual of {@link httpCarrier}. It serves the * secure exchange protocol (handshake → token session → encrypted frames) over web-standard * `Request`/`Response`. The crypto identity, runtime coordinate, dictionary version, and accepted security * levels are all supplied centrally by `serveChannel`, so this only needs to say which requests carry an * action envelope and how to answer CORS. */ declare function httpAcceptorCarrier(options?: IHttpAcceptorCarrierOptions): IExchangeAcceptorCarrier; //#endregion //#region src/ActionRuntime/Transport/Carrier/exchange/http/httpCarrier.d.ts interface IHttpCarrierOptions { /** Override the reuse key (defaults to `[url]`, so one session is shared per endpoint). */ getTransportCacheKey?: (input: TTransportRouteParams) => string[]; /** Override the devtools route info for a specific action. */ getRouteInfo?: (input: TTransportRouteParams) => ITransportRouteInfo; /** Override `fetch` (e.g. to route to an in-memory handler in tests). Defaults to global `fetch`. */ fetch?: TCarrierFetch; } /** * An HTTP {@link IExchangeCarrierSource} — the action instantiation of wire's `httpCarrier` (plan * Phase 2 / E2): each `exchange` POSTs one frame body to the action endpoint and resolves with the * response body as the single correlated reply. Pass it as a `carrier` to `connectChannel` — a * secure HTTP transport then runs the *same* secure session as a duplex carrier. */ declare function httpCarrier$1(createRequest: (input: TTransportRouteParams) => IHttpCarrierRequest, options?: IHttpCarrierOptions): IExchangeCarrierSource; //#endregion //#region src/ActionRuntime/Transport/err_nice_transport.d.ts declare enum EErrId_NiceTransport { timeout = "timeout", not_found = "not_found", unsupported = "unsupported", initialization_failed = "initialization_failed", send_failed = "send_failed", invalid_action_response = "invalid_action_response", reliable_outbox_overflow = "reliable_outbox_overflow", reliable_delivery_abandoned = "reliable_delivery_abandoned", reliable_stream_closed = "reliable_stream_closed" } declare const err_nice_transport: import("@nice-code/error").NiceErrorDomain<{ domain: string; allDomains: [string, string, string, "err_nice"]; schema: { timeout: import("@nice-code/error").INiceErrorIdMetadata<{ timeout: number; }, import("@nice-code/error").JSONSerializableValue>; not_found: import("@nice-code/error").INiceErrorIdMetadata<{ actionId: string; }, import("@nice-code/error").JSONSerializableValue>; unsupported: import("@nice-code/error").INiceErrorIdMetadata<{ transportShapes: ETransportShape$1[]; }, import("@nice-code/error").JSONSerializableValue>; initialization_failed: import("@nice-code/error").INiceErrorIdMetadata<{ actionId: string; /** The most relevant underlying failure (e.g. a rejected handshake) — surfaced in the message. */ cause?: string; /** * The endpoint the failing transport actually dialed (meteor-connect-bridge feedback W2). * One line that makes the error self-locating instead of "some transport, somewhere". */ endpoint?: string; /** * Answered-wrongly vs unreachable vs rejected, typed (feedback W3) — branch on this rather * than matching the message, which is exactly the brittleness consumers had to write: * * ```ts * if ( * err_nice_transport.isExact(e) && * e.hasId("initialization_failed") && * e.getContext("initialization_failed").kind === * EWireConnectFailureKind.endpoint_unreachable * ) { ... } * ``` */ kind?: EWireConnectFailureKind; }, import("@nice-code/error").JSONSerializableValue>; send_failed: import("@nice-code/error").INiceErrorIdMetadata<{ actionState: string; actionId: string; httpStatusCode?: number; message?: string; }, import("@nice-code/error").JSONSerializableValue>; invalid_action_response: import("@nice-code/error").INiceErrorIdMetadata<{ actionId: string; }, import("@nice-code/error").JSONSerializableValue>; reliable_outbox_overflow: import("@nice-code/error").INiceErrorIdMetadata<{ actionId: string; streamId: string; maxUnacked: number; }, import("@nice-code/error").JSONSerializableValue>; reliable_delivery_abandoned: import("@nice-code/error").INiceErrorIdMetadata<{ actionId: string; streamId: string; timeout: number; }, import("@nice-code/error").JSONSerializableValue>; reliable_stream_closed: import("@nice-code/error").INiceErrorIdMetadata<{ actionId: string; streamId: string; }, import("@nice-code/error").JSONSerializableValue>; }; }>; //#endregion //#region src/ActionRuntime/Transport/SecureSession/exchangeAcceptor.d.ts /** Acceptor secure config for the exchange (HTTP) endpoint — same identity an `ChannelAcceptor` uses. */ interface IExchangeAcceptorSecurity { /** This acceptor's crypto identity (verify + exchange key pairs, optionally persisted). */ link: ClientCryptoKeyLink; /** This acceptor's coordinate — its identity to clients during the handshake. */ localCoordinate: IRuntimeCoordinate; /** Wire dictionary version — a fixed string (single channel), or a resolver composing it from the * `hello.channels` tags (multi-channel). The handshake rejects on a mismatch / unknown channel. */ dictionaryVersion: string | TServerDictionaryVersionResolver; /** Accepted level(s) — a single level is strict, an array is a negotiable allowed set. */ securityLevel: ESecurityLevel | readonly ESecurityLevel[]; /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */ verifyKeyResolver?: IClientVerifyKeyResolver; } interface IExchangeAcceptorConfig { security: IExchangeAcceptorSecurity; /** The runtime that executes an inbound action wire and produces its result. */ runtime: ActionRuntime; /** Optional server-side logger — called per inbound action request with its outcome. */ logger?: IActionServeLogger; /** Short carrier-kind label surfaced to the logger as the request's transport (default `"http"`). */ transportLabel?: string; /** * How long a minted session ticket stays valid (ms). After it expires the client's next action is * rejected and it must re-handshake. Defaults to 12h — long enough for an ordinary session's life, * since a sealed ticket carries no server state to revoke before then. Keep it shorter for a more * tightly time-boxed session. */ sessionTtlMs?: number; } /** * The action lane over wire's {@link WireExchangeAcceptor} (shared-base-connect plan, Phase 6) — * the HTTP counterpart to `ChannelAcceptor` composing on `WireAcceptor`. Wire owns the exchange * mechanics: the envelope, the server handshake over the two `hs` POSTs, the sealed `hsc`/`t` * tokens that make the whole endpoint **stateless** across isolates, and the per-session frame * crypto. What stays here is the lane: validating the opened wire as an action payload, binding * the handshake-authenticated identity onto it (never the wire's self-asserted one), routing it * through the runtime, logging, and returning the result wire for the same response. * * The public config is unchanged by the split — `dictionaryVersion` remains on * {@link IExchangeAcceptorSecurity} (it is the lane's own handshake payload, E5); internally it * feeds wire's exchange lane while the neutral core feeds wire's security block, mirroring how * `ChannelAcceptor` splits its `IAcceptorSecurity`. */ declare class ExchangeAcceptor { private readonly _core; private readonly _runtime; private readonly _logger?; private readonly _transportLabel; constructor(config: IExchangeAcceptorConfig); /** Process one POST body (an exchange envelope), returning the reply body to send back. */ handlePost(body: string): Promise; /** Serve one opened `act` wire: validate → bind identity → log → run → return the result wire. */ private _onExchange; } //#endregion //#region src/ActionRuntime/Transport/SecureSession/exchangeProtocol.d.ts /** * The application-level envelope for secure action traffic over an {@link IExchangeCarrier} (HTTP). An * exchange carrier only moves one request frame → one reply frame with no unsolicited push, so the * handshake and the per-action token + crypto all ride in this envelope (a JSON string body) rather than * on a persistent channel. The three security levels share it: * * - `none` — no handshake, no token: an `act` envelope carries the plaintext wire both ways. * - `authenticated` — a one-time handshake yields a session `token`; each later `act` carries it + * the plaintext wire. * - `encrypted` — same, but the wire is AES-GCM ciphertext, base64 in the `c` field. * * The handshake runs as two `hs` exchanges (hello→welcome, prove→accept). The acceptor keeps **no** * in-flight state between them: the `welcome` reply carries a sealed handshake-continuation token `hsc` * (the acceptor's `pending` state, opaque to the client), which the connector echoes on the `prove` * request. The `accept` reply then carries the (sealed) session `t`oken replayed on every later `act`. * All server-side continuity rides these sealed tokens, so no request needs to co-locate with another. * * Since the shared-base-connect plan's Phase 6 the envelope is **owned by `@nice-code/wire`** * (`Connect/exchangeEnvelope.ts`, with `w` opaque — the lane's wire) and driven entirely by wire's * `establishPlain/SecureExchangeSession` + `WireExchangeAcceptor`. This module remains the lane's * *typed view* of the same byte-frozen JSON — the public API for consumers that speak the envelope * with action payloads in the `w` slot. */ type TWireJson = TActionPayload_Any_JsonObject; /** Connector → acceptor request envelope. */ type TExchangeRequest = { k: "hs"; m: string; hsc?: string; } | { k: "act"; t?: string; w: TWireJson; } | { k: "act"; t?: string; c: string; }; /** Acceptor → connector reply envelope. */ type TExchangeReply = { k: "hs"; m: string; hsc?: string; t?: string; } | { k: "act"; w: TWireJson; } | { k: "act"; c: string; } | { k: "err"; message: string; }; declare function encodeExchange(envelope: TExchangeRequest | TExchangeReply): string; declare function decodeExchangeRequest(raw: string): TExchangeRequest | undefined; declare function decodeExchangeReply(raw: string): TExchangeReply | undefined; //#endregion //#region src/errors/err_nice_action.d.ts declare enum EErrId_NiceAction { not_implemented = "not_implemented", action_id_not_in_domain = "action_id_not_in_domain", domain_already_exists_in_hierarchy = "domain_already_exists_in_hierarchy", domain_name_collision = "domain_name_collision", domain_no_handler = "domain_no_handler", hydration_domain_mismatch = "hydration_domain_mismatch", hydration_action_state_mismatch = "hydration_action_state_mismatch", hydration_action_id_not_found = "hydration_action_id_not_found", no_action_execution_handler = "no_action_execution_handler", wire_action_not_payload = "wire_action_not_payload", wire_not_action_data = "wire_not_action_data", client_runtime_already_registered = "client_runtime_already_registered", client_runtime_not_registered = "client_runtime_not_registered", runtime_reset = "runtime_reset", no_client_runtimes_registered = "no_client_runtimes_registered", action_input_validation_failed = "action_input_validation_failed", action_input_validation_promise = "action_input_validation_promise", action_output_validation_failed = "action_output_validation_failed", action_output_validation_promise = "action_output_validation_promise" } declare const err_nice_action: import("@nice-code/error").NiceErrorDomain<{ domain: string; allDomains: [string, "err_nice"]; schema: { not_implemented: import("@nice-code/error").INiceErrorIdMetadata<{ label: string; }, import("@nice-code/error").JSONSerializableValue>; action_id_not_in_domain: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; }, import("@nice-code/error").JSONSerializableValue>; domain_already_exists_in_hierarchy: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; allParentDomains: string[]; parentDomain: string; }, import("@nice-code/error").JSONSerializableValue>; domain_name_collision: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; existingParentDomains: string[]; incomingParentDomains: string[]; }, import("@nice-code/error").JSONSerializableValue>; domain_no_handler: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; }, import("@nice-code/error").JSONSerializableValue>; hydration_domain_mismatch: import("@nice-code/error").INiceErrorIdMetadata<{ expected: string; received: string; }, import("@nice-code/error").JSONSerializableValue>; hydration_action_state_mismatch: import("@nice-code/error").INiceErrorIdMetadata<{ expected: string; received: string; }, import("@nice-code/error").JSONSerializableValue>; hydration_action_id_not_found: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; }, import("@nice-code/error").JSONSerializableValue>; no_action_execution_handler: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; specifiedClient?: RuntimeCoordinate; }, import("@nice-code/error").JSONSerializableValue>; wire_action_not_payload: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; actionState: string | undefined; }, import("@nice-code/error").JSONSerializableValue>; wire_not_action_data: import("@nice-code/error").INiceErrorIdMetadata; runtime_reset: import("@nice-code/error").INiceErrorIdMetadata; client_runtime_already_registered: import("@nice-code/error").INiceErrorIdMetadata<{ context?: IActionRuntimeManagerContext; client: RuntimeCoordinate; }, import("@nice-code/error").JSONSerializableValue>; client_runtime_not_registered: import("@nice-code/error").INiceErrorIdMetadata<{ context?: IActionRuntimeManagerContext; clientStringId: TRuntimeCoordinateStringId; }, import("@nice-code/error").JSONSerializableValue>; no_client_runtimes_registered: import("@nice-code/error").INiceErrorIdMetadata<{ context?: IActionRuntimeManagerContext; }, import("@nice-code/error").JSONSerializableValue>; action_input_validation_failed: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; validationMessage: string; }, import("@nice-code/error").JSONSerializableValue>; action_input_validation_promise: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; }, import("@nice-code/error").JSONSerializableValue>; action_output_validation_failed: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; validationMessage: string; }, import("@nice-code/error").JSONSerializableValue>; action_output_validation_promise: import("@nice-code/error").INiceErrorIdMetadata<{ domain: string; actionId: string; }, import("@nice-code/error").JSONSerializableValue>; }; }>; //#endregion //#region src/utils/decodeActionFrame.d.ts /** * Minimal codec shape needed to turn an incoming channel frame back into action wire JSON. Matches * the `formatMessage` object the WebSocket transport (and `createBinaryWireAdapter`) provide. */ interface IActionFrameDecoder { incoming?: (frame: string | ArrayBuffer | Uint8Array | Blob) => TActionPayload_Any_JsonObject | undefined; } /** * Decode a single inbound channel frame (text or binary) into validated action wire JSON, or * `undefined` if it isn't a recognisable action payload. * * Shared by the WebSocket transport's message listener and the server-side `ChannelAcceptor` so * both decode identically: a binary `decoder.incoming` (e.g. msgpackr) takes precedence, and plain * text frames fall back to JSON — keeping binary and JSON clients interoperable on one channel. */ declare function decodeActionFrame(frame: string | ArrayBuffer | Uint8Array, decoder?: IActionFrameDecoder): TActionPayload_Any_JsonObject | undefined; //#endregion //#region src/utils/isActionPayload_Any_JsonObject.d.ts declare function isActionPayload_Any_JsonObject(obj: unknown): obj is TActionPayload_Any_JsonObject; //#endregion //#region src/utils/isActionPayload_Request_JsonObject.d.ts declare const isActionPayload_Request_JsonObject: (obj: unknown) => obj is IActionPayload_Request_JsonObject; //#endregion //#region src/utils/isActionPayload_Result_JsonObject.d.ts declare const isActionPayload_Result_JsonObject: (obj: unknown) => obj is IActionPayload_Result_JsonObject; //#endregion //#region src/ActionDefinition/Action/Payload/ActionPayload.types.d.ts declare enum EActionPayloadType { request = "request", progress = "progress", result = "result", stream = "stream", push = "push" } interface IActionPayload_Data_Base { time: number; } interface IActionPayload_Base

extends IActionBase, IActionPayload_Data_Base { readonly type: DT; readonly context: ActionContext; } type IActionRouteItemHandler = IActionHandler_Local_Json | (IActionHandler_Peer_Json & { transShape: ETransportShape$1; transOrd: number; transInfo?: ITransportRouteInfo; }); /** * [ ] * [ ACTION PAYLOAD TYPES ] * [ ] */ /** * * [ RESULT ] * */ /** * The outcome of an action. * * - `ok: true` — the action produced `output`. * - `ok: false; expected: true` — the action failed with one of the errors it * declared via `.throws()`; `error` is narrowed to that declared union. * - `ok: false; expected: false` — any other failure (a `NiceError` from a domain * the action didn't declare, or a wrapped foreign throw). Inspect * `error.isUnhandled` to tell those two apart. * * Structurally a superset of `nice-error`'s `TNiceResult`, so `niceTry` composes. */ type TActionResultOutcome> = { ok: true; output: OUT; } | { ok: false; expected: true; error: DECLARED; } | { ok: false; expected: false; error: NiceError; }; /** * Wire form of {@link TActionResultOutcome}: the `error` is the serialized * `INiceErrorJsonObject` (never a live `NiceError` instance), so the frame is plain * data for any transport — `JSON.stringify` *and* binary codecs (msgpackr) alike. * `expected` is carried for inspection but is re-derived against the receiver's own * schema on hydrate, never trusted from the wire. */ type TActionResultOutcome_JsonObject = { ok: true; output: OUT_SERDE; } | { ok: false; expected: boolean; error: INiceErrorJsonObject; }; interface IActionPayload_Result extends IActionPayload_Base { readonly result: TActionResultOutcome["Output"], TInferActionError>; } /** * * [ PROGRESS ] * */ declare enum EActionProgressType { none = "none", percentage = "percentage", custom = "custom" } interface IActionProgress_None { type: EActionProgressType.none; } interface IActionProgress_Percentage { type: EActionProgressType.percentage; progress: number; message?: string; } interface IActionProgress_Custom { type: EActionProgressType.custom; data: any; } type TActionProgress = IActionProgress_None | IActionProgress_Percentage | IActionProgress_Custom; interface IActionPayload_Progress extends IActionPayload_Base { readonly progress: TActionProgress; } /** * * [ ] * [ Wire JSON types ] * [ ] * */ interface IActionPayload_Base_JsonObject
extends IActionBase_JsonObject { type: DT; context: IActionContext_Data_JsonObject; time: number; } interface IActionPayload_Request_JsonObject extends IActionPayload_Base_JsonObject { type: EActionPayloadType.request; input: TInferInputFromSchema["SerdeInput"]; inputHash: string; } interface IActionPayload_Progress_JsonObject extends IActionPayload_Base_JsonObject { type: EActionPayloadType.progress; progress: TActionProgress; } interface IActionPayload_Result_JsonObject extends IActionPayload_Base_JsonObject { type: EActionPayloadType.result; result: TActionResultOutcome_JsonObject["SerdeOutput"]>; outputHash: string; } /** * * [ ] * [ COMBINED TYPES ] * [ ] * */ type TActionPayload_Any_Instance = ActionPayload_Request | ActionPayload_Result | ActionPayload_Progress; type TActionPayload_Any_JsonObject = IActionPayload_Request_JsonObject | IActionPayload_Progress_JsonObject | IActionPayload_Result_JsonObject; //#endregion //#region src/ActionRuntime/Handler/ActionHandler.types.d.ts declare enum EActionHandlerType { peer = "peer", local = "local" } interface IActionHandler_Json { type: T; } interface IActionHandler_Peer_Json extends IActionHandler_Json { client: IRuntimeCoordinate; } interface IActionHandler_Local_Json extends IActionHandler_Json {} type TActionHandler_Json = IActionHandler_Local_Json | IActionHandler_Peer_Json; interface IHandleActionOptions { timeout?: number; targetPeer?: RuntimeCoordinate; targetLocalRuntime?: ActionRuntime; /** * Reliable delivery (E3): an app-chosen key scoping an **independent** ordered reliable stream of this * action (e.g. one per room id), instead of the single `(peer, action)` stream. Only meaningful for a * `.reliable()` action; ignored otherwise. Absent → the default single stream (unchanged). */ streamKey?: string; } interface IExecuteActionOptions extends IHandleActionOptions { listeners?: TRunningActionUpdateListener[]; } interface IActionHandler_Base { cuid: string; handlerType: T; getActionRouter: () => ActionRouter$1; } /** * * LOCAL ACTION HANDLER * */ interface IHandleActionOptions_Local extends IHandleActionOptions {} interface IActionHandler_Local extends IActionHandler_Base { handleActionRequest: (action: ActionPayload_Request, config?: IHandleActionOptions_Local) => Promise>; } /** * * PEER-LINK ACTION HANDLER * */ interface IHandleActionOptions_Peer extends IHandleActionOptions {} interface IActionHandler_Peer extends IActionHandler_Base { peerClient: RuntimeCoordinate; handleActionRequest: (action: ActionPayload_Request, config?: IHandleActionOptions_Peer) => Promise>; _setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject) => void): void; } /** * * COMBINED * */ type TActionHandler = IActionHandler_Local | IActionHandler_Peer; //#endregion //#region src/ActionDefinition/Action/Payload/ActionPayload_Request.d.ts declare class ActionPayload_Request extends ActionPayload { readonly input: TInferInputFromSchema["Input"]; readonly inputHash: string; _callSite?: string; constructor(params: { context: ActionContext; }, input: TInferInputFromSchema["Input"], data: IActionPayload_Data_Base); successResult(...args: [TInferOutputFromSchema["Output"]] extends [never] ? [] | [output: TInferOutputFromSchema["Output"]] : [output: TInferOutputFromSchema["Output"]]): ActionPayload_Result; /** * Build a failed result from any `NiceError`. The result's `expected` flag is * derived from the action's `.throws()` declarations during construction, so * declared errors surface as `expected: true` (typed) and everything else as * `expected: false`. */ errorResult(err: NiceError): ActionPayload_Result; progress(progress: TActionProgress): ActionPayload_Progress; toJsonObject(): IActionPayload_Request_JsonObject; toJsonString(): string; runToOutput(options?: IExecuteActionOptions): Promise["Output"]>; runToResultPayload(options?: IExecuteActionOptions): Promise>; /** * Run and resolve to the bare result outcome (`{ ok } | { ok:false; expected; error }`) * — the ergonomic way to handle expected vs unexpected errors without throwing. */ runToResult(options?: IExecuteActionOptions): Promise["result"]>; run(options?: IExecuteActionOptions): Promise>; } //#endregion //#region src/ActionDefinition/Action/Core/ActionCore.d.ts declare class ActionCore extends ActionBase implements IActionCore { readonly _domain: ActionDomain; readonly form = EActionForm.core; constructor(_domain: ActionDomain, id: ID); is>(action: ACT | unknown | null | undefined): action is TNarrowActionType; /** * Type-guard for the throw-style path (`runToOutput` rethrows on failure): * narrows a caught value to this action's declared error union when it is one * the action declared via `.throws()`. Everything else (foreign throws, * undeclared `NiceError`s) returns `false`. */ isExpectedError(error: unknown): error is TInferActionError; toJsonObject(): IActionBase_JsonObject; request(...args: [TInferInputFromSchema["Input"]] extends [never] ? [input?: never] : [input: TInferInputFromSchema["Input"]]): ActionPayload_Request; deserializeInput(serialized: TInferInputFromSchema["SerdeInput"]): TInferInputFromSchema["Input"]; serializeInput(raw: TInferInputFromSchema["Input"]): TInferInputFromSchema["SerdeInput"]; validateInput(input: unknown): TInferInputFromSchema["Input"]; validateOutput(output: unknown): TInferOutputFromSchema["Output"]; } //#endregion //#region src/ActionRuntime/ActionRuntime.d.ts declare class ActionRuntime { private _coordinate; readonly timeCreated: number; readonly runtimeInfo: IRuntimeMeta; private readonly actionRouter; private readonly _pendingRunningActions; private readonly _registeredPeerHandlers; private _applied; static getDefault(): ActionRuntime; constructor(coordinate: RuntimeCoordinate); get coordinate(): RuntimeCoordinate; specifyRuntimeCoordinate(specifics: IRuntimeCoordinateSpecifics & { envId?: string; }): void; registerRunningAction(ra: RunningAction): void; resolveIncomingActionPayload(json: TActionPayload_Any_JsonObject): void; /** * Handle an incoming action wire (e.g. from a transport layer), route it to * the correct handler, and return the response. The most specific handler * match is chosen (action-ID-specific beats domain-wildcard). */ handleActionPayloadWire(wire: TActionPayload_Any_JsonObject): Promise>; handleActionPayloadWire(wire: unknown): Promise>; /** * The declared {@link EActionResponseMode} for a wire frame's action, looked up from its registered * domain — `undefined` if the route isn't registered. Lets a transport decide, without executing the * action, whether a reply will come back (so e.g. the reliable acceptor knows whether an ack must ride a * standalone control frame vs. piggyback a reply). */ responseModeForWire(wire: TActionPayload_Any_JsonObject): EActionResponseMode | undefined; /** * The declared {@link EReliabilityTier} for a wire frame's action, looked up from its registered domain — * `undefined` if the route isn't registered. Lets the reliable acceptor route a `persisted` stream through * its persisted store (vs. the in-memory inbox for the `session` tier) without any wire flag: the tier is * derived from the shared `domain:id`, exactly as the connector derives it to opt a send into the outbox. */ reliabilityTierForWire(wire: TActionPayload_Any_JsonObject): EReliabilityTier | undefined; /** * {@link reliabilityTierForWire} by bare `domain:id` route — for callers holding only a route, e.g. the * reliable acceptor resolving which receive store an `rskip` control message (no action payload) applies to. */ reliabilityTierForRoute(domain: string, id: string): EReliabilityTier | undefined; handleActionPayload(action: TActionPayload_Any_Instance, options?: Omit): Promise>; /** * @internal * * Return the first handler registered for the given action, or `undefined` * if none has been registered (action-ID-specific beats domain-wildcard). */ _getHandlerForAction>(action: ACT, options?: Omit): TActionHandler | undefined; getHandlerForActionOrThrow>(action: ACT, options?: Omit): TActionHandler; /** * Register one or more handlers. Each handler's own `actionRouter` defines * which domains/actions it handles — those routing keys are mirrored into * this runtime's router so the same action can be served by multiple handlers. * Duplicate registrations (same handler cuid for the same key) are skipped. */ addHandlers(handlers: TActionRuntimeHandler[]): this; /** * @internal Low-level primitive — the public way to open a connection is `connectChannel`, which * derives routing from a channel and binds the crypto identity for you. This stays as the raw building * block it sits on (it restates domain lists by hand) and is not part of the supported surface. * * Declare an external "backend client" in one call: build an * {@link ChannelConnector} for `externalCoordinate` carrying the given * `transports`, route the listed `domains`/`actions` to it, register it (plus any * `localHandlers` — e.g. server→client push handlers that share the same channel) * on this runtime, and `apply()`. Returns the external handler so the caller can * later `clearTransportCache()` it. */ connectTo(externalCoordinate: RuntimeCoordinate, options: { transports: Transport[]; domains?: ActionDomain[]; actions?: ActionCore[]; localHandlers?: TActionRuntimeHandler[]; defaultTimeout?: number; reliableActionTimeout?: number; wireMux?: import("@nice-code/wire").WireProtocolMux; /** The negotiated security level frame protocols ride at (PLAN-security Phase 4.1). */ securityLevel?: import("@nice-code/wire").ESecurityLevel; /** Auto-redial a protocol-carrying (realm) duplex link on drop (DESYNC F6 / Phase 8b). */ keepLinkAlive?: boolean; }): ChannelConnector; private applyRuntimeForDomain; /** * Register this runtime with all root domains covered by its currently-added handlers, * making it eligible to execute actions dispatched from those domains. * After apply() is called, any subsequent addHandlers() calls also auto-register. */ apply(): this; /** * Find the best registered external handler that can reach `originClient` directly. * Used to locate the return-path channel for dispatching results back to the action origin. * Returns `undefined` if no handler matches (score > 0 required, i.e. at least id must match). * * A handler that currently holds the origin's *live* connection always wins, regardless of its * coordinate score — owning the live socket bound to the origin's exact coordinate (set from the * handshake) is a strictly more precise match than any env-level `peerClient` score. This lets one * server accept clients of *several* envs over a single acceptor (a multi-role Durable Object): the * result/push routes back over the carrier the client actually connected on even when the handler's * `clientEnv` is unset or names a different env. Only when no handler owns a live connection do we fall * back to the plain best-coordinate-score pick (the offline-return and connector-only cases). */ getReturnHandlerForOrigin(originClient: RuntimeCoordinate): PeerLink | undefined; resetRuntime(): void; private _trySetupReturnDispatch; } //#endregion //#region src/ActionDefinition/Domain/ActionDomain.d.ts type TActionMap = { [K in keyof ACT_DOM["actionSchema"] & string]: ActionCore }; declare class ActionDomain extends ActionDomainBase { private _rootDomain; private readonly _actionMap; constructor(definition: ACT_DOM, { rootDomain }: { rootDomain: ActionRootDomain; }); get rootDomain(): ActionRootDomain; /** * @internal * All action observers that should see actions on this domain: the root domain's * observers plus this subdomain's own. Mirrors the listener set the local-dispatch * path assembles in `runAction`/`_runAction`, so inbound actions (pushed from a * backend or another client) can be wired up identically and surface in devtools. */ _collectActionObservers(): TRunningActionUpdateListener[]; _registerRuntime(runtime: ActionRuntime): void; createChildDomain(subDomainDef: SUB_DOM & { [K in Exclude]: never }): ActionDomain>; get action(): TActionMap; actionsMap(): TActionMap; actionForId(id: ID): ActionCore; wrapAsPartialLocalHandler(wrappedActionExecutor: Partial>): ActionLocalHandler; wrapAsLocalHandler(wrappedActionExecutor: TWrappableDomainActionHandler): ActionLocalHandler; hydrateContext(id: ID, contextData: IActionContext_Data_JsonObject): ActionContext; isDomainAction>(action: ACT | unknown | null | undefined): action is TDistributedDomainActions; hydrateRequestPayload>(serialized: P): TDistributeActionPayload_Request; hydrateResultPayload>(serialized: R): TDistributeActionPayload_Result; hydrateAnyAction>(actionJson: AJ): TNarrowActionJsonTypeToActionInstanceType; runAction>(request: ACT, options?: IExecuteActionOptions): Promise>; private createActionMap; } //#endregion //#region src/ActionDefinition/Action/ActionBase.types.d.ts declare enum EActionForm { core = "core", context = "context", data = "data" } interface INiceActionIdAndDomain { domain: DOM["domain"]; id: keyof DOM["actionSchema"] & string; } interface IActionBase extends INiceActionIdAndDomain { id: ID; form: FORM; _domain: ActionDomain; allDomains: DOM["allDomains"]; schema: DOM["actionSchema"][ID]; } interface IActionBase_JsonObject { form: FORM; domain: DOM["domain"]; allDomains: DOM["allDomains"]; id: ID; } //#endregion //#region src/ActionDefinition/Action/Action.combined.types.d.ts /** * Distributes a union ID into a proper discriminated union of ActionPayload_Request instances, * so that narrowing on `.id` also narrows `.input`. */ type TDistributeActionPayload_Request = ID extends keyof DOM["actionSchema"] & string ? ActionPayload_Request : never; /** * Distributes a union ID into a proper discriminated union of ActionPayload_Result instances, * so that narrowing on `.id` also narrows `.result`. */ type TDistributeActionPayload_Result = ID extends keyof DOM["actionSchema"] & string ? ActionPayload_Result : never; /** * * COMBINED JSON TYPES * */ type TAction_Any_JsonObject = IActionCore_JsonObject | TActionPayload_Any_JsonObject | IActionContext_JsonObject; /** * * UTILITY TYPES * */ type TDistributedDomainActions> = { [ID in keyof DOM["actionSchema"] & string]: TNarrowActionType }[keyof DOM["actionSchema"] & string]; type TNarrowActionType, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ACT extends ActionPayload_Result ? ActionPayload_Result : ACT extends ActionPayload_Request ? ActionPayload_Request : ACT extends ActionPayload_Progress ? ActionPayload_Progress : ACT extends ActionCore ? ActionCore : never; type TNarrowActionJsonTypeToActionInstanceType, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ACT extends IActionPayload_Request_JsonObject ? ActionPayload_Request : ACT extends IActionPayload_Result_JsonObject ? ActionPayload_Result : ACT extends IActionPayload_Progress_JsonObject ? ActionPayload_Progress : ACT extends IActionCore_JsonObject ? ActionCore : never; //#endregion //#region src/ActionRuntime/Handler/PeerLink/Acceptor/ChannelAcceptor.d.ts /** The codec shape `ChannelAcceptor` uses to pack/unpack frames — same as the Link transport's. */ type TActionChannelFormatMessage = IActionWireFormat; /** How a connection encodes its frames, remembered so we answer each client in its own dialect. */ type TActionConnectionEncoding = "json" | "binary"; /** * A connection's restorable identity — since plan Phase 4 this is wire's versioned * {@link IWireConnectionBinding} (`v: 1`; client coordinate + secure-session state + advertised * protocol ids owned by wire, with the lane's own facts in the opaque `lane` slot — see * {@link IAcceptorLaneBindingState}). Persisted attachments from before the versioned schema are * ignored on rehydrate (the socket is treated as fresh). */ type IAcceptorConnectionBinding = IWireConnectionBinding; /** * An acceptor-side frame protocol (M1 multiplex seam, realm plan §3) — since plan Phase 4 this is * wire's {@link IWireAcceptorProtocol}: the server half of a protocol riding the same connections * as action frames. Register via {@link ChannelAcceptor.registerFrameProtocol} (or the underlying * `WireAcceptor` directly); the wire acceptor dispatches reserved-prefix frames (`0x01`–`0x0F`) * per connection, advertises `proto:` in its handshake welcome, gates traffic on the * connection's security level (review A.6 — set `allowPlain` for a deliberately plain endpoint), * and fires attach/detach around the connection lifecycle (including hibernation resume). */ type IAcceptorFrameProtocol = IWireAcceptorProtocol; /** * Server-side secure-channel config. When set, each connection negotiates a level from * {@link securityLevel}: an `authenticated`/`encrypted` client must complete the handshake (and is then * bound to its *authenticated* coordinate) before any action frame is accepted. A `none` client (only * when `none` is in the allowed set) is accepted as-is with a self-asserted identity. For the * `encrypted` level the codec source should be a session factory (`createFormatMessage`). */ interface IAcceptorSecurity { /** * Accepted level(s). A single level is strict; an array is a negotiable allowed set — the server * adopts whichever level each client requests (e.g. `[none, authenticated, encrypted]` serves all * three over one endpoint). */ securityLevel: ESecurityLevel | readonly ESecurityLevel[]; /** This server's crypto identity (verify + exchange key pairs, optionally persisted). */ link: ClientCryptoKeyLink; /** This server's coordinate — its identity to clients during the handshake. */ localCoordinate: IRuntimeCoordinate; /** Wire dictionary version — a fixed string (single channel), or a resolver composing it from the * `hello.channels` tags (multi-channel). The handshake rejects on a mismatch / unknown channel. */ dictionaryVersion: string | TServerDictionaryVersionResolver; /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */ verifyKeyResolver?: IClientVerifyKeyResolver; } interface IChannelAcceptorBaseOptions { /** * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`), * scored against an action's `originClient` to pick this handler when *no* handler holds the client's * live connection (the offline-return fallback). A handler that currently owns the live socket always * wins regardless, so this is optional: omit it for a multi-role server that accepts several client envs * over one acceptor — it then defaults to `RuntimeCoordinate.unknown` (scores 0 against every client). */ clientEnv?: RuntimeCoordinate; /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */ send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void; /** * The runtime this handler belongs to. When set, {@link ChannelAcceptor.broadcast} can be called * without threading a runtime through each call. Optional — `pushToClient` still takes one explicitly. */ runtime?: ActionRuntime; /** Timeout (ms) applied to server-initiated actions awaiting a client response. */ defaultTimeout?: number; /** * Called once when a connection is first bound to a client identity. Use it to persist the binding * for transports that can resume after eviction — e.g. a Durable Object's hibernatable WebSocket: * `(ws, binding) => ws.serializeAttachment(binding)` — then replay it via {@link ChannelAcceptor.rehydrate} * when the channel comes back. */ onConnectionBound?: (connection: TConn, binding: IAcceptorConnectionBinding) => void; /** * Enable the authenticated (optionally encrypted) handshake. When omitted, connections are trusted * as-is (identity self-asserted) — fine for dev / trusted networks. */ security?: IAcceptorSecurity; /** Optional server-side logger — called per inbound action request with its served outcome. */ logger?: IActionServeLogger; /** * The server's wire-observation seam — the mirror of `connectChannel({ wireTap })`. Every frame * crossing a carrier is reported with its true wire byte size (post-encryption) and its lane * (`handshake` / `keepalive` / `action` / a protocol id such as `realm`), tagged with the bound * client as `linkId`. Sizes only, never payload contents. */ wireTap?: import("@nice-code/wire").TWireTapFn; /** Short carrier-kind label surfaced to the logger as the request's transport (e.g. `"ws"`). */ transportLabel?: string; /** * Persisted receive store for the **persisted** reliability tier (`.reliable({ persist: true })`). When set, * a frame whose action declares the `persisted` tier is deduped through this store (e.g. a `ReliableLog` over * a Durable Object's SQL) instead of the in-memory inbox — so its high-water survives eviction and a * replayed stream dedups rather than redelivering. Omit it and persisted-tier streams degrade gracefully to * the in-memory (session) behavior. `session`-tier streams always use the in-memory inbox. * * On the high-level serve surface this is the `reliableStore` option (`serveChannel` / * `serveDurableObject`) — one store, two entry points; both names describe the same * `IReliableReceiver`. */ persistedReceiver?: IReliableReceiver>; /** * Max distinct **keyed** (`streamKey`, E3) reliable streams tracked per bound client (default 256). * Every keyed frame mints receiver state named by a client-chosen string, so without a cap one client * could allocate unbounded server-side stream state; past the cap a new key's frames are served * best-effort (delivered, but no ordering/dedup state is created) with a one-time warning. */ maxKeyedStreamsPerClient?: number; } /** * Multi-channel codec selection: resolve a connection's advertised channel tags (`hello.channels`) into the * codec factory it should use, so one acceptor can serve several channels and pick each connection's codec * from the subset it connected. Returns `undefined` only for an unknown/unserved set (the handshake already * rejects those); given no tags it returns the default (single/combined) channel's factory. */ type TAcceptorResolveCodec = (tags: readonly string[] | undefined) => (() => TActionChannelFormatMessage) | undefined; /** * Provide exactly one codec source: * - `formatMessage` — a single shared codec for every connection (stateless, e.g. `createBinaryWireAdapter`). * - `createFormatMessage` — a per-connection factory for stateful codecs (e.g. * `createBinaryWireSessionFactory`, whose sessions hold correlation + identity state). Required for the * leanest binary wire; the handler creates and caches one codec per connection. */ type IChannelAcceptorOptions = IChannelAcceptorBaseOptions & ({ formatMessage: TActionChannelFormatMessage; createFormatMessage?: never; resolveCodec?: never; } | { createFormatMessage: () => TActionChannelFormatMessage; formatMessage?: never; resolveCodec?: never; } | { resolveCodec: TAcceptorResolveCodec; formatMessage?: never; createFormatMessage?: never; }); /** * A connection-aware execution case (see {@link ChannelAcceptor.forConnectionDomainCases}). It receives * the primed request plus a per-invocation `context` — whatever the wiring's context mapper produces from * the originating connection. The low-level handler passes the raw connection (`TConn | undefined`); the * higher-level `serveChannel` enriches it into an `IConnectionContext` (state + broadcast + pushBack). A * case may return the action's raw output, a result payload, or nothing (auto-wrapped as an empty * success) — exactly like a local handler case. */ type TAcceptorCaseFn = (action: TDistributeActionPayload_Request, context: TCtx) => ReturnType> | void; /** * The connection-aware case the bare {@link ChannelAcceptor} serves: its `context` is the originating * client's live connection (resolved from the request's `originClient`, `undefined` if the socket is * gone). It's {@link TAcceptorCaseFn} fixed to `TConn | undefined` — the un-enriched shape used by * {@link ChannelAcceptor.forConnectionDomainCases} and `acceptChannelConnections`. */ type TAcceptorConnectionCaseFn = TAcceptorCaseFn; /** * Server-side handler for backends that accept many client connections over a single open channel * (WebSockets, Durable Objects, …). It is transport-agnostic: you feed it inbound frames with * {@link receive} and tell it how to write outbound frames via the `send` option. * * Since plan Phase 4 this class is the **action lane** over a wire-owned {@link WireAcceptor} * (reachable as {@link wireAcceptor}): the backbone — handshake accept + secure sessions, security * levels, the client-identity registry, frame-protocol registration/dispatch/lifecycle, versioned * binding persistence + rehydrate, and keepalive — lives in wire; this handler keeps the action * half — codec negotiation, reliable receive (inbox/dedup/acks), request execution routing, and * the results/pushes going back out. * * Add it alongside your local execution handler: * ```ts * const serverHandler = createChannelAcceptor({ clientEnv, formatMessage, send: (ws, f) => ws.send(f) }); * runtime.addHandlers([localHandler, serverHandler]); * // per inbound message (e.g. a Durable Object's webSocketMessage): * serverHandler.receive(ws, message); * ``` * * Inbound requests route to your local handler; the runtime's return dispatch then calls this * handler back (it is an external handler keyed to `clientEnv`) to send the result to the originating * connection. It registers an empty action router, so it is never chosen to *execute* an inbound * request — only to ferry results/pushes back out. */ declare class ChannelAcceptor extends PeerLink { /** Accept-in over a live (duplex) connection registry — it pushes results/broadcasts to bound sockets. */ readonly canPush = true; /** The wire-owned acceptor backbone this lane composes on (plan Phase 4). */ readonly wireAcceptor: WireAcceptor; private readonly _formatMessage?; private readonly _createFormatMessage?; private readonly _resolveCodec?; private readonly _runtime?; private readonly _serverTimeout; private readonly _connEncoding; private readonly _codecByConn; /** The channel tags each connection negotiated — drives its `resolveCodec` selection. */ private readonly _connTags; private readonly _inbox; private readonly _persistedReceiver?; private readonly _replyStreamKeys; private readonly _keyedStreamsByClient; private readonly _keyedCapWarned; private readonly _maxKeyedStreams; private readonly _resyncRequested; private readonly _logger?; private readonly _transportLabel; private readonly _pendingReports; constructor(options: IChannelAcceptorOptions); /** The lane's opaque binding slot for a connection — persisted by the wire acceptor. */ private _laneBindingState; /** * Admit (or refuse) a **keyed** reliable stream for a client — the allocation bound on receiver stream * state, since keys are client-chosen strings. Past the cap the caller serves the frame best-effort * (delivered, no state) and this warns once per client. */ private _admitKeyedStream; /** * Log an inbound request (basic action/transport/origin facts) before it executes, stashing the returned * reporter by `cuid` so {@link _reportServed} can pair it with the result. Only requests are logged; * result/progress frames replying to *our* pushes are not. */ private _logRequestIn; /** Report a served result back to the reporter stashed by {@link _logRequestIn} (no-op if none/not a result). */ private _reportServed; /** * The codec for a connection: a per-connection session (cached) when a factory was provided, else * the single shared `formatMessage`. */ private _codecFor; /** * Register (or replace) the connection-bound persistence callback after construction. Used by * lifecycle helpers like {@link createHibernatableWsServerAdapter} so persistence and replay are * owned by one place instead of being split across the constructor options. */ setOnConnectionBound(onConnectionBound: (connection: TConn, binding: IAcceptorConnectionBinding) => void): void; /** * Register a frame protocol (M1 multiplex seam) on the wire acceptor: every connection can then * exchange the protocol's prefixed frames beside action frames (subject to the wire security * gate, review A.6). Register before serving; protocols are advertised in the handshake welcome. */ registerFrameProtocol(protocol: IAcceptorFrameProtocol): void; /** * Feed one inbound frame from a connection into the wire acceptor. Prefixed protocol frames and * the handshake/keepalive are handled there; everything else — the lane's namespace — comes back * through {@link _onLaneFrame} to be decoded, identity-bound, and routed (requests execute * locally; results/progress resolve pending server-initiated actions). */ receive(connection: TConn, frame: string | ArrayBuffer | Uint8Array): void; /** One inbound lane frame (already decrypted), with the connection's negotiated level. */ private _onLaneFrame; /** * Route a decoded inbound frame to the runtime. A best-effort frame is logged + emitted as before. A * **reliable** request (its frame carries a `seq`) goes through the {@link ReliableInbox}: it's * dispatched only in contiguous order (out-of-order frames buffer), duplicates are suppressed, and a * duplicate/gap sends a standalone ack so the client's outbox drains. `frame` is the raw (decrypted) * encoded bytes the codec re-reads for the reliability slot. */ private _dispatchInbound; /** Ordered/dedup dispatch of one reliable request via the inbox (see {@link _dispatchInbound}). */ private _dispatchReliableRequest; /** * Stamp the receiver-side reliability facts onto a delivered frame's **local** context (the * `originClient`-overwrite pattern) so the executing handler reads them as `action.context.reliability` * — the free idempotency key. Local-only: `toJsonObject()` never writes the field, so a reply built off * this context is byte-identical with and without the stamp. */ private _stampHandledReliability; /** * Log + emit each frame the receiver decided to deliver in this receive. Only the **first** (the frame * actually received this call) carries the reliability facts for the *logger*; drained buffered frames * were logged/seq'd on their own earlier arrivals, so they log without a (stale) seq. Every delivered * frame is stamped with its **own** seq for the executing handler, though — delivered frames are a * contiguous run ending at the new high-water (`ack`), so frame `i` carries `ack - (length - 1 - i)`. */ private _emitReliableDelivered; /** * The receive store a reliable frame's stream is served by: the persisted {@link _persistedReceiver} for a * `persisted`-tier action (when one is configured), else the in-memory {@link _inbox}. A persisted action with * no configured store degrades to the inbox (session-tier behavior) rather than failing. */ private _receiverForWire; /** {@link _receiverForWire} by bare route — for control messages, which carry no action payload. */ private _receiverForRoute; /** * A sender-originated transport control message arrived on a connection. `rskip` and `rclose` are the * meaningful ones server-side (`rack`/`rsync` flow the other way — a decoded one here is simply * ignored). `rskip`: the client's outbox **abandoned** every undelivered frame `<= seq` of the stream * (their actions aborted / delivery deadlines expired), so advance the stream past the abandoned seqs * and deliver whatever was buffered behind the gap — the stream continues instead of wedging on frames * that will never arrive; the new high-water is re-acked so the client can retire the skip. `rclose`: * the client closed the stream for good — release its receiver state (a pure reclaim). */ private _onControlMessage; /** How long a stashed reply streamKey survives without its reply being sent (sweep bound). */ private static readonly _REPLY_STREAMKEY_TTL_MS; /** Remember a dispatched keyed reply-carrying request's streamKey until its reply goes out. */ private _stashReplyStreamKey; /** Encode + send a transport {@link TControlMessage} to a connection (through its secure session if any). */ private _sendControl; /** * Ensure an inbound request carries the client's identity and that this connection is bound to it, * so its result can be routed back. A session codec omits `originClient` after the first request, so * when it's missing we restore it from the (possibly rehydrated) binding instead. (Plain mode only; * secure mode binds the authenticated coordinate at handshake time.) */ private _resolveRequestIdentity; /** * Restore a connection→client binding without an inbound frame — for transports that resume after * eviction. Pair it with the {@link IChannelAcceptorOptions.onConnectionBound} hook: persist * the binding there, then replay each live connection here when the channel comes back (e.g. a * Durable Object iterating `ctx.getWebSockets()` as it wakes from hibernation). A binding persisted * before the versioned (`v: 1`) schema is ignored — the socket is treated as fresh. */ rehydrate(connection: TConn, binding: IAcceptorConnectionBinding): void; toJsonObject(): IActionHandler_Peer_Json; toHandlerRouteItem(): IActionRouteItemHandler; /** Forget a connection (call on socket close) so stale entries don't misroute later results. */ drop(connection: TConn): void; /** Permanently quiesce this acceptor and detach every live/rehydrated connection. */ dispose(): void; /** Lane cleanup when the wire acceptor drops a connection (fired with the bound client, if any). */ private _onConnectionDropped; /** Live connection for a client coordinate, if currently registered. */ getConnectionForClient(client: RuntimeCoordinate): TConn | undefined; /** This acceptor owns the origin's return path when it currently holds a live connection bound to it. */ ownsLiveConnectionFor(origin: RuntimeCoordinate): boolean; /** Whether this acceptor currently tracks `connection` — used to pick the owning handler among several. */ hasConnection(connection: TConn): boolean; /** * Send (and optionally await) a server-initiated action to a specific connected client. Pass the * connection token directly (e.g. the `ws`) or a client `RuntimeCoordinate` to look one up. */ pushToClient(runtime: ActionRuntime, target: TConn | RuntimeCoordinate, request: ActionPayload_Request, options?: { timeout?: number; }): RunningAction; /** * Build a local handler whose cases are connection-aware: each case receives the primed request and * the originating client's live connection (resolved from `originClient`), so handlers don't repeat * the `getConnectionForClient(action.context.originClient)` lookup. Cases may return raw output or * nothing, just like {@link ActionLocalHandler.forDomainActionCases}. Add the returned handler to the * runtime alongside this server handler: * ```ts * runtime.addHandlers([serverHandler.forConnectionDomainCases(domain, { … }), serverHandler]); * ``` */ forConnectionDomainCases(domain: ActionDomain, cases: { [ID in keyof FOR_DOM["actionSchema"] & string]?: TAcceptorConnectionCaseFn }): ActionLocalHandler; /** * Like {@link forConnectionDomainCases} but spanning several domains with one merged case map — used * by channel-derived wiring (`acceptChannelConnections` / `serveChannel`) where the channel's * `toAcceptor` domains are served together. Each domain takes only the cases whose ids it owns, so a * single map can cover several domains and unrelated ids are ignored. * * `mapContext` turns the resolved connection into whatever the case's second argument should be: the * raw connection for the low-level helper, or an enriched `IConnectionContext` for `serveChannel`. It's * called once per inbound action, after the originating connection is resolved. */ forConnectionDomainCasesMulti(domains: readonly ActionDomain[], cases: Record | undefined>, mapContext: (connection: TConn | undefined, request: ActionPayload_Request) => TCtx): ActionLocalHandler; /** * Fan a server-initiated request out to every currently-bound connection. A fresh request is built * per connection (each push mutates its own action context) and dispatched fire-and-forget. Pass * `except` to skip the originating socket and `where` to filter by connection (e.g. read its * attachment for a role). Iterating bound connections (rather than every accepted socket) skips * sockets that are still mid-handshake and so can't yet receive a frame. */ broadcast(makeRequest: () => ActionPayload_Request, options?: { runtime?: ActionRuntime; except?: TConn | null; where?: (connection: TConn) => boolean; timeout?: number; onError?: (error: unknown, connection: TConn) => void; }): void; sendReturnPayload(payload: TActionPayload_Any_Instance, config: { targetLocalRuntime: ActionRuntime; }): Promise; handleActionRequest(action: ActionPayload_Request, config?: IHandleActionOptions): Promise>; private _dispatch; /** Encode + send one payload; returns the encoded frame's size so a logger can report it. */ private _sendPayload; private _resolveConnection; private _resolveSingleConnection; } declare const createChannelAcceptor: (options: IChannelAcceptorOptions) => ChannelAcceptor; //#endregion export { isActionPayload_Request_JsonObject as $, ISecureClientConfig as $n, ActionPayload_Result as $r, IDuplexAcceptorCarrier as $t, TActionProgress as A, IDuplexCarrierSource as An, IActionServeLogger as Ar, inMemoryCarrier$1 as At, IRuntimeCoordinate$1 as B, ITransportConnectionContext as Bn, ActionRootDomain as Br, IRouteContext as Bt, IActionPayload_Result_JsonObject as C, TActionSerializationDefinition as Ci, connectChannel as Cn, TTransportRouteParams as Cr, IWsAcceptorCarrierOptions as Ct, IActionRouteItemHandler as D, createBinaryWireSessionFactory as Dn, TransportConnection$1 as Dr, IRtcCarrierOptions as Dt, IActionProgress_Percentage as E, IBinaryWireSessionOptions as En, TUpdateActionRunConfig as Er, err_nice_transport_ws as Et, IClientVerifyKeyResolveInput as F, ChannelConnector as Fn, createDefaultServeLogger as Fr, actionRouter as Ft, TRuntimeCoordinateStringId$1 as G, IActionTransportDef as Gn, IRunningActionUpdate_Abort as Gr, IChannelServer as Gt, IRuntimeFullCoordinates as H, PeerLink as Hn, ERunningActionFinishedType as Hr, IChannelHostAdapter as Ht, IClientVerifyKeyResolver$1 as I, createChannelConnector as In, ActionLocalHandler as Ir, IFetchHandler as It, createInMemoryTofuVerifyKeyResolver$1 as J, IActionTransportReadyData_Base as Jn, IRunningActionUpdate_Started as Jr, IServeConnectionStateOptions as Jt, classifyConnectFailure as K, IActionTransportInitialized as Kn, IRunningActionUpdate_Progress as Kr, IConnectionContext as Kt, IInMemoryChannelPair as L, IReliableStreamPressure as Ln, createLocalHandler as Lr, IForwardContext as Lt, TActionResultOutcome_JsonObject as M, IExchangeCarrierSource as Mn, IActionServeResultInfo as Mr, ActionRouter as Mt, ESecurityLevel$1 as N, TCarrier as Nn, IDefaultServeLoggerOptions as Nr, IActionRouterOptions as Nt, TActionPayload_Any_Instance as O, IActionWireFormat as On, ISecureChannelAcceptorOptions as Or, rtcCarrier$1 as Ot, EWireConnectFailureKind$1 as P, TFrame as Pn, TActionServeResultReporter as Pr, TRoutable as Pt, isActionPayload_Result_JsonObject as Q, IFrameReliabilityWire$1 as Qn, TRunningActionUpdateListener as Qr, IAcceptorAttachmentStore as Qt, IInMemoryServerEndpoint$1 as R, TLinkEvent as Rn, MaybePromise as Rr, IForwardTarget as Rt, IActionPayload_Result as S, TActionSchemaOptions as Si, combineChannels as Sn, TTransportInitializationFinishedInfo as Sr, wsCarrier$1 as St, IActionProgress_None as T, TTransportedValue as Ti, defineChannel as Tn, TTransportStatusInfo_GetTransport_Output$1 as Tr, EErrId_NiceTransport_WebSocket as Tt, RuntimeCoordinate$1 as U, ETransportShape$1 as Un, ERunningActionState as Ur, TServeHostOptions as Ut, IRuntimeCoordinateSpecifics$1 as V, Transport as Vn, RunningAction as Vr, forwardTo as Vt, TRuntimeCoordinateEnvId as W, ETransportStatus as Wn, ERunningActionUpdateType as Wr, serveHost as Wt, rtcDataChannelByteChannel as X, IActionTransportResolvers as Xn, TRunningActionUpdate as Xr, serveChannel as Xt, createStorageTofuVerifyKeyResolver$1 as Y, IActionTransportReadyData_Methods as Yn, IRunningActionUpdate_Success as Yr, buildChannelSubsetResolver as Yt, runtimeLinkId as Z, IFrameReliability$1 as Zn, TRunningActionUpdateFinished as Zr, serveChannels as Zt, IActionPayload_Base_JsonObject as _, EReliabilityTier as _i, TChannelPushHandlers as _n, TOnResolveIncomingResponse as _r, TCarrierFetch$1 as _t, TAcceptorCaseFn as a, IActionDomain as ai, IHibernatableWsServerAdapterOptions as an, ITransportRouteInfo as ar, TExchangeReply as at, IActionPayload_Progress_JsonObject as b, actionSchema as bi, acceptChannel as bn, TSendReturnDataMethod as br, httpAcceptorCarrier as bt, TActionConnectionEncoding as c, TActionDomainChildDef as ci, IConnectionAttachment as cn, ITransportStatusInfo_Initializing as cr, decodeExchangeRequest as ct, ActionRuntime as d, TInferInputFromSchema as di, TActionRuntimeHandler as dn, IUpdateActionRunConfig_Output as dr, IExchangeAcceptorConfig as dt, ActionPayload_Progress as ei, IExchangeAcceptorCarrier as en, ITransportDispatchAction as er, isActionPayload_Any_JsonObject as et, ActionCore as f, TInferOutputFromSchema as fi, IAcceptChannelOptions as fn, TGetTransportFn as fr, IExchangeAcceptorSecurity as ft, IActionPayload_Base as g, EActionResponseMode as gi, TChannelAcceptorCases as gn, TOnResolveIncomingRequestJson as gr, IHttpCarrierRequest$1 as gt, EActionProgressType as h, ActionSchema as hi, IConnectTransport as hn, TOnResolveIncomingRequest as hr, IHttpCarrierOptions as ht, IChannelAcceptorOptions as i, IHandledReliability as ii, IDuplexConnectionRouter as in, ITransportRouteConnectParams as ir, err_nice_action as it, TActionResultOutcome as j, IExchangeCarrier$1 as jn, IActionServeRequestInfo as jr, err_nice_external_client as jt, TActionPayload_Any_JsonObject as k, IDuplexCarrier$1 as kn, createSecureChannelAcceptor as kr, IInMemoryCarrier as kt, createChannelAcceptor as l, TActionDomainSchema as li, IConnectionStateStoreOptions as ln, ITransportStatusInfo_Ready as lr, encodeExchange as lt, EActionPayloadType as m, TPossibleDomainIdList as mi, IConnectChannelOptions as mn, TOnResolveAnyIncomingActionData_Json as mr, err_nice_transport as mt, IAcceptorConnectionBinding as n, IActionCore_JsonObject as ni, TAcceptorCarrier as nn, ITransportRouteActionParams as nr, decodeActionFrame as nt, TAcceptorConnectionCaseFn as o, IActionDomainChildOptions as oi, createHibernatableWsServerAdapter as on, ITransportStatusInfo_Base as or, TExchangeRequest as ot, ActionPayload_Request as p, TPossibleDomainId as pi, IActionChannel as pn, TOnResolveAnyIncomingActionData as pr, EErrId_NiceTransport as pt, createInMemoryChannelPair$1 as q, IActionTransportReady as qn, IRunningActionUpdate_Reliability as qr, IServeChannelOptions as qt, IAcceptorFrameProtocol as r, IActionRouteItem as ri, isExchangeAcceptorCarrier as rn, ITransportRouteClientParams as rr, EErrId_NiceAction as rt, TActionChannelFormatMessage as s, IActionRootDomain as si, ConnectionStateStore as sn, ITransportStatusInfo_Failed as sr, decodeExchangeReply as st, ChannelAcceptor as t, IActionCore as ti, IInboundFrameLimits as tn, ITransportMethod_SendActionData_Input as tr, IActionFrameDecoder as tt, ActionDomain as u, TDomainActionId as ui, createConnectionStateStore as un, ITransportStatusInfo_Unsupported as ur, ExchangeAcceptor as ut, IActionPayload_Data_Base as v, TActionThrownError as vi, TCombinedAcceptorDomains as vn, TOnResolveIncomingResponseJson as vr, httpCarrier$1 as vt, IActionProgress_Custom as w, TInferDeclaredErrors as wi, connectChannels as wn, TTransportStatusInfo as wr, wsAcceptorCarrier as wt, IActionPayload_Request_JsonObject as x, IActionErrorDeclaration as xi, acceptChannelConnections as xn, TTransportCache$1 as xr, IWsCarrierOptions as xt, IActionPayload_Progress as y, TInferActionError as yi, TCombinedConnectorDomains as yn, TSendActionDataMethod as yr, IHttpAcceptorCarrierOptions as yt, IRtcDataChannelLike$1 as z, TReliableStreamEvent as zn, createActionRootDomain as zr, IForwardToOptions as zt }; //# sourceMappingURL=ChannelAcceptor-Cmu6jyhn.d.mts.map