@nice-code/action 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +71 -26
  2. package/build/{ActionPayload.types-B-OSg09t.d.mts → AcceptorHandler-BizUtq4u.d.mts} +1267 -1543
  3. package/build/{ActionPayload.types-DIOeVapm.d.cts → AcceptorHandler-CxPfZtIl.d.cts} +1267 -1543
  4. package/build/{ActionDevtoolsCore-BjbhFqc0.d.mts → ActionDevtoolsCore-D9KBBI2V.d.cts} +2 -2
  5. package/build/{ActionDevtoolsCore-kk7oZBv9.d.cts → ActionDevtoolsCore-xZjAtB4H.d.mts} +2 -2
  6. package/build/advanced/index.cjs +115 -0
  7. package/build/advanced/index.cjs.map +1 -0
  8. package/build/advanced/index.d.cts +249 -0
  9. package/build/advanced/index.d.mts +249 -0
  10. package/build/advanced/index.mjs +88 -0
  11. package/build/advanced/index.mjs.map +1 -0
  12. package/build/{httpAcceptorCarrier-DJVxzDVd.mjs → createHibernatableWsServerAdapter-BkjESd01.mjs} +243 -429
  13. package/build/createHibernatableWsServerAdapter-BkjESd01.mjs.map +1 -0
  14. package/build/{httpAcceptorCarrier-hYPuoNuP.cjs → createHibernatableWsServerAdapter-FSDWrxoF.cjs} +268 -478
  15. package/build/createHibernatableWsServerAdapter-FSDWrxoF.cjs.map +1 -0
  16. package/build/devtools/browser/index.d.cts +1 -1
  17. package/build/devtools/browser/index.d.mts +1 -1
  18. package/build/devtools/server/index.d.cts +1 -1
  19. package/build/devtools/server/index.d.mts +1 -1
  20. package/build/httpAcceptorCarrier-BQYaXI9j.cjs +454 -0
  21. package/build/httpAcceptorCarrier-BQYaXI9j.cjs.map +1 -0
  22. package/build/httpAcceptorCarrier-DWqsCz3h.mjs +401 -0
  23. package/build/httpAcceptorCarrier-DWqsCz3h.mjs.map +1 -0
  24. package/build/index.cjs +73 -449
  25. package/build/index.cjs.map +1 -1
  26. package/build/index.d.cts +2 -2
  27. package/build/index.d.mts +2 -2
  28. package/build/index.mjs +13 -365
  29. package/build/index.mjs.map +1 -1
  30. package/build/platform/cloudflare/index.cjs +45 -1
  31. package/build/platform/cloudflare/index.cjs.map +1 -1
  32. package/build/platform/cloudflare/index.d.cts +42 -4
  33. package/build/platform/cloudflare/index.d.mts +42 -4
  34. package/build/platform/cloudflare/index.mjs +45 -2
  35. package/build/platform/cloudflare/index.mjs.map +1 -1
  36. package/build/react-query/index.d.cts +1 -1
  37. package/build/react-query/index.d.mts +1 -1
  38. package/package.json +15 -4
  39. package/build/httpAcceptorCarrier-DJVxzDVd.mjs.map +0 -1
  40. package/build/httpAcceptorCarrier-hYPuoNuP.cjs.map +0 -1
@@ -175,6 +175,28 @@ type TInferOutputFromSchema<SCH extends ActionSchema<any, any, any>> = SCH exten
175
175
  } : never;
176
176
  type TWrappableDomainActionHandler<DOM extends IActionDomain> = { [K in TDomainActionId<DOM>]: (...args: [TInferInputFromSchema<DOM["actionSchema"][K]>["Input"]] extends [never] ? [] : [input: TInferInputFromSchema<DOM["actionSchema"][K]>["Input"]]) => [TInferOutputFromSchema<DOM["actionSchema"][K]>["Output"]] extends [never] ? Promise<void> | void : Promise<TInferOutputFromSchema<DOM["actionSchema"][K]>["Output"]> };
177
177
  //#endregion
178
+ //#region src/ActionDefinition/Action/ActionBase.d.ts
179
+ declare abstract class ActionBase<FORM extends EActionForm, DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> implements IActionBase<FORM, DOM, ID> {
180
+ readonly form: FORM;
181
+ readonly _domain: ActionDomain<DOM>;
182
+ readonly id: ID;
183
+ readonly domain: DOM["domain"];
184
+ readonly allDomains: DOM["allDomains"];
185
+ readonly schema: DOM["actionSchema"][ID];
186
+ constructor(form: FORM, _domain: ActionDomain<DOM>, id: ID);
187
+ protected toJsonObject(): IActionBase_JsonObject<FORM, DOM, ID>;
188
+ protected toJsonString(): string;
189
+ }
190
+ //#endregion
191
+ //#region src/ActionDefinition/Action/Core/ActionCore.types.d.ts
192
+ interface IActionCore<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends IActionBase<EActionForm.core, DOM, ID> {}
193
+ type IActionCore_JsonObject<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = {
194
+ form: EActionForm.core;
195
+ domain: DOM["domain"];
196
+ allDomains: DOM["allDomains"];
197
+ id: ID;
198
+ };
199
+ //#endregion
178
200
  //#region src/ActionRuntime/RuntimeCoordinate.d.ts
179
201
  interface IRuntimeCoordinateSpecifics {
180
202
  /**
@@ -238,43 +260,6 @@ declare class RuntimeCoordinate implements IRuntimeCoordinate {
238
260
  toStringIds(): TRuntimeCoordinateStringId[];
239
261
  }
240
262
  //#endregion
241
- //#region src/ActionDefinition/Action/ActionBase.types.d.ts
242
- declare enum EActionForm {
243
- core = "core",
244
- context = "context",
245
- data = "data"
246
- }
247
- interface INiceActionIdAndDomain<DOM extends IActionDomain = IActionDomain> {
248
- domain: DOM["domain"];
249
- id: keyof DOM["actionSchema"] & string;
250
- }
251
- interface IActionBase<FORM extends EActionForm, DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends INiceActionIdAndDomain<DOM> {
252
- id: ID;
253
- form: FORM;
254
- _domain: ActionDomain<DOM>;
255
- allDomains: DOM["allDomains"];
256
- schema: DOM["actionSchema"][ID];
257
- }
258
- interface IActionBase_JsonObject<FORM extends EActionForm = EActionForm, DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> {
259
- form: FORM;
260
- domain: DOM["domain"];
261
- allDomains: DOM["allDomains"];
262
- id: ID;
263
- }
264
- //#endregion
265
- //#region src/ActionDefinition/Action/ActionBase.d.ts
266
- declare abstract class ActionBase<FORM extends EActionForm, DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> implements IActionBase<FORM, DOM, ID> {
267
- readonly form: FORM;
268
- readonly _domain: ActionDomain<DOM>;
269
- readonly id: ID;
270
- readonly domain: DOM["domain"];
271
- readonly allDomains: DOM["allDomains"];
272
- readonly schema: DOM["actionSchema"][ID];
273
- constructor(form: FORM, _domain: ActionDomain<DOM>, id: ID);
274
- protected toJsonObject(): IActionBase_JsonObject<FORM, DOM, ID>;
275
- protected toJsonString(): string;
276
- }
277
- //#endregion
278
263
  //#region src/ActionDefinition/Action/Context/ActionContext.types.d.ts
279
264
  interface IActionRouteItem {
280
265
  runtime: RuntimeCoordinate;
@@ -338,6 +323,17 @@ declare abstract class ActionPayload<DT extends EActionPayloadType, DOM extends
338
323
  abstract toJsonObject(): IActionPayload_Base_JsonObject<DT, DOM, ID>;
339
324
  }
340
325
  //#endregion
326
+ //#region src/ActionDefinition/Action/Payload/ActionPayload_Progress.d.ts
327
+ declare class ActionPayload_Progress<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionPayload<EActionPayloadType.progress, DOM, ID> implements IActionPayload_Progress<DOM, ID> {
328
+ readonly progress: TActionProgress;
329
+ constructor(params: {
330
+ context: ActionContext<DOM, ID>;
331
+ } | ActionPayload_Request<DOM, ID>, progress: TActionProgress, data: IActionPayload_Data_Base);
332
+ toJsonObject(): IActionPayload_Progress_JsonObject<DOM, ID>;
333
+ toJsonString(): string;
334
+ toHttpResponse(): Response;
335
+ }
336
+ //#endregion
341
337
  //#region src/ActionDefinition/Action/Payload/ActionPayload_Result.d.ts
342
338
  declare class ActionPayload_Result<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionPayload<EActionPayloadType.result, DOM, ID> {
343
339
  readonly result: TActionResultOutcome<TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"], TInferActionError<DOM["actionSchema"][ID]>>;
@@ -420,40 +416,79 @@ interface IRunningActionUserMethods<DOM extends IActionDomain, ID extends keyof
420
416
  abort(reason?: unknown): void;
421
417
  }
422
418
  //#endregion
423
- //#region src/ActionDefinition/Action/Core/ActionCore.types.d.ts
424
- interface IActionCore<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends IActionBase<EActionForm.core, DOM, ID> {}
425
- type IActionCore_JsonObject<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = {
426
- form: EActionForm.core;
427
- domain: DOM["domain"];
428
- allDomains: DOM["allDomains"];
429
- id: ID;
430
- };
419
+ //#region src/ActionDefinition/Action/RunningAction.d.ts
420
+ declare class RunningAction<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> implements IRunningActionUserMethods<DOM, ID> {
421
+ protected _state: IRunningActionState<DOM, ID>;
422
+ readonly context: ActionContext<DOM, ID>;
423
+ readonly cuid: string;
424
+ readonly id: ID;
425
+ readonly _domain: ActionDomain<DOM>;
426
+ readonly domain: DOM["domain"];
427
+ readonly allDomains: DOM["allDomains"];
428
+ readonly parentCuid?: string;
429
+ readonly callSite?: string;
430
+ private readonly _resultPayloadPromise;
431
+ private _resolveResult;
432
+ private _rejectResult;
433
+ private _isAborted;
434
+ private readonly _updates;
435
+ private readonly _updateListeners;
436
+ constructor(initialState: IRunningActionState_ConstructorParams<DOM, ID>);
437
+ get state(): IRunningActionState<DOM, ID>;
438
+ abort(reason?: unknown): void;
439
+ addUpdateListeners(listeners: TRunningActionUpdateListener<DOM, ID>[]): () => void;
440
+ iterateUpdates(): AsyncIterable<TRunningActionUpdate<DOM, ID>>;
441
+ _sendUpdate(update: TRunningActionUpdate<DOM, ID>): void;
442
+ _completeWithResult(result: ActionPayload_Result<DOM, ID>): boolean;
443
+ _abort(reason?: unknown): boolean;
444
+ _failWithError(error: unknown): boolean;
445
+ _updateProgress(progress: ActionPayload_Progress<DOM, ID>): void;
446
+ waitForResultPayload(): Promise<ActionPayload_Result<DOM, ID>>;
447
+ _resolveFromJson(resultJson: IActionPayload_Result_JsonObject<DOM, ID>): boolean;
448
+ }
431
449
  //#endregion
432
- //#region src/ActionDefinition/Action/Action.combined.types.d.ts
433
- /**
434
- * Distributes a union ID into a proper discriminated union of ActionPayload_Request instances,
435
- * so that narrowing on `.id` also narrows `.input`.
436
- */
437
- type TDistributeActionPayload_Request<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string> = ID extends keyof DOM["actionSchema"] & string ? ActionPayload_Request<DOM, ID> : never;
438
- /**
439
- * Distributes a union ID into a proper discriminated union of ActionPayload_Result instances,
440
- * so that narrowing on `.id` also narrows `.result`.
441
- */
442
- type TDistributeActionPayload_Result<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string> = ID extends keyof DOM["actionSchema"] & string ? ActionPayload_Result<DOM, ID> : never;
443
- /**
444
- *
445
- * COMBINED JSON TYPES
446
- *
447
- */
448
- type TAction_Any_JsonObject<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = IActionCore_JsonObject<DOM, ID> | TActionPayload_Any_JsonObject<DOM, ID> | IActionContext_JsonObject<DOM, ID>;
449
- /**
450
- *
451
- * UTILITY TYPES
452
- *
453
- */
454
- type TDistributedDomainActions<DOM extends IActionDomain, ACT extends IActionBase<any, DOM, any>> = { [ID in keyof DOM["actionSchema"] & string]: TNarrowActionType<DOM, ACT, ID> }[keyof DOM["actionSchema"] & string];
455
- type TNarrowActionType<DOM extends IActionDomain, ACT extends IActionBase<any, DOM, any>, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ACT extends ActionPayload_Result<any> ? ActionPayload_Result<DOM, ID> : ACT extends ActionPayload_Request<any> ? ActionPayload_Request<DOM, ID> : ACT extends ActionPayload_Progress<any> ? ActionPayload_Progress<DOM, ID> : ACT extends ActionCore<any> ? ActionCore<DOM, ID> : never;
456
- type TNarrowActionJsonTypeToActionInstanceType<DOM extends IActionDomain, ACT extends TAction_Any_JsonObject<DOM>, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ACT extends IActionPayload_Request_JsonObject<any> ? ActionPayload_Request<DOM, ID> : ACT extends IActionPayload_Result_JsonObject<any> ? ActionPayload_Result<DOM, ID> : ACT extends IActionPayload_Progress_JsonObject<any> ? ActionPayload_Progress<DOM, ID> : ACT extends IActionCore_JsonObject<any> ? ActionCore<DOM, ID> : never;
450
+ //#region src/ActionDefinition/Domain/ActionDomainBase.d.ts
451
+ declare abstract class ActionDomainBase<ACT_DOM extends IActionDomain = IActionDomain> implements IActionDomain<ACT_DOM["allDomains"], ACT_DOM["actionSchema"]> {
452
+ readonly domain: ACT_DOM["domain"];
453
+ readonly allDomains: ACT_DOM["allDomains"];
454
+ readonly actionSchema: ACT_DOM["actionSchema"];
455
+ protected _listeners: TRunningActionUpdateListener<any, any>[];
456
+ constructor(definition: ACT_DOM);
457
+ /**
458
+ * Add an observer that is called after every action dispatched through this domain.
459
+ * Returns an unsubscribe function — call it to remove the listener.
460
+ */
461
+ addActionListener(listener: TDistributeRunningActionUpdateListener<ACT_DOM, keyof ACT_DOM["actionSchema"] & string>): () => void;
462
+ /**
463
+ * @internal
464
+ * Observers registered directly on this domain via {@link addActionListener}.
465
+ * Used to wire observers (e.g. devtools) onto RunningActions that aren't created
466
+ * through the local-dispatch path notably inbound actions pushed from a backend
467
+ * or another client over a bidirectional transport.
468
+ */
469
+ _getActionObservers(): TRunningActionUpdateListener<any, any>[];
470
+ }
471
+ //#endregion
472
+ //#region src/ActionDefinition/Domain/ActionRootDomain.d.ts
473
+ declare class ActionRootDomain<ROOT_DOM extends IActionRootDomain = IActionRootDomain> extends ActionDomainBase<ROOT_DOM> {
474
+ readonly domainDefinition: {
475
+ domain: ROOT_DOM["domain"];
476
+ };
477
+ private _actionRuntimeManager;
478
+ constructor(domainDefinition: {
479
+ domain: ROOT_DOM["domain"];
480
+ });
481
+ createChildDomain<SUB_DOM extends IActionDomainChildOptions>(subDomainDef: SUB_DOM & { [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never }): ActionDomain<TActionDomainChildDef<ROOT_DOM, SUB_DOM>>;
482
+ _registerRuntime(runtime: ActionRuntime): void;
483
+ _hasRuntime(runtime: ActionRuntime): boolean;
484
+ getRuntime(clientSpecifier: IRuntimeCoordinate): ActionRuntime | undefined;
485
+ _runAction<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string, ACT extends ActionPayload_Request<DOM, ID> = ActionPayload_Request<DOM, ID>>(actionPayload: ACT, options?: IExecuteActionOptions<DOM, ID>): Promise<RunningAction<DOM, ID>>;
486
+ }
487
+ //#endregion
488
+ //#region src/ActionDefinition/Domain/helpers/createRootActionDomain.d.ts
489
+ declare const createActionRootDomain: <ID extends string>(definition: {
490
+ domain: ID;
491
+ }) => ActionRootDomain<IActionRootDomain<ID>>;
457
492
  //#endregion
458
493
  //#region src/ActionRuntime/ActionDomainManager.d.ts
459
494
  declare class ActionDomainManager {
@@ -532,133 +567,20 @@ declare class ActionRouter<DATA> {
532
567
  private _push;
533
568
  }
534
569
  //#endregion
535
- //#region src/ActionRuntime/Handler/ActionHandler.types.d.ts
536
- declare enum EActionHandlerType {
537
- peer = "peer",
538
- local = "local"
539
- }
540
- interface IActionHandler_Json<T extends EActionHandlerType> {
541
- type: T;
542
- }
543
- interface IActionHandler_Peer_Json extends IActionHandler_Json<EActionHandlerType.peer> {
544
- client: IRuntimeCoordinate;
545
- }
546
- interface IActionHandler_Local_Json extends IActionHandler_Json<EActionHandlerType.local> {}
547
- type TActionHandler_Json = IActionHandler_Local_Json | IActionHandler_Peer_Json;
548
- interface IHandleActionOptions {
549
- timeout?: number;
550
- targetPeer?: RuntimeCoordinate;
551
- targetLocalRuntime?: ActionRuntime;
552
- }
553
- interface IExecuteActionOptions<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends IHandleActionOptions {
554
- listeners?: TRunningActionUpdateListener<DOM, ID>[];
555
- }
556
- interface IActionHandler_Base<T extends EActionHandlerType> {
557
- cuid: string;
558
- handlerType: T;
559
- getActionRouter: () => ActionRouter<any>;
560
- }
561
- /**
562
- *
563
- * LOCAL ACTION HANDLER
564
- *
565
- */
566
- interface IHandleActionOptions_Local extends IHandleActionOptions {}
567
- interface IActionHandler_Local extends IActionHandler_Base<EActionHandlerType.local> {
568
- handleActionRequest: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions_Local) => Promise<RunningAction<DOM, ID>>;
569
- }
570
- /**
571
- *
572
- * PEER-LINK ACTION HANDLER
573
- *
574
- */
575
- interface IHandleActionOptions_Peer extends IHandleActionOptions {}
576
- interface IActionHandler_Peer extends IActionHandler_Base<EActionHandlerType.peer> {
577
- peerClient: RuntimeCoordinate;
578
- handleActionRequest: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions_Peer) => Promise<RunningAction<DOM, ID>>;
579
- _setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject<any>) => void): void;
570
+ //#region src/ActionRuntime/Handler/ActionHandler.d.ts
571
+ declare abstract class ActionHandler<T extends EActionHandlerType> implements IActionHandler_Base<T> {
572
+ abstract readonly handlerType: T;
573
+ readonly cuid: string;
574
+ abstract readonly actionRouter: ActionRouter<any>;
575
+ constructor();
576
+ getActionRouter(): ActionRouter<any>;
577
+ abstract handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
578
+ abstract toJsonObject(): TActionHandler_Json;
579
+ abstract toHandlerRouteItem(...args: any[]): IActionRouteItemHandler;
580
580
  }
581
- /**
582
- *
583
- * COMBINED
584
- *
585
- */
586
- type TActionHandler = IActionHandler_Local | IActionHandler_Peer;
587
581
  //#endregion
588
- //#region src/ActionDefinition/Action/Payload/ActionPayload_Request.d.ts
589
- declare class ActionPayload_Request<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionPayload<EActionPayloadType.request, DOM, ID> {
590
- readonly input: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"];
591
- readonly inputHash: string;
592
- _callSite?: string;
593
- constructor(params: {
594
- context: ActionContext<DOM, ID>;
595
- }, input: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"], data: IActionPayload_Data_Base);
596
- successResult(...args: [TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]] extends [never] ? [] | [output: TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]] : [output: TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]]): ActionPayload_Result<DOM, ID>;
597
- errorResult(err: TInferActionError<DOM["actionSchema"][ID]>): ActionPayload_Result<DOM, ID>;
598
- progress(progress: TActionProgress): ActionPayload_Progress<DOM, ID>;
599
- toJsonObject(): IActionPayload_Request_JsonObject<DOM, ID>;
600
- toJsonString(): string;
601
- runToOutput(options?: IExecuteActionOptions<DOM, ID>): Promise<TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]>;
602
- runToResultPayload(options?: IExecuteActionOptions<DOM, ID>): Promise<ActionPayload_Result<DOM, ID>>;
603
- run(options?: IExecuteActionOptions<DOM, ID>): Promise<RunningAction<DOM, ID>>;
604
- }
605
- //#endregion
606
- //#region src/ActionDefinition/Action/Payload/ActionPayload_Progress.d.ts
607
- declare class ActionPayload_Progress<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionPayload<EActionPayloadType.progress, DOM, ID> implements IActionPayload_Progress<DOM, ID> {
608
- readonly progress: TActionProgress;
609
- constructor(params: {
610
- context: ActionContext<DOM, ID>;
611
- } | ActionPayload_Request<DOM, ID>, progress: TActionProgress, data: IActionPayload_Data_Base);
612
- toJsonObject(): IActionPayload_Progress_JsonObject<DOM, ID>;
613
- toJsonString(): string;
614
- toHttpResponse(): Response;
615
- }
616
- //#endregion
617
- //#region src/ActionDefinition/Action/RunningAction.d.ts
618
- declare class RunningAction<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> implements IRunningActionUserMethods<DOM, ID> {
619
- protected _state: IRunningActionState<DOM, ID>;
620
- readonly context: ActionContext<DOM, ID>;
621
- readonly cuid: string;
622
- readonly id: ID;
623
- readonly _domain: ActionDomain<DOM>;
624
- readonly domain: DOM["domain"];
625
- readonly allDomains: DOM["allDomains"];
626
- readonly parentCuid?: string;
627
- readonly callSite?: string;
628
- private readonly _resultPayloadPromise;
629
- private _resolveResult;
630
- private _rejectResult;
631
- private _isAborted;
632
- private readonly _updates;
633
- private readonly _updateListeners;
634
- constructor(initialState: IRunningActionState_ConstructorParams<DOM, ID>);
635
- get state(): IRunningActionState<DOM, ID>;
636
- abort(reason?: unknown): void;
637
- addUpdateListeners(listeners: TRunningActionUpdateListener<DOM, ID>[]): () => void;
638
- iterateUpdates(): AsyncIterable<TRunningActionUpdate<DOM, ID>>;
639
- _sendUpdate(update: TRunningActionUpdate<DOM, ID>): void;
640
- _completeWithResult(result: ActionPayload_Result<DOM, ID>): boolean;
641
- _abort(reason?: unknown): boolean;
642
- _failWithError(error: unknown): boolean;
643
- _updateProgress(progress: ActionPayload_Progress<DOM, ID>): void;
644
- waitForResultPayload(): Promise<ActionPayload_Result<DOM, ID>>;
645
- _resolveFromJson(resultJson: IActionPayload_Result_JsonObject<DOM, ID>): boolean;
646
- }
647
- //#endregion
648
- //#region src/ActionRuntime/Handler/ActionHandler.d.ts
649
- declare abstract class ActionHandler<T extends EActionHandlerType> implements IActionHandler_Base<T> {
650
- abstract readonly handlerType: T;
651
- readonly cuid: string;
652
- abstract readonly actionRouter: ActionRouter<any>;
653
- constructor();
654
- getActionRouter(): ActionRouter<any>;
655
- abstract handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
656
- abstract toJsonObject(): TActionHandler_Json;
657
- abstract toHandlerRouteItem(...args: any[]): IActionRouteItemHandler;
658
- }
659
- //#endregion
660
- //#region src/utils/typescript/MaybePromise.d.ts
661
- type MaybePromise<T> = T | Promise<T>;
582
+ //#region src/utils/typescript/MaybePromise.d.ts
583
+ type MaybePromise<T> = T | Promise<T>;
662
584
  //#endregion
663
585
  //#region src/ActionRuntime/Handler/Local/ActionLocalHandler.types.d.ts
664
586
  type THandleActionExecutionFn<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = (action: TDistributeActionPayload_Request<DOM, ID>) => MaybePromise<ActionPayload_Result<DOM, ID> | IActionPayload_Result_JsonObject<DOM, ID> | TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"] | undefined>;
@@ -708,72 +630,6 @@ declare class ActionLocalHandler extends ActionHandler<EActionHandlerType.local>
708
630
  }
709
631
  declare const createLocalHandler: () => ActionLocalHandler;
710
632
  //#endregion
711
- //#region src/ActionRuntime/Handler/PeerLink/PeerLinkHandler.d.ts
712
- /**
713
- * Shared base for every handler that routes a domain set to/from *another runtime* (a "peer") — the
714
- * unified peer-link concept. Both specializations extend this as siblings, differing only in *who
715
- * establishes the connection*, which is a transport trait, not a routing one:
716
- *
717
- * - {@link ConnectorHandler} — **dial-out**: this runtime opens connection(s) to one peer
718
- * over a transport stack (with caching + fallback). The classic "client → backend" link.
719
- * - {@link AcceptorHandler} — **accept-in**: connections are accepted from many peers and fed in
720
- * via `receive()`; it keeps a per-connection registry and can push to any of them.
721
- *
722
- * To the runtime there is no "client" vs "server" — both are peer-link handlers (`handlerType =
723
- * external`) keyed to a peer coordinate, chosen by the return-path dispatch via {@link sendReturnPayload}.
724
- */
725
- declare abstract class PeerLinkHandler extends ActionHandler<EActionHandlerType.peer> implements IActionHandler_Peer {
726
- /** The peer runtime this handler links to (an env-only coordinate for an accept-in handler). */
727
- readonly peerClient: RuntimeCoordinate;
728
- readonly handlerType = EActionHandlerType.peer;
729
- /**
730
- * Whether this link can deliver an *unsolicited* frame to the peer (a result/progress pushed back on
731
- * the return path, or a `broadcast`). A duplex carrier (WebSocket/WebRTC/…) can; an exchange-only
732
- * carrier (HTTP) cannot — its reply must ride the response to its own request. The runtime's
733
- * return-path dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) skips handlers that can't
734
- * push, so an exchange-only handler is never asked to deliver one.
735
- */
736
- abstract readonly canPush: boolean;
737
- readonly actionRouter: ActionRouter<true>;
738
- /** Listeners installed by the runtime (`resolveIncomingActionPayload`) for inbound peer frames. */
739
- private readonly _incomingActionDataListeners;
740
- constructor(peerCoordinate: RuntimeCoordinate);
741
- forDomain<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>): this;
742
- forAction<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM["actionSchema"] & string>(action: ActionCore<ACT_DOM, ID>): this;
743
- forActionIds<ACT_DOM extends IActionDomain, IDS extends ReadonlyArray<keyof ACT_DOM["actionSchema"] & string>>(domain: ActionDomain<ACT_DOM>, ids: IDS): this;
744
- _setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject<any, any>) => void): void;
745
- /** Hand a decoded inbound frame to the runtime (called by each specialization's receive path). */
746
- protected _emitIncoming(json: TActionPayload_Any_JsonObject<any, any>): void;
747
- /**
748
- * Dispatch a result/progress payload back to the action's origin peer over this link. The runtime's
749
- * return-path dispatch calls it on whichever peer-link handler best reaches `originClient`. Returns
750
- * `true` if it was sent, `false` if no channel was available.
751
- */
752
- abstract sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
753
- targetLocalRuntime: ActionRuntime;
754
- }): Promise<boolean>;
755
- /**
756
- * Whether this handler currently holds a *live* connection bound to `origin`. The runtime's return-path
757
- * dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) prefers a handler that owns the origin's
758
- * connection over a mere coordinate match, so with several duplex acceptors a result/push routes back
759
- * over the carrier the client connected on. Defaults to `false`; an acceptor overrides it from its
760
- * connection registry.
761
- */
762
- ownsLiveConnectionFor(_origin: RuntimeCoordinate): boolean;
763
- /** Release any long-lived connections this handler owns (a teardown). No-op by default. */
764
- clearTransportCache(): void;
765
- }
766
- //#endregion
767
- //#region src/ActionRuntime/ActionRuntime.types.d.ts
768
- interface IRuntimeMeta {
769
- assumed: boolean;
770
- runtimeName: RuntimeName;
771
- }
772
- interface IActionRuntimeManagerContext {
773
- domain?: string;
774
- }
775
- type TActionRuntimeHandler = ActionLocalHandler | PeerLinkHandler;
776
- //#endregion
777
633
  //#region src/ActionRuntime/Transport/crypto/actionHandshake.d.ts
778
634
  /** How much the channel protects after the handshake — chosen by the consumer (perf vs security). */
779
635
  declare enum ESecurityLevel {
@@ -958,6 +814,51 @@ declare function createServerHandshake(config: IServerHandshakeConfig): {
958
814
  getResult(): IHandshakeResult | undefined;
959
815
  };
960
816
  //#endregion
817
+ //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createSecureActionServer.d.ts
818
+ interface ISecureAcceptorHandlerOptions<TConn> {
819
+ /** The shared channel identity (codec + dictionary version) — same one the clients use. */
820
+ channel: IActionChannel;
821
+ /**
822
+ * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
823
+ * used as the offline-return scoring fallback (a live connection always wins regardless). Optional —
824
+ * omit it for a multi-role server accepting several client envs over one acceptor.
825
+ */
826
+ clientEnv?: RuntimeCoordinate;
827
+ /** This server's runtime — its coordinate is the server identity presented in the handshake. */
828
+ runtime: ActionRuntime;
829
+ /**
830
+ * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins.
831
+ * Their keys don't collide, so a single adapter is enough; back it with persistent storage (e.g. a
832
+ * Durable Object's storage) so identity and pins survive eviction.
833
+ */
834
+ storage: StorageAdapter;
835
+ /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
836
+ send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
837
+ /**
838
+ * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`.
839
+ * Pass an existing link to share one identity across several acceptors on the same server (e.g. a
840
+ * WebSocket acceptor and a secure-HTTP {@link createActionFetchHandler}), so they present the same
841
+ * verify/exchange keys — avoiding a divergent-key race when two fresh links initialize concurrently.
842
+ */
843
+ link?: ClientCryptoKeyLink;
844
+ /** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */
845
+ securityLevel?: ESecurityLevel | readonly ESecurityLevel[];
846
+ /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */
847
+ verifyKeyResolver?: IClientVerifyKeyResolver;
848
+ /** Timeout (ms) applied to server-initiated actions awaiting a client response. */
849
+ defaultTimeout?: number;
850
+ }
851
+ /**
852
+ * Build an {@link AcceptorHandler} for the secure binary channel with the boilerplate folded in:
853
+ * it creates the {@link ClientCryptoKeyLink} and the storage-backed TOFU resolver from a single
854
+ * `storage`, installs the channel's per-connection codec, and assembles the `security` block
855
+ * from the runtime coordinate + channel version (accepting all three levels by default).
856
+ *
857
+ * For a hibernatable transport (e.g. a Durable Object), pair it with
858
+ * {@link createHibernatableWsServerAdapter} to wire persistence + replay.
859
+ */
860
+ declare function createSecureAcceptorHandler<TConn = unknown>(options: ISecureAcceptorHandlerOptions<TConn>): AcceptorHandler<TConn>;
861
+ //#endregion
961
862
  //#region src/ActionRuntime/Transport/Transport.d.ts
962
863
  /**
963
864
  * Context handed to a {@link Transport} definition when a handler builds a live connection from it.
@@ -967,9 +868,8 @@ interface ITransportConnectionContext {
967
868
  resolvers?: IActionTransportResolvers;
968
869
  }
969
870
  /**
970
- * Reusable transport definition. Devs construct these (`secureTransport({ carrier: wsCarrier(() => ({ url })) })`,
971
- * `plainTransport({ carrier: httpCarrier(...) })`, …) and pass them to a
972
- * `ConnectorHandler`. A single
871
+ * Reusable transport definition. Built by the internal `transport({ carrier, secure })` factory (which
872
+ * `connectChannel` / `serveChannel` drive) and passed to a `ConnectorHandler`. A single
973
873
  * definition can be shared across multiple handlers — each handler builds its own live
974
874
  * {@link TransportConnection} via {@link TransportConnection._createConnection}.
975
875
  */
@@ -1197,6 +1097,62 @@ interface IActionTransportDef<TYPE extends ETransportShape, INIT extends IAction
1197
1097
  initialize: () => INIT;
1198
1098
  }
1199
1099
  //#endregion
1100
+ //#region src/ActionRuntime/Handler/PeerLink/PeerLinkHandler.d.ts
1101
+ /**
1102
+ * Shared base for every handler that routes a domain set to/from *another runtime* (a "peer") — the
1103
+ * unified peer-link concept. Both specializations extend this as siblings, differing only in *who
1104
+ * establishes the connection*, which is a transport trait, not a routing one:
1105
+ *
1106
+ * - {@link ConnectorHandler} — **dial-out**: this runtime opens connection(s) to one peer
1107
+ * over a transport stack (with caching + fallback). The classic "client → backend" link.
1108
+ * - {@link AcceptorHandler} — **accept-in**: connections are accepted from many peers and fed in
1109
+ * via `receive()`; it keeps a per-connection registry and can push to any of them.
1110
+ *
1111
+ * To the runtime there is no "client" vs "server" — both are peer-link handlers (`handlerType =
1112
+ * external`) keyed to a peer coordinate, chosen by the return-path dispatch via {@link sendReturnPayload}.
1113
+ */
1114
+ declare abstract class PeerLinkHandler extends ActionHandler<EActionHandlerType.peer> implements IActionHandler_Peer {
1115
+ /** The peer runtime this handler links to (an env-only coordinate for an accept-in handler). */
1116
+ readonly peerClient: RuntimeCoordinate;
1117
+ readonly handlerType = EActionHandlerType.peer;
1118
+ /**
1119
+ * Whether this link can deliver an *unsolicited* frame to the peer (a result/progress pushed back on
1120
+ * the return path, or a `broadcast`). A duplex carrier (WebSocket/WebRTC/…) can; an exchange-only
1121
+ * carrier (HTTP) cannot — its reply must ride the response to its own request. The runtime's
1122
+ * return-path dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) skips handlers that can't
1123
+ * push, so an exchange-only handler is never asked to deliver one.
1124
+ */
1125
+ abstract readonly canPush: boolean;
1126
+ readonly actionRouter: ActionRouter<true>;
1127
+ /** Listeners installed by the runtime (`resolveIncomingActionPayload`) for inbound peer frames. */
1128
+ private readonly _incomingActionDataListeners;
1129
+ constructor(peerCoordinate: RuntimeCoordinate);
1130
+ forDomain<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>): this;
1131
+ forAction<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM["actionSchema"] & string>(action: ActionCore<ACT_DOM, ID>): this;
1132
+ forActionIds<ACT_DOM extends IActionDomain, IDS extends ReadonlyArray<keyof ACT_DOM["actionSchema"] & string>>(domain: ActionDomain<ACT_DOM>, ids: IDS): this;
1133
+ _setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject<any, any>) => void): void;
1134
+ /** Hand a decoded inbound frame to the runtime (called by each specialization's receive path). */
1135
+ protected _emitIncoming(json: TActionPayload_Any_JsonObject<any, any>): void;
1136
+ /**
1137
+ * Dispatch a result/progress payload back to the action's origin peer over this link. The runtime's
1138
+ * return-path dispatch calls it on whichever peer-link handler best reaches `originClient`. Returns
1139
+ * `true` if it was sent, `false` if no channel was available.
1140
+ */
1141
+ abstract sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
1142
+ targetLocalRuntime: ActionRuntime;
1143
+ }): Promise<boolean>;
1144
+ /**
1145
+ * Whether this handler currently holds a *live* connection bound to `origin`. The runtime's return-path
1146
+ * dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) prefers a handler that owns the origin's
1147
+ * connection over a mere coordinate match, so with several duplex acceptors a result/push routes back
1148
+ * over the carrier the client connected on. Defaults to `false`; an acceptor overrides it from its
1149
+ * connection registry.
1150
+ */
1151
+ ownsLiveConnectionFor(_origin: RuntimeCoordinate): boolean;
1152
+ /** Release any long-lived connections this handler owns (a teardown). No-op by default. */
1153
+ clearTransportCache(): void;
1154
+ }
1155
+ //#endregion
1200
1156
  //#region src/ActionRuntime/Handler/PeerLink/Connector/ConnectorHandler.types.d.ts
1201
1157
  interface IConnectorHandlerConfig {
1202
1158
  defaultTimeout?: number;
@@ -1242,180 +1198,98 @@ declare class ConnectorHandler extends PeerLinkHandler {
1242
1198
  }
1243
1199
  declare const createConnectorHandler: (config: IConnectorHandlerConfig) => ConnectorHandler;
1244
1200
  //#endregion
1245
- //#region src/ActionRuntime/ActionRuntime.d.ts
1246
- declare class ActionRuntime {
1247
- private _coordinate;
1248
- readonly timeCreated: number;
1249
- readonly runtimeInfo: IRuntimeMeta;
1250
- private readonly actionRouter;
1251
- private readonly _pendingRunningActions;
1252
- private readonly _registeredPeerHandlers;
1253
- private _applied;
1254
- static getDefault(): ActionRuntime;
1255
- constructor(coordinate: RuntimeCoordinate);
1256
- get coordinate(): RuntimeCoordinate;
1257
- specifyRuntimeCoordinate(specifics: IRuntimeCoordinateSpecifics & {
1258
- envId?: string;
1259
- }): void;
1260
- registerRunningAction(ra: RunningAction<any, any>): void;
1261
- resolveIncomingActionPayload(json: TActionPayload_Any_JsonObject<any, any>): void;
1262
- /**
1263
- * Handle an incoming action wire (e.g. from a transport layer), route it to
1264
- * the correct handler, and return the response. The most specific handler
1265
- * match is chosen (action-ID-specific beats domain-wildcard).
1266
- */
1267
- handleActionPayloadWire<D extends IActionDomain, ID extends keyof D["actionSchema"] & string>(wire: TActionPayload_Any_JsonObject<D, ID>): Promise<RunningAction<D, ID>>;
1268
- handleActionPayloadWire(wire: unknown): Promise<RunningAction<any, any>>;
1269
- handleActionPayload<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: TActionPayload_Any_Instance<DOM, ID>, options?: Omit<IHandleActionOptions, "targetLocalRuntime">): Promise<RunningAction<DOM, ID>>;
1270
- /**
1271
- * @internal
1272
- *
1273
- * Return the first handler registered for the given action, or `undefined`
1274
- * if none has been registered (action-ID-specific beats domain-wildcard).
1275
- */
1276
- _getHandlerForAction<ACT extends TActionPayload_Any_Instance<any, any>>(action: ACT, options?: Omit<IHandleActionOptions, "targetLocalRuntime">): TActionHandler | undefined;
1277
- getHandlerForActionOrThrow<ACT extends TActionPayload_Any_Instance<any, any>>(action: ACT, options?: Omit<IHandleActionOptions, "localRuntime">): TActionHandler;
1278
- /**
1279
- * Register one or more handlers. Each handler's own `actionRouter` defines
1280
- * which domains/actions it handles — those routing keys are mirrored into
1281
- * this runtime's router so the same action can be served by multiple handlers.
1282
- * Duplicate registrations (same handler cuid for the same key) are skipped.
1283
- */
1284
- addHandlers(handlers: TActionRuntimeHandler[]): this;
1285
- /**
1286
- * @internal Low-level primitive — the public way to open a connection is `connectChannel`, which
1287
- * derives routing from a channel and binds the crypto identity for you. This stays as the raw building
1288
- * block it sits on (it restates domain lists by hand) and is not part of the supported surface.
1289
- *
1290
- * Declare an external "backend client" in one call: build an
1291
- * {@link ConnectorHandler} for `externalCoordinate` carrying the given
1292
- * `transports`, route the listed `domains`/`actions` to it, register it (plus any
1293
- * `localHandlers` — e.g. server→client push handlers that share the same channel)
1294
- * on this runtime, and `apply()`. Returns the external handler so the caller can
1295
- * later `clearTransportCache()` it.
1296
- */
1297
- connectTo(externalCoordinate: RuntimeCoordinate, options: {
1298
- transports: Transport[];
1299
- domains?: ActionDomain<any>[];
1300
- actions?: ActionCore<any, any>[];
1301
- localHandlers?: TActionRuntimeHandler[];
1302
- defaultTimeout?: number;
1303
- }): ConnectorHandler;
1304
- private applyRuntimeForDomain;
1305
- /**
1306
- * Register this runtime with all root domains covered by its currently-added handlers,
1307
- * making it eligible to execute actions dispatched from those domains.
1308
- * After apply() is called, any subsequent addHandlers() calls also auto-register.
1309
- */
1310
- apply(): this;
1311
- /**
1312
- * Find the best registered external handler that can reach `originClient` directly.
1313
- * Used to locate the return-path channel for dispatching results back to the action origin.
1314
- * Returns `undefined` if no handler matches (score > 0 required, i.e. at least id must match).
1315
- *
1316
- * A handler that currently holds the origin's *live* connection always wins over a mere coordinate
1317
- * match — so with several duplex acceptors (e.g. WS + WebRTC) a result/push routes back over the carrier
1318
- * the client actually connected on, never a same-coordinate sibling that lacks the socket. Only when no
1319
- * handler owns a live connection do we fall back to the plain best-coordinate-score pick (the
1320
- * single-acceptor and connector-only cases, unchanged).
1321
- */
1322
- getReturnHandlerForOrigin(originClient: RuntimeCoordinate): PeerLinkHandler | undefined;
1323
- resetRuntime(): void;
1324
- private _trySetupReturnDispatch;
1325
- }
1326
- //#endregion
1327
- //#region src/ActionDefinition/Domain/ActionDomainBase.d.ts
1328
- declare abstract class ActionDomainBase<ACT_DOM extends IActionDomain = IActionDomain> implements IActionDomain<ACT_DOM["allDomains"], ACT_DOM["actionSchema"]> {
1329
- readonly domain: ACT_DOM["domain"];
1330
- readonly allDomains: ACT_DOM["allDomains"];
1331
- readonly actionSchema: ACT_DOM["actionSchema"];
1332
- protected _listeners: TRunningActionUpdateListener<any, any>[];
1333
- constructor(definition: ACT_DOM);
1334
- /**
1335
- * Add an observer that is called after every action dispatched through this domain.
1336
- * Returns an unsubscribe function — call it to remove the listener.
1337
- */
1338
- addActionListener(listener: TDistributeRunningActionUpdateListener<ACT_DOM, keyof ACT_DOM["actionSchema"] & string>): () => void;
1201
+ //#region src/ActionRuntime/Transport/Carrier/Carrier.types.d.ts
1202
+ /**
1203
+ * Carrier shapes — the only transport-specific surface a new protocol must implement. The secure
1204
+ * session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;
1205
+ * a carrier just moves frames. Two shapes capture every carrier:
1206
+ *
1207
+ * - {@link IDuplexCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,
1208
+ * Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client
1209
+ * pushes (the return path + broadcast).
1210
+ * - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push
1211
+ * (HTTP, and anything request/response-shaped). The reply rides the response to its own request.
1212
+ *
1213
+ * Frames are `string` (text handshake control messages and JSON action frames) or binary
1214
+ * (`Uint8Array`/`ArrayBuffer` — the optimized binary wire / encrypted frames).
1215
+ */
1216
+ type TFrame$1 = string | Uint8Array | ArrayBuffer;
1217
+ /**
1218
+ * A bidirectional, push-capable byte stream. Reduces every duplex carrier to "open, send bytes, receive
1219
+ * bytes, close" a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe
1220
+ * all satisfy this, so the identical secure session runs over each.
1221
+ */
1222
+ interface IDuplexCarrier {
1223
+ /** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */
1224
+ readonly ready: Promise<void>;
1225
+ /** Whether the carrier is currently open (a synchronous guard before `send`). */
1226
+ isOpen(): boolean;
1227
+ /** Write one frame to the peer. */
1228
+ send(frame: TFrame$1): void;
1339
1229
  /**
1340
- * @internal
1341
- * Observers registered directly on this domain via {@link addActionListener}.
1342
- * Used to wire observers (e.g. devtools) onto RunningActions that aren't created
1343
- * through the local-dispatch path — notably inbound actions pushed from a backend
1344
- * or another client over a bidirectional transport.
1230
+ * Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`
1231
+ * receives every inbound frame; `onClose` fires when the carrier goes away.
1345
1232
  */
1346
- _getActionObservers(): TRunningActionUpdateListener<any, any>[];
1233
+ attach(handlers: {
1234
+ onMessage: (frame: TFrame$1) => void;
1235
+ onClose: () => void;
1236
+ onError?: (error: unknown) => void;
1237
+ }): void;
1238
+ /** Close the carrier deliberately (a teardown). */
1239
+ close(): void;
1240
+ /** Optional human-readable endpoint for the devtools route chip. */
1241
+ readonly label?: string;
1347
1242
  }
1348
- //#endregion
1349
- //#region src/ActionDefinition/Domain/ActionRootDomain.d.ts
1350
- declare class ActionRootDomain<ROOT_DOM extends IActionRootDomain = IActionRootDomain> extends ActionDomainBase<ROOT_DOM> {
1351
- readonly domainDefinition: {
1352
- domain: ROOT_DOM["domain"];
1353
- };
1354
- private _actionRuntimeManager;
1355
- constructor(domainDefinition: {
1356
- domain: ROOT_DOM["domain"];
1357
- });
1358
- createChildDomain<SUB_DOM extends IActionDomainChildOptions>(subDomainDef: SUB_DOM & { [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never }): ActionDomain<TActionDomainChildDef<ROOT_DOM, SUB_DOM>>;
1359
- _registerRuntime(runtime: ActionRuntime): void;
1360
- _hasRuntime(runtime: ActionRuntime): boolean;
1361
- getRuntime(clientSpecifier: IRuntimeCoordinate): ActionRuntime | undefined;
1362
- _runAction<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string, ACT extends ActionPayload_Request<DOM, ID> = ActionPayload_Request<DOM, ID>>(actionPayload: ACT, options?: IExecuteActionOptions<DOM, ID>): Promise<RunningAction<DOM, ID>>;
1243
+ /**
1244
+ * A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a
1245
+ * frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the
1246
+ * HTTP transaction), so no correlation id is needed on the wire.
1247
+ */
1248
+ interface IExchangeCarrier {
1249
+ /** Send one frame, await the single correlated reply frame. */
1250
+ exchange(frame: TFrame$1, opts?: {
1251
+ signal?: AbortSignal;
1252
+ }): Promise<TFrame$1>;
1253
+ /** Optional human-readable endpoint for the devtools route chip. */
1254
+ readonly label?: string;
1363
1255
  }
1364
- //#endregion
1365
- //#region src/ActionDefinition/Domain/ActionDomain.d.ts
1366
- type TActionMap<ACT_DOM extends IActionDomain> = { [K in keyof ACT_DOM["actionSchema"] & string]: ActionCore<ACT_DOM, K> };
1367
- declare class ActionDomain<ACT_DOM extends IActionDomain = IActionDomain> extends ActionDomainBase<ACT_DOM> {
1368
- private _rootDomain;
1369
- private readonly _actionMap;
1370
- constructor(definition: ACT_DOM, {
1371
- rootDomain
1372
- }: {
1373
- rootDomain: ActionRootDomain<any>;
1374
- });
1375
- get rootDomain(): ActionRootDomain<any>;
1376
- /**
1377
- * @internal
1378
- * All action observers that should see actions on this domain: the root domain's
1379
- * observers plus this subdomain's own. Mirrors the listener set the local-dispatch
1380
- * path assembles in `runAction`/`_runAction`, so inbound actions (pushed from a
1381
- * backend or another client) can be wired up identically and surface in devtools.
1382
- */
1383
- _collectActionObservers(): TRunningActionUpdateListener<any, any>[];
1384
- _registerRuntime(runtime: ActionRuntime): void;
1385
- createChildDomain<SUB_DOM extends IActionDomainChildOptions>(subDomainDef: SUB_DOM & { [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never }): ActionDomain<TActionDomainChildDef<ACT_DOM, SUB_DOM>>;
1386
- get action(): TActionMap<ACT_DOM>;
1387
- actionsMap(): TActionMap<ACT_DOM>;
1388
- actionForId<ID extends keyof ACT_DOM["actionSchema"] & string>(id: ID): ActionCore<ACT_DOM, ID>;
1389
- wrapAsPartialLocalHandler(wrappedActionExecutor: Partial<TWrappableDomainActionHandler<ACT_DOM>>): ActionLocalHandler;
1390
- wrapAsLocalHandler(wrappedActionExecutor: TWrappableDomainActionHandler<ACT_DOM>): ActionLocalHandler;
1391
- hydrateContext<ID extends keyof ACT_DOM["actionSchema"] & string>(id: ID, contextData: IActionContext_Data_JsonObject): ActionContext<ACT_DOM, ID>;
1392
- isDomainAction<ACT extends IActionBase<any, ACT_DOM, any>>(action: ACT | unknown | null | undefined): action is TDistributedDomainActions<ACT_DOM, ACT>;
1393
- hydrateRequestPayload<ID extends keyof ACT_DOM["actionSchema"] & string, P extends IActionPayload_Request_JsonObject<ACT_DOM, ID>>(serialized: P): TDistributeActionPayload_Request<ACT_DOM, ID>;
1394
- hydrateResultPayload<ID extends keyof ACT_DOM["actionSchema"] & string, R extends IActionPayload_Result_JsonObject<ACT_DOM, ID>>(serialized: R): TDistributeActionPayload_Result<ACT_DOM, ID>;
1395
- hydrateAnyAction<ID extends keyof ACT_DOM["actionSchema"] & string, AJ extends TAction_Any_JsonObject<ACT_DOM, ID>>(actionJson: AJ): TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID>;
1396
- runAction<ID extends keyof ACT_DOM["actionSchema"] & string, ACT extends ActionPayload_Request<ACT_DOM, ID>>(request: ACT, options?: IExecuteActionOptions<ACT_DOM, ID>): Promise<RunningAction<ACT_DOM, ID>>;
1397
- private createActionMap;
1256
+ type TCarrier = IDuplexCarrier | IExchangeCarrier;
1257
+ /**
1258
+ * A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.
1259
+ * Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and passed as a
1260
+ * `carrier` to `connectChannel`'s transports (the internal `transport()` factory drives it) — so adding a
1261
+ * new carrier is "write one of these", nothing else.
1262
+ */
1263
+ interface IDuplexCarrierSource {
1264
+ /** Open (or reuse) the carrier for an action. */
1265
+ open: (input: ITransportRouteActionParams) => IDuplexCarrier;
1266
+ /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
1267
+ getCacheKey?: (input: ITransportRouteActionParams) => string[];
1268
+ /** Devtools route info for an action routed over this carrier. */
1269
+ getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
1270
+ /** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`, `"memory"`). */
1271
+ readonly carrierLabel: string;
1398
1272
  }
1399
- //#endregion
1400
- //#region src/ActionDefinition/Action/Core/ActionCore.d.ts
1401
- declare class ActionCore<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionBase<EActionForm.core, DOM, ID> implements IActionCore<DOM, ID> {
1402
- readonly _domain: ActionDomain<DOM>;
1403
- readonly form = EActionForm.core;
1404
- constructor(_domain: ActionDomain<DOM>, id: ID);
1405
- is<ACT extends IActionBase<any, any, any>>(action: ACT | unknown | null | undefined): action is TNarrowActionType<DOM, ACT, ID>;
1406
- toJsonObject(): IActionBase_JsonObject<EActionForm.core, DOM, ID>;
1407
- request(...args: [TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"]] extends [never] ? [input?: never] : [input: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"]]): ActionPayload_Request<DOM, ID>;
1408
- deserializeInput(serialized: TInferInputFromSchema<DOM["actionSchema"][ID]>["SerdeInput"]): TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"];
1409
- serializeInput(raw: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"]): TInferInputFromSchema<DOM["actionSchema"][ID]>["SerdeInput"];
1410
- validateInput(input: unknown): TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"];
1411
- validateOutput(output: unknown): TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"];
1273
+ /**
1274
+ * The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an
1275
+ * {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by
1276
+ * `httpCarrier` and passed as a `carrier` to `connectChannel`'s transports — adding a new request/reply
1277
+ * protocol is "write one of these". The `shape` tag lets the internal `transport()` factory pick the
1278
+ * duplex vs exchange transport.
1279
+ */
1280
+ interface IExchangeCarrierSource {
1281
+ /** Discriminant so a generic factory can tell an exchange source from a duplex one. */
1282
+ readonly shape: "exchange";
1283
+ /** Open (or reuse) the carrier for an action. */
1284
+ open: (input: ITransportRouteActionParams) => IExchangeCarrier;
1285
+ /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
1286
+ getCacheKey?: (input: ITransportRouteActionParams) => string[];
1287
+ /** Devtools route info for an action routed over this carrier. */
1288
+ getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
1289
+ /** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
1290
+ readonly carrierLabel: string;
1412
1291
  }
1413
1292
  //#endregion
1414
- //#region src/ActionDefinition/Domain/helpers/createRootActionDomain.d.ts
1415
- declare const createActionRootDomain: <ID extends string>(definition: {
1416
- domain: ID;
1417
- }) => ActionRootDomain<IActionRootDomain<ID>>;
1418
- //#endregion
1419
1293
  //#region src/ActionRuntime/Transport/codec/actionWireCodec.d.ts
1420
1294
  /**
1421
1295
  * Shared building blocks for the binary action codecs (the stateless {@link createBinaryWireAdapter} and
@@ -1441,841 +1315,468 @@ interface IActionWireFormat {
1441
1315
  incoming?: (input: string | ArrayBuffer | Uint8Array | Blob) => TActionPayload_Any_JsonObject<any, any> | undefined;
1442
1316
  }
1443
1317
  //#endregion
1444
- //#region src/ActionRuntime/Handler/PeerLink/Acceptor/AcceptorHandler.d.ts
1445
- /** The codec shape `AcceptorHandler` uses to pack/unpack frames — same as the Link transport's. */
1446
- type TActionChannelFormatMessage = IActionWireFormat;
1447
- /** How a connection encodes its frames, remembered so we answer each client in its own dialect. */
1448
- type TActionConnectionEncoding = "json" | "binary";
1449
- /** A connection's restorable identity — what to persist so a binding survives transport eviction. */
1450
- interface IAcceptorConnectionBinding {
1451
- /** Full client coordinate, so `originClient` can be re-injected into frames that omit it. */
1452
- client: IRuntimeCoordinate;
1453
- encoding: TActionConnectionEncoding;
1454
- /**
1455
- * Secure-session state (set once a connection's handshake completes). Persist it alongside the
1456
- * binding so an authenticated/encrypted connection resumes after eviction without re-handshaking —
1457
- * the `keyMaterial` lets the server re-derive the shared key from its own persisted identity.
1458
- */
1459
- secure?: {
1460
- securityLevel: ESecurityLevel;
1461
- linkedClientId: TTypeAndId;
1462
- keyMaterial?: IHandshakeEncryptionKeyMaterial;
1463
- };
1318
+ //#region src/ActionRuntime/Transport/codec/createBinaryWireSessionFactory.d.ts
1319
+ type TFormatMessage = IActionWireFormat;
1320
+ interface IBinaryWireSessionOptions {
1321
+ /** Override how long an unresolved correlation is retained before being swept (ms). */
1322
+ correlationTtlMs?: number;
1464
1323
  }
1465
1324
  /**
1466
- * Server-side secure-channel config. When set, each connection negotiates a level from
1467
- * {@link securityLevel}: an `authenticated`/`encrypted` client must complete the handshake (and is then
1468
- * bound to its *authenticated* coordinate) before any action frame is accepted. A `none` client (only
1469
- * when `none` is in the allowed set) is accepted as-is with a self-asserted identity. For the
1470
- * `encrypted` level the codec source should be a session factory (`createFormatMessage`).
1325
+ * Builds a factory of *stateful, per-connection* codecs for {@link LinkTransport} /
1326
+ * `AcceptorHandler` the maximally compact binary wire. Call the returned factory once per live
1327
+ * connection (each socket on the client, each accepted connection on the server) so every channel
1328
+ * gets its own correlation + identity state.
1329
+ *
1330
+ * On top of everything {@link createBinaryWireAdapter} drops, a session also drops:
1331
+ * - **`cuid`** — replaced by a per-connection integer correlation id. The initiator maps it to its
1332
+ * real cuid; the responder echoes it; each side reconstructs the cuid from its own map. Correlation
1333
+ * only needs to be unique per socket, so a counter suffices.
1334
+ * - **`originClient` after the first request** — the first request each side sends carries its
1335
+ * identity; the peer remembers it and injects it into later frames. Replies omit it entirely (a
1336
+ * reply carries the initiator's own origin, which the initiator already knows).
1337
+ *
1338
+ * Both ends MUST build the factory from the same domains in the same order (positional dictionary).
1339
+ * Text frames still return `undefined` from `incoming`, so JSON clients remain interoperable.
1340
+ *
1341
+ * Hibernation note: after a server connection is evicted its session resets, so a still-connected
1342
+ * client (whose session persists) will keep omitting `originClient`. The server must therefore restore
1343
+ * the connection→client binding from its own store (see `AcceptorHandler.rehydrateConnection`) and
1344
+ * inject `originClient` from there — the session alone can't recover it.
1471
1345
  */
1472
- interface IAcceptorSecurity {
1473
- /**
1474
- * Accepted level(s). A single level is strict; an array is a negotiable allowed set — the server
1475
- * adopts whichever level each client requests (e.g. `[none, authenticated, encrypted]` serves all
1476
- * three over one endpoint).
1346
+ declare function createBinaryWireSessionFactory(domains: ActionDomain<any>[], options?: IBinaryWireSessionOptions): () => TFormatMessage;
1347
+ //#endregion
1348
+ //#region src/ActionRuntime/Channel/ActionChannel.d.ts
1349
+ /**
1350
+ * A transport-agnostic routing contract between two runtimes, declared *by role* rather than by
1351
+ * "client"/"server". The two ends are named for the only asymmetry that survives every carrier (WS,
1352
+ * WebRTC, BLE, raw TCP): which side dials and which side accepts.
1353
+ *
1354
+ * - The **connector** dials out and opens the link ({@link connectChannel}).
1355
+ * - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}).
1356
+ *
1357
+ * `toAcceptor` domains flow connector→acceptor (the classic "request"); `toConnector` domains flow
1358
+ * acceptor→connector (the classic "push"). Both ends derive their routing from the same channel instead
1359
+ * of restating domain lists — and because the contract is independent of how bytes move, the very same
1360
+ * channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback).
1361
+ *
1362
+ * Beyond the routing, a channel also carries its *wire identity* — the per-connection binary codec both
1363
+ * ends build from the same domain list, plus the `dictionaryVersion` the handshake checks for drift.
1364
+ * Whether a given transport runs encrypted is a per-transport choice (see {@link IConnectTransport.secure}
1365
+ * and the acceptor's `securityLevel`), not a property of the channel — so one `defineChannel` definition
1366
+ * serves both plain and secure transports.
1367
+ */
1368
+ interface IActionChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[]> {
1369
+ /**
1370
+ * Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards
1371
+ * them over its transport(s); the acceptor executes them.
1477
1372
  */
1478
- securityLevel: ESecurityLevel | readonly ESecurityLevel[];
1479
- /** This server's crypto identity (verify + exchange key pairs, optionally persisted). */
1480
- link: ClientCryptoKeyLink;
1481
- /** This server's coordinate its identity to clients during the handshake. */
1482
- localCoordinate: IRuntimeCoordinate;
1483
- /** Wire dictionary version; the handshake rejects a client on a mismatch. */
1373
+ toAcceptorDomains: TO_ACCEPTOR;
1374
+ /**
1375
+ * Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local
1376
+ * handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a
1377
+ * bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply
1378
+ * never flow.
1379
+ */
1380
+ toConnectorDomains: TO_CONNECTOR;
1381
+ /** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */
1484
1382
  dictionaryVersion: string;
1485
- /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */
1486
- verifyKeyResolver?: IClientVerifyKeyResolver;
1383
+ /** Per-connection session codec factory (call once per live connection). */
1384
+ createCodec: () => IActionWireFormat;
1487
1385
  }
1488
- interface IAcceptorHandlerBaseOptions<TConn> {
1386
+ /**
1387
+ * Declare a transport-agnostic channel by role — the single source of truth both peers share. Each end
1388
+ * MUST call this with the same domains in the same order (the binary wire dictionary is positional); the
1389
+ * `dictionaryVersion` is derived from those domains unless you pin an explicit one. The wire dictionary
1390
+ * spans `[...toAcceptor, ...toConnector]` in that order, so add new domains to the end of their list to
1391
+ * keep older peers compatible.
1392
+ *
1393
+ * Declare the domains *by role* — `toAcceptor` (connector→acceptor requests) and `toConnector`
1394
+ * (acceptor→connector pushes) — so the routing for both ends is derived from the channel (see
1395
+ * {@link connectChannel} and {@link serveChannel}) instead of being restated at each end. Security is a
1396
+ * per-transport concern, not a channel one, so this same definition is used whether a transport runs
1397
+ * plain or encrypted.
1398
+ */
1399
+ declare function defineChannel<const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [], const TO_CONNECTOR extends readonly ActionDomain<any>[] = []>(options: {
1400
+ /** Domains the connector sends to the acceptor (connector→acceptor requests), in a stable order. */toAcceptor: TO_ACCEPTOR; /** Domains the acceptor pushes to the connector (acceptor→connector), in a stable order. */
1401
+ toConnector: TO_CONNECTOR; /** Pin a human-readable version instead of the derived hash (must match on both ends). */
1402
+ dictionaryVersion?: string; /** Tuning for the per-connection binary session (e.g. correlation TTL). */
1403
+ sessionOptions?: IBinaryWireSessionOptions;
1404
+ }): IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>;
1405
+ type TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
1406
+ type TDomainPushHandlers<D> = D extends ActionDomain<infer DEF> ? Partial<TWrappableDomainActionHandler<DEF>> : never;
1407
+ /**
1408
+ * The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action
1409
+ * handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so
1410
+ * the keys and input types follow the channel definition.
1411
+ */
1412
+ type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> = TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;
1413
+ /**
1414
+ * One transport to the peer, declared by *carrier* — the dial-out dual of `serveChannel`'s acceptor
1415
+ * carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto
1416
+ * identity) into each one, so a descriptor only carries what differs between transports: the carrier and
1417
+ * whether it runs the secure handshake.
1418
+ *
1419
+ * A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier
1420
+ * (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection
1421
+ * prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback).
1422
+ */
1423
+ interface IConnectTransport {
1424
+ /** How to reach the peer — a duplex carrier (push-capable) or an exchange carrier (request/reply). */
1425
+ carrier: IDuplexCarrierSource | IExchangeCarrierSource;
1489
1426
  /**
1490
- * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`).
1491
- * The runtime's return-path dispatch scores incoming actions' `originClient` against this to pick
1492
- * this handler for sending results/pushes back over the right channel.
1427
+ * Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport
1428
+ * draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport
1429
+ * (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`.
1493
1430
  */
1494
- clientEnv: RuntimeCoordinate;
1495
- /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
1496
- send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
1431
+ secure?: boolean;
1432
+ /** Security level for this secure transport; defaults to the connection-level `securityLevel`. */
1433
+ securityLevel?: ESecurityLevel;
1497
1434
  /**
1498
- * The runtime this handler belongs to. When set, {@link AcceptorHandler.broadcast} can be called
1499
- * without threading a runtime through each call. Optional `pushToClient` still takes one explicitly.
1435
+ * Optional availability gate when it returns `false` this transport is skipped and the connection
1436
+ * falls through to the next in preference order, re-evaluated per dispatch. Omit = always available.
1500
1437
  */
1501
- runtime?: ActionRuntime;
1502
- /** Timeout (ms) applied to server-initiated actions awaiting a client response. */
1503
- defaultTimeout?: number;
1438
+ available?: (input: ITransportRouteActionParams) => boolean;
1439
+ /** Override the devtools chip label (defaults to the carrier's own label). */
1440
+ label?: string;
1441
+ }
1442
+ interface IConnectChannelOptions<TO_CONNECTOR extends readonly ActionDomain<any>[]> {
1443
+ /** The peer's runtime coordinate — the acceptor this connection dials. */
1444
+ peer: RuntimeCoordinate;
1504
1445
  /**
1505
- * Called once when a connection is first bound to a client identity. Use it to persist the binding
1506
- * for transports that can resume after eviction e.g. a Durable Object's hibernatable WebSocket:
1507
- * `(ws, binding) => ws.serializeAttachment(binding)` then replay it via {@link AcceptorHandler.rehydrateConnection}
1508
- * when the channel comes back.
1446
+ * The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback).
1447
+ * They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and
1448
+ * falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into
1449
+ * each the dial-out dual of `serveChannel`'s `carriers`.
1509
1450
  */
1510
- onConnectionBound?: (connection: TConn, binding: IAcceptorConnectionBinding) => void;
1451
+ transports: readonly IConnectTransport[];
1511
1452
  /**
1512
- * Enable the authenticated (optionally encrypted) handshake. When omitted, connections are trusted
1513
- * as-is (identity self-asserted) fine for dev / trusted networks.
1453
+ * One backing store for this connection's crypto identity, fanned across every *secure* transport so
1454
+ * they present the same verify/exchange keys. Required when any transport is secure (the default); a
1455
+ * fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an
1456
+ * existing identity.
1514
1457
  */
1515
- security?: IAcceptorSecurity;
1458
+ storage?: StorageAdapter;
1459
+ /** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */
1460
+ link?: ClientCryptoKeyLink;
1461
+ /** Default security level for secure transports; defaults to `authenticated`. */
1462
+ securityLevel?: ESecurityLevel;
1463
+ /** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */
1464
+ onPush?: TChannelPushHandlers<TO_CONNECTOR>;
1465
+ /** Default per-action timeout for this connection. */
1466
+ defaultTimeout?: number;
1516
1467
  }
1517
1468
  /**
1518
- * Provide exactly one codec source:
1519
- * - `formatMessage` a single shared codec for every connection (stateless, e.g. `createBinaryWireAdapter`).
1520
- * - `createFormatMessage` a per-connection factory for stateful codecs (e.g.
1521
- * `createBinaryWireSessionFactory`, whose sessions hold correlation + identity state). Required for the
1522
- * leanest binary wire; the handler creates and caches one codec per connection.
1469
+ * Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is
1470
+ * the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer,
1471
+ * `toConnector` pushes handled locally from `onPush`); the call binds the shared facts the channel's
1472
+ * codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over
1473
+ * `storage`) into every transport in `transports`, so none of them restate the channel or runtime.
1474
+ * List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback):
1475
+ * ```ts
1476
+ * const handler = connectChannel(runtime, lobbyChannel, {
1477
+ * peer: runtime_coordinate_lobby_do,
1478
+ * storage,
1479
+ * transports: [{ carrier: wsCarrier(() => ({ url })) }, { carrier: httpCarrier(...), secure: false }],
1480
+ * onPush: { player_joined: (p) => { … } },
1481
+ * });
1482
+ * ```
1483
+ * Returns the {@link ConnectorHandler} so the caller can later `clearTransportCache()` it.
1523
1484
  */
1524
- type IAcceptorHandlerOptions<TConn> = IAcceptorHandlerBaseOptions<TConn> & ({
1525
- formatMessage: TActionChannelFormatMessage;
1526
- createFormatMessage?: never;
1527
- } | {
1528
- createFormatMessage: () => TActionChannelFormatMessage;
1529
- formatMessage?: never;
1530
- });
1485
+ declare function connectChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[]>(runtime: ActionRuntime, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IConnectChannelOptions<TO_CONNECTOR>): ConnectorHandler;
1486
+ type TDomainAcceptorCases<D, TCtx> = D extends ActionDomain<infer DEF> ? { [ID in keyof DEF["actionSchema"] & string]?: TAcceptorCaseFn<DEF, ID, TCtx> } : never;
1531
1487
  /**
1532
- * A connection-aware execution case (see {@link AcceptorHandler.forConnectionDomainCases}). It receives
1533
- * the primed request plus a per-invocation `context` — whatever the wiring's context mapper produces from
1534
- * the originating connection. The low-level handler passes the raw connection (`TConn | undefined`); the
1535
- * higher-level `serveChannel` enriches it into an `IConnectionContext` (state + broadcast + pushBack). A
1536
- * case may return the action's raw output, a result payload, or nothing (auto-wrapped as an empty
1537
- * success) exactly like a local handler case.
1488
+ * The connection-aware case map for a channel's acceptor side: the merged set of every
1489
+ * connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus a per-action
1490
+ * `context`. `TCtx` is whatever the wiring supplies as that second argument the raw connection
1491
+ * (`TConn | undefined`) for the low-level `acceptChannelConnections`, or an enriched `IConnectionContext`
1492
+ * for `serveChannel`'s `channelCases`. Derived from the channel's `toAcceptorDomains`, so the keys and
1493
+ * input/output types follow the channel.
1538
1494
  */
1539
- type TAcceptorCaseFn<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string, TCtx> = (action: TDistributeActionPayload_Request<DOM, ID>, context: TCtx) => ReturnType<THandleActionExecutionFn<DOM, ID>> | void;
1495
+ type TChannelAcceptorCases<TO_ACCEPTOR extends readonly ActionDomain<any>[], TCtx> = TUnionToIntersection<TDomainAcceptorCases<TO_ACCEPTOR[number], TCtx>>;
1540
1496
  /**
1541
- * The connection-aware case the bare {@link AcceptorHandler} serves: its `context` is the originating
1542
- * client's live connection (resolved from the request's `originClient`, `undefined` if the socket is
1543
- * gone). It's {@link TAcceptorCaseFn} fixed to `TConn | undefined` — the un-enriched shape used by
1544
- * {@link AcceptorHandler.forConnectionDomainCases} and `acceptChannelConnections`.
1497
+ * Register an acceptor handler's execution for a channel straight from its definition: the channel's
1498
+ * `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets
1499
+ * the primed request + the originating connection, as with
1500
+ * {@link AcceptorHandler.forConnectionDomainCases}). The domain list is taken from the channel,
1501
+ * never restated. Add the returned handler to the runtime alongside the acceptor handler:
1502
+ * ```ts
1503
+ * runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]);
1504
+ * ```
1505
+ *
1506
+ * The case's second argument is the raw connection (`TConn | undefined`). For the richer state +
1507
+ * broadcast + pushBack context, serve the channel through `serveChannel`'s `channelCases` instead.
1545
1508
  */
1546
- type TAcceptorConnectionCaseFn<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string, TConn> = TAcceptorCaseFn<DOM, ID, TConn | undefined>;
1509
+ declare function acceptChannelConnections<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn>(serverHandler: AcceptorHandler<TConn>, channel: IActionChannel<TO_ACCEPTOR, any>, cases: TChannelAcceptorCases<TO_ACCEPTOR, TConn | undefined>): ActionLocalHandler;
1547
1510
  /**
1548
- * Server-side handler for backends that accept many client connections over a single open channel
1549
- * (WebSockets, Durable Objects, …). It is transport-agnostic: you feed it inbound frames with
1550
- * {@link receive} and tell it how to write outbound frames via the `send` option.
1551
- *
1552
- * Add it alongside your local execution handler:
1511
+ * {@link acceptChannel}'s options the secure-acceptor builder options minus the `channel` and `runtime`
1512
+ * it already takes positionally. One option bag, shared with the underlying {@link createSecureAcceptorHandler}.
1513
+ */
1514
+ type IAcceptChannelOptions<TConn> = Omit<ISecureAcceptorHandlerOptions<TConn>, "channel" | "runtime">;
1515
+ /**
1516
+ * Build the secure {@link AcceptorHandler} for a channel — the accept-in counterpart to
1517
+ * {@link connectChannel}. It folds in the boilerplate of {@link createSecureAcceptorHandler} (the
1518
+ * `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storage`, the channel's codec +
1519
+ * dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel,
1520
+ * options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution:
1553
1521
  * ```ts
1554
- * const serverHandler = createAcceptorHandler({ clientEnv, formatMessage, send: (ws, f) => ws.send(f) });
1555
- * runtime.addHandlers([localHandler, serverHandler]);
1556
- * // per inbound message (e.g. a Durable Object's webSocketMessage):
1557
- * serverHandler.receive(ws, message);
1522
+ * const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storage, send });
1523
+ * runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]);
1558
1524
  * ```
1559
- *
1560
- * Inbound requests route to your local handler; the runtime's return dispatch then calls this
1561
- * handler back (it is an external handler keyed to `clientEnv`) to send the result to the originating
1562
- * connection. The handler keeps a per-connection identity registry so each result lands on the right
1563
- * socket, and remembers each connection's encoding so binary and JSON clients can share the channel.
1564
- *
1565
- * It registers an empty action router, so it is never chosen to *execute* an inbound request — only
1566
- * to ferry results/pushes back out.
1567
1525
  */
1568
- declare class AcceptorHandler<TConn = unknown> extends PeerLinkHandler {
1569
- /** Accept-in over a live (duplex) connection registry — it pushes results/broadcasts to bound sockets. */
1570
- readonly canPush = true;
1571
- private readonly _formatMessage?;
1572
- private readonly _createFormatMessage?;
1573
- private readonly _send;
1574
- private readonly _runtime?;
1575
- private readonly _serverTimeout;
1576
- private _onConnectionBound?;
1577
- private readonly _security?;
1578
- /** Normalized accepted levels; whether `none` (plain) is allowed; whether any level needs a handshake. */
1579
- private readonly _allowedLevels;
1580
- private readonly _noneAllowed;
1581
- private readonly _handshakeMode;
1582
- private readonly _connByClient;
1583
- private readonly _clientByConn;
1584
- private readonly _connEncoding;
1585
- private readonly _codecByConn;
1586
- private readonly _sessionByConn;
1587
- constructor(options: IAcceptorHandlerOptions<TConn>);
1526
+ declare function acceptChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn = unknown>(runtime: ActionRuntime, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IAcceptChannelOptions<TConn>): AcceptorHandler<TConn>;
1527
+ //#endregion
1528
+ //#region src/ActionRuntime/ActionRuntime.types.d.ts
1529
+ interface IRuntimeMeta {
1530
+ assumed: boolean;
1531
+ runtimeName: RuntimeName;
1532
+ }
1533
+ interface IActionRuntimeManagerContext {
1534
+ domain?: string;
1535
+ }
1536
+ type TActionRuntimeHandler = ActionLocalHandler | PeerLinkHandler;
1537
+ //#endregion
1538
+ //#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore.d.ts
1539
+ /**
1540
+ * The composite value persisted to a connection's attachment: the consumer's own app state plus the
1541
+ * {@link AcceptorHandler} routing binding. Co-storing them in one slot means a transport whose
1542
+ * sockets outlive process eviction (e.g. a Durable Object's hibernatable WebSocket) recovers both the
1543
+ * application identity *and* the action routing from a single attachment after a wake — no storage reads.
1544
+ */
1545
+ interface IConnectionAttachment<TApp> {
1546
+ app?: TApp;
1547
+ binding?: IAcceptorConnectionBinding;
1548
+ }
1549
+ interface IConnectionStateStoreOptions<TConn, TApp> {
1550
+ /** Read a connection's raw attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
1551
+ read: (connection: TConn) => unknown;
1552
+ /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
1553
+ write: (connection: TConn, value: IConnectionAttachment<TApp>) => void;
1588
1554
  /**
1589
- * The codec for a connection: a per-connection session (cached) when a factory was provided, else
1590
- * the single shared `formatMessage`.
1555
+ * All currently-live connections (e.g. `() => ctx.getWebSockets()`). Used to replay routing bindings
1556
+ * after a wake (via {@link createConnectionStateStore}) and to enumerate app state in
1557
+ * {@link ConnectionStateStore.entries}.
1591
1558
  */
1592
- private _codecFor;
1559
+ getConnections: () => TConn[];
1593
1560
  /**
1594
- * Register (or replace) the connection-bound persistence callback after construction. Used by
1595
- * lifecycle helpers like {@link createHibernatableWsServerAdapter} so persistence and replay are
1596
- * owned by one place instead of being split across the constructor options.
1597
- */
1598
- setOnConnectionBound(onConnectionBound: (connection: TConn, binding: IAcceptorConnectionBinding) => void): void;
1599
- /**
1600
- * Feed one inbound frame from a connection into the runtime. Decodes text or binary, binds the
1601
- * connection to the requesting client's identity, then routes it (requests execute locally;
1602
- * results/progress resolve pending server-initiated actions).
1603
- */
1604
- receive(connection: TConn, frame: string | ArrayBuffer | Uint8Array): void;
1605
- private _receivePlain;
1606
- /**
1607
- * The secure session for a connection (built lazily on its first secure-mode frame), with the
1608
- * handler-owned effects — raw send, identity binding + persistence, and inbound routing — wired in as
1609
- * callbacks. The session owns all crypto/handshake/chain state; the handler keeps only the registry.
1610
- */
1611
- private _sessionFor;
1612
- /** Bind + persist a connection's authenticated identity once its handshake completes. */
1613
- private _onConnectionAuthenticated;
1614
- /** Decode a decrypted authenticated frame, inject the *authenticated* identity, and route it. */
1615
- private _routeAuthedActionBytes;
1616
- /**
1617
- * Ensure an inbound request carries the client's identity and that this connection is bound to it,
1618
- * so its result can be routed back. A session codec omits `originClient` after the first request, so
1619
- * when it's missing we restore it from the (possibly rehydrated) binding instead. (Plain mode only;
1620
- * secure mode binds the authenticated coordinate at handshake time.)
1621
- */
1622
- private _resolveRequestIdentity;
1623
- /**
1624
- * Restore a connection→client binding without an inbound frame — for transports that resume after
1625
- * eviction. Pair it with the {@link IAcceptorHandlerOptions.onConnectionBound} hook: persist
1626
- * the binding there, then replay each live connection here when the channel comes back (e.g. a
1627
- * Durable Object iterating `ctx.getWebSockets()` as it wakes from hibernation).
1628
- */
1629
- rehydrateConnection(connection: TConn, binding: IAcceptorConnectionBinding): void;
1630
- toJsonObject(): IActionHandler_Peer_Json;
1631
- toHandlerRouteItem(): IActionRouteItemHandler;
1632
- /** Forget a connection (call on socket close) so stale entries don't misroute later results. */
1633
- dropConnection(connection: TConn): void;
1634
- /** Live connection for a client coordinate, if currently registered. */
1635
- getConnectionForClient(client: RuntimeCoordinate): TConn | undefined;
1636
- /** This acceptor owns the origin's return path when it currently holds a live connection bound to it. */
1637
- ownsLiveConnectionFor(origin: RuntimeCoordinate): boolean;
1638
- /** Whether this acceptor currently tracks `connection` — used to pick the owning handler among several. */
1639
- hasConnection(connection: TConn): boolean;
1640
- /**
1641
- * Send (and optionally await) a server-initiated action to a specific connected client. Pass the
1642
- * connection token directly (e.g. the `ws`) or a client `RuntimeCoordinate` to look one up.
1643
- */
1644
- pushToClient<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(runtime: ActionRuntime, target: TConn | RuntimeCoordinate, request: ActionPayload_Request<DOM, ID>, options?: {
1645
- timeout?: number;
1646
- }): RunningAction<DOM, ID>;
1647
- /**
1648
- * Build a local handler whose cases are connection-aware: each case receives the primed request and
1649
- * the originating client's live connection (resolved from `originClient`), so handlers don't repeat
1650
- * the `getConnectionForClient(action.context.originClient)` lookup. Cases may return raw output or
1651
- * nothing, just like {@link ActionLocalHandler.forDomainActionCases}. Add the returned handler to the
1652
- * runtime alongside this server handler:
1653
- * ```ts
1654
- * runtime.addHandlers([serverHandler.forConnectionDomainCases(domain, { … }), serverHandler]);
1655
- * ```
1656
- */
1657
- forConnectionDomainCases<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>, cases: { [ID in keyof FOR_DOM["actionSchema"] & string]?: TAcceptorConnectionCaseFn<FOR_DOM, ID, TConn> }): ActionLocalHandler;
1658
- /**
1659
- * Like {@link forConnectionDomainCases} but spanning several domains with one merged case map — used
1660
- * by channel-derived wiring (`acceptChannelConnections` / `serveChannel`) where the channel's
1661
- * `toAcceptor` domains are served together. Each domain takes only the cases whose ids it owns, so a
1662
- * single map can cover several domains and unrelated ids are ignored.
1663
- *
1664
- * `mapContext` turns the resolved connection into whatever the case's second argument should be: the
1665
- * raw connection for the low-level helper, or an enriched `IConnectionContext` for `serveChannel`. It's
1666
- * called once per inbound action, after the originating connection is resolved.
1667
- */
1668
- forConnectionDomainCasesMulti<TCtx>(domains: readonly ActionDomain<any>[], cases: Record<string, TAcceptorCaseFn<any, any, TCtx> | undefined>, mapContext: (connection: TConn | undefined, request: ActionPayload_Request<any, any>) => TCtx): ActionLocalHandler;
1669
- /**
1670
- * Fan a server-initiated request out to every currently-bound connection. A fresh request is built
1671
- * per connection (each push mutates its own action context) and dispatched fire-and-forget. Pass
1672
- * `except` to skip the originating socket and `where` to filter by connection (e.g. read its
1673
- * attachment for a role). Iterating bound connections (rather than every accepted socket) skips
1674
- * sockets that are still mid-handshake and so can't yet receive a frame.
1561
+ * Optional Standard Schema (valibot, zod, …) validating the *app* portion on read. A value that
1562
+ * fails validation reads back as `null` the same lenient behavior as a hand-written safeParse
1563
+ * helper. The binding is the library's own shape and is never validated.
1675
1564
  */
1676
- broadcast<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(makeRequest: () => ActionPayload_Request<DOM, ID>, options?: {
1677
- runtime?: ActionRuntime;
1678
- except?: TConn | null;
1679
- where?: (connection: TConn) => boolean;
1680
- timeout?: number;
1681
- onError?: (error: unknown, connection: TConn) => void;
1682
- }): void;
1683
- sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
1684
- targetLocalRuntime: ActionRuntime;
1685
- }): Promise<boolean>;
1686
- handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
1687
- private _dispatch;
1688
- private _sendPayload;
1689
- private _bindConnection;
1690
- private _resolveConnection;
1691
- private _resolveSingleConnection;
1565
+ schema?: StandardSchemaV1<unknown, TApp>;
1692
1566
  }
1693
- declare const createAcceptorHandler: <TConn = unknown>(options: IAcceptorHandlerOptions<TConn>) => AcceptorHandler<TConn>;
1694
- //#endregion
1695
- //#region src/ActionRuntime/Transport/Carrier/Carrier.types.d.ts
1696
1567
  /**
1697
- * Carrier shapes the only transport-specific surface a new protocol must implement. The secure
1698
- * session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;
1699
- * a carrier just moves frames. Two shapes capture every carrier:
1568
+ * A typed per-connection state store that co-owns the app state and the acceptor handler's routing
1569
+ * binding in one attachment, so neither the consumer nor the handler has to hand-merge the two. Create
1570
+ * it through {@link createConnectionStateStore} (which also wires binding persistence and replays
1571
+ * surviving connections after a wake), then `get`/`set`/`clearApp` the app state directly.
1700
1572
  *
1701
- * - {@link IDuplexCarrier}a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,
1702
- * Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client
1703
- * pushes (the return path + broadcast).
1704
- * - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push
1705
- * (HTTP, and anything request/response-shaped). The reply rides the response to its own request.
1573
+ * The mechanism is carrier-neutral it only needs read/write/enumerate callbacks for the connection's
1574
+ * attachment but it pays off on transports whose connections outlive process eviction (e.g. a
1575
+ * Durable Object's hibernatable WebSockets), which is why it lives beside the hibernation adapter.
1706
1576
  *
1707
- * Frames are `string` (text — handshake control messages and JSON action frames) or binary
1708
- * (`Uint8Array`/`ArrayBuffer` the optimized binary wire / encrypted frames).
1709
- */
1710
- type TFrame$1 = string | Uint8Array | ArrayBuffer;
1711
- /**
1712
- * A bidirectional, push-capable byte stream. Reduces every duplex carrier to "open, send bytes, receive
1713
- * bytes, close" — a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe
1714
- * all satisfy this, so the identical secure session runs over each.
1577
+ * ```ts
1578
+ * const players = createConnectionStateStore(serverHandler, {
1579
+ * schema: vs_player,
1580
+ * read: (ws) => ws.deserializeAttachment(),
1581
+ * write: (ws, v) => ws.serializeAttachment(v),
1582
+ * getConnections: () => ctx.getWebSockets(),
1583
+ * });
1584
+ * players.set(ws, player); // binding is preserved automatically
1585
+ * const player = players.get(ws);
1586
+ * ```
1715
1587
  */
1716
- interface IDuplexCarrier {
1717
- /** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */
1718
- readonly ready: Promise<void>;
1719
- /** Whether the carrier is currently open (a synchronous guard before `send`). */
1720
- isOpen(): boolean;
1721
- /** Write one frame to the peer. */
1722
- send(frame: TFrame$1): void;
1723
- /**
1724
- * Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`
1725
- * receives every inbound frame; `onClose` fires when the carrier goes away.
1726
- */
1727
- attach(handlers: {
1728
- onMessage: (frame: TFrame$1) => void;
1729
- onClose: () => void;
1730
- onError?: (error: unknown) => void;
1731
- }): void;
1732
- /** Close the carrier deliberately (a teardown). */
1733
- close(): void;
1734
- /** Optional human-readable endpoint for the devtools route chip. */
1735
- readonly label?: string;
1588
+ declare class ConnectionStateStore<TConn, TApp> {
1589
+ private readonly options;
1590
+ constructor(options: IConnectionStateStoreOptions<TConn, TApp>);
1591
+ /** The validated app state for a connection, or `null` if unset / invalid. */
1592
+ get(connection: TConn): TApp | null;
1593
+ /** Set the app state, preserving the runtime binding already pinned to the connection. */
1594
+ set(connection: TConn, app: TApp): void;
1595
+ /** Clear the app state but keep the binding (e.g. a spectator that stopped watching). */
1596
+ clearApp(connection: TConn): void;
1597
+ /** Every live connection paired with its (validated) app state for rebuilding in-memory state after a wake. */
1598
+ entries(): [TConn, TApp | null][];
1599
+ /** @internal Persist a freshly-bound connection's binding, preserving any app state already stored. */
1600
+ _persistBinding(connection: TConn, binding: IAcceptorConnectionBinding): void;
1601
+ /** @internal The persisted binding for a connection, if any (used to replay routing after a wake). */
1602
+ _readBinding(connection: TConn): IAcceptorConnectionBinding | undefined;
1603
+ private _readAttachment;
1604
+ private _validateApp;
1736
1605
  }
1737
1606
  /**
1738
- * A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a
1739
- * frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the
1740
- * HTTP transaction), so no correlation id is needed on the wire.
1607
+ * Build a per-connection {@link ConnectionStateStore} bound to an {@link AcceptorHandler}: it registers
1608
+ * itself as the handler's connection-bound persistence callback (so bindings are written without
1609
+ * overwriting app state) and immediately replays every live connection's stored binding via
1610
+ * {@link AcceptorHandler.rehydrateConnection} — so on a transport that resumes after eviction (e.g. a
1611
+ * Durable Object waking from hibernation) both the app identity and the action routing come back from a
1612
+ * single attachment, with no storage reads and no hand-rolled merge.
1613
+ *
1614
+ * Lives outside the handler so the generic {@link AcceptorHandler} stays free of any attachment/
1615
+ * hibernation concern — it exposes only the neutral `setOnConnectionBound` + `rehydrateConnection`
1616
+ * hooks this builder drives.
1741
1617
  */
1742
- interface IExchangeCarrier {
1743
- /** Send one frame, await the single correlated reply frame. */
1744
- exchange(frame: TFrame$1, opts?: {
1745
- signal?: AbortSignal;
1746
- }): Promise<TFrame$1>;
1747
- /** Optional human-readable endpoint for the devtools route chip. */
1748
- readonly label?: string;
1618
+ declare function createConnectionStateStore<TConn, TApp>(handler: AcceptorHandler<TConn>, options: IConnectionStateStoreOptions<TConn, TApp>): ConnectionStateStore<TConn, TApp>;
1619
+ //#endregion
1620
+ //#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter.d.ts
1621
+ interface IHibernatableWsServerAdapterOptions<TConn> {
1622
+ /** The handler to drive (from `createSecureAcceptorHandler` or `createAcceptorHandler`). */
1623
+ handler: AcceptorHandler<TConn>;
1624
+ /** All currently-live connections — replayed on construction to rebuild bindings after a wake. */
1625
+ getConnections: () => TConn[];
1626
+ /** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */
1627
+ getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined;
1628
+ /** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */
1629
+ setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void;
1749
1630
  }
1750
- type TCarrier = IDuplexCarrier | IExchangeCarrier;
1751
1631
  /**
1752
- * A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.
1753
- * Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and handed to
1754
- * {@link secureTransport} / `LinkTransport` so adding a new carrier is "write one of these", nothing
1755
- * else.
1632
+ * The neutral lifecycle surface for a duplex (push-capable) acceptor: feed it each inbound frame and tell
1633
+ * it when a connection goes away. Carrier-agnostic a WebSocket, a WebRTC data channel, or any other
1634
+ * duplex connection drives the same two methods.
1756
1635
  */
1757
- interface IDuplexCarrierSource {
1758
- /** Open (or reuse) the carrier for an action. */
1759
- open: (input: ITransportRouteActionParams) => IDuplexCarrier;
1760
- /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
1761
- getCacheKey?: (input: ITransportRouteActionParams) => string[];
1762
- /** Devtools route info for an action routed over this carrier. */
1763
- getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
1764
- /** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`, `"memory"`). */
1765
- readonly carrierLabel: string;
1636
+ interface IDuplexConnectionRouter<TConn> {
1637
+ /** Feed one inbound frame from a connection into the handler. */
1638
+ receive: (connection: TConn, frame: string | ArrayBuffer | Uint8Array) => void;
1639
+ /** Forget a connection (call on socket close/error). */
1640
+ drop: (connection: TConn) => void;
1766
1641
  }
1767
1642
  /**
1768
- * The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an
1769
- * {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by
1770
- * `httpCarrier` and handed to {@link secureTransport} adding a new request/reply protocol is "write
1771
- * one of these". The `shape` tag lets {@link secureTransport} pick the duplex vs exchange transport.
1643
+ * Wire the hibernation lifecycle for an acceptor handler on a transport whose connections outlive process
1644
+ * eviction (e.g. a Durable Object's hibernatable WebSockets). It owns persistence end to end:
1645
+ * registers `setAttachment` as the handler's connection-bound callback and immediately replays every
1646
+ * live connection's stored binding via `getAttachment`, so results/pushes still route after a wake.
1647
+ *
1648
+ * Layered on top of the generic {@link AcceptorHandler} — it touches only the handler's neutral
1649
+ * `setOnConnectionBound` / `rehydrateConnection` / `receive` / `dropConnection` surface, so no
1650
+ * hibernation concern leaks into the handler itself.
1651
+ *
1652
+ * Construct it once when the handler is built, then forward connection events:
1653
+ * ```ts
1654
+ * const duplex = createHibernatableWsServerAdapter({ handler, getConnections, getAttachment, setAttachment });
1655
+ * // webSocketMessage(ws, msg) => duplex.receive(ws, msg);
1656
+ * // webSocketClose/Error(ws) => duplex.drop(ws);
1657
+ * ```
1772
1658
  */
1773
- interface IExchangeCarrierSource {
1774
- /** Discriminant so a generic factory can tell an exchange source from a duplex one. */
1775
- readonly shape: "exchange";
1776
- /** Open (or reuse) the carrier for an action. */
1777
- open: (input: ITransportRouteActionParams) => IExchangeCarrier;
1778
- /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
1779
- getCacheKey?: (input: ITransportRouteActionParams) => string[];
1780
- /** Devtools route info for an action routed over this carrier. */
1781
- getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
1782
- /** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
1783
- readonly carrierLabel: string;
1784
- }
1659
+ declare function createHibernatableWsServerAdapter<TConn>(options: IHibernatableWsServerAdapterOptions<TConn>): IDuplexConnectionRouter<TConn>;
1785
1660
  //#endregion
1786
- //#region src/ActionRuntime/Transport/codec/createBinaryWireSessionFactory.d.ts
1787
- type TFormatMessage = IActionWireFormat;
1788
- interface IBinaryWireSessionOptions {
1789
- /** Override how long an unresolved correlation is retained before being swept (ms). */
1790
- correlationTtlMs?: number;
1791
- }
1661
+ //#region src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.d.ts
1792
1662
  /**
1793
- * Builds a factory of *stateful, per-connection* codecs for {@link LinkTransport} /
1794
- * `AcceptorHandler` the maximally compact binary wire. Call the returned factory once per live
1795
- * connection (each socket on the client, each accepted connection on the server) so every channel
1796
- * gets its own correlation + identity state.
1797
- *
1798
- * On top of everything {@link createBinaryWireAdapter} drops, a session also drops:
1799
- * - **`cuid`** — replaced by a per-connection integer correlation id. The initiator maps it to its
1800
- * real cuid; the responder echoes it; each side reconstructs the cuid from its own map. Correlation
1801
- * only needs to be unique per socket, so a counter suffices.
1802
- * - **`originClient` after the first request** — the first request each side sends carries its
1803
- * identity; the peer remembers it and injects it into later frames. Replies omit it entirely (a
1804
- * reply carries the initiator's own origin, which the initiator already knows).
1805
- *
1806
- * Both ends MUST build the factory from the same domains in the same order (positional dictionary).
1807
- * Text frames still return `undefined` from `incoming`, so JSON clients remain interoperable.
1663
+ * Acceptor-side carrier descriptors the accept-in dual of the connector's {@link IDuplexCarrierSource}
1664
+ * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an
1665
+ * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral
1666
+ * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block
1667
+ * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates
1668
+ * it.
1808
1669
  *
1809
- * Hibernation note: after a server connection is evicted its session resets, so a still-connected
1810
- * client (whose session persists) will keep omitting `originClient`. The server must therefore restore
1811
- * the connection→client binding from its own store (see `AcceptorHandler.rehydrateConnection`) and
1812
- * inject `originClient` from there — the session alone can't recover it.
1813
- */
1814
- declare function createBinaryWireSessionFactory(domains: ActionDomain<any>[], options?: IBinaryWireSessionOptions): () => TFormatMessage;
1815
- //#endregion
1816
- //#region src/ActionRuntime/Channel/secureChannel.d.ts
1817
- /** The per-connection binary session codec — built once per socket from the channel's domains. */
1818
- type TChannelCodec = IActionWireFormat;
1819
- /**
1820
- * The shared identity of a secure channel (carrier-agnostic — WS, WebRTC, HTTP, …): the wire
1821
- * dictionary version both ends check during the handshake, plus the per-connection codec factory both
1822
- * ends build from the *same* domain list. Define it once (typically in code shared by both peers) and
1823
- * hand it to `secureTransport` on the connector and `serveChannel` (or the lower-level `acceptChannel` /
1824
- * `createSecureAcceptorHandler`) on the acceptor, so the codec and version can never drift apart.
1825
- */
1826
- interface ISecureChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[]> extends IActionChannel<TO_ACCEPTOR, TO_CONNECTOR> {
1827
- /** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */
1828
- dictionaryVersion: string;
1829
- /** Per-connection session codec factory (call once per live connection). */
1830
- createCodec: () => TChannelCodec;
1831
- }
1832
- /**
1833
- * Bundle a secure channel's shared identity from its transported domains. Both ends MUST call this
1834
- * with the same domains in the same order (the binary wire dictionary is positional). The
1835
- * `dictionaryVersion` is derived from those domains unless you pin an explicit one.
1670
+ * Two shapes mirror the connector side:
1836
1671
  *
1837
- * Declare the domains *by role* `toAcceptor` (connector→acceptor requests) and `toConnector`
1838
- * (acceptor→connector pushes) so the routing for both ends is derived from the channel (see
1839
- * {@link connectChannel} and `acceptChannelConnections`) instead of being restated at each end. The
1840
- * wire dictionary spans `[...toAcceptor, ...toConnector]` in that order; add new domains to the end of
1841
- * their list to keep older peers compatible. (`domains` is still accepted as a legacy alias for
1842
- * `toAcceptor`.)
1672
+ * - {@link IDuplexAcceptorCarrier} a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can
1673
+ * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step
1674
+ * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.
1675
+ * - {@link IExchangeAcceptorCarrier} a request single-correlated-reply carrier with no unsolicited push
1676
+ * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to
1677
+ * upgrade.
1843
1678
  */
1844
- declare function defineSecureChannel<const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [], const TO_CONNECTOR extends readonly ActionDomain<any>[] = []>(options: {
1845
- /** Domains the connector sends to the acceptor (connector→acceptor requests), in a stable order. */toAcceptor: TO_ACCEPTOR; /** Domains the acceptor pushes to the connector (acceptor→connector), in a stable order. */
1846
- toConnector: TO_CONNECTOR; /** Pin a human-readable version instead of the derived hash (must match on both ends). */
1847
- dictionaryVersion?: string; /** Tuning for the per-connection binary session (e.g. correlation TTL). */
1848
- sessionOptions?: IBinaryWireSessionOptions;
1849
- }): ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>;
1850
- //#endregion
1851
- //#region src/ActionRuntime/Channel/ActionChannel.d.ts
1852
1679
  /**
1853
- * A transport-agnostic routing contract between two runtimes, declared *by role* rather than by
1854
- * "client"/"server". The two ends are named for the only asymmetry that survives every carrier (WS,
1855
- * WebRTC, BLE, raw TCP): which side dials and which side accepts.
1856
- *
1857
- * - The **connector** dials out and opens the link ({@link connectChannel}).
1858
- * - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}).
1859
- *
1860
- * `toAcceptor` domains flow connector→acceptor (the classic "request"); `toConnector` domains flow
1861
- * acceptor→connector (the classic "push"). Both ends derive their routing from the same channel instead
1862
- * of restating domain lists — and because the contract is independent of how bytes move, the very same
1863
- * channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback).
1680
+ * Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections
1681
+ * outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional omit for a
1682
+ * transport that never hibernates (per-connection state is then in-memory only).
1864
1683
  *
1865
- * Build a plain one with {@link defineChannel}; layer wire-specific identity on top with
1866
- * `defineSecureChannel` (which returns an `ISecureChannel` still an `IActionChannel`, so it works
1867
- * everywhere a channel is expected).
1684
+ * `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when
1685
+ * `connectionState` is requested) per-connection app state as one composite in this single slot, so both
1686
+ * survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections.
1868
1687
  */
1869
- interface IActionChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[]> {
1870
- /**
1871
- * Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards
1872
- * them over its transport(s); the acceptor executes them.
1873
- */
1874
- toAcceptorDomains: TO_ACCEPTOR;
1875
- /**
1876
- * Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local
1877
- * handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a
1878
- * bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply
1879
- * never flow.
1880
- */
1881
- toConnectorDomains: TO_CONNECTOR;
1688
+ interface IAcceptorAttachmentStore<TConn> {
1689
+ /** All currently-live connections — enumerated on build to replay binding + app state after a wake. */
1690
+ getConnections: () => TConn[];
1691
+ /** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
1692
+ read: (connection: TConn) => unknown;
1693
+ /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
1694
+ write: (connection: TConn, value: unknown) => void;
1882
1695
  }
1883
1696
  /**
1884
- * Declare a transport-agnostic channel by role. Use it for HTTP, custom transports, or as the routing
1885
- * half of a richer channel. The order of each list is part of the contract for wire formats that pack
1886
- * positionally (see `defineSecureChannel`) add new domains to the end of their list. (`domains` is
1887
- * accepted as a legacy alias for `toAcceptor`.)
1888
- */
1889
- declare function defineChannel<const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [], const TO_CONNECTOR extends readonly ActionDomain<any>[] = []>(options: {
1890
- toAcceptor: TO_ACCEPTOR;
1891
- toConnector: TO_CONNECTOR;
1892
- }): IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>;
1893
- type TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
1894
- type TDomainPushHandlers<D> = D extends ActionDomain<infer DEF> ? Partial<TWrappableDomainActionHandler<DEF>> : never;
1895
- /**
1896
- * The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action
1897
- * handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so
1898
- * the keys and input types follow the channel definition.
1697
+ * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each
1698
+ * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a
1699
+ * server with *several* duplex carriers routes each connection's traffic to the right one you hold the
1700
+ * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if
1701
+ * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)
1899
1702
  */
1900
- type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> = TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;
1703
+ interface IDuplexCarrierLifecycle<TConn> {
1704
+ /** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */
1705
+ receive(connection: TConn, frame: TFrame$1): void;
1706
+ /** Forget a connection on close/error. No-op until the carrier is served. */
1707
+ drop(connection: TConn): void;
1708
+ /** @internal `serveChannel` binds this carrier's live connection router here. */
1709
+ _activate(router: IDuplexConnectionRouter<TConn>): void;
1710
+ }
1901
1711
  /**
1902
- * One transport to the peer, declared by *carrier* the dial-out dual of `serveChannel`'s acceptor
1903
- * carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto
1904
- * identity) into each one, so a descriptor only carries what differs between transports: the carrier and
1905
- * whether it runs the secure handshake.
1906
- *
1907
- * A duplex carrier (`wsCarrier(() => ({ url }))`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier
1908
- * (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection
1909
- * prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback).
1712
+ * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live
1713
+ * connection, how to perform the transport-specific upgrade that admits one, which requests are such
1714
+ * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and
1715
+ * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see
1716
+ * {@link IDuplexCarrierLifecycle}).
1910
1717
  */
1911
- interface IConnectTransport {
1912
- /** How to reach the peer a duplex carrier (push-capable) or an exchange carrier (request/reply). */
1913
- carrier: IDuplexCarrierSource | IExchangeCarrierSource;
1718
+ interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {
1719
+ /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */
1720
+ readonly shape: ETransportShape.duplex;
1914
1721
  /**
1915
- * Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport
1916
- * draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport
1917
- * (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`.
1722
+ * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex
1723
+ * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no
1724
+ * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain
1725
+ * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.
1918
1726
  */
1919
1727
  secure?: boolean;
1920
- /** Security level for this secure transport; defaults to the connection-level `securityLevel`. */
1921
- securityLevel?: ESecurityLevel;
1728
+ /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
1729
+ send: (connection: TConn, frame: TFrame$1) => void;
1922
1730
  /**
1923
- * Optional availability gate when it returns `false` this transport is skipped and the connection
1924
- * falls through to the next in preference order, re-evaluated per dispatch. Omit = always available.
1731
+ * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a
1732
+ * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` a `101`). Omit for a carrier that
1733
+ * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).
1925
1734
  */
1926
- available?: (input: ITransportRouteActionParams) => boolean;
1927
- /** Override the devtools chip label (defaults to the carrier's own label). */
1928
- label?: string;
1929
- }
1930
- interface IConnectChannelOptions<TO_CONNECTOR extends readonly ActionDomain<any>[]> {
1931
- /** The peer's runtime coordinate — the acceptor this connection dials. */
1932
- peer: RuntimeCoordinate;
1735
+ upgrade?: (request: Request, url: URL) => Response | Promise<Response>;
1933
1736
  /**
1934
- * The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback).
1935
- * They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and
1936
- * falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into
1937
- * each — the dial-out dual of `serveChannel`'s `carriers`.
1737
+ * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.
1738
+ * Only consulted when {@link upgrade} is present.
1938
1739
  */
1939
- transports: readonly IConnectTransport[];
1740
+ isUpgrade?: (request: Request, url: URL) => boolean;
1940
1741
  /**
1941
- * One backing store for this connection's crypto identity, fanned across every *secure* transport so
1942
- * they present the same verify/exchange keys. Required when any transport is secure (the default); a
1943
- * fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an
1944
- * existing identity.
1742
+ * Optional attachment read/write for connections that survive eviction (Durable Object hibernation).
1743
+ * Present `serveChannel` persists the routing binding (and any `connectionState`) here and replays it
1744
+ * on wake. Absent per-connection state is in-memory only.
1945
1745
  */
1946
- storage?: StorageAdapter;
1947
- /** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */
1948
- link?: ClientCryptoKeyLink;
1949
- /** Default security level for secure transports; defaults to `authenticated`. */
1950
- securityLevel?: ESecurityLevel;
1951
- /** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */
1952
- onPush?: TChannelPushHandlers<TO_CONNECTOR>;
1953
- /** Default per-action timeout for this connection. */
1954
- defaultTimeout?: number;
1746
+ attachmentStore?: IAcceptorAttachmentStore<TConn>;
1747
+ /** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`). */
1748
+ readonly carrierLabel: string;
1955
1749
  }
1956
1750
  /**
1957
- * Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is
1958
- * the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer,
1959
- * `toConnector` pushes handled locally from `onPush`); the call binds the shared facts the channel's
1960
- * codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over
1961
- * `storage`) into every transport in `transports`, so none of them restate the channel or runtime.
1962
- * List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback):
1963
- * ```ts
1964
- * const handler = connectChannel(runtime, lobbyChannel, {
1965
- * peer: runtime_coordinate_lobby_do,
1966
- * storage,
1967
- * transports: [{ carrier: wsCarrier(() => ({ url })) }, { carrier: httpCarrier(...), secure: false }],
1968
- * onPush: { player_joined: (p) => { … } },
1969
- * });
1970
- * ```
1971
- * Returns the {@link ConnectorHandler} so the caller can later `clearTransportCache()` it.
1972
- */
1973
- declare function connectChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[]>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IConnectChannelOptions<TO_CONNECTOR>): ConnectorHandler;
1974
- type TDomainAcceptorCases<D, TCtx> = D extends ActionDomain<infer DEF> ? { [ID in keyof DEF["actionSchema"] & string]?: TAcceptorCaseFn<DEF, ID, TCtx> } : never;
1975
- /**
1976
- * The connection-aware case map for a channel's acceptor side: the merged set of every
1977
- * connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus a per-action
1978
- * `context`. `TCtx` is whatever the wiring supplies as that second argument — the raw connection
1979
- * (`TConn | undefined`) for the low-level `acceptChannelConnections`, or an enriched `IConnectionContext`
1980
- * for `serveChannel`'s `channelCases`. Derived from the channel's `toAcceptorDomains`, so the keys and
1981
- * input/output types follow the channel.
1982
- */
1983
- type TChannelAcceptorCases<TO_ACCEPTOR extends readonly ActionDomain<any>[], TCtx> = TUnionToIntersection<TDomainAcceptorCases<TO_ACCEPTOR[number], TCtx>>;
1984
- /**
1985
- * Register an acceptor handler's execution for a channel straight from its definition: the channel's
1986
- * `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets
1987
- * the primed request + the originating connection, as with
1988
- * {@link AcceptorHandler.forConnectionDomainCases}). The domain list is taken from the channel,
1989
- * never restated. Add the returned handler to the runtime alongside the acceptor handler:
1990
- * ```ts
1991
- * runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]);
1992
- * ```
1993
- *
1994
- * The case's second argument is the raw connection (`TConn | undefined`). For the richer state +
1995
- * broadcast + pushBack context, serve the channel through `serveChannel`'s `channelCases` instead.
1751
+ * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By
1752
+ * default it speaks the *secure* exchange protocol (handshake token session encrypted frames), whose
1753
+ * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint
1754
+ * that POSTs the raw action wire and returns the result inline the request/reply dual of the connector's
1755
+ * plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). So a server can pair a secure
1756
+ * duplex (WebSocket) with a plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.
1996
1757
  */
1997
- declare function acceptChannelConnections<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn>(serverHandler: AcceptorHandler<TConn>, channel: IActionChannel<TO_ACCEPTOR, any>, cases: TChannelAcceptorCases<TO_ACCEPTOR, TConn | undefined>): ActionLocalHandler;
1998
- interface IAcceptChannelOptions<TConn> {
1999
- /**
2000
- * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
2001
- * used to score return-path dispatch back to the right connection.
2002
- */
2003
- clientEnv: RuntimeCoordinate;
1758
+ interface IExchangeAcceptorCarrier {
1759
+ /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */
1760
+ readonly shape: ETransportShape.exchange;
2004
1761
  /**
2005
- * One backing store for the acceptor's crypto identity *and* its trust-on-first-use verify-key pins
2006
- * (their keys don't collide). Back it with persistent storage so identity + pins survive eviction.
1762
+ * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain
1763
+ * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,
1764
+ * or encryption. A plain endpoint ignores the central crypto identity entirely.
2007
1765
  */
2008
- storageAdapter: StorageAdapter;
2009
- /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
2010
- send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
1766
+ secure?: boolean;
1767
+ /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */
1768
+ isActionPath?: (url: URL) => boolean;
2011
1769
  /**
2012
- * The acceptor's crypto identity. Defaults to a fresh `ClientCryptoKeyLink` over `storageAdapter`.
2013
- * Pass an existing link to share one identity across several acceptors on the same server (e.g. this
2014
- * WebSocket acceptor and a secure-HTTP `createActionFetchHandler`), so they present the same keys.
1770
+ * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the
1771
+ * permissive `*` set; pass `false` to attach no CORS headers at all.
2015
1772
  */
2016
- link?: ClientCryptoKeyLink;
2017
- /** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */
2018
- securityLevel?: ESecurityLevel | readonly ESecurityLevel[];
2019
- /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storageAdapter`. */
2020
- verifyKeyResolver?: IClientVerifyKeyResolver;
2021
- /** Timeout (ms) applied to acceptor-initiated actions awaiting a client response. */
2022
- defaultTimeout?: number;
1773
+ cors?: Record<string, string> | false;
1774
+ /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */
1775
+ useErrorStatus?: boolean;
1776
+ /** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
1777
+ readonly carrierLabel: string;
2023
1778
  }
2024
- /**
2025
- * Build the secure {@link AcceptorHandler} for a channel — the accept-in counterpart to
2026
- * {@link connectChannel}. It folds in the same boilerplate as {@link createSecureAcceptorHandler} (the
2027
- * `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storageAdapter`, the channel's codec +
2028
- * dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel,
2029
- * options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution:
2030
- * ```ts
2031
- * const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storageAdapter, send });
2032
- * runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]);
2033
- * ```
2034
- */
2035
- declare function acceptChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn = unknown>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IAcceptChannelOptions<TConn>): AcceptorHandler<TConn>;
2036
- //#endregion
2037
- //#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore.d.ts
2038
- /**
2039
- * The composite value persisted to a connection's attachment: the consumer's own app state plus the
2040
- * {@link AcceptorHandler} routing binding. Co-storing them in one slot means a transport whose
2041
- * sockets outlive process eviction (e.g. a Durable Object's hibernatable WebSocket) recovers both the
2042
- * application identity *and* the action routing from a single attachment after a wake — no storage reads.
2043
- */
2044
- interface IConnectionAttachment<TApp> {
2045
- app?: TApp;
2046
- binding?: IAcceptorConnectionBinding;
2047
- }
2048
- interface IConnectionStateStoreOptions<TConn, TApp> {
2049
- /** Read a connection's raw attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
2050
- read: (connection: TConn) => unknown;
2051
- /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
2052
- write: (connection: TConn, value: IConnectionAttachment<TApp>) => void;
2053
- /**
2054
- * All currently-live connections (e.g. `() => ctx.getWebSockets()`). Used to replay routing bindings
2055
- * after a wake (via {@link createConnectionStateStore}) and to enumerate app state in
2056
- * {@link ConnectionStateStore.entries}.
2057
- */
2058
- getConnections: () => TConn[];
2059
- /**
2060
- * Optional Standard Schema (valibot, zod, …) validating the *app* portion on read. A value that
2061
- * fails validation reads back as `null` — the same lenient behavior as a hand-written safeParse
2062
- * helper. The binding is the library's own shape and is never validated.
2063
- */
2064
- schema?: StandardSchemaV1<unknown, TApp>;
2065
- }
2066
- /**
2067
- * A typed per-connection state store that co-owns the app state and the acceptor handler's routing
2068
- * binding in one attachment, so neither the consumer nor the handler has to hand-merge the two. Create
2069
- * it through {@link createConnectionStateStore} (which also wires binding persistence and replays
2070
- * surviving connections after a wake), then `get`/`set`/`clearApp` the app state directly.
2071
- *
2072
- * The mechanism is carrier-neutral — it only needs read/write/enumerate callbacks for the connection's
2073
- * attachment — but it pays off on transports whose connections outlive process eviction (e.g. a
2074
- * Durable Object's hibernatable WebSockets), which is why it lives beside the hibernation adapter.
2075
- *
2076
- * ```ts
2077
- * const players = createConnectionStateStore(serverHandler, {
2078
- * schema: vs_player,
2079
- * read: (ws) => ws.deserializeAttachment(),
2080
- * write: (ws, v) => ws.serializeAttachment(v),
2081
- * getConnections: () => ctx.getWebSockets(),
2082
- * });
2083
- * players.set(ws, player); // binding is preserved automatically
2084
- * const player = players.get(ws);
2085
- * ```
2086
- */
2087
- declare class ConnectionStateStore<TConn, TApp> {
2088
- private readonly options;
2089
- constructor(options: IConnectionStateStoreOptions<TConn, TApp>);
2090
- /** The validated app state for a connection, or `null` if unset / invalid. */
2091
- get(connection: TConn): TApp | null;
2092
- /** Set the app state, preserving the runtime binding already pinned to the connection. */
2093
- set(connection: TConn, app: TApp): void;
2094
- /** Clear the app state but keep the binding (e.g. a spectator that stopped watching). */
2095
- clearApp(connection: TConn): void;
2096
- /** Every live connection paired with its (validated) app state — for rebuilding in-memory state after a wake. */
2097
- entries(): [TConn, TApp | null][];
2098
- /** @internal Persist a freshly-bound connection's binding, preserving any app state already stored. */
2099
- _persistBinding(connection: TConn, binding: IAcceptorConnectionBinding): void;
2100
- /** @internal The persisted binding for a connection, if any (used to replay routing after a wake). */
2101
- _readBinding(connection: TConn): IAcceptorConnectionBinding | undefined;
2102
- private _readAttachment;
2103
- private _validateApp;
2104
- }
2105
- /**
2106
- * Build a per-connection {@link ConnectionStateStore} bound to an {@link AcceptorHandler}: it registers
2107
- * itself as the handler's connection-bound persistence callback (so bindings are written without
2108
- * overwriting app state) and immediately replays every live connection's stored binding via
2109
- * {@link AcceptorHandler.rehydrateConnection} — so on a transport that resumes after eviction (e.g. a
2110
- * Durable Object waking from hibernation) both the app identity and the action routing come back from a
2111
- * single attachment, with no storage reads and no hand-rolled merge.
2112
- *
2113
- * Lives outside the handler so the generic {@link AcceptorHandler} stays free of any attachment/
2114
- * hibernation concern — it exposes only the neutral `setOnConnectionBound` + `rehydrateConnection`
2115
- * hooks this builder drives.
2116
- */
2117
- declare function createConnectionStateStore<TConn, TApp>(handler: AcceptorHandler<TConn>, options: IConnectionStateStoreOptions<TConn, TApp>): ConnectionStateStore<TConn, TApp>;
2118
- //#endregion
2119
- //#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter.d.ts
2120
- interface IHibernatableWsServerAdapterOptions<TConn> {
2121
- /** The handler to drive (from `createSecureAcceptorHandler` or `createAcceptorHandler`). */
2122
- handler: AcceptorHandler<TConn>;
2123
- /** All currently-live connections — replayed on construction to rebuild bindings after a wake. */
2124
- getConnections: () => TConn[];
2125
- /** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */
2126
- getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined;
2127
- /** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */
2128
- setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void;
2129
- }
2130
- /**
2131
- * The neutral lifecycle surface for a duplex (push-capable) acceptor: feed it each inbound frame and tell
2132
- * it when a connection goes away. Carrier-agnostic — a WebSocket, a WebRTC data channel, or any other
2133
- * duplex connection drives the same two methods.
2134
- */
2135
- interface IDuplexConnectionRouter<TConn> {
2136
- /** Feed one inbound frame from a connection into the handler. */
2137
- receive: (connection: TConn, frame: string | ArrayBuffer | Uint8Array) => void;
2138
- /** Forget a connection (call on socket close/error). */
2139
- drop: (connection: TConn) => void;
2140
- }
2141
- /**
2142
- * Wire the hibernation lifecycle for an acceptor handler on a transport whose connections outlive process
2143
- * eviction (e.g. a Durable Object's hibernatable WebSockets). It owns persistence end to end:
2144
- * registers `setAttachment` as the handler's connection-bound callback and immediately replays every
2145
- * live connection's stored binding via `getAttachment`, so results/pushes still route after a wake.
2146
- *
2147
- * Layered on top of the generic {@link AcceptorHandler} — it touches only the handler's neutral
2148
- * `setOnConnectionBound` / `rehydrateConnection` / `receive` / `dropConnection` surface, so no
2149
- * hibernation concern leaks into the handler itself.
2150
- *
2151
- * Construct it once when the handler is built, then forward connection events:
2152
- * ```ts
2153
- * const duplex = createHibernatableWsServerAdapter({ handler, getConnections, getAttachment, setAttachment });
2154
- * // webSocketMessage(ws, msg) => duplex.receive(ws, msg);
2155
- * // webSocketClose/Error(ws) => duplex.drop(ws);
2156
- * ```
2157
- */
2158
- declare function createHibernatableWsServerAdapter<TConn>(options: IHibernatableWsServerAdapterOptions<TConn>): IDuplexConnectionRouter<TConn>;
2159
- //#endregion
2160
- //#region src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.d.ts
2161
- /**
2162
- * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}
2163
- * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an
2164
- * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral
2165
- * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block
2166
- * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates
2167
- * it.
2168
- *
2169
- * Two shapes mirror the connector side:
2170
- *
2171
- * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can
2172
- * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step
2173
- * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.
2174
- * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push
2175
- * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to
2176
- * upgrade.
2177
- */
2178
- /**
2179
- * Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections
2180
- * outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional — omit for a
2181
- * transport that never hibernates (per-connection state is then in-memory only).
2182
- *
2183
- * `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when
2184
- * `connectionState` is requested) per-connection app state as one composite in this single slot, so both
2185
- * survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections.
2186
- */
2187
- interface IAcceptorAttachmentStore<TConn> {
2188
- /** All currently-live connections — enumerated on build to replay binding + app state after a wake. */
2189
- getConnections: () => TConn[];
2190
- /** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
2191
- read: (connection: TConn) => unknown;
2192
- /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
2193
- write: (connection: TConn, value: unknown) => void;
2194
- }
2195
- /**
2196
- * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each
2197
- * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a
2198
- * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the
2199
- * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if
2200
- * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)
2201
- */
2202
- interface IDuplexCarrierLifecycle<TConn> {
2203
- /** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */
2204
- receive(connection: TConn, frame: TFrame$1): void;
2205
- /** Forget a connection on close/error. No-op until the carrier is served. */
2206
- drop(connection: TConn): void;
2207
- /** @internal `serveChannel` binds this carrier's live connection router here. */
2208
- _activate(router: IDuplexConnectionRouter<TConn>): void;
2209
- }
2210
- /**
2211
- * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live
2212
- * connection, how to perform the transport-specific upgrade that admits one, which requests are such
2213
- * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and
2214
- * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see
2215
- * {@link IDuplexCarrierLifecycle}).
2216
- */
2217
- interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {
2218
- /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */
2219
- readonly shape: ETransportShape.duplex;
2220
- /**
2221
- * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex
2222
- * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no
2223
- * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain
2224
- * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.
2225
- */
2226
- secure?: boolean;
2227
- /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
2228
- send: (connection: TConn, frame: TFrame$1) => void;
2229
- /**
2230
- * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a
2231
- * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that
2232
- * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).
2233
- */
2234
- upgrade?: (request: Request, url: URL) => Response | Promise<Response>;
2235
- /**
2236
- * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.
2237
- * Only consulted when {@link upgrade} is present.
2238
- */
2239
- isUpgrade?: (request: Request, url: URL) => boolean;
2240
- /**
2241
- * Optional attachment read/write for connections that survive eviction (Durable Object hibernation).
2242
- * Present → `serveChannel` persists the routing binding (and any `connectionState`) here and replays it
2243
- * on wake. Absent → per-connection state is in-memory only.
2244
- */
2245
- attachmentStore?: IAcceptorAttachmentStore<TConn>;
2246
- /** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`). */
2247
- readonly carrierLabel: string;
2248
- }
2249
- /**
2250
- * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By
2251
- * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose
2252
- * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint
2253
- * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's
2254
- * `plainTransport({ carrier: httpCarrier(...) })`. So a server can pair a secure duplex (WebSocket) with a
2255
- * plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.
2256
- */
2257
- interface IExchangeAcceptorCarrier {
2258
- /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */
2259
- readonly shape: ETransportShape.exchange;
2260
- /**
2261
- * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain
2262
- * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,
2263
- * or encryption. A plain endpoint ignores the central crypto identity entirely.
2264
- */
2265
- secure?: boolean;
2266
- /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */
2267
- isActionPath?: (url: URL) => boolean;
2268
- /**
2269
- * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the
2270
- * permissive `*` set; pass `false` to attach no CORS headers at all.
2271
- */
2272
- cors?: Record<string, string> | false;
2273
- /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */
2274
- useErrorStatus?: boolean;
2275
- /** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
2276
- readonly carrierLabel: string;
2277
- }
2278
- type TAcceptorCarrier<TConn = unknown> = IDuplexAcceptorCarrier<TConn> | IExchangeAcceptorCarrier;
1779
+ type TAcceptorCarrier<TConn = unknown> = IDuplexAcceptorCarrier<TConn> | IExchangeAcceptorCarrier;
2279
1780
  /**
2280
1781
  * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch
2281
1782
  * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex
@@ -2332,9 +1833,12 @@ interface IConnectionContext<TConn, TApp = unknown> {
2332
1833
  interface IServeChannelOptions<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn, TApp = unknown> {
2333
1834
  /**
2334
1835
  * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
2335
- * used to score return-path dispatch back to the right connection.
1836
+ * used only as the offline-return scoring fallback — a result/push to a live client always routes over
1837
+ * the carrier it connected on regardless of this. Optional: omit it for a multi-role server that accepts
1838
+ * clients of several envs over one acceptor (it then scores 0 against every client, so the live
1839
+ * connection always decides).
2336
1840
  */
2337
- clientEnv: RuntimeCoordinate;
1841
+ clientEnv?: RuntimeCoordinate;
2338
1842
  /**
2339
1843
  * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins
2340
1844
  * (their keys don't collide). It is built once and shared across every carrier, so the WebSocket and the
@@ -2465,12 +1969,12 @@ interface IChannelServer<TConn, TApp = unknown> {
2465
1969
  * carrier, and so on — so it stays carrier-agnostic. Passing `connectionState` narrows the return so
2466
1970
  * `server.connections` is non-optional.
2467
1971
  */
2468
- declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn, TApp>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp> & {
1972
+ declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn, TApp>(runtime: ActionRuntime, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp> & {
2469
1973
  connectionState: IServeConnectionStateOptions<TApp>;
2470
1974
  }): IChannelServer<TConn, TApp> & {
2471
1975
  connections: ConnectionStateStore<TConn, TApp>;
2472
1976
  };
2473
- declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>): IChannelServer<TConn, TApp>;
1977
+ declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>): IChannelServer<TConn, TApp>;
2474
1978
  //#endregion
2475
1979
  //#region src/ActionRuntime/Channel/serveHost.d.ts
2476
1980
  /**
@@ -2503,157 +2007,12 @@ type TServeHostOptions<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn,
2503
2007
  * the host adapter and nothing else. Passing `connectionState` narrows the return so `server.connections`
2504
2008
  * is non-optional, exactly as with `serveChannel`.
2505
2009
  */
2506
- declare function serveHost<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn, TApp>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, host: IChannelHostAdapter<TConn>, options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp> & {
2010
+ declare function serveHost<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn, TApp>(runtime: ActionRuntime, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, host: IChannelHostAdapter<TConn>, options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp> & {
2507
2011
  connectionState: IServeConnectionStateOptions<TApp>;
2508
2012
  }): IChannelServer<TConn, TApp> & {
2509
2013
  connections: ConnectionStateStore<TConn, TApp>;
2510
2014
  };
2511
- declare function serveHost<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, host: IChannelHostAdapter<TConn>, options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp>): IChannelServer<TConn, TApp>;
2512
- //#endregion
2513
- //#region src/ActionRuntime/Transport/SecureSession/exchangeAcceptor.d.ts
2514
- /** Acceptor secure config for the exchange (HTTP) endpoint — same identity an `AcceptorHandler` uses. */
2515
- interface IExchangeAcceptorSecurity {
2516
- /** This acceptor's crypto identity (verify + exchange key pairs, optionally persisted). */
2517
- link: ClientCryptoKeyLink;
2518
- /** This acceptor's coordinate — its identity to clients during the handshake. */
2519
- localCoordinate: IRuntimeCoordinate;
2520
- /** Wire dictionary version; the handshake rejects a client on a mismatch. */
2521
- dictionaryVersion: string;
2522
- /** Accepted level(s) — a single level is strict, an array is a negotiable allowed set. */
2523
- securityLevel: ESecurityLevel | readonly ESecurityLevel[];
2524
- /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */
2525
- verifyKeyResolver?: IClientVerifyKeyResolver;
2526
- }
2527
- interface IExchangeAcceptorConfig {
2528
- security: IExchangeAcceptorSecurity;
2529
- /** The runtime that executes an inbound action wire and produces its result. */
2530
- runtime: ActionRuntime;
2531
- }
2532
- /**
2533
- * Acceptor (accept-in) side of the secure exchange protocol — the HTTP counterpart to
2534
- * {@link AcceptorSecureSession}. Each POST body is one {@link decodeExchangeRequest} envelope; the
2535
- * acceptor drives the server handshake over the two `hs` POSTs (correlated by `hsid`, since stateless
2536
- * requests can't rely on channel ordering), mints a session **token** on accept, and on every later `act`
2537
- * POST resolves the session by token, decrypts the body (at `encrypted`), routes it through the runtime,
2538
- * and returns the (encrypted) result inline as the reply.
2539
- *
2540
- * Sessions and in-flight handshakes are held in memory — fine for a single-instance server. (Surviving a
2541
- * Durable-Object eviction would persist each token's `keyMaterial` and re-derive the key on a miss, the
2542
- * same primitive `AcceptorSecureSession.rehydrate` uses; left as a follow-up.)
2543
- */
2544
- declare class ExchangeAcceptor {
2545
- private readonly _security;
2546
- private readonly _runtime;
2547
- private readonly _allowedLevels;
2548
- private readonly _noneAllowed;
2549
- private readonly _pendingHandshakes;
2550
- private readonly _sessions;
2551
- constructor(config: IExchangeAcceptorConfig);
2552
- /** Process one POST body (an exchange envelope), returning the reply body to send back. */
2553
- handlePost(body: string): Promise<string>;
2554
- private _handleHandshake;
2555
- private _handleAction;
2556
- private _err;
2557
- }
2558
- //#endregion
2559
- //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createActionFetchHandler.d.ts
2560
- interface IActionFetchHandlerOptions {
2561
- /**
2562
- * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204` with them).
2563
- * Defaults to permissive `*`; pass `false` to attach no CORS headers at all.
2564
- */
2565
- cors?: Record<string, string> | false;
2566
- /** Which requests carry an action wire on `POST`. Default: pathname ends with `/action`. */
2567
- isActionPath?: (url: URL) => boolean;
2568
- /** Which requests are WebSocket upgrades. Default: pathname ends with `/ws`. */
2569
- isWebSocketPath?: (url: URL) => boolean;
2570
- /**
2571
- * Whether a request is a WebSocket upgrade for this endpoint, given the whole request (not just the
2572
- * URL). When set it *replaces* the default gate (an `Upgrade: websocket` header on an
2573
- * {@link isWebSocketPath} match) — use it when the discriminant needs a header or method, not only the
2574
- * path. Only consulted when {@link onWebSocketUpgrade} is present.
2575
- */
2576
- isWebSocketUpgrade?: (request: Request, url: URL) => boolean;
2577
- /**
2578
- * Perform the transport-specific WebSocket upgrade (e.g. a Durable Object's
2579
- * `new WebSocketPair()` + `ctx.acceptWebSocket()` returning a `101`). Omit for HTTP-only endpoints.
2580
- * Its response is returned as-is — a `101` upgrade carries no CORS headers.
2581
- */
2582
- onWebSocketUpgrade?: (request: Request, url: URL) => Response | Promise<Response>;
2583
- /** Forwarded to `ActionPayload_Result.toHttpResponse` — use the error's HTTP status (default true). */
2584
- useErrorStatus?: boolean;
2585
- /**
2586
- * Enable the secure exchange protocol (handshake + token sessions + body encryption) on the `/action`
2587
- * endpoint, mirroring an `AcceptorHandler`'s `security`. The matching connector is
2588
- * `secureTransport({ carrier: httpCarrier(...), securityLevel })`. When omitted, the endpoint speaks
2589
- * today's plain protocol (the raw action wire is POSTed and the result is the response body).
2590
- */
2591
- security?: IExchangeAcceptorSecurity;
2592
- }
2593
- /**
2594
- * Build the `fetch` handler a server/Durable-Object exposes for action traffic, folding in the
2595
- * boilerplate every endpoint repeats: CORS (incl. the `OPTIONS` preflight), routing the `/action`
2596
- * `POST` body through the runtime (`handleActionPayloadWire` → `waitForResultPayload` →
2597
- * `toHttpResponse`), an optional WebSocket-upgrade hook, and a `404` fallback.
2598
- *
2599
- * It only touches web-standard `Request`/`Response`, so it stays transport-agnostic — the one
2600
- * environment-specific bit (the WS upgrade) is injected via {@link IActionFetchHandlerOptions.onWebSocketUpgrade}:
2601
- * ```ts
2602
- * this.fetchHandler = createActionFetchHandler(this.runtime, {
2603
- * onWebSocketUpgrade: () => {
2604
- * const pair = new WebSocketPair();
2605
- * this.ctx.acceptWebSocket(pair[1]);
2606
- * return new Response(null, { status: 101, webSocket: pair[0] });
2607
- * },
2608
- * });
2609
- * // async fetch(request) { return this.fetchHandler(request); }
2610
- * ```
2611
- */
2612
- declare function createActionFetchHandler(runtime: ActionRuntime, options?: IActionFetchHandlerOptions): (request: Request) => Promise<Response>;
2613
- //#endregion
2614
- //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createSecureActionServer.d.ts
2615
- interface ISecureAcceptorHandlerOptions<TConn> {
2616
- /** The shared channel identity (codec + dictionary version) — same one the clients use. */
2617
- channel: ISecureChannel;
2618
- /**
2619
- * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
2620
- * used to route results/pushes back over this handler.
2621
- */
2622
- clientEnv: RuntimeCoordinate;
2623
- /** This server's runtime — its coordinate is the server identity presented in the handshake. */
2624
- runtime: ActionRuntime;
2625
- /**
2626
- * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins.
2627
- * Their keys don't collide, so a single adapter is enough; back it with persistent storage (e.g. a
2628
- * Durable Object's storage) so identity and pins survive eviction.
2629
- */
2630
- storageAdapter: StorageAdapter;
2631
- /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
2632
- send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
2633
- /**
2634
- * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storageAdapter`.
2635
- * Pass an existing link to share one identity across several acceptors on the same server (e.g. a
2636
- * WebSocket acceptor and a secure-HTTP {@link createActionFetchHandler}), so they present the same
2637
- * verify/exchange keys — avoiding a divergent-key race when two fresh links initialize concurrently.
2638
- */
2639
- link?: ClientCryptoKeyLink;
2640
- /** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */
2641
- securityLevel?: ESecurityLevel | readonly ESecurityLevel[];
2642
- /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storageAdapter`. */
2643
- verifyKeyResolver?: IClientVerifyKeyResolver;
2644
- /** Timeout (ms) applied to server-initiated actions awaiting a client response. */
2645
- defaultTimeout?: number;
2646
- }
2647
- /**
2648
- * Build an {@link AcceptorHandler} for the secure binary channel with the boilerplate folded in:
2649
- * it creates the {@link ClientCryptoKeyLink} and the storage-backed TOFU resolver from a single
2650
- * `storageAdapter`, installs the channel's per-connection codec, and assembles the `security` block
2651
- * from the runtime coordinate + channel version (accepting all three levels by default).
2652
- *
2653
- * For a hibernatable transport (e.g. a Durable Object), pair it with
2654
- * {@link createHibernatableWsServerAdapter} to wire persistence + replay.
2655
- */
2656
- declare function createSecureAcceptorHandler<TConn = unknown>(options: ISecureAcceptorHandlerOptions<TConn>): AcceptorHandler<TConn>;
2015
+ declare function serveHost<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, host: IChannelHostAdapter<TConn>, options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp>): IChannelServer<TConn, TApp>;
2657
2016
  //#endregion
2658
2017
  //#region src/ActionRuntime/Handler/PeerLink/Connector/err_nice_external_client.d.ts
2659
2018
  declare const err_nice_external_client: import("@nice-code/error").NiceErrorDomain<{
@@ -2693,16 +2052,15 @@ declare function createInMemoryChannelPair(): IInMemoryChannelPair;
2693
2052
  //#endregion
2694
2053
  //#region src/ActionRuntime/Transport/Carrier/duplex/inMemory/inMemoryCarrier.d.ts
2695
2054
  interface IInMemoryCarrier {
2696
- /** The connector end — pass as the `carrier` to {@link secureTransport} / `LinkTransport`. */
2055
+ /** The connector end — pass as the `carrier` to one of `connectChannel`'s transports. */
2697
2056
  carrier: IDuplexCarrierSource;
2698
2057
  /** The acceptor end — wire into an `AcceptorHandler` (`send` + `receive`). */
2699
2058
  serverEndpoint: IInMemoryServerEndpoint;
2700
2059
  }
2701
2060
  /**
2702
2061
  * A loopback duplex carrier with no socket — two cross-wired in-process ends. The connector end is an
2703
- * {@link IDuplexCarrierSource} for {@link secureTransport}; the acceptor end plugs into an
2704
- * `AcceptorHandler`. Ideal for tests and for running two runtimes in one process, or proving a
2705
- * non-WS carrier end to end.
2062
+ * {@link IDuplexCarrierSource} for `connectChannel`; the acceptor end plugs into an `AcceptorHandler`.
2063
+ * Ideal for tests and for running two runtimes in one process, or proving a non-WS carrier end to end.
2706
2064
  */
2707
2065
  declare function inMemoryCarrier(): IInMemoryCarrier;
2708
2066
  //#endregion
@@ -2738,8 +2096,8 @@ interface IRtcCarrierOptions {
2738
2096
  }
2739
2097
  /**
2740
2098
  * A WebRTC {@link IDuplexCarrierSource} over an already-negotiated `RTCDataChannel` (signaling is the
2741
- * app's concern). Hand it to {@link secureTransport} so two browsers/apps linked peer-to-peer run the
2742
- * identical secure session as a WebSocket.
2099
+ * app's concern). Pass it as a `carrier` to `connectChannel` so two browsers/apps linked peer-to-peer run
2100
+ * the identical secure session as a WebSocket.
2743
2101
  */
2744
2102
  declare function rtcCarrier(dataChannel: IRtcDataChannelLike, options?: IRtcCarrierOptions): IDuplexCarrierSource;
2745
2103
  //#endregion
@@ -2806,8 +2164,8 @@ interface IWsCarrierOptions {
2806
2164
  }
2807
2165
  /**
2808
2166
  * A WebSocket {@link IDuplexCarrierSource}: opens an `arraybuffer` socket (cached per endpoint) and adapts
2809
- * it to a carrier. Hand it to {@link secureTransport} (or `LinkTransport`) — the WebSocket is now "just
2810
- * another carrier" under the shared secure session, with no WS-specific transport class.
2167
+ * it to a carrier. Pass it as a `carrier` to `connectChannel` — the WebSocket is now "just another
2168
+ * carrier" under the shared secure session, with no WS-specific transport class.
2811
2169
  *
2812
2170
  * `createRequest` derives the socket URL per action (keep it simple with `() => ({ url })`), so dynamic
2813
2171
  * endpoints (e.g. a per-run query param) need no `createWebSocket` escape hatch.
@@ -2819,8 +2177,9 @@ interface IHttpAcceptorCarrierOptions {
2819
2177
  /**
2820
2178
  * Whether this endpoint runs the secure exchange protocol (default `true`). Pass `false` for a plain
2821
2179
  * endpoint — the body is the raw action wire and the result is the response body, the request/reply dual
2822
- * of `plainTransport({ carrier: httpCarrier(...) })`. A plain endpoint ignores the crypto identity, so it
2823
- * can sit alongside a secure WebSocket on the same server (e.g. a secure WS preferred, plain HTTP fallback).
2180
+ * of a connector's plain HTTP transport (`{ carrier: httpCarrier(...), secure: false }`). A plain
2181
+ * endpoint ignores the crypto identity, so it can sit alongside a secure WebSocket on the same server
2182
+ * (e.g. a secure WS preferred, plain HTTP fallback).
2824
2183
  */
2825
2184
  secure?: boolean;
2826
2185
  /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */
@@ -2863,129 +2222,16 @@ interface IHttpCarrierOptions {
2863
2222
  }
2864
2223
  /**
2865
2224
  * An HTTP {@link IExchangeCarrierSource}: each `exchange` POSTs one frame body to the action endpoint and
2866
- * resolves with the response body as the single correlated reply. Hand it to {@link secureTransport}
2867
- * HTTP then runs the *same* secure session as a duplex carrier (handshake → token → encrypted frames),
2868
- * the request/reply correlation provided for free by the HTTP transaction.
2225
+ * resolves with the response body as the single correlated reply. Pass it as a `carrier` to
2226
+ * `connectChannel` — a secure HTTP transport then runs the *same* secure session as a duplex carrier
2227
+ * (handshake → token → encrypted frames), the request/reply correlation provided for free by the HTTP
2228
+ * transaction.
2869
2229
  *
2870
2230
  * `createRequest` derives the URL/headers per action (keep it simple with `() => ({ url })`). The body is
2871
2231
  * the session's responsibility, so it is never built here.
2872
2232
  */
2873
2233
  declare function httpCarrier(createRequest: (input: ITransportRouteActionParams) => IHttpCarrierRequest, options?: IHttpCarrierOptions): IExchangeCarrierSource;
2874
2234
  //#endregion
2875
- //#region src/ActionRuntime/Transport/codec/createBinaryWireAdapter.d.ts
2876
- /**
2877
- * Builds a *stateless* `formatMessage` pipeline for {@link LinkTransport}, packing action
2878
- * payloads into a compact msgpackr binary frame instead of JSON. The `domain`/`id` route collapses to
2879
- * a single integer drawn from a shared dictionary; `form`/`type`, the recomputable
2880
- * `inputHash`/`outputHash`, and the per-frame `context.routing`/`context.timeCreated` are all dropped
2881
- * (see {@link ENVELOPE}).
2882
- *
2883
- * No validation runs here: `incoming` blindly reconstructs the wire JSON shape and hands it back to
2884
- * the connection, which flows into `ActionRuntime` → `domain.hydrateAnyAction()` where the Valibot
2885
- * schemas validate it exactly as they would for a JSON frame.
2886
- *
2887
- * Both ends of the socket MUST construct the adapter with the same domains in the same order — the
2888
- * integer dictionary is positional. Mismatched dictionaries will route to the wrong action.
2889
- *
2890
- * Because `incoming` returns `undefined` for text frames, a binary server can still serve plain-JSON
2891
- * clients on the same runtime (the connection falls back to its built-in JSON parser).
2892
- */
2893
- declare function createBinaryWireAdapter(domains: ActionDomain<any>[]): IActionWireFormat;
2894
- //#endregion
2895
- //#region src/ActionRuntime/Transport/crypto/actionFrameCrypto.d.ts
2896
- /**
2897
- * Async AES-GCM transform for the `encrypted` security level. It wraps the opaque binary frame a
2898
- * session codec produces (it does NOT look inside it), encrypting on the way out and decrypting on the
2899
- * way in with the shared key established by the handshake.
2900
- *
2901
- * It is deliberately separate from the (synchronous) session `formatMessage`: WebCrypto is always
2902
- * Promise-based, so encryption has to happen at the transport's async I/O boundary — the connection
2903
- * encrypts after `session.outgoing()` and decrypts before `session.incoming()`. The `authenticated`
2904
- * and `none` levels use no crypto transform at all (frames go out as the session produced them).
2905
- *
2906
- * Wire shape of an encrypted frame: `pack([nonceBytes, ciphertextBytes])` — msgpack carries the two
2907
- * binary fields with a couple of bytes of overhead, no base64 inflation.
2908
- */
2909
- interface IActionFrameCrypto {
2910
- /** Encrypt one session frame for sending. */
2911
- encryptFrame(frame: Uint8Array): Promise<Uint8Array>;
2912
- /** Decrypt one received frame back to the session frame. Throws on a non-binary / malformed /
2913
- * tampered frame — the caller (transport) decides how to react (drop / close). */
2914
- decryptFrame(frame: string | ArrayBuffer | Uint8Array): Promise<Uint8Array>;
2915
- }
2916
- interface IActionFrameCryptoConfig {
2917
- link: ClientCryptoKeyLink;
2918
- /** The handshake-established link id for the remote (key + connection-registry id). */
2919
- linkedClientId: TTypeAndId;
2920
- }
2921
- /**
2922
- * Build the encrypt/decrypt transform for a connection whose handshake settled on the `encrypted`
2923
- * level. Keyed by the link + `linkedClientId`, so it reuses the cached shared AES-GCM key.
2924
- */
2925
- declare function createActionFrameCrypto({
2926
- link,
2927
- linkedClientId
2928
- }: IActionFrameCryptoConfig): IActionFrameCrypto;
2929
- //#endregion
2930
- //#region src/ActionRuntime/Transport/Exchange/TransportExchange.types.d.ts
2931
- interface IActionTransportReadyData_Exchange extends IActionTransportReadyData_Base {
2932
- /** The live request/reply carrier this connection drives its session over. */
2933
- carrier: IExchangeCarrier;
2934
- /** Optional authenticated/encrypted config; the handshake runs once at bring-up when set. */
2935
- secureChannel?: ISecureClientConfig;
2936
- }
2937
- interface IActionTransportInitialized_Exchange extends IActionTransportInitialized<ITransportRouteActionParams, IActionTransportReadyData_Exchange> {}
2938
- interface IActionTransportDef_Exchange extends IActionTransportDef<ETransportShape.exchange, IActionTransportInitialized_Exchange> {}
2939
- //#endregion
2940
- //#region src/ActionRuntime/Transport/Exchange/ExchangeConnection.d.ts
2941
- /**
2942
- * Carrier-agnostic live connection for the exchange (request → single reply) shape — the HTTP
2943
- * counterpart to {@link LinkConnection}. It owns only the bring-up (run the secure handshake on first
2944
- * use); the request/reply lifecycle + crypto live in the shared `establishExchangeSession`.
2945
- */
2946
- declare class ExchangeConnection extends TransportConnection<ETransportShape.exchange, ITransportRouteActionParams, IActionTransportReadyData_Exchange, IActionTransportInitialized_Exchange, IActionTransportDef_Exchange> {
2947
- constructor(def: Omit<IActionTransportDef_Exchange, "type">);
2948
- protected _getCacheKey(input: ITransportRouteActionParams): string;
2949
- protected _needsAsyncBringUp(data: IActionTransportReadyData_Exchange): boolean;
2950
- protected _finalizeReady(data: IActionTransportReadyData_Exchange): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods>;
2951
- _finalizeTransportMethods(data: IActionTransportReadyData_Exchange): IActionTransportReadyData_Methods;
2952
- private _sessionContext;
2953
- }
2954
- //#endregion
2955
- //#region src/ActionRuntime/Transport/Exchange/ExchangeTransport.d.ts
2956
- interface IExchangeTransportOptions {
2957
- /** Open (or reuse) the exchange carrier for an action — e.g. `httpCarrier(...).open`. */
2958
- openCarrier: (input: ITransportRouteActionParams) => IExchangeCarrier;
2959
- /** Secure config; when set (and `securityLevel !== none`) the handshake runs once at bring-up. */
2960
- security?: ISecureClientConfig;
2961
- updateRunConfig?: TUpdateActionRunConfig;
2962
- /** Keys identifying a reusable session, so one carrier is shared across actions to the same peer. */
2963
- getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
2964
- /**
2965
- * Optional availability gate. When it returns `false`, the manager skips this transport for that action
2966
- * (reporting `unsupported`) and falls through to the next — without opening the carrier or computing its
2967
- * cache key. Re-evaluated per dispatch, so the transport can become available later with no reconnect.
2968
- */
2969
- available?: (input: ITransportRouteActionParams) => boolean;
2970
- /** Short label for the devtools chip (defaults to "exchange"). */
2971
- label?: string;
2972
- getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
2973
- }
2974
- /**
2975
- * A carrier-agnostic exchange (request → single reply) transport: it drives nice-action's secure session
2976
- * over any {@link IExchangeCarrier} (HTTP being the one built-in). The duplex counterpart is
2977
- * {@link LinkTransport}; this is the no-push half — its reply rides the response to its own request, so it
2978
- * can't deliver an unsolicited frame (the runtime never picks it for the return path).
2979
- */
2980
- declare class ExchangeTransport extends Transport<ETransportShape.exchange> {
2981
- private readonly options;
2982
- readonly type = ETransportShape.exchange;
2983
- constructor(options: IExchangeTransportOptions);
2984
- static create(options: IExchangeTransportOptions): ExchangeTransport;
2985
- _createConnection(_ctx: ITransportConnectionContext): ExchangeConnection;
2986
- getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
2987
- }
2988
- //#endregion
2989
2235
  //#region src/ActionRuntime/Transport/err_nice_transport.d.ts
2990
2236
  declare enum EErrId_NiceTransport {
2991
2237
  timeout = "timeout",
@@ -3023,79 +2269,50 @@ declare const err_nice_transport: import("@nice-code/error").NiceErrorDomain<{
3023
2269
  };
3024
2270
  }>;
3025
2271
  //#endregion
3026
- //#region src/ActionRuntime/Transport/Link/TransportLink.types.d.ts
3027
- /** The per-connection codec (positional binary wire / JSON fallback) the carrier's session uses. */
3028
- type TLinkFormatMessage = IActionWireFormat;
3029
- interface IActionTransportReadyData_Link extends IActionTransportReadyData_Base {
3030
- /** The live carrier this connection drives its session over. */
3031
- channel: IDuplexCarrier;
3032
- formatMessage?: TLinkFormatMessage;
3033
- /** Optional authenticated/encrypted channel; the connection runs the handshake during init. */
3034
- secureChannel?: ISecureClientConfig;
3035
- }
3036
- interface IActionTransportInitialized_Link extends IActionTransportInitialized<ITransportRouteActionParams, IActionTransportReadyData_Link> {}
3037
- interface IActionTransportDef_Link extends IActionTransportDef<ETransportShape.duplex, IActionTransportInitialized_Link> {}
3038
- //#endregion
3039
- //#region src/ActionRuntime/Transport/Link/LinkConnection.d.ts
3040
- /**
3041
- * Carrier-agnostic live connection. It owns only the *bring-up* (open the carrier, then run the secure
3042
- * session); the session itself handshake, frame crypto, codec, send/receive lives in the shared
3043
- * {@link finalizeSecureLinkMethods}/{@link finalizePlainLinkMethods}, so a WebSocket, a WebRTC data
3044
- * channel, a Bluetooth characteristic, and an in-memory pipe all run the identical secure layer.
3045
- */
3046
- declare class LinkConnection extends TransportConnection<ETransportShape.duplex, ITransportRouteActionParams, IActionTransportReadyData_Link, IActionTransportInitialized_Link, IActionTransportDef_Link> {
3047
- private resolvers;
3048
- constructor(def: Omit<IActionTransportDef_Link, "type">, resolvers?: IActionTransportResolvers);
3049
- protected _getCacheKey(input: ITransportRouteActionParams): string;
3050
- protected _needsAsyncBringUp(): boolean;
3051
- protected _awaitCarrierReady(data: IActionTransportReadyData_Link): Promise<void>;
3052
- protected _finalizeReady(data: IActionTransportReadyData_Link): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods>;
3053
- private _sessionContext;
3054
- _finalizeTransportMethods(data: IActionTransportReadyData_Link): IActionTransportReadyData_Methods;
3055
- }
3056
- //#endregion
3057
- //#region src/ActionRuntime/Transport/Link/LinkTransport.d.ts
3058
- interface ILinkTransportOptions {
3059
- /**
3060
- * Open (or reuse) the carrier for an action — a WebSocket adapter, a WebRTC data channel, a Bluetooth
3061
- * characteristic, an in-memory pipe, anything that satisfies {@link IDuplexCarrier}.
3062
- */
3063
- openChannel: (input: ITransportRouteActionParams) => IDuplexCarrier;
3064
- /** Shared codec for every channel (stateless). */
3065
- formatMessage?: TLinkFormatMessage;
3066
- /**
3067
- * Per-channel codec factory — called once per opened channel so stateful codecs (e.g. the binary
3068
- * session) get their own instance. Takes precedence over `formatMessage`.
3069
- */
3070
- createFormatMessage?: () => TLinkFormatMessage;
3071
- /** Secure-channel config; when set (and `securityLevel !== none`) the handshake runs on init. */
3072
- security?: ISecureClientConfig;
3073
- updateRunConfig?: TUpdateActionRunConfig;
3074
- /** Keys identifying a reusable channel, so one carrier is shared across actions to the same peer. */
3075
- getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
3076
- /**
3077
- * Optional availability gate. When it returns `false`, the manager skips this transport for that action
3078
- * (reporting `unsupported`) and falls through to the next — without opening the carrier or computing its
3079
- * cache key. Re-evaluated per dispatch, so the transport can become available later with no reconnect.
3080
- */
3081
- available?: (input: ITransportRouteActionParams) => boolean;
3082
- /** Short label for the devtools chip (defaults to "link"). */
3083
- label?: string;
3084
- getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
2272
+ //#region src/ActionRuntime/Transport/SecureSession/exchangeAcceptor.d.ts
2273
+ /** Acceptor secure config for the exchange (HTTP) endpoint same identity an `AcceptorHandler` uses. */
2274
+ interface IExchangeAcceptorSecurity {
2275
+ /** This acceptor's crypto identity (verify + exchange key pairs, optionally persisted). */
2276
+ link: ClientCryptoKeyLink;
2277
+ /** This acceptor's coordinate — its identity to clients during the handshake. */
2278
+ localCoordinate: IRuntimeCoordinate;
2279
+ /** Wire dictionary version; the handshake rejects a client on a mismatch. */
2280
+ dictionaryVersion: string;
2281
+ /** Accepted level(s) — a single level is strict, an array is a negotiable allowed set. */
2282
+ securityLevel: ESecurityLevel | readonly ESecurityLevel[];
2283
+ /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */
2284
+ verifyKeyResolver?: IClientVerifyKeyResolver;
2285
+ }
2286
+ interface IExchangeAcceptorConfig {
2287
+ security: IExchangeAcceptorSecurity;
2288
+ /** The runtime that executes an inbound action wire and produces its result. */
2289
+ runtime: ActionRuntime;
3085
2290
  }
3086
2291
  /**
3087
- * A carrier-agnostic transport: it drives nice-action's secure session + action routing over any
3088
- * {@link IDuplexCarrier}. The WebSocket transport is the special case that opens a `WebSocket`;
3089
- * this opens whatever `openChannel` returns, so the identical secure layer works over WebRTC, Bluetooth,
3090
- * or an in-memory pipe. Reported with an overridable carrier label in the devtools (defaults to "link").
2292
+ * Acceptor (accept-in) side of the secure exchange protocol the HTTP counterpart to
2293
+ * {@link AcceptorSecureSession}. Each POST body is one {@link decodeExchangeRequest} envelope; the
2294
+ * acceptor drives the server handshake over the two `hs` POSTs (correlated by `hsid`, since stateless
2295
+ * requests can't rely on channel ordering), mints a session **token** on accept, and on every later `act`
2296
+ * POST resolves the session by token, decrypts the body (at `encrypted`), routes it through the runtime,
2297
+ * and returns the (encrypted) result inline as the reply.
2298
+ *
2299
+ * Sessions and in-flight handshakes are held in memory — fine for a single-instance server. (Surviving a
2300
+ * Durable-Object eviction would persist each token's `keyMaterial` and re-derive the key on a miss, the
2301
+ * same primitive `AcceptorSecureSession.rehydrate` uses; left as a follow-up.)
3091
2302
  */
3092
- declare class LinkTransport extends Transport<ETransportShape.duplex> {
3093
- private readonly options;
3094
- readonly type = ETransportShape.duplex;
3095
- constructor(options: ILinkTransportOptions);
3096
- static create(options: ILinkTransportOptions): LinkTransport;
3097
- _createConnection(ctx: ITransportConnectionContext): LinkConnection;
3098
- getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
2303
+ declare class ExchangeAcceptor {
2304
+ private readonly _security;
2305
+ private readonly _runtime;
2306
+ private readonly _allowedLevels;
2307
+ private readonly _noneAllowed;
2308
+ private readonly _pendingHandshakes;
2309
+ private readonly _sessions;
2310
+ constructor(config: IExchangeAcceptorConfig);
2311
+ /** Process one POST body (an exchange envelope), returning the reply body to send back. */
2312
+ handlePost(body: string): Promise<string>;
2313
+ private _handleHandshake;
2314
+ private _handleAction;
2315
+ private _err;
3099
2316
  }
3100
2317
  //#endregion
3101
2318
  //#region src/ActionRuntime/Transport/SecureSession/exchangeProtocol.d.ts
@@ -3372,5 +2589,512 @@ interface IActionPayload_Result_JsonObject<DOM extends IActionDomain = IActionDo
3372
2589
  type TActionPayload_Any_Instance<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ActionPayload_Request<DOM, ID> | ActionPayload_Result<DOM, ID> | ActionPayload_Progress<DOM, ID>;
3373
2590
  type TActionPayload_Any_JsonObject<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = IActionPayload_Request_JsonObject<DOM, ID> | IActionPayload_Progress_JsonObject<DOM, ID> | IActionPayload_Result_JsonObject<DOM, ID>;
3374
2591
  //#endregion
3375
- export { wsAcceptorCarrier as $, TTransportStatusInfo_GetTransport_Output as $n, TPossibleDomainId as $r, IDuplexCarrier as $t, encodeExchange as A, ITransportDispatchAction as An, ERunningActionUpdateType as Ar, TAcceptorCarrier as At, IActionFrameCrypto as B, IUpdateActionRunConfig_Output as Bn, IRuntimeCoordinateSpecifics as Br, IActionChannel as Bt, decodeActionFrame as C, IActionTransportDef as Cn, RunningAction as Cr, IConnectionContext as Ct, TExchangeRequest as D, IActionTransportReadyData_Methods as Dn, IActionCore_JsonObject as Dr, IAcceptorAttachmentStore as Dt, TExchangeReply as E, IActionTransportReadyData_Base as En, IActionCore as Er, serveChannel as Et, EErrId_NiceTransport as F, ITransportStatusInfo_Base as Fn, TRunningActionUpdate as Fr, ConnectionStateStore as Ft, IHttpCarrierRequest as G, TOnResolveIncomingRequestJson as Gn, IActionDomain as Gr, acceptChannel as Gt, createActionFrameCrypto as H, TOnResolveAnyIncomingActionData as Hn, RuntimeCoordinate as Hr, IConnectTransport as Ht, err_nice_transport as I, ITransportStatusInfo_Failed as In, TRunningActionUpdateFinished as Ir, IConnectionAttachment as It, IHttpAcceptorCarrierOptions as J, TSendActionDataMethod as Jn, TActionDomainChildDef as Jr, defineChannel as Jt, TCarrierFetch as K, TOnResolveIncomingResponse as Kn, IActionDomainChildOptions as Kr, acceptChannelConnections as Kt, ExchangeTransport as L, ITransportStatusInfo_Initializing as Ln, TRunningActionUpdateListener as Lr, IConnectionStateStoreOptions as Lt, LinkTransport as M, ITransportRouteActionParams as Mn, IRunningActionUpdate_Progress as Mr, IDuplexConnectionRouter as Mt, IActionTransportReadyData_Link as N, ITransportRouteClientParams as Nn, IRunningActionUpdate_Started as Nr, IHibernatableWsServerAdapterOptions as Nt, decodeExchangeReply as O, IActionTransportResolvers as On, ERunningActionFinishedType as Or, IDuplexAcceptorCarrier as Ot, TLinkFormatMessage as P, ITransportRouteInfo as Pn, IRunningActionUpdate_Success as Pr, createHibernatableWsServerAdapter as Pt, IWsAcceptorCarrierOptions as Q, TTransportStatusInfo as Qn, TInferOutputFromSchema as Qr, createBinaryWireSessionFactory as Qt, IExchangeTransportOptions as R, ITransportStatusInfo_Ready as Rn, ActionPayload_Result as Rr, createConnectionStateStore as Rt, IActionFrameDecoder as S, ETransportStatus as Sn, MaybePromise as Sr, IChannelServer as St, err_nice_action as T, IActionTransportReady as Tn, ActionPayload_Request as Tr, IServeConnectionStateOptions as Tt, createBinaryWireAdapter as U, TOnResolveAnyIncomingActionData_Json as Un, TRuntimeCoordinateEnvId as Ur, TChannelAcceptorCases as Ut, IActionFrameCryptoConfig as V, TGetTransportFn as Vn, IRuntimeFullCoordinates as Vr, IConnectChannelOptions as Vt, IHttpCarrierOptions as W, TOnResolveIncomingRequest as Wn, TRuntimeCoordinateStringId as Wr, TChannelPushHandlers as Wt, IWsCarrierOptions as X, TTransportCache as Xn, TDomainActionId as Xr, defineSecureChannel as Xt, httpAcceptorCarrier as Y, TSendReturnDataMethod as Yn, TActionDomainSchema as Yr, ISecureChannel as Yt, wsCarrier as Z, TTransportInitializationFinishedInfo as Zn, TInferInputFromSchema as Zr, IBinaryWireSessionOptions as Zt, TActionProgress as _, ActionRootDomain as _n, encodeHandshakeMessage as _r, IExchangeAcceptorConfig as _t, IActionPayload_Data_Base as a, TActionSchemaOptions as ai, AcceptorHandler as an, IClientHandshakeConfig as ar, rtcDataChannelByteChannel as at, isActionPayload_Request_JsonObject as b, createConnectorHandler as bn, ActionLocalHandler as br, TServeHostOptions as bt, IActionPayload_Request_JsonObject as c, TAcceptorCaseFn as cn, IHandshakeEncryptionKeyMaterial as cr, IInMemoryChannelPair as ct, IActionProgress_Custom as d, TActionConnectionEncoding as dn, THandshakeMessage as dr, err_nice_external_client as dt, TPossibleDomainIdList as ei, IDuplexCarrierSource as en, TUpdateActionRunConfig as er, EErrId_NiceTransport_WebSocket as et, IActionProgress_None as f, createAcceptorHandler as fn, createClientHandshake as fr, ISecureAcceptorHandlerOptions as ft, TActionPayload_Any_JsonObject as g, ActionDomain as gn, decodeHandshakeMessage as gr, ExchangeAcceptor as gt, TActionPayload_Any_Instance as h, ActionCore as hn, createStorageTofuVerifyKeyResolver as hr, createActionFetchHandler as ht, IActionPayload_Base_JsonObject as i, actionSchema as ii, TFrame$1 as in, ESecurityLevel as ir, IRtcDataChannelLike as it, ILinkTransportOptions as j, ITransportMethod_SendActionData_Input as jn, IRunningActionUpdate_Abort as jr, isExchangeAcceptorCarrier as jt, decodeExchangeRequest as k, ISecureClientConfig as kn, ERunningActionState as kr, IExchangeAcceptorCarrier as kt, IActionPayload_Result as l, TAcceptorConnectionCaseFn as ln, IHandshakeResult as lr, IInMemoryServerEndpoint as lt, IActionRouteItemHandler as m, createActionRootDomain as mn, createServerHandshake as mr, IActionFetchHandlerOptions as mt, EActionProgressType as n, EActionResponseMode as ni, IExchangeCarrierSource as nn, Transport as nr, IRtcCarrierOptions as nt, IActionPayload_Progress as o, TActionSerializationDefinition as oi, IAcceptorConnectionBinding as on, IClientVerifyKeyResolveInput as or, IInMemoryCarrier as ot, IActionProgress_Percentage as p, IActionWireFormat as pn, createInMemoryTofuVerifyKeyResolver as pr, createSecureAcceptorHandler as pt, httpCarrier as q, TOnResolveIncomingResponseJson as qn, IActionRootDomain as qr, connectChannel as qt, IActionPayload_Base as r, TInferActionError as ri, TCarrier as rn, EHandshakeMessageType as rr, rtcCarrier as rt, IActionPayload_Progress_JsonObject as s, TTransportedValue as si, IAcceptorHandlerOptions as sn, IClientVerifyKeyResolver as sr, inMemoryCarrier as st, EActionPayloadType as t, ActionSchema as ti, IExchangeCarrier as tn, ITransportConnectionContext as tr, err_nice_transport_ws as tt, IActionPayload_Result_JsonObject as u, TActionChannelFormatMessage as un, IServerHandshakeConfig as ur, createInMemoryChannelPair as ut, TActionResultOutcome as v, ActionRuntime as vn, runtimeLinkId as vr, IExchangeAcceptorSecurity as vt, EErrId_NiceAction as w, IActionTransportInitialized as wn, ActionPayload_Progress as wr, IServeChannelOptions as wt, isActionPayload_Any_JsonObject as x, ETransportShape as xn, createLocalHandler as xr, serveHost as xt, isActionPayload_Result_JsonObject as y, ConnectorHandler as yn, PeerLinkHandler as yr, IChannelHostAdapter as yt, IActionTransportReadyData_Exchange as z, ITransportStatusInfo_Unsupported as zn, IRuntimeCoordinate as zr, IAcceptChannelOptions as zt };
3376
- //# sourceMappingURL=ActionPayload.types-B-OSg09t.d.mts.map
2592
+ //#region src/ActionRuntime/Handler/ActionHandler.types.d.ts
2593
+ declare enum EActionHandlerType {
2594
+ peer = "peer",
2595
+ local = "local"
2596
+ }
2597
+ interface IActionHandler_Json<T extends EActionHandlerType> {
2598
+ type: T;
2599
+ }
2600
+ interface IActionHandler_Peer_Json extends IActionHandler_Json<EActionHandlerType.peer> {
2601
+ client: IRuntimeCoordinate;
2602
+ }
2603
+ interface IActionHandler_Local_Json extends IActionHandler_Json<EActionHandlerType.local> {}
2604
+ type TActionHandler_Json = IActionHandler_Local_Json | IActionHandler_Peer_Json;
2605
+ interface IHandleActionOptions {
2606
+ timeout?: number;
2607
+ targetPeer?: RuntimeCoordinate;
2608
+ targetLocalRuntime?: ActionRuntime;
2609
+ }
2610
+ interface IExecuteActionOptions<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends IHandleActionOptions {
2611
+ listeners?: TRunningActionUpdateListener<DOM, ID>[];
2612
+ }
2613
+ interface IActionHandler_Base<T extends EActionHandlerType> {
2614
+ cuid: string;
2615
+ handlerType: T;
2616
+ getActionRouter: () => ActionRouter<any>;
2617
+ }
2618
+ /**
2619
+ *
2620
+ * LOCAL ACTION HANDLER
2621
+ *
2622
+ */
2623
+ interface IHandleActionOptions_Local extends IHandleActionOptions {}
2624
+ interface IActionHandler_Local extends IActionHandler_Base<EActionHandlerType.local> {
2625
+ handleActionRequest: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions_Local) => Promise<RunningAction<DOM, ID>>;
2626
+ }
2627
+ /**
2628
+ *
2629
+ * PEER-LINK ACTION HANDLER
2630
+ *
2631
+ */
2632
+ interface IHandleActionOptions_Peer extends IHandleActionOptions {}
2633
+ interface IActionHandler_Peer extends IActionHandler_Base<EActionHandlerType.peer> {
2634
+ peerClient: RuntimeCoordinate;
2635
+ handleActionRequest: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions_Peer) => Promise<RunningAction<DOM, ID>>;
2636
+ _setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject<any>) => void): void;
2637
+ }
2638
+ /**
2639
+ *
2640
+ * COMBINED
2641
+ *
2642
+ */
2643
+ type TActionHandler = IActionHandler_Local | IActionHandler_Peer;
2644
+ //#endregion
2645
+ //#region src/ActionDefinition/Action/Payload/ActionPayload_Request.d.ts
2646
+ declare class ActionPayload_Request<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionPayload<EActionPayloadType.request, DOM, ID> {
2647
+ readonly input: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"];
2648
+ readonly inputHash: string;
2649
+ _callSite?: string;
2650
+ constructor(params: {
2651
+ context: ActionContext<DOM, ID>;
2652
+ }, input: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"], data: IActionPayload_Data_Base);
2653
+ successResult(...args: [TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]] extends [never] ? [] | [output: TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]] : [output: TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]]): ActionPayload_Result<DOM, ID>;
2654
+ errorResult(err: TInferActionError<DOM["actionSchema"][ID]>): ActionPayload_Result<DOM, ID>;
2655
+ progress(progress: TActionProgress): ActionPayload_Progress<DOM, ID>;
2656
+ toJsonObject(): IActionPayload_Request_JsonObject<DOM, ID>;
2657
+ toJsonString(): string;
2658
+ runToOutput(options?: IExecuteActionOptions<DOM, ID>): Promise<TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"]>;
2659
+ runToResultPayload(options?: IExecuteActionOptions<DOM, ID>): Promise<ActionPayload_Result<DOM, ID>>;
2660
+ run(options?: IExecuteActionOptions<DOM, ID>): Promise<RunningAction<DOM, ID>>;
2661
+ }
2662
+ //#endregion
2663
+ //#region src/ActionDefinition/Action/Core/ActionCore.d.ts
2664
+ declare class ActionCore<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionBase<EActionForm.core, DOM, ID> implements IActionCore<DOM, ID> {
2665
+ readonly _domain: ActionDomain<DOM>;
2666
+ readonly form = EActionForm.core;
2667
+ constructor(_domain: ActionDomain<DOM>, id: ID);
2668
+ is<ACT extends IActionBase<any, any, any>>(action: ACT | unknown | null | undefined): action is TNarrowActionType<DOM, ACT, ID>;
2669
+ toJsonObject(): IActionBase_JsonObject<EActionForm.core, DOM, ID>;
2670
+ request(...args: [TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"]] extends [never] ? [input?: never] : [input: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"]]): ActionPayload_Request<DOM, ID>;
2671
+ deserializeInput(serialized: TInferInputFromSchema<DOM["actionSchema"][ID]>["SerdeInput"]): TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"];
2672
+ serializeInput(raw: TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"]): TInferInputFromSchema<DOM["actionSchema"][ID]>["SerdeInput"];
2673
+ validateInput(input: unknown): TInferInputFromSchema<DOM["actionSchema"][ID]>["Input"];
2674
+ validateOutput(output: unknown): TInferOutputFromSchema<DOM["actionSchema"][ID]>["Output"];
2675
+ }
2676
+ //#endregion
2677
+ //#region src/ActionRuntime/ActionRuntime.d.ts
2678
+ declare class ActionRuntime {
2679
+ private _coordinate;
2680
+ readonly timeCreated: number;
2681
+ readonly runtimeInfo: IRuntimeMeta;
2682
+ private readonly actionRouter;
2683
+ private readonly _pendingRunningActions;
2684
+ private readonly _registeredPeerHandlers;
2685
+ private _applied;
2686
+ static getDefault(): ActionRuntime;
2687
+ constructor(coordinate: RuntimeCoordinate);
2688
+ get coordinate(): RuntimeCoordinate;
2689
+ specifyRuntimeCoordinate(specifics: IRuntimeCoordinateSpecifics & {
2690
+ envId?: string;
2691
+ }): void;
2692
+ registerRunningAction(ra: RunningAction<any, any>): void;
2693
+ resolveIncomingActionPayload(json: TActionPayload_Any_JsonObject<any, any>): void;
2694
+ /**
2695
+ * Handle an incoming action wire (e.g. from a transport layer), route it to
2696
+ * the correct handler, and return the response. The most specific handler
2697
+ * match is chosen (action-ID-specific beats domain-wildcard).
2698
+ */
2699
+ handleActionPayloadWire<D extends IActionDomain, ID extends keyof D["actionSchema"] & string>(wire: TActionPayload_Any_JsonObject<D, ID>): Promise<RunningAction<D, ID>>;
2700
+ handleActionPayloadWire(wire: unknown): Promise<RunningAction<any, any>>;
2701
+ handleActionPayload<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: TActionPayload_Any_Instance<DOM, ID>, options?: Omit<IHandleActionOptions, "targetLocalRuntime">): Promise<RunningAction<DOM, ID>>;
2702
+ /**
2703
+ * @internal
2704
+ *
2705
+ * Return the first handler registered for the given action, or `undefined`
2706
+ * if none has been registered (action-ID-specific beats domain-wildcard).
2707
+ */
2708
+ _getHandlerForAction<ACT extends TActionPayload_Any_Instance<any, any>>(action: ACT, options?: Omit<IHandleActionOptions, "targetLocalRuntime">): TActionHandler | undefined;
2709
+ getHandlerForActionOrThrow<ACT extends TActionPayload_Any_Instance<any, any>>(action: ACT, options?: Omit<IHandleActionOptions, "localRuntime">): TActionHandler;
2710
+ /**
2711
+ * Register one or more handlers. Each handler's own `actionRouter` defines
2712
+ * which domains/actions it handles — those routing keys are mirrored into
2713
+ * this runtime's router so the same action can be served by multiple handlers.
2714
+ * Duplicate registrations (same handler cuid for the same key) are skipped.
2715
+ */
2716
+ addHandlers(handlers: TActionRuntimeHandler[]): this;
2717
+ /**
2718
+ * @internal Low-level primitive — the public way to open a connection is `connectChannel`, which
2719
+ * derives routing from a channel and binds the crypto identity for you. This stays as the raw building
2720
+ * block it sits on (it restates domain lists by hand) and is not part of the supported surface.
2721
+ *
2722
+ * Declare an external "backend client" in one call: build an
2723
+ * {@link ConnectorHandler} for `externalCoordinate` carrying the given
2724
+ * `transports`, route the listed `domains`/`actions` to it, register it (plus any
2725
+ * `localHandlers` — e.g. server→client push handlers that share the same channel)
2726
+ * on this runtime, and `apply()`. Returns the external handler so the caller can
2727
+ * later `clearTransportCache()` it.
2728
+ */
2729
+ connectTo(externalCoordinate: RuntimeCoordinate, options: {
2730
+ transports: Transport[];
2731
+ domains?: ActionDomain<any>[];
2732
+ actions?: ActionCore<any, any>[];
2733
+ localHandlers?: TActionRuntimeHandler[];
2734
+ defaultTimeout?: number;
2735
+ }): ConnectorHandler;
2736
+ private applyRuntimeForDomain;
2737
+ /**
2738
+ * Register this runtime with all root domains covered by its currently-added handlers,
2739
+ * making it eligible to execute actions dispatched from those domains.
2740
+ * After apply() is called, any subsequent addHandlers() calls also auto-register.
2741
+ */
2742
+ apply(): this;
2743
+ /**
2744
+ * Find the best registered external handler that can reach `originClient` directly.
2745
+ * Used to locate the return-path channel for dispatching results back to the action origin.
2746
+ * Returns `undefined` if no handler matches (score > 0 required, i.e. at least id must match).
2747
+ *
2748
+ * A handler that currently holds the origin's *live* connection always wins, regardless of its
2749
+ * coordinate score — owning the live socket bound to the origin's exact coordinate (set from the
2750
+ * handshake) is a strictly more precise match than any env-level `peerClient` score. This lets one
2751
+ * server accept clients of *several* envs over a single acceptor (a multi-role Durable Object): the
2752
+ * result/push routes back over the carrier the client actually connected on even when the handler's
2753
+ * `clientEnv` is unset or names a different env. Only when no handler owns a live connection do we fall
2754
+ * back to the plain best-coordinate-score pick (the offline-return and connector-only cases).
2755
+ */
2756
+ getReturnHandlerForOrigin(originClient: RuntimeCoordinate): PeerLinkHandler | undefined;
2757
+ resetRuntime(): void;
2758
+ private _trySetupReturnDispatch;
2759
+ }
2760
+ //#endregion
2761
+ //#region src/ActionDefinition/Domain/ActionDomain.d.ts
2762
+ type TActionMap<ACT_DOM extends IActionDomain> = { [K in keyof ACT_DOM["actionSchema"] & string]: ActionCore<ACT_DOM, K> };
2763
+ declare class ActionDomain<ACT_DOM extends IActionDomain = IActionDomain> extends ActionDomainBase<ACT_DOM> {
2764
+ private _rootDomain;
2765
+ private readonly _actionMap;
2766
+ constructor(definition: ACT_DOM, {
2767
+ rootDomain
2768
+ }: {
2769
+ rootDomain: ActionRootDomain<any>;
2770
+ });
2771
+ get rootDomain(): ActionRootDomain<any>;
2772
+ /**
2773
+ * @internal
2774
+ * All action observers that should see actions on this domain: the root domain's
2775
+ * observers plus this subdomain's own. Mirrors the listener set the local-dispatch
2776
+ * path assembles in `runAction`/`_runAction`, so inbound actions (pushed from a
2777
+ * backend or another client) can be wired up identically and surface in devtools.
2778
+ */
2779
+ _collectActionObservers(): TRunningActionUpdateListener<any, any>[];
2780
+ _registerRuntime(runtime: ActionRuntime): void;
2781
+ createChildDomain<SUB_DOM extends IActionDomainChildOptions>(subDomainDef: SUB_DOM & { [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never }): ActionDomain<TActionDomainChildDef<ACT_DOM, SUB_DOM>>;
2782
+ get action(): TActionMap<ACT_DOM>;
2783
+ actionsMap(): TActionMap<ACT_DOM>;
2784
+ actionForId<ID extends keyof ACT_DOM["actionSchema"] & string>(id: ID): ActionCore<ACT_DOM, ID>;
2785
+ wrapAsPartialLocalHandler(wrappedActionExecutor: Partial<TWrappableDomainActionHandler<ACT_DOM>>): ActionLocalHandler;
2786
+ wrapAsLocalHandler(wrappedActionExecutor: TWrappableDomainActionHandler<ACT_DOM>): ActionLocalHandler;
2787
+ hydrateContext<ID extends keyof ACT_DOM["actionSchema"] & string>(id: ID, contextData: IActionContext_Data_JsonObject): ActionContext<ACT_DOM, ID>;
2788
+ isDomainAction<ACT extends IActionBase<any, ACT_DOM, any>>(action: ACT | unknown | null | undefined): action is TDistributedDomainActions<ACT_DOM, ACT>;
2789
+ hydrateRequestPayload<ID extends keyof ACT_DOM["actionSchema"] & string, P extends IActionPayload_Request_JsonObject<ACT_DOM, ID>>(serialized: P): TDistributeActionPayload_Request<ACT_DOM, ID>;
2790
+ hydrateResultPayload<ID extends keyof ACT_DOM["actionSchema"] & string, R extends IActionPayload_Result_JsonObject<ACT_DOM, ID>>(serialized: R): TDistributeActionPayload_Result<ACT_DOM, ID>;
2791
+ hydrateAnyAction<ID extends keyof ACT_DOM["actionSchema"] & string, AJ extends TAction_Any_JsonObject<ACT_DOM, ID>>(actionJson: AJ): TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID>;
2792
+ runAction<ID extends keyof ACT_DOM["actionSchema"] & string, ACT extends ActionPayload_Request<ACT_DOM, ID>>(request: ACT, options?: IExecuteActionOptions<ACT_DOM, ID>): Promise<RunningAction<ACT_DOM, ID>>;
2793
+ private createActionMap;
2794
+ }
2795
+ //#endregion
2796
+ //#region src/ActionDefinition/Action/ActionBase.types.d.ts
2797
+ declare enum EActionForm {
2798
+ core = "core",
2799
+ context = "context",
2800
+ data = "data"
2801
+ }
2802
+ interface INiceActionIdAndDomain<DOM extends IActionDomain = IActionDomain> {
2803
+ domain: DOM["domain"];
2804
+ id: keyof DOM["actionSchema"] & string;
2805
+ }
2806
+ interface IActionBase<FORM extends EActionForm, DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends INiceActionIdAndDomain<DOM> {
2807
+ id: ID;
2808
+ form: FORM;
2809
+ _domain: ActionDomain<DOM>;
2810
+ allDomains: DOM["allDomains"];
2811
+ schema: DOM["actionSchema"][ID];
2812
+ }
2813
+ interface IActionBase_JsonObject<FORM extends EActionForm = EActionForm, DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> {
2814
+ form: FORM;
2815
+ domain: DOM["domain"];
2816
+ allDomains: DOM["allDomains"];
2817
+ id: ID;
2818
+ }
2819
+ //#endregion
2820
+ //#region src/ActionDefinition/Action/Action.combined.types.d.ts
2821
+ /**
2822
+ * Distributes a union ID into a proper discriminated union of ActionPayload_Request instances,
2823
+ * so that narrowing on `.id` also narrows `.input`.
2824
+ */
2825
+ type TDistributeActionPayload_Request<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string> = ID extends keyof DOM["actionSchema"] & string ? ActionPayload_Request<DOM, ID> : never;
2826
+ /**
2827
+ * Distributes a union ID into a proper discriminated union of ActionPayload_Result instances,
2828
+ * so that narrowing on `.id` also narrows `.result`.
2829
+ */
2830
+ type TDistributeActionPayload_Result<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string> = ID extends keyof DOM["actionSchema"] & string ? ActionPayload_Result<DOM, ID> : never;
2831
+ /**
2832
+ *
2833
+ * COMBINED JSON TYPES
2834
+ *
2835
+ */
2836
+ type TAction_Any_JsonObject<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = IActionCore_JsonObject<DOM, ID> | TActionPayload_Any_JsonObject<DOM, ID> | IActionContext_JsonObject<DOM, ID>;
2837
+ /**
2838
+ *
2839
+ * UTILITY TYPES
2840
+ *
2841
+ */
2842
+ type TDistributedDomainActions<DOM extends IActionDomain, ACT extends IActionBase<any, DOM, any>> = { [ID in keyof DOM["actionSchema"] & string]: TNarrowActionType<DOM, ACT, ID> }[keyof DOM["actionSchema"] & string];
2843
+ type TNarrowActionType<DOM extends IActionDomain, ACT extends IActionBase<any, DOM, any>, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ACT extends ActionPayload_Result<any> ? ActionPayload_Result<DOM, ID> : ACT extends ActionPayload_Request<any> ? ActionPayload_Request<DOM, ID> : ACT extends ActionPayload_Progress<any> ? ActionPayload_Progress<DOM, ID> : ACT extends ActionCore<any> ? ActionCore<DOM, ID> : never;
2844
+ type TNarrowActionJsonTypeToActionInstanceType<DOM extends IActionDomain, ACT extends TAction_Any_JsonObject<DOM>, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ACT extends IActionPayload_Request_JsonObject<any> ? ActionPayload_Request<DOM, ID> : ACT extends IActionPayload_Result_JsonObject<any> ? ActionPayload_Result<DOM, ID> : ACT extends IActionPayload_Progress_JsonObject<any> ? ActionPayload_Progress<DOM, ID> : ACT extends IActionCore_JsonObject<any> ? ActionCore<DOM, ID> : never;
2845
+ //#endregion
2846
+ //#region src/ActionRuntime/Handler/PeerLink/Acceptor/AcceptorHandler.d.ts
2847
+ /** The codec shape `AcceptorHandler` uses to pack/unpack frames — same as the Link transport's. */
2848
+ type TActionChannelFormatMessage = IActionWireFormat;
2849
+ /** How a connection encodes its frames, remembered so we answer each client in its own dialect. */
2850
+ type TActionConnectionEncoding = "json" | "binary";
2851
+ /** A connection's restorable identity — what to persist so a binding survives transport eviction. */
2852
+ interface IAcceptorConnectionBinding {
2853
+ /** Full client coordinate, so `originClient` can be re-injected into frames that omit it. */
2854
+ client: IRuntimeCoordinate;
2855
+ encoding: TActionConnectionEncoding;
2856
+ /**
2857
+ * Secure-session state (set once a connection's handshake completes). Persist it alongside the
2858
+ * binding so an authenticated/encrypted connection resumes after eviction without re-handshaking —
2859
+ * the `keyMaterial` lets the server re-derive the shared key from its own persisted identity.
2860
+ */
2861
+ secure?: {
2862
+ securityLevel: ESecurityLevel;
2863
+ linkedClientId: TTypeAndId;
2864
+ keyMaterial?: IHandshakeEncryptionKeyMaterial;
2865
+ };
2866
+ }
2867
+ /**
2868
+ * Server-side secure-channel config. When set, each connection negotiates a level from
2869
+ * {@link securityLevel}: an `authenticated`/`encrypted` client must complete the handshake (and is then
2870
+ * bound to its *authenticated* coordinate) before any action frame is accepted. A `none` client (only
2871
+ * when `none` is in the allowed set) is accepted as-is with a self-asserted identity. For the
2872
+ * `encrypted` level the codec source should be a session factory (`createFormatMessage`).
2873
+ */
2874
+ interface IAcceptorSecurity {
2875
+ /**
2876
+ * Accepted level(s). A single level is strict; an array is a negotiable allowed set — the server
2877
+ * adopts whichever level each client requests (e.g. `[none, authenticated, encrypted]` serves all
2878
+ * three over one endpoint).
2879
+ */
2880
+ securityLevel: ESecurityLevel | readonly ESecurityLevel[];
2881
+ /** This server's crypto identity (verify + exchange key pairs, optionally persisted). */
2882
+ link: ClientCryptoKeyLink;
2883
+ /** This server's coordinate — its identity to clients during the handshake. */
2884
+ localCoordinate: IRuntimeCoordinate;
2885
+ /** Wire dictionary version; the handshake rejects a client on a mismatch. */
2886
+ dictionaryVersion: string;
2887
+ /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */
2888
+ verifyKeyResolver?: IClientVerifyKeyResolver;
2889
+ }
2890
+ interface IAcceptorHandlerBaseOptions<TConn> {
2891
+ /**
2892
+ * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
2893
+ * scored against an action's `originClient` to pick this handler when *no* handler holds the client's
2894
+ * live connection (the offline-return fallback). A handler that currently owns the live socket always
2895
+ * wins regardless, so this is optional: omit it for a multi-role server that accepts several client envs
2896
+ * over one acceptor — it then defaults to `RuntimeCoordinate.unknown` (scores 0 against every client).
2897
+ */
2898
+ clientEnv?: RuntimeCoordinate;
2899
+ /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
2900
+ send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
2901
+ /**
2902
+ * The runtime this handler belongs to. When set, {@link AcceptorHandler.broadcast} can be called
2903
+ * without threading a runtime through each call. Optional — `pushToClient` still takes one explicitly.
2904
+ */
2905
+ runtime?: ActionRuntime;
2906
+ /** Timeout (ms) applied to server-initiated actions awaiting a client response. */
2907
+ defaultTimeout?: number;
2908
+ /**
2909
+ * Called once when a connection is first bound to a client identity. Use it to persist the binding
2910
+ * for transports that can resume after eviction — e.g. a Durable Object's hibernatable WebSocket:
2911
+ * `(ws, binding) => ws.serializeAttachment(binding)` — then replay it via {@link AcceptorHandler.rehydrateConnection}
2912
+ * when the channel comes back.
2913
+ */
2914
+ onConnectionBound?: (connection: TConn, binding: IAcceptorConnectionBinding) => void;
2915
+ /**
2916
+ * Enable the authenticated (optionally encrypted) handshake. When omitted, connections are trusted
2917
+ * as-is (identity self-asserted) — fine for dev / trusted networks.
2918
+ */
2919
+ security?: IAcceptorSecurity;
2920
+ }
2921
+ /**
2922
+ * Provide exactly one codec source:
2923
+ * - `formatMessage` — a single shared codec for every connection (stateless, e.g. `createBinaryWireAdapter`).
2924
+ * - `createFormatMessage` — a per-connection factory for stateful codecs (e.g.
2925
+ * `createBinaryWireSessionFactory`, whose sessions hold correlation + identity state). Required for the
2926
+ * leanest binary wire; the handler creates and caches one codec per connection.
2927
+ */
2928
+ type IAcceptorHandlerOptions<TConn> = IAcceptorHandlerBaseOptions<TConn> & ({
2929
+ formatMessage: TActionChannelFormatMessage;
2930
+ createFormatMessage?: never;
2931
+ } | {
2932
+ createFormatMessage: () => TActionChannelFormatMessage;
2933
+ formatMessage?: never;
2934
+ });
2935
+ /**
2936
+ * A connection-aware execution case (see {@link AcceptorHandler.forConnectionDomainCases}). It receives
2937
+ * the primed request plus a per-invocation `context` — whatever the wiring's context mapper produces from
2938
+ * the originating connection. The low-level handler passes the raw connection (`TConn | undefined`); the
2939
+ * higher-level `serveChannel` enriches it into an `IConnectionContext` (state + broadcast + pushBack). A
2940
+ * case may return the action's raw output, a result payload, or nothing (auto-wrapped as an empty
2941
+ * success) — exactly like a local handler case.
2942
+ */
2943
+ type TAcceptorCaseFn<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string, TCtx> = (action: TDistributeActionPayload_Request<DOM, ID>, context: TCtx) => ReturnType<THandleActionExecutionFn<DOM, ID>> | void;
2944
+ /**
2945
+ * The connection-aware case the bare {@link AcceptorHandler} serves: its `context` is the originating
2946
+ * client's live connection (resolved from the request's `originClient`, `undefined` if the socket is
2947
+ * gone). It's {@link TAcceptorCaseFn} fixed to `TConn | undefined` — the un-enriched shape used by
2948
+ * {@link AcceptorHandler.forConnectionDomainCases} and `acceptChannelConnections`.
2949
+ */
2950
+ type TAcceptorConnectionCaseFn<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string, TConn> = TAcceptorCaseFn<DOM, ID, TConn | undefined>;
2951
+ /**
2952
+ * Server-side handler for backends that accept many client connections over a single open channel
2953
+ * (WebSockets, Durable Objects, …). It is transport-agnostic: you feed it inbound frames with
2954
+ * {@link receive} and tell it how to write outbound frames via the `send` option.
2955
+ *
2956
+ * Add it alongside your local execution handler:
2957
+ * ```ts
2958
+ * const serverHandler = createAcceptorHandler({ clientEnv, formatMessage, send: (ws, f) => ws.send(f) });
2959
+ * runtime.addHandlers([localHandler, serverHandler]);
2960
+ * // per inbound message (e.g. a Durable Object's webSocketMessage):
2961
+ * serverHandler.receive(ws, message);
2962
+ * ```
2963
+ *
2964
+ * Inbound requests route to your local handler; the runtime's return dispatch then calls this
2965
+ * handler back (it is an external handler keyed to `clientEnv`) to send the result to the originating
2966
+ * connection. The handler keeps a per-connection identity registry so each result lands on the right
2967
+ * socket, and remembers each connection's encoding so binary and JSON clients can share the channel.
2968
+ *
2969
+ * It registers an empty action router, so it is never chosen to *execute* an inbound request — only
2970
+ * to ferry results/pushes back out.
2971
+ */
2972
+ declare class AcceptorHandler<TConn = unknown> extends PeerLinkHandler {
2973
+ /** Accept-in over a live (duplex) connection registry — it pushes results/broadcasts to bound sockets. */
2974
+ readonly canPush = true;
2975
+ private readonly _formatMessage?;
2976
+ private readonly _createFormatMessage?;
2977
+ private readonly _send;
2978
+ private readonly _runtime?;
2979
+ private readonly _serverTimeout;
2980
+ private _onConnectionBound?;
2981
+ private readonly _security?;
2982
+ /** Normalized accepted levels; whether `none` (plain) is allowed; whether any level needs a handshake. */
2983
+ private readonly _allowedLevels;
2984
+ private readonly _noneAllowed;
2985
+ private readonly _handshakeMode;
2986
+ private readonly _connByClient;
2987
+ private readonly _clientByConn;
2988
+ private readonly _connEncoding;
2989
+ private readonly _codecByConn;
2990
+ private readonly _sessionByConn;
2991
+ constructor(options: IAcceptorHandlerOptions<TConn>);
2992
+ /**
2993
+ * The codec for a connection: a per-connection session (cached) when a factory was provided, else
2994
+ * the single shared `formatMessage`.
2995
+ */
2996
+ private _codecFor;
2997
+ /**
2998
+ * Register (or replace) the connection-bound persistence callback after construction. Used by
2999
+ * lifecycle helpers like {@link createHibernatableWsServerAdapter} so persistence and replay are
3000
+ * owned by one place instead of being split across the constructor options.
3001
+ */
3002
+ setOnConnectionBound(onConnectionBound: (connection: TConn, binding: IAcceptorConnectionBinding) => void): void;
3003
+ /**
3004
+ * Feed one inbound frame from a connection into the runtime. Decodes text or binary, binds the
3005
+ * connection to the requesting client's identity, then routes it (requests execute locally;
3006
+ * results/progress resolve pending server-initiated actions).
3007
+ */
3008
+ receive(connection: TConn, frame: string | ArrayBuffer | Uint8Array): void;
3009
+ private _receivePlain;
3010
+ /**
3011
+ * The secure session for a connection (built lazily on its first secure-mode frame), with the
3012
+ * handler-owned effects — raw send, identity binding + persistence, and inbound routing — wired in as
3013
+ * callbacks. The session owns all crypto/handshake/chain state; the handler keeps only the registry.
3014
+ */
3015
+ private _sessionFor;
3016
+ /** Bind + persist a connection's authenticated identity once its handshake completes. */
3017
+ private _onConnectionAuthenticated;
3018
+ /** Decode a decrypted authenticated frame, inject the *authenticated* identity, and route it. */
3019
+ private _routeAuthedActionBytes;
3020
+ /**
3021
+ * Ensure an inbound request carries the client's identity and that this connection is bound to it,
3022
+ * so its result can be routed back. A session codec omits `originClient` after the first request, so
3023
+ * when it's missing we restore it from the (possibly rehydrated) binding instead. (Plain mode only;
3024
+ * secure mode binds the authenticated coordinate at handshake time.)
3025
+ */
3026
+ private _resolveRequestIdentity;
3027
+ /**
3028
+ * Restore a connection→client binding without an inbound frame — for transports that resume after
3029
+ * eviction. Pair it with the {@link IAcceptorHandlerOptions.onConnectionBound} hook: persist
3030
+ * the binding there, then replay each live connection here when the channel comes back (e.g. a
3031
+ * Durable Object iterating `ctx.getWebSockets()` as it wakes from hibernation).
3032
+ */
3033
+ rehydrateConnection(connection: TConn, binding: IAcceptorConnectionBinding): void;
3034
+ toJsonObject(): IActionHandler_Peer_Json;
3035
+ toHandlerRouteItem(): IActionRouteItemHandler;
3036
+ /** Forget a connection (call on socket close) so stale entries don't misroute later results. */
3037
+ dropConnection(connection: TConn): void;
3038
+ /** Live connection for a client coordinate, if currently registered. */
3039
+ getConnectionForClient(client: RuntimeCoordinate): TConn | undefined;
3040
+ /** This acceptor owns the origin's return path when it currently holds a live connection bound to it. */
3041
+ ownsLiveConnectionFor(origin: RuntimeCoordinate): boolean;
3042
+ /** Whether this acceptor currently tracks `connection` — used to pick the owning handler among several. */
3043
+ hasConnection(connection: TConn): boolean;
3044
+ /**
3045
+ * Send (and optionally await) a server-initiated action to a specific connected client. Pass the
3046
+ * connection token directly (e.g. the `ws`) or a client `RuntimeCoordinate` to look one up.
3047
+ */
3048
+ pushToClient<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(runtime: ActionRuntime, target: TConn | RuntimeCoordinate, request: ActionPayload_Request<DOM, ID>, options?: {
3049
+ timeout?: number;
3050
+ }): RunningAction<DOM, ID>;
3051
+ /**
3052
+ * Build a local handler whose cases are connection-aware: each case receives the primed request and
3053
+ * the originating client's live connection (resolved from `originClient`), so handlers don't repeat
3054
+ * the `getConnectionForClient(action.context.originClient)` lookup. Cases may return raw output or
3055
+ * nothing, just like {@link ActionLocalHandler.forDomainActionCases}. Add the returned handler to the
3056
+ * runtime alongside this server handler:
3057
+ * ```ts
3058
+ * runtime.addHandlers([serverHandler.forConnectionDomainCases(domain, { … }), serverHandler]);
3059
+ * ```
3060
+ */
3061
+ forConnectionDomainCases<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>, cases: { [ID in keyof FOR_DOM["actionSchema"] & string]?: TAcceptorConnectionCaseFn<FOR_DOM, ID, TConn> }): ActionLocalHandler;
3062
+ /**
3063
+ * Like {@link forConnectionDomainCases} but spanning several domains with one merged case map — used
3064
+ * by channel-derived wiring (`acceptChannelConnections` / `serveChannel`) where the channel's
3065
+ * `toAcceptor` domains are served together. Each domain takes only the cases whose ids it owns, so a
3066
+ * single map can cover several domains and unrelated ids are ignored.
3067
+ *
3068
+ * `mapContext` turns the resolved connection into whatever the case's second argument should be: the
3069
+ * raw connection for the low-level helper, or an enriched `IConnectionContext` for `serveChannel`. It's
3070
+ * called once per inbound action, after the originating connection is resolved.
3071
+ */
3072
+ forConnectionDomainCasesMulti<TCtx>(domains: readonly ActionDomain<any>[], cases: Record<string, TAcceptorCaseFn<any, any, TCtx> | undefined>, mapContext: (connection: TConn | undefined, request: ActionPayload_Request<any, any>) => TCtx): ActionLocalHandler;
3073
+ /**
3074
+ * Fan a server-initiated request out to every currently-bound connection. A fresh request is built
3075
+ * per connection (each push mutates its own action context) and dispatched fire-and-forget. Pass
3076
+ * `except` to skip the originating socket and `where` to filter by connection (e.g. read its
3077
+ * attachment for a role). Iterating bound connections (rather than every accepted socket) skips
3078
+ * sockets that are still mid-handshake and so can't yet receive a frame.
3079
+ */
3080
+ broadcast<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(makeRequest: () => ActionPayload_Request<DOM, ID>, options?: {
3081
+ runtime?: ActionRuntime;
3082
+ except?: TConn | null;
3083
+ where?: (connection: TConn) => boolean;
3084
+ timeout?: number;
3085
+ onError?: (error: unknown, connection: TConn) => void;
3086
+ }): void;
3087
+ sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
3088
+ targetLocalRuntime: ActionRuntime;
3089
+ }): Promise<boolean>;
3090
+ handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
3091
+ private _dispatch;
3092
+ private _sendPayload;
3093
+ private _bindConnection;
3094
+ private _resolveConnection;
3095
+ private _resolveSingleConnection;
3096
+ }
3097
+ declare const createAcceptorHandler: <TConn = unknown>(options: IAcceptorHandlerOptions<TConn>) => AcceptorHandler<TConn>;
3098
+ //#endregion
3099
+ export { httpAcceptorCarrier as $, createInMemoryTofuVerifyKeyResolver as $n, TCarrier as $t, TActionResultOutcome as A, TOnResolveIncomingResponseJson as An, IActionCore_JsonObject as Ar, IHibernatableWsServerAdapterOptions as At, decodeExchangeReply as B, Transport as Bn, TPossibleDomainIdList as Br, TChannelAcceptorCases as Bt, IActionProgress_Custom as C, IUpdateActionRunConfig_Output as Cn, IRuntimeCoordinate as Cr, serveChannel as Ct, TActionPayload_Any_Instance as D, TOnResolveIncomingRequest as Dn, TRuntimeCoordinateEnvId as Dr, TAcceptorCarrier as Dt, IActionRouteItemHandler as E, TOnResolveAnyIncomingActionData_Json as En, RuntimeCoordinate as Er, IExchangeAcceptorCarrier as Et, decodeActionFrame as F, TTransportStatusInfo as Fn, TActionDomainSchema as Fr, createConnectionStateStore as Ft, IExchangeAcceptorSecurity as G, IClientHandshakeConfig as Gn, TActionSchemaOptions as Gr, defineChannel as Gt, encodeExchange as H, createSecureAcceptorHandler as Hn, EActionResponseMode as Hr, acceptChannel as Ht, EErrId_NiceAction as I, TTransportStatusInfo_GetTransport_Output as In, TDomainActionId as Ir, IAcceptChannelOptions as It, IHttpCarrierOptions as J, IHandshakeEncryptionKeyMaterial as Jn, IActionWireFormat as Jt, EErrId_NiceTransport as K, IClientVerifyKeyResolveInput as Kn, TActionSerializationDefinition as Kr, IBinaryWireSessionOptions as Kt, err_nice_action as L, TUpdateActionRunConfig as Ln, TInferInputFromSchema as Lr, IActionChannel as Lt, isActionPayload_Request_JsonObject as M, TSendReturnDataMethod as Mn, IActionDomainChildOptions as Mr, ConnectionStateStore as Mt, isActionPayload_Any_JsonObject as N, TTransportCache as Nn, IActionRootDomain as Nr, IConnectionAttachment as Nt, TActionPayload_Any_JsonObject as O, TOnResolveIncomingRequestJson as On, TRuntimeCoordinateStringId as Or, isExchangeAcceptorCarrier as Ot, IActionFrameDecoder as P, TTransportInitializationFinishedInfo as Pn, TActionDomainChildDef as Pr, IConnectionStateStoreOptions as Pt, IHttpAcceptorCarrierOptions as Q, createClientHandshake as Qn, IExchangeCarrierSource as Qt, TExchangeReply as R, TransportConnection as Rn, TInferOutputFromSchema as Rr, IConnectChannelOptions as Rt, IActionPayload_Result_JsonObject as S, ITransportStatusInfo_Unsupported as Sn, ActionPayload_Progress as Sr, IServeConnectionStateOptions as St, IActionProgress_Percentage as T, TOnResolveAnyIncomingActionData as Tn, IRuntimeFullCoordinates as Tr, IDuplexAcceptorCarrier as Tt, ExchangeAcceptor as U, EHandshakeMessageType as Un, TInferActionError as Ur, acceptChannelConnections as Ut, decodeExchangeRequest as V, ISecureAcceptorHandlerOptions as Vn, ActionSchema as Vr, TChannelPushHandlers as Vt, IExchangeAcceptorConfig as W, ESecurityLevel as Wn, actionSchema as Wr, connectChannel as Wt, TCarrierFetch as X, IServerHandshakeConfig as Xn, IDuplexCarrierSource as Xt, IHttpCarrierRequest as Y, IHandshakeResult as Yn, IDuplexCarrier as Yt, httpCarrier as Z, THandshakeMessage as Zn, IExchangeCarrier as Zt, IActionPayload_Data_Base as _, ITransportRouteInfo as _n, IRunningActionUpdate_Success as _r, TServeHostOptions as _t, TAcceptorConnectionCaseFn as a, ETransportStatus as an, ActionLocalHandler as ar, err_nice_transport_ws as at, IActionPayload_Request_JsonObject as b, ITransportStatusInfo_Initializing as bn, TRunningActionUpdateListener as br, IConnectionContext as bt, createAcceptorHandler as c, IActionTransportReady as cn, createActionRootDomain as cr, IRtcDataChannelLike as ct, ActionCore as d, IActionTransportResolvers as dn, ERunningActionFinishedType as dr, inMemoryCarrier as dt, TFrame$1 as en, createServerHandshake as er, IWsCarrierOptions as et, ActionPayload_Request as f, ISecureClientConfig as fn, ERunningActionState as fr, IInMemoryChannelPair as ft, IActionPayload_Base_JsonObject as g, ITransportRouteClientParams as gn, IRunningActionUpdate_Started as gr, IChannelHostAdapter as gt, IActionPayload_Base as h, ITransportRouteActionParams as hn, IRunningActionUpdate_Progress as hr, err_nice_external_client as ht, TAcceptorCaseFn as i, ETransportShape as in, runtimeLinkId as ir, EErrId_NiceTransport_WebSocket as it, isActionPayload_Result_JsonObject as j, TSendActionDataMethod as jn, IActionDomain as jr, createHibernatableWsServerAdapter as jt, TActionProgress as k, TOnResolveIncomingResponse as kn, IActionCore as kr, IDuplexConnectionRouter as kt, ActionDomain as l, IActionTransportReadyData_Base as ln, ActionRootDomain as lr, rtcDataChannelByteChannel as lt, EActionProgressType as m, ITransportMethod_SendActionData_Input as mn, IRunningActionUpdate_Abort as mr, createInMemoryChannelPair as mt, IAcceptorConnectionBinding as n, createConnectorHandler as nn, decodeHandshakeMessage as nr, IWsAcceptorCarrierOptions as nt, TActionChannelFormatMessage as o, IActionTransportDef as on, createLocalHandler as or, IRtcCarrierOptions as ot, EActionPayloadType as p, ITransportDispatchAction as pn, ERunningActionUpdateType as pr, IInMemoryServerEndpoint as pt, err_nice_transport as q, IClientVerifyKeyResolver as qn, TTransportedValue as qr, createBinaryWireSessionFactory as qt, IAcceptorHandlerOptions as r, PeerLinkHandler as rn, encodeHandshakeMessage as rr, wsAcceptorCarrier as rt, TActionConnectionEncoding as s, IActionTransportInitialized as sn, MaybePromise as sr, rtcCarrier as st, AcceptorHandler as t, ConnectorHandler as tn, createStorageTofuVerifyKeyResolver as tr, wsCarrier as tt, ActionRuntime as u, IActionTransportReadyData_Methods as un, RunningAction as ur, IInMemoryCarrier as ut, IActionPayload_Progress as v, ITransportStatusInfo_Base as vn, TRunningActionUpdate as vr, serveHost as vt, IActionProgress_None as w, TGetTransportFn as wn, IRuntimeCoordinateSpecifics as wr, IAcceptorAttachmentStore as wt, IActionPayload_Result as x, ITransportStatusInfo_Ready as xn, ActionPayload_Result as xr, IServeChannelOptions as xt, IActionPayload_Progress_JsonObject as y, ITransportStatusInfo_Failed as yn, TRunningActionUpdateFinished as yr, IChannelServer as yt, TExchangeRequest as z, ITransportConnectionContext as zn, TPossibleDomainId as zr, IConnectTransport as zt };
3100
+ //# sourceMappingURL=AcceptorHandler-BizUtq4u.d.mts.map