@nice-code/action 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +582 -582
- package/build/{ActionDevtoolsCore-9PsnscvK.mjs → ActionDevtoolsCore-BcItqP-C.mjs} +7 -7
- package/build/ActionDevtoolsCore-BcItqP-C.mjs.map +1 -0
- package/build/{ActionDevtoolsCore-CCRLYASa.d.cts → ActionDevtoolsCore-C5XrQI1K.d.mts} +3 -3
- package/build/{ActionDevtoolsCore-DtgXwPBZ.cjs → ActionDevtoolsCore-Cb_QR44N.cjs} +7 -7
- package/build/ActionDevtoolsCore-Cb_QR44N.cjs.map +1 -0
- package/build/{ActionDevtoolsCore-CYGD2o6C.d.mts → ActionDevtoolsCore-Dd1qJAwK.d.cts} +3 -3
- package/build/{ActionPayload.types-BN-rXFBK.d.cts → ActionPayload.types-BchJrBIX.d.mts} +1966 -1139
- package/build/{ActionPayload.types-D28ELKXC.d.mts → ActionPayload.types-snDlSIF-.d.cts} +1966 -1139
- package/build/devtools/browser/index.cjs +5 -5
- package/build/devtools/browser/index.cjs.map +1 -1
- package/build/devtools/browser/index.d.cts +1 -1
- package/build/devtools/browser/index.d.mts +1 -1
- package/build/devtools/browser/index.mjs +5 -5
- package/build/devtools/browser/index.mjs.map +1 -1
- package/build/devtools/server/index.cjs +1 -1
- package/build/devtools/server/index.cjs.map +1 -1
- package/build/devtools/server/index.d.cts +1 -1
- package/build/devtools/server/index.d.mts +1 -1
- package/build/devtools/server/index.mjs +1 -1
- package/build/devtools/server/index.mjs.map +1 -1
- package/build/index.cjs +2667 -1752
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +2 -2
- package/build/index.d.mts +2 -2
- package/build/index.mjs +2635 -1738
- package/build/index.mjs.map +1 -1
- package/build/platform/cloudflare/index.cjs +56 -0
- package/build/platform/cloudflare/index.cjs.map +1 -0
- package/build/platform/cloudflare/index.d.cts +67 -0
- package/build/platform/cloudflare/index.d.mts +67 -0
- package/build/platform/cloudflare/index.mjs +54 -0
- package/build/platform/cloudflare/index.mjs.map +1 -0
- package/build/react-query/index.cjs.map +1 -1
- package/build/react-query/index.d.cts +1 -1
- package/build/react-query/index.d.mts +1 -1
- package/build/react-query/index.mjs.map +1 -1
- package/build/wsAcceptorCarrier-CXGlQU_f.mjs +80 -0
- package/build/wsAcceptorCarrier-CXGlQU_f.mjs.map +1 -0
- package/build/wsAcceptorCarrier-DHRbsY1X.cjs +103 -0
- package/build/wsAcceptorCarrier-DHRbsY1X.cjs.map +1 -0
- package/package.json +15 -4
- package/build/ActionDevtoolsCore-9PsnscvK.mjs.map +0 -1
- package/build/ActionDevtoolsCore-DtgXwPBZ.cjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { INiceErrorDomainProps, InferNiceError, NiceError, NiceErrorDomain, err_cast_not_nice } from "@nice-code/error";
|
|
2
|
-
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
2
|
import { RuntimeName } from "std-env";
|
|
4
3
|
import { ClientCryptoKeyLink, StorageAdapter, TSerializedCryptoKeyData_Ed25519_Raw, TSerializedCryptoKeyData_X25519_Raw, TTypeAndId } from "@nice-code/util";
|
|
5
4
|
import * as v from "valibot";
|
|
5
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
6
|
|
|
7
7
|
//#region src/ActionDefinition/Schema/ActionSchema.types.d.ts
|
|
8
8
|
type TTransportedValue<RAW_VAL, SERDE_VAL> = [RAW_VAL] | [RAW_VAL, SERDE_VAL];
|
|
@@ -35,12 +35,45 @@ type TInferErrorFromDeclaration<D> = D extends IActionErrorDeclaration<infer DEF
|
|
|
35
35
|
type TInferDeclaredErrors<DECLS extends readonly IActionErrorDeclaration[]> = TInferErrorFromDeclaration<DECLS[number]>;
|
|
36
36
|
//#endregion
|
|
37
37
|
//#region src/ActionDefinition/Schema/ActionSchema.d.ts
|
|
38
|
+
/**
|
|
39
|
+
* What a sender should expect back from an action — declared on its schema so both ends agree without
|
|
40
|
+
* any wire flag (each derives the mode from the shared `domain:id`).
|
|
41
|
+
*
|
|
42
|
+
* - `payload` — a typed output (the action has `.output(...)`); the sender awaits it.
|
|
43
|
+
* - `ack` — an empty success confirming receipt (no output); the sender may await it to know the
|
|
44
|
+
* receiver handled it (or to surface an error). This is the default for an action with no output.
|
|
45
|
+
* - `none` — fire-and-forget: the receiver sends no reply and the sender doesn't wait. The sender's
|
|
46
|
+
* running action completes as soon as the frame is on the wire (no pending reply, no timeout).
|
|
47
|
+
*/
|
|
48
|
+
declare enum EActionResponseMode {
|
|
49
|
+
payload = "payload",
|
|
50
|
+
ack = "ack",
|
|
51
|
+
none = "none"
|
|
52
|
+
}
|
|
38
53
|
declare class ActionSchema<INPUT extends TTransportedValue<any, any> = never, OUTPUT extends TTransportedValue<any, any> = never, ERRORS extends readonly IActionErrorDeclaration<any, any>[] = readonly []> {
|
|
39
54
|
private _errorDeclarations;
|
|
40
55
|
private inputOptions;
|
|
41
56
|
private outputOptions;
|
|
57
|
+
private _responseMode;
|
|
42
58
|
get inputSchema(): StandardSchemaV1 | undefined;
|
|
43
59
|
get outputSchema(): StandardSchemaV1 | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* The response contract for this action. Defaults are inferred — `payload` when an output schema is
|
|
62
|
+
* declared, otherwise `ack` — and made explicit by {@link ack} / {@link fireAndForget}.
|
|
63
|
+
*/
|
|
64
|
+
get responseMode(): EActionResponseMode;
|
|
65
|
+
/**
|
|
66
|
+
* Mark this action as expecting only an acknowledgment (an empty success). Mostly for clarity — an
|
|
67
|
+
* output-less action already acks by default — but it documents intent and reads as the deliberate
|
|
68
|
+
* counterpart to {@link fireAndForget}.
|
|
69
|
+
*/
|
|
70
|
+
ack(): this;
|
|
71
|
+
/**
|
|
72
|
+
* Mark this action as fire-and-forget: the receiver sends no reply, and the sender's running action
|
|
73
|
+
* completes the moment the frame is sent (no awaited reply, no timeout). Ideal for high-frequency
|
|
74
|
+
* server→client pushes (presence, ticks) where an ack would only add wire chatter.
|
|
75
|
+
*/
|
|
76
|
+
fireAndForget(): this;
|
|
44
77
|
/**
|
|
45
78
|
* Declare the input schema (JSON-native or with explicit SERDE type param).
|
|
46
79
|
* For non-JSON-native inputs, prefer the 3-argument form below to avoid
|
|
@@ -501,20 +534,20 @@ declare class ActionRouter<DATA> {
|
|
|
501
534
|
//#endregion
|
|
502
535
|
//#region src/ActionRuntime/Handler/ActionHandler.types.d.ts
|
|
503
536
|
declare enum EActionHandlerType {
|
|
504
|
-
|
|
537
|
+
peer = "peer",
|
|
505
538
|
local = "local"
|
|
506
539
|
}
|
|
507
540
|
interface IActionHandler_Json<T extends EActionHandlerType> {
|
|
508
541
|
type: T;
|
|
509
542
|
}
|
|
510
|
-
interface
|
|
543
|
+
interface IActionHandler_Peer_Json extends IActionHandler_Json<EActionHandlerType.peer> {
|
|
511
544
|
client: IRuntimeCoordinate;
|
|
512
545
|
}
|
|
513
546
|
interface IActionHandler_Local_Json extends IActionHandler_Json<EActionHandlerType.local> {}
|
|
514
|
-
type TActionHandler_Json = IActionHandler_Local_Json |
|
|
547
|
+
type TActionHandler_Json = IActionHandler_Local_Json | IActionHandler_Peer_Json;
|
|
515
548
|
interface IHandleActionOptions {
|
|
516
549
|
timeout?: number;
|
|
517
|
-
|
|
550
|
+
targetPeer?: RuntimeCoordinate;
|
|
518
551
|
targetLocalRuntime?: ActionRuntime;
|
|
519
552
|
}
|
|
520
553
|
interface IExecuteActionOptions<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends IHandleActionOptions {
|
|
@@ -536,13 +569,13 @@ interface IActionHandler_Local extends IActionHandler_Base<EActionHandlerType.lo
|
|
|
536
569
|
}
|
|
537
570
|
/**
|
|
538
571
|
*
|
|
539
|
-
*
|
|
572
|
+
* PEER-LINK ACTION HANDLER
|
|
540
573
|
*
|
|
541
574
|
*/
|
|
542
|
-
interface
|
|
543
|
-
interface
|
|
544
|
-
|
|
545
|
-
handleActionRequest: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?:
|
|
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>>;
|
|
546
579
|
_setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject<any>) => void): void;
|
|
547
580
|
}
|
|
548
581
|
/**
|
|
@@ -550,7 +583,7 @@ interface IActionHandler_ExternalClient extends IActionHandler_Base<EActionHandl
|
|
|
550
583
|
* COMBINED
|
|
551
584
|
*
|
|
552
585
|
*/
|
|
553
|
-
type TActionHandler = IActionHandler_Local |
|
|
586
|
+
type TActionHandler = IActionHandler_Local | IActionHandler_Peer;
|
|
554
587
|
//#endregion
|
|
555
588
|
//#region src/ActionDefinition/Action/Payload/ActionPayload_Request.d.ts
|
|
556
589
|
declare class ActionPayload_Request<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> extends ActionPayload<EActionPayloadType.request, DOM, ID> {
|
|
@@ -624,229 +657,6 @@ declare abstract class ActionHandler<T extends EActionHandlerType> implements IA
|
|
|
624
657
|
abstract toHandlerRouteItem(...args: any[]): IActionRouteItemHandler;
|
|
625
658
|
}
|
|
626
659
|
//#endregion
|
|
627
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/TransportConnection.d.ts
|
|
628
|
-
/**
|
|
629
|
-
* Live, per-handler transport runtime built from a reusable {@link Transport} definition. Holds the
|
|
630
|
-
* connection-scoped state (ordinal, initialized config, sockets / abort sets) that must not be shared
|
|
631
|
-
* across handlers. Construct these via `definition._createConnection(...)`, never directly.
|
|
632
|
-
*/
|
|
633
|
-
declare abstract class TransportConnection<T extends ETransportType = ETransportType, RP extends ITransportRouteActionParams = ITransportRouteActionParams, RD extends IActionTransportReadyData_Base = IActionTransportReadyData_Base, I extends IActionTransportInitialized<RP, RD> = IActionTransportInitialized<RP, RD>, DEF extends IActionTransportDef<T, I> = IActionTransportDef<T, I>> {
|
|
634
|
-
readonly def: DEF;
|
|
635
|
-
readonly transOrd: number;
|
|
636
|
-
readonly type: T;
|
|
637
|
-
readonly initialized: I;
|
|
638
|
-
/** Backref to the public definition that created this connection (used for devtools route info). */
|
|
639
|
-
definition?: Transport<T>;
|
|
640
|
-
constructor(def: DEF);
|
|
641
|
-
/**
|
|
642
|
-
* Devtools route info for an action routed through this live connection. Defaults to the stateless
|
|
643
|
-
* {@link definition}'s info; connections override to enrich it from live state (e.g. the actual
|
|
644
|
-
* resolved socket URL) when the definition couldn't resolve it on its own.
|
|
645
|
-
*/
|
|
646
|
-
getRouteInfo(input: RP): ITransportRouteInfo | undefined;
|
|
647
|
-
protected abstract _finalizeTransportMethods(inputs: RD): IActionTransportReadyData_Methods;
|
|
648
|
-
protected _getCacheKey(input: RP): string | null;
|
|
649
|
-
getCacheKey(input: RP): string | null;
|
|
650
|
-
getTransport(input: RP): TTransportStatusInfo<IActionTransportReadyData_Methods>;
|
|
651
|
-
protected _processTransportStatus(input: RP): TTransportStatusInfo<IActionTransportReadyData_Methods>;
|
|
652
|
-
}
|
|
653
|
-
//#endregion
|
|
654
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/Transport.types.d.ts
|
|
655
|
-
declare enum ETransportType {
|
|
656
|
-
ws = "ws",
|
|
657
|
-
http = "http",
|
|
658
|
-
custom = "custom"
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* Serializable, display-only description of how an action was routed through a transport. Stored on
|
|
662
|
-
* the action's route items and shown in the devtools external-handler chips. Keep this free of
|
|
663
|
-
* sensitive data (e.g. auth headers) — route items travel over the wire.
|
|
664
|
-
*/
|
|
665
|
-
interface ITransportRouteInfo {
|
|
666
|
-
type: ETransportType;
|
|
667
|
-
/** Short label for chips, e.g. "POST /resolve_action" or "ws host/resolve_action/ws". */
|
|
668
|
-
summary?: string;
|
|
669
|
-
url?: string;
|
|
670
|
-
method?: string;
|
|
671
|
-
detail?: Record<string, string | number | boolean>;
|
|
672
|
-
}
|
|
673
|
-
interface IUpdateActionRunConfig_Output {
|
|
674
|
-
timeout?: number;
|
|
675
|
-
}
|
|
676
|
-
type TUpdateActionRunConfig = (input: ITransportRouteActionParams & {
|
|
677
|
-
timeout: number;
|
|
678
|
-
}) => IUpdateActionRunConfig_Output;
|
|
679
|
-
interface IActionTransportReadyData_Base {
|
|
680
|
-
updateRunConfig?: TUpdateActionRunConfig;
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
|
-
*
|
|
684
|
-
* TRANSPORT READINESS RESPONSE
|
|
685
|
-
*
|
|
686
|
-
*/
|
|
687
|
-
declare enum ETransportStatus {
|
|
688
|
-
uninitialized = "uninitialized",
|
|
689
|
-
unsupported = "unsupported",
|
|
690
|
-
initializing = "initializing",
|
|
691
|
-
ready = "ready",
|
|
692
|
-
failed = "failed"
|
|
693
|
-
}
|
|
694
|
-
interface ITransportStatusInfo_Base<S extends ETransportStatus> {
|
|
695
|
-
status: S;
|
|
696
|
-
}
|
|
697
|
-
interface ITransportStatusInfo_Failed extends ITransportStatusInfo_Base<ETransportStatus.failed> {
|
|
698
|
-
error: NiceError;
|
|
699
|
-
timeFailed: number;
|
|
700
|
-
}
|
|
701
|
-
interface ITransportStatusInfo_Unsupported extends ITransportStatusInfo_Base<ETransportStatus.unsupported> {}
|
|
702
|
-
interface ITransportStatusInfo_Ready<READY extends IActionTransportReadyData_Base> extends ITransportStatusInfo_Base<ETransportStatus.ready> {
|
|
703
|
-
readyData: READY;
|
|
704
|
-
}
|
|
705
|
-
type TTransportInitializationFinishedInfo<READY extends IActionTransportReadyData_Base> = ITransportStatusInfo_Ready<READY> | ITransportStatusInfo_Failed | ITransportStatusInfo_Unsupported;
|
|
706
|
-
interface ITransportStatusInfo_Initializing<READY extends IActionTransportReadyData_Base> extends ITransportStatusInfo_Base<ETransportStatus.initializing> {
|
|
707
|
-
timeStarted: number;
|
|
708
|
-
initializationPromise: Promise<TTransportInitializationFinishedInfo<READY>>;
|
|
709
|
-
}
|
|
710
|
-
type TTransportStatusInfo<READY extends IActionTransportReadyData_Base> = ITransportStatusInfo_Base<ETransportStatus.uninitialized> | ITransportStatusInfo_Unsupported | ITransportStatusInfo_Initializing<READY> | ITransportStatusInfo_Ready<READY> | ITransportStatusInfo_Failed;
|
|
711
|
-
type TTransportStatusInfo_GetTransport_Output<READY extends IActionTransportReadyData_Base> = ITransportStatusInfo_Base<ETransportStatus.uninitialized> | ITransportStatusInfo_Unsupported | Omit<ITransportStatusInfo_Initializing<READY>, "timeStarted"> | ITransportStatusInfo_Ready<READY> | Omit<ITransportStatusInfo_Failed, "timeFailed">;
|
|
712
|
-
/**
|
|
713
|
-
*
|
|
714
|
-
* TRANSPORT ROUTING
|
|
715
|
-
*
|
|
716
|
-
*/
|
|
717
|
-
interface ITransportRouteClientParams {
|
|
718
|
-
localClient: RuntimeCoordinate;
|
|
719
|
-
externalClient: RuntimeCoordinate;
|
|
720
|
-
}
|
|
721
|
-
interface ITransportRouteActionParams extends ITransportRouteClientParams {
|
|
722
|
-
action: TActionPayload_Any_Instance<any, any>;
|
|
723
|
-
}
|
|
724
|
-
interface ITransportMethod_SendActionData_Input extends ITransportRouteActionParams {
|
|
725
|
-
runningAction: RunningAction<any, any>;
|
|
726
|
-
timeout: number;
|
|
727
|
-
}
|
|
728
|
-
interface ITransportDispatchAction<P> extends ITransportMethod_SendActionData_Input {
|
|
729
|
-
params: P;
|
|
730
|
-
}
|
|
731
|
-
type TSendActionDataMethod = (input: ITransportMethod_SendActionData_Input) => void;
|
|
732
|
-
type TSendReturnDataMethod = (payload: TActionPayload_Any_Instance<any, any>,
|
|
733
|
-
/**
|
|
734
|
-
* The local/external client pair this payload is being returned over. Bidirectional transports use
|
|
735
|
-
* it to build the full route params their outgoing formatter expects (e.g. binary packing). Optional
|
|
736
|
-
* so existing return-data implementations keep type-checking.
|
|
737
|
-
*/
|
|
738
|
-
|
|
739
|
-
clients?: ITransportRouteClientParams) => void;
|
|
740
|
-
interface IActionTransportReadyData_Methods extends IActionTransportReadyData_Base {
|
|
741
|
-
sendActionData: TSendActionDataMethod;
|
|
742
|
-
/**
|
|
743
|
-
* Optional — implement on bidirectional transports (WebSocket, Custom) to enable return-path
|
|
744
|
-
* routing. When present, the runtime uses this to dispatch results and progress payloads directly
|
|
745
|
-
* back to `originClient` without going through the original request transport.
|
|
746
|
-
*/
|
|
747
|
-
sendReturnData?: TSendReturnDataMethod;
|
|
748
|
-
addOnDisconnectListener?: (callback: () => void) => void;
|
|
749
|
-
/**
|
|
750
|
-
* Optional — implement on transports holding a long-lived connection (WebSocket, Custom) to close it
|
|
751
|
-
* deliberately. Called by `ActionExternalClientHandler.clearTransportCache()` so a teardown actually
|
|
752
|
-
* releases the underlying socket instead of leaving it open until GC.
|
|
753
|
-
*/
|
|
754
|
-
disconnect?: () => void;
|
|
755
|
-
}
|
|
756
|
-
interface IActionTransportReady {
|
|
757
|
-
methods: IActionTransportReadyData_Methods;
|
|
758
|
-
transport: TransportConnection;
|
|
759
|
-
}
|
|
760
|
-
type TTransportCache = Map<string, IActionTransportReady | Promise<IActionTransportReady>>;
|
|
761
|
-
type TOnResolveIncomingRequest = (request: ActionPayload_Request<any>) => void;
|
|
762
|
-
type TOnResolveIncomingRequestJson = (request: IActionPayload_Request_JsonObject<any>) => void;
|
|
763
|
-
type TOnResolveIncomingResponse = (response: ActionPayload_Result<any>) => void;
|
|
764
|
-
type TOnResolveIncomingResponseJson = (response: IActionPayload_Result_JsonObject<any>) => void;
|
|
765
|
-
type TOnResolveAnyIncomingActionData = (actionData: ActionPayload_Request<any> | ActionPayload_Result<any>) => void;
|
|
766
|
-
type TOnResolveAnyIncomingActionData_Json = (actionData: TActionPayload_Any_JsonObject<any>) => void;
|
|
767
|
-
interface IActionTransportResolvers {
|
|
768
|
-
onIncomingActionDataJson: TOnResolveAnyIncomingActionData_Json;
|
|
769
|
-
}
|
|
770
|
-
type TGetTransportFn<IN extends ITransportRouteActionParams, READY extends IActionTransportReadyData_Base> = (input: IN) => TTransportStatusInfo_GetTransport_Output<READY>;
|
|
771
|
-
interface IActionTransportInitialized<IN extends ITransportRouteActionParams, READY extends IActionTransportReadyData_Base> {
|
|
772
|
-
getTransportCacheKey?: (input: IN) => string[];
|
|
773
|
-
getTransport: TGetTransportFn<IN, READY>;
|
|
774
|
-
}
|
|
775
|
-
interface IActionTransportDef<TYPE extends ETransportType, INIT extends IActionTransportInitialized<any, any>> {
|
|
776
|
-
type: TYPE;
|
|
777
|
-
initialize: () => INIT;
|
|
778
|
-
}
|
|
779
|
-
//#endregion
|
|
780
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/Transport.d.ts
|
|
781
|
-
/**
|
|
782
|
-
* Context handed to a {@link Transport} definition when a handler builds a live connection from it.
|
|
783
|
-
* Only bidirectional transports (WebSocket / Custom) make use of `resolvers`.
|
|
784
|
-
*/
|
|
785
|
-
interface ITransportConnectionContext {
|
|
786
|
-
resolvers?: IActionTransportResolvers;
|
|
787
|
-
}
|
|
788
|
-
/**
|
|
789
|
-
* Reusable transport definition. Devs construct these (`HttpTransport.create({ createRequest })`,
|
|
790
|
-
* `WebSocketTransport.create({ createWebSocket })`, …) and pass them to an
|
|
791
|
-
* `ActionExternalClientHandler`. A single
|
|
792
|
-
* definition can be shared across multiple handlers — each handler builds its own live
|
|
793
|
-
* {@link TransportConnection} via {@link TransportConnection._createConnection}.
|
|
794
|
-
*/
|
|
795
|
-
declare abstract class Transport<T extends ETransportType = ETransportType> {
|
|
796
|
-
abstract readonly type: T;
|
|
797
|
-
/** Internal: build a fresh, per-handler live connection from this definition. */
|
|
798
|
-
abstract _createConnection(ctx: ITransportConnectionContext): TransportConnection<T>;
|
|
799
|
-
/**
|
|
800
|
-
* Resolve human-readable info about how a specific action would be routed through this transport
|
|
801
|
-
* (e.g. the request URL/method, or the WebSocket endpoint). Surfaced in the action devtools.
|
|
802
|
-
*/
|
|
803
|
-
abstract getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
|
|
804
|
-
}
|
|
805
|
-
//#endregion
|
|
806
|
-
//#region src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.types.d.ts
|
|
807
|
-
interface IActionExternalClientRequestHandlerConfig {
|
|
808
|
-
defaultTimeout?: number;
|
|
809
|
-
runtimeCoordinate: RuntimeCoordinate;
|
|
810
|
-
transports: Transport[];
|
|
811
|
-
}
|
|
812
|
-
//#endregion
|
|
813
|
-
//#region src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.d.ts
|
|
814
|
-
declare class ActionExternalClientHandler extends ActionHandler<EActionHandlerType.external> implements IActionHandler_ExternalClient {
|
|
815
|
-
readonly externalClient: RuntimeCoordinate;
|
|
816
|
-
readonly handlerType = EActionHandlerType.external;
|
|
817
|
-
readonly cuid: string;
|
|
818
|
-
private _defaultTimeout;
|
|
819
|
-
private _transportCache;
|
|
820
|
-
private transportManager;
|
|
821
|
-
private _incomingActionDataListeners;
|
|
822
|
-
readonly actionRouter: ActionRouter<true>;
|
|
823
|
-
constructor({
|
|
824
|
-
runtimeCoordinate: externalClientSpecifier,
|
|
825
|
-
transports,
|
|
826
|
-
defaultTimeout
|
|
827
|
-
}: IActionExternalClientRequestHandlerConfig);
|
|
828
|
-
forDomain<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>): this;
|
|
829
|
-
forAction<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM["actionSchema"] & string>(action: ActionCore<ACT_DOM, ID>): this;
|
|
830
|
-
forActionIds<ACT_DOM extends IActionDomain, IDS extends ReadonlyArray<keyof ACT_DOM["actionSchema"] & string>>(domain: ActionDomain<ACT_DOM>, ids: IDS): this;
|
|
831
|
-
_setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject<any, any>) => void): void;
|
|
832
|
-
handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
|
|
833
|
-
private _dispatchWhenTransportReady;
|
|
834
|
-
/**
|
|
835
|
-
* Dispatch a result or progress payload directly back to the external client via the best
|
|
836
|
-
* available bidirectional transport (WebSocket / Custom). Used for return-path routing when the
|
|
837
|
-
* local runtime recognises that it has a direct channel to the action's originClient.
|
|
838
|
-
*
|
|
839
|
-
* Returns `true` if the payload was sent, `false` if no suitable transport was available.
|
|
840
|
-
*/
|
|
841
|
-
sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
|
|
842
|
-
targetLocalRuntime: ActionRuntime;
|
|
843
|
-
}): Promise<boolean>;
|
|
844
|
-
toJsonObject(): IActionHandler_ExternalClient_Json;
|
|
845
|
-
toHandlerRouteItem(transport: TransportConnection, input: ITransportRouteActionParams): IActionRouteItemHandler;
|
|
846
|
-
clearTransportCache(): void;
|
|
847
|
-
}
|
|
848
|
-
declare const createExternalClientHandler: (config: IActionExternalClientRequestHandlerConfig) => ActionExternalClientHandler;
|
|
849
|
-
//#endregion
|
|
850
660
|
//#region src/utils/typescript/MaybePromise.d.ts
|
|
851
661
|
type MaybePromise<T> = T | Promise<T>;
|
|
852
662
|
//#endregion
|
|
@@ -898,6 +708,62 @@ declare class ActionLocalHandler extends ActionHandler<EActionHandlerType.local>
|
|
|
898
708
|
}
|
|
899
709
|
declare const createLocalHandler: () => ActionLocalHandler;
|
|
900
710
|
//#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
|
|
901
767
|
//#region src/ActionRuntime/ActionRuntime.types.d.ts
|
|
902
768
|
interface IRuntimeMeta {
|
|
903
769
|
assumed: boolean;
|
|
@@ -906,34 +772,486 @@ interface IRuntimeMeta {
|
|
|
906
772
|
interface IActionRuntimeManagerContext {
|
|
907
773
|
domain?: string;
|
|
908
774
|
}
|
|
909
|
-
type TActionRuntimeHandler = ActionLocalHandler |
|
|
775
|
+
type TActionRuntimeHandler = ActionLocalHandler | PeerLinkHandler;
|
|
910
776
|
//#endregion
|
|
911
|
-
//#region src/ActionRuntime/
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
777
|
+
//#region src/ActionRuntime/Transport/crypto/actionHandshake.d.ts
|
|
778
|
+
/** How much the channel protects after the handshake — chosen by the consumer (perf vs security). */
|
|
779
|
+
declare enum ESecurityLevel {
|
|
780
|
+
/** No handshake; identity is self-asserted (fastest, dev / trusted networks). */
|
|
781
|
+
none = "none",
|
|
782
|
+
/** Handshake authenticates identity (sign/verify + key pin); frames stay plaintext over TLS. */
|
|
783
|
+
authenticated = "authenticated",
|
|
784
|
+
/** Authenticated handshake + every frame AES-GCM encrypted with the derived shared key. */
|
|
785
|
+
encrypted = "encrypted"
|
|
786
|
+
}
|
|
787
|
+
declare enum EHandshakeMessageType {
|
|
788
|
+
hello = "hello",
|
|
789
|
+
welcome = "welcome",
|
|
790
|
+
prove = "prove",
|
|
791
|
+
accept = "accept",
|
|
792
|
+
reject = "reject"
|
|
793
|
+
}
|
|
794
|
+
declare const vHsHello: v.ObjectSchema<{
|
|
795
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.hello, undefined>;
|
|
796
|
+
readonly protocol: v.StringSchema<undefined>;
|
|
797
|
+
readonly securityLevel: v.PicklistSchema<[ESecurityLevel.none, ESecurityLevel.authenticated, ESecurityLevel.encrypted], undefined>;
|
|
798
|
+
readonly dictionaryVersion: v.StringSchema<undefined>;
|
|
799
|
+
readonly client: v.ObjectSchema<{
|
|
800
|
+
readonly envId: v.StringSchema<undefined>;
|
|
801
|
+
readonly perId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
802
|
+
readonly insId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
803
|
+
}, undefined>;
|
|
804
|
+
readonly clientNonce: v.StringSchema<undefined>;
|
|
805
|
+
readonly verifyPublicKey: v.CustomSchema<`ed25519::raw_base64::${string}`, undefined>;
|
|
806
|
+
readonly exchangePublicKey: v.OptionalSchema<v.CustomSchema<`x25519::raw_base64::${string}`, undefined>, undefined>;
|
|
807
|
+
}, undefined>;
|
|
808
|
+
declare const vHsWelcome: v.ObjectSchema<{
|
|
809
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.welcome, undefined>;
|
|
810
|
+
readonly securityLevel: v.PicklistSchema<[ESecurityLevel.none, ESecurityLevel.authenticated, ESecurityLevel.encrypted], undefined>;
|
|
811
|
+
readonly dictionaryVersion: v.StringSchema<undefined>;
|
|
812
|
+
readonly server: v.ObjectSchema<{
|
|
813
|
+
readonly envId: v.StringSchema<undefined>;
|
|
814
|
+
readonly perId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
815
|
+
readonly insId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
816
|
+
}, undefined>;
|
|
817
|
+
readonly serverNonce: v.StringSchema<undefined>;
|
|
818
|
+
readonly verifyPublicKey: v.CustomSchema<`ed25519::raw_base64::${string}`, undefined>;
|
|
819
|
+
readonly exchangePublicKey: v.OptionalSchema<v.CustomSchema<`x25519::raw_base64::${string}`, undefined>, undefined>;
|
|
820
|
+
}, undefined>;
|
|
821
|
+
declare const vHsProve: v.ObjectSchema<{
|
|
822
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.prove, undefined>;
|
|
823
|
+
readonly signatureBase64: v.StringSchema<undefined>;
|
|
824
|
+
}, undefined>;
|
|
825
|
+
declare const vHsAccept: v.ObjectSchema<{
|
|
826
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.accept, undefined>;
|
|
827
|
+
readonly signatureBase64: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
828
|
+
}, undefined>;
|
|
829
|
+
declare const vHsReject: v.ObjectSchema<{
|
|
830
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.reject, undefined>;
|
|
831
|
+
readonly reason: v.StringSchema<undefined>;
|
|
832
|
+
}, undefined>;
|
|
833
|
+
declare const vHandshakeMessage: v.VariantSchema<"t", [v.ObjectSchema<{
|
|
834
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.hello, undefined>;
|
|
835
|
+
readonly protocol: v.StringSchema<undefined>;
|
|
836
|
+
readonly securityLevel: v.PicklistSchema<[ESecurityLevel.none, ESecurityLevel.authenticated, ESecurityLevel.encrypted], undefined>;
|
|
837
|
+
readonly dictionaryVersion: v.StringSchema<undefined>;
|
|
838
|
+
readonly client: v.ObjectSchema<{
|
|
839
|
+
readonly envId: v.StringSchema<undefined>;
|
|
840
|
+
readonly perId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
841
|
+
readonly insId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
842
|
+
}, undefined>;
|
|
843
|
+
readonly clientNonce: v.StringSchema<undefined>;
|
|
844
|
+
readonly verifyPublicKey: v.CustomSchema<`ed25519::raw_base64::${string}`, undefined>;
|
|
845
|
+
readonly exchangePublicKey: v.OptionalSchema<v.CustomSchema<`x25519::raw_base64::${string}`, undefined>, undefined>;
|
|
846
|
+
}, undefined>, v.ObjectSchema<{
|
|
847
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.welcome, undefined>;
|
|
848
|
+
readonly securityLevel: v.PicklistSchema<[ESecurityLevel.none, ESecurityLevel.authenticated, ESecurityLevel.encrypted], undefined>;
|
|
849
|
+
readonly dictionaryVersion: v.StringSchema<undefined>;
|
|
850
|
+
readonly server: v.ObjectSchema<{
|
|
851
|
+
readonly envId: v.StringSchema<undefined>;
|
|
852
|
+
readonly perId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
853
|
+
readonly insId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
854
|
+
}, undefined>;
|
|
855
|
+
readonly serverNonce: v.StringSchema<undefined>;
|
|
856
|
+
readonly verifyPublicKey: v.CustomSchema<`ed25519::raw_base64::${string}`, undefined>;
|
|
857
|
+
readonly exchangePublicKey: v.OptionalSchema<v.CustomSchema<`x25519::raw_base64::${string}`, undefined>, undefined>;
|
|
858
|
+
}, undefined>, v.ObjectSchema<{
|
|
859
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.prove, undefined>;
|
|
860
|
+
readonly signatureBase64: v.StringSchema<undefined>;
|
|
861
|
+
}, undefined>, v.ObjectSchema<{
|
|
862
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.accept, undefined>;
|
|
863
|
+
readonly signatureBase64: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
864
|
+
}, undefined>, v.ObjectSchema<{
|
|
865
|
+
readonly t: v.LiteralSchema<EHandshakeMessageType.reject, undefined>;
|
|
866
|
+
readonly reason: v.StringSchema<undefined>;
|
|
867
|
+
}, undefined>], undefined>;
|
|
868
|
+
type THsHello = v.InferOutput<typeof vHsHello>;
|
|
869
|
+
type THsWelcome = v.InferOutput<typeof vHsWelcome>;
|
|
870
|
+
type THsProve = v.InferOutput<typeof vHsProve>;
|
|
871
|
+
type THsAccept = v.InferOutput<typeof vHsAccept>;
|
|
872
|
+
type THsReject = v.InferOutput<typeof vHsReject>;
|
|
873
|
+
type THandshakeMessage = v.InferOutput<typeof vHandshakeMessage>;
|
|
874
|
+
/** Serialize a handshake message for the wire (handshake frames are JSON — they aren't the hot path). */
|
|
875
|
+
declare function encodeHandshakeMessage(message: THandshakeMessage): string;
|
|
876
|
+
/** Parse + structurally validate an incoming handshake frame; `undefined` if it isn't one. */
|
|
877
|
+
declare function decodeHandshakeMessage(raw: string): THandshakeMessage | undefined;
|
|
878
|
+
/** Stable link id for a runtime coordinate — the key both the crypto link and the connection use. */
|
|
879
|
+
declare function runtimeLinkId(coordinate: IRuntimeCoordinate): TTypeAndId;
|
|
880
|
+
/**
|
|
881
|
+
* Everything needed to re-derive the shared AES-GCM key for an `encrypted` link after a restart —
|
|
882
|
+
* the remote's public keys + the HKDF salt/info used at handshake time. The local (server) key pair is
|
|
883
|
+
* recovered from its own persisted `ClientCryptoKeyLink` storage, so re-linking with this material
|
|
884
|
+
* yields the identical shared key without a fresh handshake.
|
|
885
|
+
*/
|
|
886
|
+
interface IHandshakeEncryptionKeyMaterial {
|
|
887
|
+
verifyPublicKey: TSerializedCryptoKeyData_Ed25519_Raw;
|
|
888
|
+
exchangePublicKey: TSerializedCryptoKeyData_X25519_Raw;
|
|
889
|
+
saltString: string;
|
|
890
|
+
infoString: string;
|
|
891
|
+
bindVerifyKeysIntoDerivation: boolean;
|
|
892
|
+
}
|
|
893
|
+
/** Outcome of a completed handshake — what the transport/handler needs to wire the channel. */
|
|
894
|
+
interface IHandshakeResult {
|
|
895
|
+
/** The crypto-link id (and connection-registry key) for the authenticated remote. */
|
|
896
|
+
linkedClientId: TTypeAndId;
|
|
897
|
+
/** The remote's authenticated coordinate. */
|
|
898
|
+
remote: IRuntimeCoordinate;
|
|
899
|
+
securityLevel: ESecurityLevel;
|
|
900
|
+
/** For the `encrypted` level: material to restore the shared key after eviction (persist it). */
|
|
901
|
+
encryptionKeyMaterial?: IHandshakeEncryptionKeyMaterial;
|
|
902
|
+
}
|
|
903
|
+
interface IClientVerifyKeyResolveInput {
|
|
904
|
+
client: IRuntimeCoordinate;
|
|
905
|
+
verifyPublicKey: TSerializedCryptoKeyData_Ed25519_Raw;
|
|
906
|
+
}
|
|
907
|
+
interface IClientVerifyKeyResolver {
|
|
908
|
+
/**
|
|
909
|
+
* Decide whether a presented verify key is trusted for a client identity. The signature is already
|
|
910
|
+
* verified by the time this runs, so this is purely the identity-pinning decision. Swap in a
|
|
911
|
+
* persistent / pre-provisioned implementation without touching the protocol.
|
|
912
|
+
*/
|
|
913
|
+
resolve(input: IClientVerifyKeyResolveInput): Promise<{
|
|
914
|
+
trusted: boolean;
|
|
915
|
+
reason?: string;
|
|
916
|
+
}>;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* In-memory trust-on-first-use resolver: trusts (and pins) the first verify key seen for a client
|
|
920
|
+
* identity, then rejects a different key for that identity. The default; replace with a storage-backed
|
|
921
|
+
* resolver for cross-restart pinning (see Step 5).
|
|
922
|
+
*/
|
|
923
|
+
declare function createInMemoryTofuVerifyKeyResolver(): IClientVerifyKeyResolver;
|
|
924
|
+
/**
|
|
925
|
+
* Storage-backed trust-on-first-use resolver: pins survive process restarts / Durable Object eviction
|
|
926
|
+
* (e.g. back it with `createDurableObjectStorageAdapter`). Same policy as the in-memory variant — trust
|
|
927
|
+
* + pin the first verify key per client identity, reject a different one thereafter.
|
|
928
|
+
*/
|
|
929
|
+
declare function createStorageTofuVerifyKeyResolver(storageAdapter: StorageAdapter): IClientVerifyKeyResolver;
|
|
930
|
+
interface IClientHandshakeConfig {
|
|
931
|
+
link: ClientCryptoKeyLink;
|
|
932
|
+
localCoordinate: IRuntimeCoordinate;
|
|
933
|
+
dictionaryVersion: string;
|
|
934
|
+
securityLevel: ESecurityLevel;
|
|
935
|
+
}
|
|
936
|
+
declare function createClientHandshake(config: IClientHandshakeConfig): {
|
|
937
|
+
createHello(): Promise<THsHello>;
|
|
938
|
+
onWelcome(welcome: THsWelcome): Promise<THsProve>;
|
|
939
|
+
onAccept(accept: THsAccept): Promise<IHandshakeResult>;
|
|
940
|
+
};
|
|
941
|
+
interface IServerHandshakeConfig {
|
|
942
|
+
link: ClientCryptoKeyLink;
|
|
943
|
+
localCoordinate: IRuntimeCoordinate;
|
|
944
|
+
dictionaryVersion: string;
|
|
945
|
+
/**
|
|
946
|
+
* The level(s) this server accepts. A single level is strict (the client must match). An array is a
|
|
947
|
+
* negotiable allowed set — the server adopts whichever level the client requests, as long as it's in
|
|
948
|
+
* the set (lets one backend serve `authenticated` and `encrypted` clients at once). `none` in the set
|
|
949
|
+
* is handled by the transport/handler (a `none` client never reaches the handshake).
|
|
950
|
+
*/
|
|
951
|
+
securityLevel: ESecurityLevel | readonly ESecurityLevel[];
|
|
952
|
+
/** Trust decision for a client's verify key. Defaults to in-memory TOFU. */
|
|
953
|
+
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
954
|
+
}
|
|
955
|
+
declare function createServerHandshake(config: IServerHandshakeConfig): {
|
|
956
|
+
onHello(hello: THsHello): Promise<THsWelcome | THsReject>;
|
|
957
|
+
onProve(prove: THsProve): Promise<THsAccept | THsReject>; /** The completed handshake result once `onProve` has accepted, else `undefined`. */
|
|
958
|
+
getResult(): IHandshakeResult | undefined;
|
|
959
|
+
};
|
|
960
|
+
//#endregion
|
|
961
|
+
//#region src/ActionRuntime/Transport/Transport.d.ts
|
|
962
|
+
/**
|
|
963
|
+
* Context handed to a {@link Transport} definition when a handler builds a live connection from it.
|
|
964
|
+
* Only bidirectional transports (WebSocket / Custom) make use of `resolvers`.
|
|
965
|
+
*/
|
|
966
|
+
interface ITransportConnectionContext {
|
|
967
|
+
resolvers?: IActionTransportResolvers;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Reusable transport definition. Devs construct these (`secureTransport({ carrier: wsCarrier(url) })`,
|
|
971
|
+
* `plainTransport({ carrier: httpCarrier(...) })`, …) and pass them to a
|
|
972
|
+
* `ConnectorHandler`. A single
|
|
973
|
+
* definition can be shared across multiple handlers — each handler builds its own live
|
|
974
|
+
* {@link TransportConnection} via {@link TransportConnection._createConnection}.
|
|
975
|
+
*/
|
|
976
|
+
declare abstract class Transport<T extends ETransportShape = ETransportShape> {
|
|
977
|
+
abstract readonly type: T;
|
|
978
|
+
/** Internal: build a fresh, per-handler live connection from this definition. */
|
|
979
|
+
abstract _createConnection(ctx: ITransportConnectionContext): TransportConnection<T>;
|
|
980
|
+
/**
|
|
981
|
+
* Resolve human-readable info about how a specific action would be routed through this transport
|
|
982
|
+
* (e.g. the request URL/method, or the WebSocket endpoint). Surfaced in the action devtools.
|
|
983
|
+
*/
|
|
984
|
+
abstract getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
|
|
985
|
+
}
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region src/ActionRuntime/Transport/TransportConnection.d.ts
|
|
988
|
+
/**
|
|
989
|
+
* Live, per-handler transport runtime built from a reusable {@link Transport} definition. Holds the
|
|
990
|
+
* connection-scoped state (ordinal, initialized config, sockets / abort sets) that must not be shared
|
|
991
|
+
* across handlers. Construct these via `definition._createConnection(...)`, never directly.
|
|
992
|
+
*/
|
|
993
|
+
declare abstract class TransportConnection<T extends ETransportShape = ETransportShape, RP extends ITransportRouteActionParams = ITransportRouteActionParams, RD extends IActionTransportReadyData_Base = IActionTransportReadyData_Base, I extends IActionTransportInitialized<RP, RD> = IActionTransportInitialized<RP, RD>, DEF extends IActionTransportDef<T, I> = IActionTransportDef<T, I>> {
|
|
994
|
+
readonly def: DEF;
|
|
995
|
+
readonly transOrd: number;
|
|
996
|
+
readonly type: T;
|
|
997
|
+
readonly initialized: I;
|
|
998
|
+
/** Backref to the public definition that created this connection (used for devtools route info). */
|
|
999
|
+
definition?: Transport<T>;
|
|
1000
|
+
constructor(def: DEF);
|
|
1001
|
+
/**
|
|
1002
|
+
* Devtools route info for an action routed through this live connection. Defaults to the stateless
|
|
1003
|
+
* {@link definition}'s info; connections override to enrich it from live state (e.g. the actual
|
|
1004
|
+
* resolved socket URL) when the definition couldn't resolve it on its own.
|
|
1005
|
+
*/
|
|
1006
|
+
getRouteInfo(input: RP): ITransportRouteInfo | undefined;
|
|
1007
|
+
/**
|
|
1008
|
+
* Build the live send/receive methods from a `ready` readyData, synchronously. This is the plain
|
|
1009
|
+
* (no-handshake) path and the only required per-transport hook. Stream carriers that must await the
|
|
1010
|
+
* carrier opening and/or run a handshake additionally override {@link _needsAsyncBringUp},
|
|
1011
|
+
* {@link _awaitCarrierReady}, and {@link _finalizeReady}.
|
|
1012
|
+
*/
|
|
1013
|
+
protected abstract _finalizeTransportMethods(inputs: RD): IActionTransportReadyData_Methods;
|
|
1014
|
+
/**
|
|
1015
|
+
* Whether a `ready`-status transport still needs asynchronous bring-up before its methods exist —
|
|
1016
|
+
* awaiting the carrier to open and/or running a handshake. Default `false`: a stateless transport
|
|
1017
|
+
* (HTTP) is usable the instant `getTransport` reports `ready`, so it stays a terminal *synchronous*
|
|
1018
|
+
* fallback in {@link ConnectionTransportManager}. Stream carriers (Link/WS) override to `true`.
|
|
1019
|
+
*/
|
|
1020
|
+
protected _needsAsyncBringUp(_readyData: RD): boolean;
|
|
1021
|
+
/** Await the carrier becoming ready to send (e.g. a socket `open`). Default: nothing to await. */
|
|
1022
|
+
protected _awaitCarrierReady(_readyData: RD): Promise<void>;
|
|
1023
|
+
/**
|
|
1024
|
+
* Finalize during async bring-up — may run a handshake, so it can be async. Defaults to the
|
|
1025
|
+
* synchronous {@link _finalizeTransportMethods}; secure stream carriers override to branch plain/secure.
|
|
1026
|
+
*/
|
|
1027
|
+
protected _finalizeReady(readyData: RD): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods>;
|
|
1028
|
+
protected _getCacheKey(input: RP): string | null;
|
|
1029
|
+
getCacheKey(input: RP): string | null;
|
|
1030
|
+
getTransport(input: RP): TTransportStatusInfo<IActionTransportReadyData_Methods>;
|
|
1031
|
+
protected _processTransportStatus(input: RP): TTransportStatusInfo<IActionTransportReadyData_Methods>;
|
|
1032
|
+
/** Await carrier readiness, then finalize (possibly running a handshake) into the live methods. */
|
|
1033
|
+
private _bringUp;
|
|
1034
|
+
}
|
|
1035
|
+
//#endregion
|
|
1036
|
+
//#region src/ActionRuntime/Transport/Transport.types.d.ts
|
|
1037
|
+
/**
|
|
1038
|
+
* Carrier-shape class identity: the one carrier-invariant trait the selection layer reasons about —
|
|
1039
|
+
* whether a carrier can push unsolicited frames (`duplex`: WS/WebRTC/BLE/in-memory) or only answers a
|
|
1040
|
+
* request with a single correlated reply (`exchange`: HTTP). The concrete carrier kind ("ws", "webrtc",
|
|
1041
|
+
* "http", …) is a free-form {@link ITransportRouteInfo.carrierLabel} for display, not a class tag.
|
|
1042
|
+
*/
|
|
1043
|
+
declare enum ETransportShape {
|
|
1044
|
+
duplex = "duplex",
|
|
1045
|
+
exchange = "exchange"
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Serializable, display-only description of how an action was routed through a transport. Stored on
|
|
1049
|
+
* the action's route items and shown in the devtools external-handler chips. Keep this free of
|
|
1050
|
+
* sensitive data (e.g. auth headers) — route items travel over the wire.
|
|
1051
|
+
*/
|
|
1052
|
+
interface ITransportRouteInfo {
|
|
1053
|
+
/** Free-form carrier kind for the devtools chip — e.g. "ws", "webrtc", "http". */
|
|
1054
|
+
carrierLabel: string;
|
|
1055
|
+
/** Short label for chips, e.g. "POST /resolve_action" or "ws host/resolve_action/ws". */
|
|
1056
|
+
summary?: string;
|
|
1057
|
+
url?: string;
|
|
1058
|
+
method?: string;
|
|
1059
|
+
detail?: Record<string, string | number | boolean>;
|
|
1060
|
+
}
|
|
1061
|
+
interface IUpdateActionRunConfig_Output {
|
|
1062
|
+
timeout?: number;
|
|
1063
|
+
}
|
|
1064
|
+
type TUpdateActionRunConfig = (input: ITransportRouteActionParams & {
|
|
1065
|
+
timeout: number;
|
|
1066
|
+
}) => IUpdateActionRunConfig_Output;
|
|
1067
|
+
interface IActionTransportReadyData_Base {
|
|
1068
|
+
updateRunConfig?: TUpdateActionRunConfig;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Client-side secure-channel config for a connector link — carrier-neutral (the secure session never
|
|
1072
|
+
* cared about the carrier). When present (and `securityLevel !== none`), the connection runs the
|
|
1073
|
+
* handshake during initialization and, at the `encrypted` level, encrypts every frame. The acceptor
|
|
1074
|
+
* side adds a negotiable level set + verify-key resolver on top of this (see Phase 3's `ISecureConfig`).
|
|
1075
|
+
*/
|
|
1076
|
+
interface ISecureClientConfig {
|
|
1077
|
+
/** The level this client requests; the peer must allow it. */
|
|
1078
|
+
securityLevel: ESecurityLevel;
|
|
1079
|
+
/** This client's crypto identity (verify + exchange key pairs, optionally persisted). */
|
|
1080
|
+
link: ClientCryptoKeyLink;
|
|
1081
|
+
/** This client's runtime coordinate — its authenticated identity to the peer. */
|
|
1082
|
+
localCoordinate: IRuntimeCoordinate;
|
|
1083
|
+
/** Wire dictionary version; the peer rejects the handshake on a mismatch. */
|
|
1084
|
+
dictionaryVersion: string;
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
*
|
|
1088
|
+
* TRANSPORT READINESS RESPONSE
|
|
1089
|
+
*
|
|
1090
|
+
*/
|
|
1091
|
+
declare enum ETransportStatus {
|
|
1092
|
+
uninitialized = "uninitialized",
|
|
1093
|
+
unsupported = "unsupported",
|
|
1094
|
+
initializing = "initializing",
|
|
1095
|
+
ready = "ready",
|
|
1096
|
+
failed = "failed"
|
|
1097
|
+
}
|
|
1098
|
+
interface ITransportStatusInfo_Base<S extends ETransportStatus> {
|
|
1099
|
+
status: S;
|
|
1100
|
+
}
|
|
1101
|
+
interface ITransportStatusInfo_Failed extends ITransportStatusInfo_Base<ETransportStatus.failed> {
|
|
1102
|
+
error: NiceError;
|
|
1103
|
+
timeFailed: number;
|
|
1104
|
+
}
|
|
1105
|
+
interface ITransportStatusInfo_Unsupported extends ITransportStatusInfo_Base<ETransportStatus.unsupported> {}
|
|
1106
|
+
interface ITransportStatusInfo_Ready<READY extends IActionTransportReadyData_Base> extends ITransportStatusInfo_Base<ETransportStatus.ready> {
|
|
1107
|
+
readyData: READY;
|
|
1108
|
+
}
|
|
1109
|
+
type TTransportInitializationFinishedInfo<READY extends IActionTransportReadyData_Base> = ITransportStatusInfo_Ready<READY> | ITransportStatusInfo_Failed | ITransportStatusInfo_Unsupported;
|
|
1110
|
+
interface ITransportStatusInfo_Initializing<READY extends IActionTransportReadyData_Base> extends ITransportStatusInfo_Base<ETransportStatus.initializing> {
|
|
1111
|
+
timeStarted: number;
|
|
1112
|
+
initializationPromise: Promise<TTransportInitializationFinishedInfo<READY>>;
|
|
1113
|
+
}
|
|
1114
|
+
type TTransportStatusInfo<READY extends IActionTransportReadyData_Base> = ITransportStatusInfo_Base<ETransportStatus.uninitialized> | ITransportStatusInfo_Unsupported | ITransportStatusInfo_Initializing<READY> | ITransportStatusInfo_Ready<READY> | ITransportStatusInfo_Failed;
|
|
1115
|
+
type TTransportStatusInfo_GetTransport_Output<READY extends IActionTransportReadyData_Base> = ITransportStatusInfo_Base<ETransportStatus.uninitialized> | ITransportStatusInfo_Unsupported | Omit<ITransportStatusInfo_Initializing<READY>, "timeStarted"> | ITransportStatusInfo_Ready<READY> | Omit<ITransportStatusInfo_Failed, "timeFailed">;
|
|
1116
|
+
/**
|
|
1117
|
+
*
|
|
1118
|
+
* TRANSPORT ROUTING
|
|
1119
|
+
*
|
|
1120
|
+
*/
|
|
1121
|
+
interface ITransportRouteClientParams {
|
|
1122
|
+
localClient: RuntimeCoordinate;
|
|
1123
|
+
externalClient: RuntimeCoordinate;
|
|
1124
|
+
}
|
|
1125
|
+
interface ITransportRouteActionParams extends ITransportRouteClientParams {
|
|
1126
|
+
action: TActionPayload_Any_Instance<any, any>;
|
|
1127
|
+
}
|
|
1128
|
+
interface ITransportMethod_SendActionData_Input extends ITransportRouteActionParams {
|
|
1129
|
+
runningAction: RunningAction<any, any>;
|
|
1130
|
+
timeout: number;
|
|
1131
|
+
}
|
|
1132
|
+
interface ITransportDispatchAction<P> extends ITransportMethod_SendActionData_Input {
|
|
1133
|
+
params: P;
|
|
1134
|
+
}
|
|
1135
|
+
type TSendActionDataMethod = (input: ITransportMethod_SendActionData_Input) => void;
|
|
1136
|
+
type TSendReturnDataMethod = (payload: TActionPayload_Any_Instance<any, any>,
|
|
1137
|
+
/**
|
|
1138
|
+
* The local/external client pair this payload is being returned over. Bidirectional transports use
|
|
1139
|
+
* it to build the full route params their outgoing formatter expects (e.g. binary packing). Optional
|
|
1140
|
+
* so existing return-data implementations keep type-checking.
|
|
1141
|
+
*/
|
|
1142
|
+
|
|
1143
|
+
clients?: ITransportRouteClientParams) => void;
|
|
1144
|
+
interface IActionTransportReadyData_Methods extends IActionTransportReadyData_Base {
|
|
1145
|
+
sendActionData: TSendActionDataMethod;
|
|
1146
|
+
/**
|
|
1147
|
+
* Optional — implement on bidirectional transports (WebSocket, Custom) to enable return-path
|
|
1148
|
+
* routing. When present, the runtime uses this to dispatch results and progress payloads directly
|
|
1149
|
+
* back to `originClient` without going through the original request transport.
|
|
1150
|
+
*/
|
|
1151
|
+
sendReturnData?: TSendReturnDataMethod;
|
|
1152
|
+
addOnDisconnectListener?: (callback: () => void) => void;
|
|
1153
|
+
/**
|
|
1154
|
+
* Optional — implement on transports holding a long-lived connection (WebSocket, Custom) to close it
|
|
1155
|
+
* deliberately. Called by `ConnectorHandler.clearTransportCache()` so a teardown actually
|
|
1156
|
+
* releases the underlying socket instead of leaving it open until GC.
|
|
1157
|
+
*/
|
|
1158
|
+
disconnect?: () => void;
|
|
1159
|
+
}
|
|
1160
|
+
interface IActionTransportReady {
|
|
1161
|
+
methods: IActionTransportReadyData_Methods;
|
|
1162
|
+
transport: TransportConnection;
|
|
1163
|
+
}
|
|
1164
|
+
type TTransportCache = Map<string, IActionTransportReady | Promise<IActionTransportReady>>;
|
|
1165
|
+
type TOnResolveIncomingRequest = (request: ActionPayload_Request<any>) => void;
|
|
1166
|
+
type TOnResolveIncomingRequestJson = (request: IActionPayload_Request_JsonObject<any>) => void;
|
|
1167
|
+
type TOnResolveIncomingResponse = (response: ActionPayload_Result<any>) => void;
|
|
1168
|
+
type TOnResolveIncomingResponseJson = (response: IActionPayload_Result_JsonObject<any>) => void;
|
|
1169
|
+
type TOnResolveAnyIncomingActionData = (actionData: ActionPayload_Request<any> | ActionPayload_Result<any>) => void;
|
|
1170
|
+
type TOnResolveAnyIncomingActionData_Json = (actionData: TActionPayload_Any_JsonObject<any>) => void;
|
|
1171
|
+
interface IActionTransportResolvers {
|
|
1172
|
+
onIncomingActionDataJson: TOnResolveAnyIncomingActionData_Json;
|
|
1173
|
+
}
|
|
1174
|
+
type TGetTransportFn<IN extends ITransportRouteActionParams, READY extends IActionTransportReadyData_Base> = (input: IN) => TTransportStatusInfo_GetTransport_Output<READY>;
|
|
1175
|
+
interface IActionTransportInitialized<IN extends ITransportRouteActionParams, READY extends IActionTransportReadyData_Base> {
|
|
1176
|
+
getTransportCacheKey?: (input: IN) => string[];
|
|
1177
|
+
getTransport: TGetTransportFn<IN, READY>;
|
|
1178
|
+
}
|
|
1179
|
+
interface IActionTransportDef<TYPE extends ETransportShape, INIT extends IActionTransportInitialized<any, any>> {
|
|
1180
|
+
type: TYPE;
|
|
1181
|
+
initialize: () => INIT;
|
|
1182
|
+
}
|
|
1183
|
+
//#endregion
|
|
1184
|
+
//#region src/ActionRuntime/Handler/PeerLink/Connector/ConnectorHandler.types.d.ts
|
|
1185
|
+
interface IConnectorHandlerConfig {
|
|
1186
|
+
defaultTimeout?: number;
|
|
1187
|
+
runtimeCoordinate: RuntimeCoordinate;
|
|
1188
|
+
transports: Transport[];
|
|
1189
|
+
}
|
|
1190
|
+
//#endregion
|
|
1191
|
+
//#region src/ActionRuntime/Handler/PeerLink/Connector/ConnectorHandler.d.ts
|
|
1192
|
+
/**
|
|
1193
|
+
* Dial-out peer link: this runtime opens connection(s) to one peer over a transport stack (cached, with
|
|
1194
|
+
* preference-ordered fallback). The classic "client → backend" handler — but to the runtime it's just a
|
|
1195
|
+
* {@link PeerLinkHandler} like the accept-in server one.
|
|
1196
|
+
*/
|
|
1197
|
+
declare class ConnectorHandler extends PeerLinkHandler {
|
|
1198
|
+
/**
|
|
1199
|
+
* Dial-out can receive (and so return) an unsolicited push only over a duplex transport. With every
|
|
1200
|
+
* transport exchange-only (HTTP), the peer can never push to us — so this link can't deliver one back.
|
|
1201
|
+
*/
|
|
1202
|
+
readonly canPush: boolean;
|
|
1203
|
+
private _defaultTimeout;
|
|
1204
|
+
private _transportCache;
|
|
1205
|
+
private transportManager;
|
|
1206
|
+
constructor({
|
|
1207
|
+
runtimeCoordinate: peerSpecifier,
|
|
1208
|
+
transports,
|
|
1209
|
+
defaultTimeout
|
|
1210
|
+
}: IConnectorHandlerConfig);
|
|
1211
|
+
handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
|
|
1212
|
+
private _dispatchWhenTransportReady;
|
|
1213
|
+
/**
|
|
1214
|
+
* Dispatch a result or progress payload directly back to the external client via the best
|
|
1215
|
+
* available bidirectional transport (WebSocket / Custom). Used for return-path routing when the
|
|
1216
|
+
* local runtime recognises that it has a direct channel to the action's originClient.
|
|
1217
|
+
*
|
|
1218
|
+
* Returns `true` if the payload was sent, `false` if no suitable transport was available.
|
|
1219
|
+
*/
|
|
1220
|
+
sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
|
|
1221
|
+
targetLocalRuntime: ActionRuntime;
|
|
1222
|
+
}): Promise<boolean>;
|
|
1223
|
+
toJsonObject(): IActionHandler_Peer_Json;
|
|
1224
|
+
toHandlerRouteItem(transport: TransportConnection, input: ITransportRouteActionParams): IActionRouteItemHandler;
|
|
1225
|
+
clearTransportCache(): void;
|
|
1226
|
+
}
|
|
1227
|
+
declare const createConnectorHandler: (config: IConnectorHandlerConfig) => ConnectorHandler;
|
|
1228
|
+
//#endregion
|
|
1229
|
+
//#region src/ActionRuntime/ActionRuntime.d.ts
|
|
1230
|
+
declare class ActionRuntime {
|
|
1231
|
+
private _coordinate;
|
|
1232
|
+
readonly timeCreated: number;
|
|
1233
|
+
readonly runtimeInfo: IRuntimeMeta;
|
|
1234
|
+
private readonly actionRouter;
|
|
1235
|
+
private readonly _pendingRunningActions;
|
|
1236
|
+
private readonly _registeredPeerHandlers;
|
|
1237
|
+
private _applied;
|
|
1238
|
+
static getDefault(): ActionRuntime;
|
|
1239
|
+
constructor(coordinate: RuntimeCoordinate);
|
|
1240
|
+
get coordinate(): RuntimeCoordinate;
|
|
1241
|
+
specifyRuntimeCoordinate(specifics: IRuntimeCoordinateSpecifics & {
|
|
1242
|
+
envId?: string;
|
|
1243
|
+
}): void;
|
|
1244
|
+
registerRunningAction(ra: RunningAction<any, any>): void;
|
|
1245
|
+
resolveIncomingActionPayload(json: TActionPayload_Any_JsonObject<any, any>): void;
|
|
1246
|
+
/**
|
|
1247
|
+
* Handle an incoming action wire (e.g. from a transport layer), route it to
|
|
1248
|
+
* the correct handler, and return the response. The most specific handler
|
|
1249
|
+
* match is chosen (action-ID-specific beats domain-wildcard).
|
|
1250
|
+
*/
|
|
1251
|
+
handleActionPayloadWire<D extends IActionDomain, ID extends keyof D["actionSchema"] & string>(wire: TActionPayload_Any_JsonObject<D, ID>): Promise<RunningAction<D, ID>>;
|
|
1252
|
+
handleActionPayloadWire(wire: unknown): Promise<RunningAction<any, any>>;
|
|
1253
|
+
handleActionPayload<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: TActionPayload_Any_Instance<DOM, ID>, options?: Omit<IHandleActionOptions, "targetLocalRuntime">): Promise<RunningAction<DOM, ID>>;
|
|
1254
|
+
/**
|
|
937
1255
|
* @internal
|
|
938
1256
|
*
|
|
939
1257
|
* Return the first handler registered for the given action, or `undefined`
|
|
@@ -950,13 +1268,13 @@ declare class ActionRuntime {
|
|
|
950
1268
|
addHandlers(handlers: TActionRuntimeHandler[]): this;
|
|
951
1269
|
/**
|
|
952
1270
|
* Declare an external "backend client" in one call: build an
|
|
953
|
-
* {@link
|
|
1271
|
+
* {@link ConnectorHandler} for `externalCoordinate` carrying the given
|
|
954
1272
|
* `transports`, route the listed `domains`/`actions` to it, register it (plus any
|
|
955
1273
|
* `localHandlers` — e.g. server→client push handlers that share the same channel)
|
|
956
1274
|
* on this runtime, and `apply()`. Returns the external handler so the caller can
|
|
957
1275
|
* later `clearTransportCache()` it.
|
|
958
1276
|
*
|
|
959
|
-
* Sugar over `new
|
|
1277
|
+
* Sugar over `new ConnectorHandler(...).forDomain(...)` + `addHandlers([...])`,
|
|
960
1278
|
* so a single runtime can host one handler per backend target with its transports
|
|
961
1279
|
* declared once and reused across every action routed to that backend.
|
|
962
1280
|
*/
|
|
@@ -966,7 +1284,7 @@ declare class ActionRuntime {
|
|
|
966
1284
|
actions?: ActionCore<any, any>[];
|
|
967
1285
|
localHandlers?: TActionRuntimeHandler[];
|
|
968
1286
|
defaultTimeout?: number;
|
|
969
|
-
}):
|
|
1287
|
+
}): ConnectorHandler;
|
|
970
1288
|
private applyRuntimeForDomain;
|
|
971
1289
|
/**
|
|
972
1290
|
* Register this runtime with all root domains covered by its currently-added handlers,
|
|
@@ -978,8 +1296,14 @@ declare class ActionRuntime {
|
|
|
978
1296
|
* Find the best registered external handler that can reach `originClient` directly.
|
|
979
1297
|
* Used to locate the return-path channel for dispatching results back to the action origin.
|
|
980
1298
|
* Returns `undefined` if no handler matches (score > 0 required, i.e. at least id must match).
|
|
1299
|
+
*
|
|
1300
|
+
* A handler that currently holds the origin's *live* connection always wins over a mere coordinate
|
|
1301
|
+
* match — so with several duplex acceptors (e.g. WS + WebRTC) a result/push routes back over the carrier
|
|
1302
|
+
* the client actually connected on, never a same-coordinate sibling that lacks the socket. Only when no
|
|
1303
|
+
* handler owns a live connection do we fall back to the plain best-coordinate-score pick (the
|
|
1304
|
+
* single-acceptor and connector-only cases, unchanged).
|
|
981
1305
|
*/
|
|
982
|
-
getReturnHandlerForOrigin(originClient: RuntimeCoordinate):
|
|
1306
|
+
getReturnHandlerForOrigin(originClient: RuntimeCoordinate): PeerLinkHandler | undefined;
|
|
983
1307
|
resetRuntime(): void;
|
|
984
1308
|
private _trySetupReturnDispatch;
|
|
985
1309
|
}
|
|
@@ -1076,640 +1400,978 @@ declare const createActionRootDomain: <ID extends string>(definition: {
|
|
|
1076
1400
|
domain: ID;
|
|
1077
1401
|
}) => ActionRootDomain<IActionRootDomain<ID>>;
|
|
1078
1402
|
//#endregion
|
|
1079
|
-
//#region src/ActionRuntime/
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1403
|
+
//#region src/ActionRuntime/Transport/codec/actionWireCodec.d.ts
|
|
1404
|
+
/**
|
|
1405
|
+
* Shared building blocks for the binary action codecs (the stateless {@link createBinaryWireAdapter} and
|
|
1406
|
+
* the per-connection `createBinaryWireSessionFactory`). Both map a `domain:id` route to a tiny integer
|
|
1407
|
+
* and reduce the verbose JSON wire to a positional tuple — they only differ in how much context they
|
|
1408
|
+
* carry per frame, so the dictionary + payload (de)assembly live here.
|
|
1409
|
+
*/
|
|
1410
|
+
/**
|
|
1411
|
+
* The carrier-neutral codec a Link connection uses to (de)serialize action payloads on the wire — the
|
|
1412
|
+
* same shape every duplex carrier (WS/WebRTC/in-memory) shares.
|
|
1413
|
+
*/
|
|
1414
|
+
interface IActionWireFormat {
|
|
1415
|
+
/**
|
|
1416
|
+
* Pack an outgoing action payload. Return a `string` for text frames (JSON) or a binary
|
|
1417
|
+
* `Uint8Array`/`ArrayBuffer` for optimized binary frames (e.g. msgpackr).
|
|
1418
|
+
*/
|
|
1419
|
+
outgoing: (input: ITransportRouteActionParams) => string | Uint8Array | ArrayBuffer;
|
|
1420
|
+
/**
|
|
1421
|
+
* Unpack an incoming frame back into the wire JSON object the runtime hydrates + validates. Return
|
|
1422
|
+
* `undefined` to defer to the connection's built-in JSON parser — this is how binary adapters stay
|
|
1423
|
+
* backward compatible with plain-JSON clients on the same socket.
|
|
1424
|
+
*/
|
|
1425
|
+
incoming?: (input: string | ArrayBuffer | Uint8Array | Blob) => TActionPayload_Any_JsonObject<any, any> | undefined;
|
|
1091
1426
|
}
|
|
1092
|
-
interface IActionTransportInitialized_Custom extends IActionTransportInitialized<ITransportRouteActionParams, IActionTransportReadyData_Custom> {}
|
|
1093
|
-
interface IActionTransportDef_Custom extends IActionTransportDef<ETransportType.custom, IActionTransportInitialized_Custom> {}
|
|
1094
|
-
type TActionTransportDef_Custom_NoType = Omit<IActionTransportDef_Custom, "type">;
|
|
1095
1427
|
//#endregion
|
|
1096
|
-
//#region src/ActionRuntime/Handler/
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1428
|
+
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/AcceptorHandler.d.ts
|
|
1429
|
+
/** The codec shape `AcceptorHandler` uses to pack/unpack frames — same as the Link transport's. */
|
|
1430
|
+
type TActionChannelFormatMessage = IActionWireFormat;
|
|
1431
|
+
/** How a connection encodes its frames, remembered so we answer each client in its own dialect. */
|
|
1432
|
+
type TActionConnectionEncoding = "json" | "binary";
|
|
1433
|
+
/** A connection's restorable identity — what to persist so a binding survives transport eviction. */
|
|
1434
|
+
interface IAcceptorConnectionBinding {
|
|
1435
|
+
/** Full client coordinate, so `originClient` can be re-injected into frames that omit it. */
|
|
1436
|
+
client: IRuntimeCoordinate;
|
|
1437
|
+
encoding: TActionConnectionEncoding;
|
|
1438
|
+
/**
|
|
1439
|
+
* Secure-session state (set once a connection's handshake completes). Persist it alongside the
|
|
1440
|
+
* binding so an authenticated/encrypted connection resumes after eviction without re-handshaking —
|
|
1441
|
+
* the `keyMaterial` lets the server re-derive the shared key from its own persisted identity.
|
|
1442
|
+
*/
|
|
1443
|
+
secure?: {
|
|
1444
|
+
securityLevel: ESecurityLevel;
|
|
1445
|
+
linkedClientId: TTypeAndId;
|
|
1446
|
+
keyMaterial?: IHandshakeEncryptionKeyMaterial;
|
|
1447
|
+
};
|
|
1100
1448
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1449
|
+
/**
|
|
1450
|
+
* Server-side secure-channel config. When set, each connection negotiates a level from
|
|
1451
|
+
* {@link securityLevel}: an `authenticated`/`encrypted` client must complete the handshake (and is then
|
|
1452
|
+
* bound to its *authenticated* coordinate) before any action frame is accepted. A `none` client (only
|
|
1453
|
+
* when `none` is in the allowed set) is accepted as-is with a self-asserted identity. For the
|
|
1454
|
+
* `encrypted` level the codec source should be a session factory (`createFormatMessage`).
|
|
1455
|
+
*/
|
|
1456
|
+
interface IAcceptorSecurity {
|
|
1457
|
+
/**
|
|
1458
|
+
* Accepted level(s). A single level is strict; an array is a negotiable allowed set — the server
|
|
1459
|
+
* adopts whichever level each client requests (e.g. `[none, authenticated, encrypted]` serves all
|
|
1460
|
+
* three over one endpoint).
|
|
1461
|
+
*/
|
|
1462
|
+
securityLevel: ESecurityLevel | readonly ESecurityLevel[];
|
|
1463
|
+
/** This server's crypto identity (verify + exchange key pairs, optionally persisted). */
|
|
1464
|
+
link: ClientCryptoKeyLink;
|
|
1465
|
+
/** This server's coordinate — its identity to clients during the handshake. */
|
|
1466
|
+
localCoordinate: IRuntimeCoordinate;
|
|
1467
|
+
/** Wire dictionary version; the handshake rejects a client on a mismatch. */
|
|
1468
|
+
dictionaryVersion: string;
|
|
1469
|
+
/** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */
|
|
1470
|
+
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
1110
1471
|
}
|
|
1111
|
-
interface
|
|
1112
|
-
/**
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1472
|
+
interface IAcceptorHandlerBaseOptions<TConn> {
|
|
1473
|
+
/**
|
|
1474
|
+
* Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`).
|
|
1475
|
+
* The runtime's return-path dispatch scores incoming actions' `originClient` against this to pick
|
|
1476
|
+
* this handler for sending results/pushes back over the right channel.
|
|
1477
|
+
*/
|
|
1478
|
+
clientEnv: RuntimeCoordinate;
|
|
1479
|
+
/** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
|
|
1480
|
+
send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
|
|
1481
|
+
/**
|
|
1482
|
+
* The runtime this handler belongs to. When set, {@link AcceptorHandler.broadcast} can be called
|
|
1483
|
+
* without threading a runtime through each call. Optional — `pushToClient` still takes one explicitly.
|
|
1484
|
+
*/
|
|
1485
|
+
runtime?: ActionRuntime;
|
|
1486
|
+
/** Timeout (ms) applied to server-initiated actions awaiting a client response. */
|
|
1487
|
+
defaultTimeout?: number;
|
|
1488
|
+
/**
|
|
1489
|
+
* Called once when a connection is first bound to a client identity. Use it to persist the binding
|
|
1490
|
+
* for transports that can resume after eviction — e.g. a Durable Object's hibernatable WebSocket:
|
|
1491
|
+
* `(ws, binding) => ws.serializeAttachment(binding)` — then replay it via {@link AcceptorHandler.rehydrateConnection}
|
|
1492
|
+
* when the channel comes back.
|
|
1493
|
+
*/
|
|
1494
|
+
onConnectionBound?: (connection: TConn, binding: IAcceptorConnectionBinding) => void;
|
|
1495
|
+
/**
|
|
1496
|
+
* Enable the authenticated (optionally encrypted) handshake. When omitted, connections are trusted
|
|
1497
|
+
* as-is (identity self-asserted) — fine for dev / trusted networks.
|
|
1498
|
+
*/
|
|
1499
|
+
security?: IAcceptorSecurity;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Provide exactly one codec source:
|
|
1503
|
+
* - `formatMessage` — a single shared codec for every connection (stateless, e.g. `createBinaryWireAdapter`).
|
|
1504
|
+
* - `createFormatMessage` — a per-connection factory for stateful codecs (e.g.
|
|
1505
|
+
* `createBinaryWireSessionFactory`, whose sessions hold correlation + identity state). Required for the
|
|
1506
|
+
* leanest binary wire; the handler creates and caches one codec per connection.
|
|
1507
|
+
*/
|
|
1508
|
+
type IAcceptorHandlerOptions<TConn> = IAcceptorHandlerBaseOptions<TConn> & ({
|
|
1509
|
+
formatMessage: TActionChannelFormatMessage;
|
|
1510
|
+
createFormatMessage?: never;
|
|
1511
|
+
} | {
|
|
1512
|
+
createFormatMessage: () => TActionChannelFormatMessage;
|
|
1513
|
+
formatMessage?: never;
|
|
1514
|
+
});
|
|
1515
|
+
/**
|
|
1516
|
+
* A connection-aware execution case (see {@link AcceptorHandler.forConnectionDomainCases}). It
|
|
1517
|
+
* receives the primed request plus the originating client's live connection (already resolved from the
|
|
1518
|
+
* request's `originClient`, `undefined` if the socket is gone), and may return the action's raw output,
|
|
1519
|
+
* a result payload, or nothing (auto-wrapped as an empty success) — exactly like a local handler case.
|
|
1520
|
+
*/
|
|
1521
|
+
type TAcceptorConnectionCaseFn<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string, TConn> = (action: TDistributeActionPayload_Request<DOM, ID>, connection: TConn | undefined) => ReturnType<THandleActionExecutionFn<DOM, ID>> | void;
|
|
1522
|
+
/**
|
|
1523
|
+
* Server-side handler for backends that accept many client connections over a single open channel
|
|
1524
|
+
* (WebSockets, Durable Objects, …). It is transport-agnostic: you feed it inbound frames with
|
|
1525
|
+
* {@link receive} and tell it how to write outbound frames via the `send` option.
|
|
1526
|
+
*
|
|
1527
|
+
* Add it alongside your local execution handler:
|
|
1528
|
+
* ```ts
|
|
1529
|
+
* const serverHandler = createAcceptorHandler({ clientEnv, formatMessage, send: (ws, f) => ws.send(f) });
|
|
1530
|
+
* runtime.addHandlers([localHandler, serverHandler]);
|
|
1531
|
+
* // per inbound message (e.g. a Durable Object's webSocketMessage):
|
|
1532
|
+
* serverHandler.receive(ws, message);
|
|
1533
|
+
* ```
|
|
1534
|
+
*
|
|
1535
|
+
* Inbound requests route to your local handler; the runtime's return dispatch then calls this
|
|
1536
|
+
* handler back (it is an external handler keyed to `clientEnv`) to send the result to the originating
|
|
1537
|
+
* connection. The handler keeps a per-connection identity registry so each result lands on the right
|
|
1538
|
+
* socket, and remembers each connection's encoding so binary and JSON clients can share the channel.
|
|
1539
|
+
*
|
|
1540
|
+
* It registers an empty action router, so it is never chosen to *execute* an inbound request — only
|
|
1541
|
+
* to ferry results/pushes back out.
|
|
1542
|
+
*/
|
|
1543
|
+
declare class AcceptorHandler<TConn = unknown> extends PeerLinkHandler {
|
|
1544
|
+
/** Accept-in over a live (duplex) connection registry — it pushes results/broadcasts to bound sockets. */
|
|
1545
|
+
readonly canPush = true;
|
|
1546
|
+
private readonly _formatMessage?;
|
|
1547
|
+
private readonly _createFormatMessage?;
|
|
1548
|
+
private readonly _send;
|
|
1549
|
+
private readonly _runtime?;
|
|
1550
|
+
private readonly _serverTimeout;
|
|
1551
|
+
private _onConnectionBound?;
|
|
1552
|
+
private readonly _security?;
|
|
1553
|
+
/** Normalized accepted levels; whether `none` (plain) is allowed; whether any level needs a handshake. */
|
|
1554
|
+
private readonly _allowedLevels;
|
|
1555
|
+
private readonly _noneAllowed;
|
|
1556
|
+
private readonly _handshakeMode;
|
|
1557
|
+
private readonly _connByClient;
|
|
1558
|
+
private readonly _clientByConn;
|
|
1559
|
+
private readonly _connEncoding;
|
|
1560
|
+
private readonly _codecByConn;
|
|
1561
|
+
private readonly _sessionByConn;
|
|
1562
|
+
constructor(options: IAcceptorHandlerOptions<TConn>);
|
|
1563
|
+
/**
|
|
1564
|
+
* The codec for a connection: a per-connection session (cached) when a factory was provided, else
|
|
1565
|
+
* the single shared `formatMessage`.
|
|
1566
|
+
*/
|
|
1567
|
+
private _codecFor;
|
|
1568
|
+
/**
|
|
1569
|
+
* Register (or replace) the connection-bound persistence callback after construction. Used by
|
|
1570
|
+
* lifecycle helpers like {@link createHibernatableWsServerAdapter} so persistence and replay are
|
|
1571
|
+
* owned by one place instead of being split across the constructor options.
|
|
1572
|
+
*/
|
|
1573
|
+
setOnConnectionBound(onConnectionBound: (connection: TConn, binding: IAcceptorConnectionBinding) => void): void;
|
|
1574
|
+
/**
|
|
1575
|
+
* Feed one inbound frame from a connection into the runtime. Decodes text or binary, binds the
|
|
1576
|
+
* connection to the requesting client's identity, then routes it (requests execute locally;
|
|
1577
|
+
* results/progress resolve pending server-initiated actions).
|
|
1578
|
+
*/
|
|
1579
|
+
receive(connection: TConn, frame: string | ArrayBuffer | Uint8Array): void;
|
|
1580
|
+
private _receivePlain;
|
|
1581
|
+
/**
|
|
1582
|
+
* The secure session for a connection (built lazily on its first secure-mode frame), with the
|
|
1583
|
+
* handler-owned effects — raw send, identity binding + persistence, and inbound routing — wired in as
|
|
1584
|
+
* callbacks. The session owns all crypto/handshake/chain state; the handler keeps only the registry.
|
|
1585
|
+
*/
|
|
1586
|
+
private _sessionFor;
|
|
1587
|
+
/** Bind + persist a connection's authenticated identity once its handshake completes. */
|
|
1588
|
+
private _onConnectionAuthenticated;
|
|
1589
|
+
/** Decode a decrypted authenticated frame, inject the *authenticated* identity, and route it. */
|
|
1590
|
+
private _routeAuthedActionBytes;
|
|
1591
|
+
/**
|
|
1592
|
+
* Ensure an inbound request carries the client's identity and that this connection is bound to it,
|
|
1593
|
+
* so its result can be routed back. A session codec omits `originClient` after the first request, so
|
|
1594
|
+
* when it's missing we restore it from the (possibly rehydrated) binding instead. (Plain mode only;
|
|
1595
|
+
* secure mode binds the authenticated coordinate at handshake time.)
|
|
1596
|
+
*/
|
|
1597
|
+
private _resolveRequestIdentity;
|
|
1598
|
+
/**
|
|
1599
|
+
* Restore a connection→client binding without an inbound frame — for transports that resume after
|
|
1600
|
+
* eviction. Pair it with the {@link IAcceptorHandlerOptions.onConnectionBound} hook: persist
|
|
1601
|
+
* the binding there, then replay each live connection here when the channel comes back (e.g. a
|
|
1602
|
+
* Durable Object iterating `ctx.getWebSockets()` as it wakes from hibernation).
|
|
1603
|
+
*/
|
|
1604
|
+
rehydrateConnection(connection: TConn, binding: IAcceptorConnectionBinding): void;
|
|
1605
|
+
toJsonObject(): IActionHandler_Peer_Json;
|
|
1606
|
+
toHandlerRouteItem(): IActionRouteItemHandler;
|
|
1607
|
+
/** Forget a connection (call on socket close) so stale entries don't misroute later results. */
|
|
1608
|
+
dropConnection(connection: TConn): void;
|
|
1609
|
+
/** Live connection for a client coordinate, if currently registered. */
|
|
1610
|
+
getConnectionForClient(client: RuntimeCoordinate): TConn | undefined;
|
|
1611
|
+
/** This acceptor owns the origin's return path when it currently holds a live connection bound to it. */
|
|
1612
|
+
ownsLiveConnectionFor(origin: RuntimeCoordinate): boolean;
|
|
1613
|
+
/** Whether this acceptor currently tracks `connection` — used to pick the owning handler among several. */
|
|
1614
|
+
hasConnection(connection: TConn): boolean;
|
|
1615
|
+
/**
|
|
1616
|
+
* Send (and optionally await) a server-initiated action to a specific connected client. Pass the
|
|
1617
|
+
* connection token directly (e.g. the `ws`) or a client `RuntimeCoordinate` to look one up.
|
|
1618
|
+
*/
|
|
1619
|
+
pushToClient<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(runtime: ActionRuntime, target: TConn | RuntimeCoordinate, request: ActionPayload_Request<DOM, ID>, options?: {
|
|
1620
|
+
timeout?: number;
|
|
1621
|
+
}): RunningAction<DOM, ID>;
|
|
1622
|
+
/**
|
|
1623
|
+
* Build a local handler whose cases are connection-aware: each case receives the primed request and
|
|
1624
|
+
* the originating client's live connection (resolved from `originClient`), so handlers don't repeat
|
|
1625
|
+
* the `getConnectionForClient(action.context.originClient)` lookup. Cases may return raw output or
|
|
1626
|
+
* nothing, just like {@link ActionLocalHandler.forDomainActionCases}. Add the returned handler to the
|
|
1627
|
+
* runtime alongside this server handler:
|
|
1628
|
+
* ```ts
|
|
1629
|
+
* runtime.addHandlers([serverHandler.forConnectionDomainCases(domain, { … }), serverHandler]);
|
|
1630
|
+
* ```
|
|
1631
|
+
*/
|
|
1632
|
+
forConnectionDomainCases<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>, cases: { [ID in keyof FOR_DOM["actionSchema"] & string]?: TAcceptorConnectionCaseFn<FOR_DOM, ID, TConn> }): ActionLocalHandler;
|
|
1633
|
+
/**
|
|
1634
|
+
* Like {@link forConnectionDomainCases} but spanning several domains with one merged case map — used
|
|
1635
|
+
* by channel-derived wiring (`acceptChannelConnections`) where the channel's `toAcceptor` domains are
|
|
1636
|
+
* served together. Each domain takes only the cases whose ids it owns, so a single map can cover
|
|
1637
|
+
* several domains and unrelated ids are ignored.
|
|
1638
|
+
*/
|
|
1639
|
+
forConnectionDomainCasesMulti(domains: readonly ActionDomain<any>[], cases: Record<string, TAcceptorConnectionCaseFn<any, any, TConn> | undefined>): ActionLocalHandler;
|
|
1640
|
+
/**
|
|
1641
|
+
* Fan a server-initiated request out to every currently-bound connection. A fresh request is built
|
|
1642
|
+
* per connection (each push mutates its own action context) and dispatched fire-and-forget. Pass
|
|
1643
|
+
* `except` to skip the originating socket and `where` to filter by connection (e.g. read its
|
|
1644
|
+
* attachment for a role). Iterating bound connections (rather than every accepted socket) skips
|
|
1645
|
+
* sockets that are still mid-handshake and so can't yet receive a frame.
|
|
1646
|
+
*/
|
|
1647
|
+
broadcast<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(makeRequest: () => ActionPayload_Request<DOM, ID>, options?: {
|
|
1648
|
+
runtime?: ActionRuntime;
|
|
1649
|
+
except?: TConn | null;
|
|
1650
|
+
where?: (connection: TConn) => boolean;
|
|
1651
|
+
timeout?: number;
|
|
1652
|
+
onError?: (error: unknown, connection: TConn) => void;
|
|
1653
|
+
}): void;
|
|
1654
|
+
sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
|
|
1655
|
+
targetLocalRuntime: ActionRuntime;
|
|
1656
|
+
}): Promise<boolean>;
|
|
1657
|
+
handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
|
|
1658
|
+
private _dispatch;
|
|
1659
|
+
private _sendPayload;
|
|
1660
|
+
private _bindConnection;
|
|
1661
|
+
private _resolveConnection;
|
|
1662
|
+
private _resolveSingleConnection;
|
|
1117
1663
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1664
|
+
declare const createAcceptorHandler: <TConn = unknown>(options: IAcceptorHandlerOptions<TConn>) => AcceptorHandler<TConn>;
|
|
1665
|
+
//#endregion
|
|
1666
|
+
//#region src/ActionRuntime/Transport/codec/createBinaryWireSessionFactory.d.ts
|
|
1667
|
+
type TFormatMessage = IActionWireFormat;
|
|
1668
|
+
interface IBinaryWireSessionOptions {
|
|
1669
|
+
/** Override how long an unresolved correlation is retained before being swept (ms). */
|
|
1670
|
+
correlationTtlMs?: number;
|
|
1121
1671
|
}
|
|
1122
|
-
type TCustomTransportOptions = (ICustomTransportSendOptions & {
|
|
1123
|
-
mode: "send";
|
|
1124
|
-
}) | (ICustomTransportAdvancedOptions & {
|
|
1125
|
-
mode: "advanced";
|
|
1126
|
-
});
|
|
1127
1672
|
/**
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
1130
|
-
*
|
|
1673
|
+
* Builds a factory of *stateful, per-connection* codecs for {@link LinkTransport} /
|
|
1674
|
+
* `AcceptorHandler` — the maximally compact binary wire. Call the returned factory once per live
|
|
1675
|
+
* connection (each socket on the client, each accepted connection on the server) so every channel
|
|
1676
|
+
* gets its own correlation + identity state.
|
|
1677
|
+
*
|
|
1678
|
+
* On top of everything {@link createBinaryWireAdapter} drops, a session also drops:
|
|
1679
|
+
* - **`cuid`** — replaced by a per-connection integer correlation id. The initiator maps it to its
|
|
1680
|
+
* real cuid; the responder echoes it; each side reconstructs the cuid from its own map. Correlation
|
|
1681
|
+
* only needs to be unique per socket, so a counter suffices.
|
|
1682
|
+
* - **`originClient` after the first request** — the first request each side sends carries its
|
|
1683
|
+
* identity; the peer remembers it and injects it into later frames. Replies omit it entirely (a
|
|
1684
|
+
* reply carries the initiator's own origin, which the initiator already knows).
|
|
1685
|
+
*
|
|
1686
|
+
* Both ends MUST build the factory from the same domains in the same order (positional dictionary).
|
|
1687
|
+
* Text frames still return `undefined` from `incoming`, so JSON clients remain interoperable.
|
|
1688
|
+
*
|
|
1689
|
+
* Hibernation note: after a server connection is evicted its session resets, so a still-connected
|
|
1690
|
+
* client (whose session persists) will keep omitting `originClient`. The server must therefore restore
|
|
1691
|
+
* the connection→client binding from its own store (see `AcceptorHandler.rehydrateConnection`) and
|
|
1692
|
+
* inject `originClient` from there — the session alone can't recover it.
|
|
1131
1693
|
*/
|
|
1132
|
-
declare
|
|
1133
|
-
private readonly options;
|
|
1134
|
-
readonly type = ETransportType.custom;
|
|
1135
|
-
constructor(options: TCustomTransportOptions);
|
|
1136
|
-
static create(options: ICustomTransportSendOptions): CustomTransport;
|
|
1137
|
-
static createAdvanced(options: ICustomTransportAdvancedOptions): CustomTransport;
|
|
1138
|
-
_createConnection(_ctx: ITransportConnectionContext): CustomConnection;
|
|
1139
|
-
getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
|
|
1140
|
-
}
|
|
1141
|
-
//#endregion
|
|
1142
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport.d.ts
|
|
1143
|
-
declare enum EErrId_NiceTransport {
|
|
1144
|
-
timeout = "timeout",
|
|
1145
|
-
not_found = "not_found",
|
|
1146
|
-
unsupported = "unsupported",
|
|
1147
|
-
initialization_failed = "initialization_failed",
|
|
1148
|
-
send_failed = "send_failed",
|
|
1149
|
-
invalid_action_response = "invalid_action_response"
|
|
1150
|
-
}
|
|
1151
|
-
declare const err_nice_transport: import("@nice-code/error").NiceErrorDomain<{
|
|
1152
|
-
domain: string;
|
|
1153
|
-
allDomains: [string, string, string, "err_nice"];
|
|
1154
|
-
schema: {
|
|
1155
|
-
timeout: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1156
|
-
timeout: number;
|
|
1157
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1158
|
-
not_found: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1159
|
-
actionId: string;
|
|
1160
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1161
|
-
unsupported: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1162
|
-
transportTypes: ETransportType[];
|
|
1163
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1164
|
-
initialization_failed: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1165
|
-
actionId: string;
|
|
1166
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1167
|
-
send_failed: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1168
|
-
actionState: string;
|
|
1169
|
-
actionId: string;
|
|
1170
|
-
httpStatusCode?: number;
|
|
1171
|
-
message?: string;
|
|
1172
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1173
|
-
invalid_action_response: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1174
|
-
actionId: string;
|
|
1175
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1176
|
-
};
|
|
1177
|
-
}>;
|
|
1178
|
-
//#endregion
|
|
1179
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport_ws.d.ts
|
|
1180
|
-
declare enum EErrId_NiceTransport_WebSocket {
|
|
1181
|
-
ws_disconnected = "ws_disconnected",
|
|
1182
|
-
ws_create_failed = "ws_create_failed",
|
|
1183
|
-
ws_error = "ws_error"
|
|
1184
|
-
}
|
|
1185
|
-
declare const err_nice_transport_ws: import("@nice-code/error").NiceErrorDomain<{
|
|
1186
|
-
domain: string;
|
|
1187
|
-
allDomains: [string, string, string, string, "err_nice"];
|
|
1188
|
-
schema: {
|
|
1189
|
-
ws_disconnected: import("@nice-code/error").INiceErrorIdMetadata<Record<string, never>, import("@nice-code/error").JSONSerializableValue>;
|
|
1190
|
-
ws_create_failed: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1191
|
-
originalError?: Error;
|
|
1192
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1193
|
-
ws_error: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
1194
|
-
originalError?: Error;
|
|
1195
|
-
}, import("@nice-code/error").JSONSerializableValue>;
|
|
1196
|
-
};
|
|
1197
|
-
}>;
|
|
1198
|
-
//#endregion
|
|
1199
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/Http/TransportHttp.types.d.ts
|
|
1200
|
-
interface IHttpRequestParams {
|
|
1201
|
-
url: string;
|
|
1202
|
-
headers?: Record<string, string>;
|
|
1203
|
-
body?: string;
|
|
1204
|
-
}
|
|
1205
|
-
interface IActionTransportReadyParams_Http {
|
|
1206
|
-
request: IHttpRequestParams;
|
|
1207
|
-
}
|
|
1208
|
-
interface IActionTransportReadyData_Http extends IActionTransportReadyData_Base {
|
|
1209
|
-
createRequest: (input: ITransportRouteActionParams) => IHttpRequestParams;
|
|
1210
|
-
}
|
|
1211
|
-
interface IActionTransportInitialized_Http extends IActionTransportInitialized<ITransportRouteActionParams, IActionTransportReadyData_Http> {}
|
|
1212
|
-
interface IActionTransportDef_Http extends IActionTransportDef<ETransportType.http, IActionTransportInitialized_Http> {}
|
|
1694
|
+
declare function createBinaryWireSessionFactory(domains: ActionDomain<any>[], options?: IBinaryWireSessionOptions): () => TFormatMessage;
|
|
1213
1695
|
//#endregion
|
|
1214
|
-
//#region src/ActionRuntime/
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1696
|
+
//#region src/ActionRuntime/Channel/secureChannel.d.ts
|
|
1697
|
+
/** The per-connection binary session codec — built once per socket from the channel's domains. */
|
|
1698
|
+
type TChannelCodec = IActionWireFormat;
|
|
1699
|
+
/**
|
|
1700
|
+
* The shared identity of a secure channel (carrier-agnostic — WS, WebRTC, HTTP, …): the wire
|
|
1701
|
+
* dictionary version both ends check during the handshake, plus the per-connection codec factory both
|
|
1702
|
+
* ends build from the *same* domain list. Define it once (typically in code shared by both peers) and
|
|
1703
|
+
* hand it to `secureTransport` on the connector and `serveChannel` (or the lower-level `acceptChannel` /
|
|
1704
|
+
* `createSecureAcceptorHandler`) on the acceptor, so the codec and version can never drift apart.
|
|
1705
|
+
*/
|
|
1706
|
+
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> {
|
|
1707
|
+
/** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */
|
|
1708
|
+
dictionaryVersion: string;
|
|
1709
|
+
/** Per-connection session codec factory (call once per live connection). */
|
|
1710
|
+
createCodec: () => TChannelCodec;
|
|
1219
1711
|
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Bundle a secure channel's shared identity from its transported domains. Both ends MUST call this
|
|
1714
|
+
* with the same domains in the same order (the binary wire dictionary is positional). The
|
|
1715
|
+
* `dictionaryVersion` is derived from those domains unless you pin an explicit one.
|
|
1716
|
+
*
|
|
1717
|
+
* Declare the domains *by role* — `toAcceptor` (connector→acceptor requests) and `toConnector`
|
|
1718
|
+
* (acceptor→connector pushes) — so the routing for both ends is derived from the channel (see
|
|
1719
|
+
* {@link connectChannel} and `acceptChannelConnections`) instead of being restated at each end. The
|
|
1720
|
+
* wire dictionary spans `[...toAcceptor, ...toConnector]` in that order; add new domains to the end of
|
|
1721
|
+
* their list to keep older peers compatible. (`domains` is still accepted as a legacy alias for
|
|
1722
|
+
* `toAcceptor`.)
|
|
1723
|
+
*/
|
|
1724
|
+
declare function defineSecureChannel<const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [], const TO_CONNECTOR extends readonly ActionDomain<any>[] = []>(options: {
|
|
1725
|
+
/** 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. */
|
|
1726
|
+
toConnector: TO_CONNECTOR; /** Pin a human-readable version instead of the derived hash (must match on both ends). */
|
|
1727
|
+
dictionaryVersion?: string; /** Tuning for the per-connection binary session (e.g. correlation TTL). */
|
|
1728
|
+
sessionOptions?: IBinaryWireSessionOptions;
|
|
1729
|
+
}): ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>;
|
|
1220
1730
|
//#endregion
|
|
1221
|
-
//#region src/ActionRuntime/
|
|
1222
|
-
|
|
1731
|
+
//#region src/ActionRuntime/Channel/ActionChannel.d.ts
|
|
1732
|
+
/**
|
|
1733
|
+
* A transport-agnostic routing contract between two runtimes, declared *by role* rather than by
|
|
1734
|
+
* "client"/"server". The two ends are named for the only asymmetry that survives every carrier (WS,
|
|
1735
|
+
* WebRTC, BLE, raw TCP): which side dials and which side accepts.
|
|
1736
|
+
*
|
|
1737
|
+
* - The **connector** dials out and opens the link ({@link connectChannel}).
|
|
1738
|
+
* - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}).
|
|
1739
|
+
*
|
|
1740
|
+
* `toAcceptor` domains flow connector→acceptor (the classic "request"); `toConnector` domains flow
|
|
1741
|
+
* acceptor→connector (the classic "push"). Both ends derive their routing from the same channel instead
|
|
1742
|
+
* of restating domain lists — and because the contract is independent of how bytes move, the very same
|
|
1743
|
+
* channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback).
|
|
1744
|
+
*
|
|
1745
|
+
* Build a plain one with {@link defineChannel}; layer wire-specific identity on top with
|
|
1746
|
+
* `defineSecureChannel` (which returns an `ISecureChannel` — still an `IActionChannel`, so it works
|
|
1747
|
+
* everywhere a channel is expected).
|
|
1748
|
+
*/
|
|
1749
|
+
interface IActionChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[]> {
|
|
1223
1750
|
/**
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1751
|
+
* Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards
|
|
1752
|
+
* them over its transport(s); the acceptor executes them.
|
|
1226
1753
|
*/
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1754
|
+
toAcceptorDomains: TO_ACCEPTOR;
|
|
1755
|
+
/**
|
|
1756
|
+
* Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local
|
|
1757
|
+
* handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a
|
|
1758
|
+
* bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply
|
|
1759
|
+
* never flow.
|
|
1760
|
+
*/
|
|
1761
|
+
toConnectorDomains: TO_CONNECTOR;
|
|
1230
1762
|
}
|
|
1231
1763
|
/**
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1764
|
+
* Declare a transport-agnostic channel by role. Use it for HTTP, custom transports, or as the routing
|
|
1765
|
+
* half of a richer channel. The order of each list is part of the contract for wire formats that pack
|
|
1766
|
+
* positionally (see `defineSecureChannel`) — add new domains to the end of their list. (`domains` is
|
|
1767
|
+
* accepted as a legacy alias for `toAcceptor`.)
|
|
1235
1768
|
*/
|
|
1236
|
-
declare
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1769
|
+
declare function defineChannel<const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [], const TO_CONNECTOR extends readonly ActionDomain<any>[] = []>(options: {
|
|
1770
|
+
toAcceptor: TO_ACCEPTOR;
|
|
1771
|
+
toConnector: TO_CONNECTOR;
|
|
1772
|
+
}): IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>;
|
|
1773
|
+
type TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
1774
|
+
type TDomainPushHandlers<D> = D extends ActionDomain<infer DEF> ? Partial<TWrappableDomainActionHandler<DEF>> : never;
|
|
1775
|
+
/**
|
|
1776
|
+
* The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action
|
|
1777
|
+
* handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so
|
|
1778
|
+
* the keys and input types follow the channel definition.
|
|
1779
|
+
*/
|
|
1780
|
+
type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> = TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;
|
|
1781
|
+
interface IConnectChannelOptions<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[]> {
|
|
1782
|
+
/** The shared channel — its `toAcceptorDomains`/`toConnectorDomains` drive all routing. */
|
|
1783
|
+
channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>;
|
|
1784
|
+
/**
|
|
1785
|
+
* Transports to the acceptor, in preference order (e.g. `[wsTransport, httpFallback]`). They all carry
|
|
1786
|
+
* the same `toAcceptor` domains; the manager prefers the first that's ready and falls through on
|
|
1787
|
+
* failure — so connector→acceptor works over whatever transport is available, WS or HTTP.
|
|
1788
|
+
*/
|
|
1789
|
+
transports: Transport[];
|
|
1790
|
+
/** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */
|
|
1791
|
+
onPush?: TChannelPushHandlers<TO_CONNECTOR>;
|
|
1792
|
+
/** Default per-action timeout for this connection. */
|
|
1793
|
+
defaultTimeout?: number;
|
|
1243
1794
|
}
|
|
1244
|
-
//#endregion
|
|
1245
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/actionFrameCrypto.d.ts
|
|
1246
1795
|
/**
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1252
|
-
*
|
|
1253
|
-
* encrypts after `session.outgoing()` and decrypts before `session.incoming()`. The `authenticated`
|
|
1254
|
-
* and `none` levels use no crypto transform at all (frames go out as the session produced them).
|
|
1796
|
+
* Wire a connection to the acceptor straight from a channel: route the channel's `toAcceptor` domains to
|
|
1797
|
+
* the acceptor over `transports`, and register local handlers for its `toConnector` pushes from
|
|
1798
|
+
* `onPush`. The channel is the single source of truth for *what* is routed in each direction — the
|
|
1799
|
+
* caller only supplies the transport(s) and the push handlers, never restated domain lists. Pass several
|
|
1800
|
+
* transports to make the connector→acceptor path transport-agnostic (e.g. secure WS preferred, HTTP
|
|
1801
|
+
* fallback).
|
|
1255
1802
|
*
|
|
1256
|
-
*
|
|
1257
|
-
*
|
|
1803
|
+
* Sugar over {@link ActionRuntime.connectTo}. Returns the acceptor handler so the caller can later
|
|
1804
|
+
* `clearTransportCache()` it.
|
|
1258
1805
|
*/
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
encryptFrame(frame: Uint8Array): Promise<Uint8Array>;
|
|
1262
|
-
/** Decrypt one received frame back to the session frame. Throws on a non-binary / malformed /
|
|
1263
|
-
* tampered frame — the caller (transport) decides how to react (drop / close). */
|
|
1264
|
-
decryptFrame(frame: string | ArrayBuffer | Uint8Array): Promise<Uint8Array>;
|
|
1265
|
-
}
|
|
1266
|
-
interface IActionFrameCryptoConfig {
|
|
1267
|
-
link: ClientCryptoKeyLink;
|
|
1268
|
-
/** The handshake-established link id for the remote (key + connection-registry id). */
|
|
1269
|
-
linkedClientId: TTypeAndId;
|
|
1270
|
-
}
|
|
1806
|
+
declare function connectChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[]>(runtime: ActionRuntime, acceptorCoordinate: RuntimeCoordinate, options: IConnectChannelOptions<TO_ACCEPTOR, TO_CONNECTOR>): ConnectorHandler;
|
|
1807
|
+
type TDomainAcceptorCases<D, TConn> = D extends ActionDomain<infer DEF> ? { [ID in keyof DEF["actionSchema"] & string]?: TAcceptorConnectionCaseFn<DEF, ID, TConn> } : never;
|
|
1271
1808
|
/**
|
|
1272
|
-
*
|
|
1273
|
-
*
|
|
1809
|
+
* The connection-aware case map for a channel's acceptor side: the merged set of every
|
|
1810
|
+
* connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus the
|
|
1811
|
+
* originating connection. Derived from the channel's `toAcceptorDomains`, so the keys and input/output
|
|
1812
|
+
* types follow the channel.
|
|
1274
1813
|
*/
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
readonly
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
readonly securityLevel: v.PicklistSchema<[ESecurityLevel.none, ESecurityLevel.authenticated, ESecurityLevel.encrypted], undefined>;
|
|
1314
|
-
readonly dictionaryVersion: v.StringSchema<undefined>;
|
|
1315
|
-
readonly server: v.ObjectSchema<{
|
|
1316
|
-
readonly envId: v.StringSchema<undefined>;
|
|
1317
|
-
readonly perId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1318
|
-
readonly insId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1319
|
-
}, undefined>;
|
|
1320
|
-
readonly serverNonce: v.StringSchema<undefined>;
|
|
1321
|
-
readonly verifyPublicKey: v.CustomSchema<`ed25519::raw_base64::${string}`, undefined>;
|
|
1322
|
-
readonly exchangePublicKey: v.OptionalSchema<v.CustomSchema<`x25519::raw_base64::${string}`, undefined>, undefined>;
|
|
1323
|
-
}, undefined>;
|
|
1324
|
-
declare const vHsProve: v.ObjectSchema<{
|
|
1325
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.prove, undefined>;
|
|
1326
|
-
readonly signatureBase64: v.StringSchema<undefined>;
|
|
1327
|
-
}, undefined>;
|
|
1328
|
-
declare const vHsAccept: v.ObjectSchema<{
|
|
1329
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.accept, undefined>;
|
|
1330
|
-
readonly signatureBase64: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1331
|
-
}, undefined>;
|
|
1332
|
-
declare const vHsReject: v.ObjectSchema<{
|
|
1333
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.reject, undefined>;
|
|
1334
|
-
readonly reason: v.StringSchema<undefined>;
|
|
1335
|
-
}, undefined>;
|
|
1336
|
-
declare const vHandshakeMessage: v.VariantSchema<"t", [v.ObjectSchema<{
|
|
1337
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.hello, undefined>;
|
|
1338
|
-
readonly protocol: v.StringSchema<undefined>;
|
|
1339
|
-
readonly securityLevel: v.PicklistSchema<[ESecurityLevel.none, ESecurityLevel.authenticated, ESecurityLevel.encrypted], undefined>;
|
|
1340
|
-
readonly dictionaryVersion: v.StringSchema<undefined>;
|
|
1341
|
-
readonly client: v.ObjectSchema<{
|
|
1342
|
-
readonly envId: v.StringSchema<undefined>;
|
|
1343
|
-
readonly perId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1344
|
-
readonly insId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1345
|
-
}, undefined>;
|
|
1346
|
-
readonly clientNonce: v.StringSchema<undefined>;
|
|
1347
|
-
readonly verifyPublicKey: v.CustomSchema<`ed25519::raw_base64::${string}`, undefined>;
|
|
1348
|
-
readonly exchangePublicKey: v.OptionalSchema<v.CustomSchema<`x25519::raw_base64::${string}`, undefined>, undefined>;
|
|
1349
|
-
}, undefined>, v.ObjectSchema<{
|
|
1350
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.welcome, undefined>;
|
|
1351
|
-
readonly securityLevel: v.PicklistSchema<[ESecurityLevel.none, ESecurityLevel.authenticated, ESecurityLevel.encrypted], undefined>;
|
|
1352
|
-
readonly dictionaryVersion: v.StringSchema<undefined>;
|
|
1353
|
-
readonly server: v.ObjectSchema<{
|
|
1354
|
-
readonly envId: v.StringSchema<undefined>;
|
|
1355
|
-
readonly perId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1356
|
-
readonly insId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1357
|
-
}, undefined>;
|
|
1358
|
-
readonly serverNonce: v.StringSchema<undefined>;
|
|
1359
|
-
readonly verifyPublicKey: v.CustomSchema<`ed25519::raw_base64::${string}`, undefined>;
|
|
1360
|
-
readonly exchangePublicKey: v.OptionalSchema<v.CustomSchema<`x25519::raw_base64::${string}`, undefined>, undefined>;
|
|
1361
|
-
}, undefined>, v.ObjectSchema<{
|
|
1362
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.prove, undefined>;
|
|
1363
|
-
readonly signatureBase64: v.StringSchema<undefined>;
|
|
1364
|
-
}, undefined>, v.ObjectSchema<{
|
|
1365
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.accept, undefined>;
|
|
1366
|
-
readonly signatureBase64: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
1367
|
-
}, undefined>, v.ObjectSchema<{
|
|
1368
|
-
readonly t: v.LiteralSchema<EHandshakeMessageType.reject, undefined>;
|
|
1369
|
-
readonly reason: v.StringSchema<undefined>;
|
|
1370
|
-
}, undefined>], undefined>;
|
|
1371
|
-
type THsHello = v.InferOutput<typeof vHsHello>;
|
|
1372
|
-
type THsWelcome = v.InferOutput<typeof vHsWelcome>;
|
|
1373
|
-
type THsProve = v.InferOutput<typeof vHsProve>;
|
|
1374
|
-
type THsAccept = v.InferOutput<typeof vHsAccept>;
|
|
1375
|
-
type THsReject = v.InferOutput<typeof vHsReject>;
|
|
1376
|
-
type THandshakeMessage = v.InferOutput<typeof vHandshakeMessage>;
|
|
1377
|
-
/** Serialize a handshake message for the wire (handshake frames are JSON — they aren't the hot path). */
|
|
1378
|
-
declare function encodeHandshakeMessage(message: THandshakeMessage): string;
|
|
1379
|
-
/** Parse + structurally validate an incoming handshake frame; `undefined` if it isn't one. */
|
|
1380
|
-
declare function decodeHandshakeMessage(raw: string): THandshakeMessage | undefined;
|
|
1381
|
-
/** Stable link id for a runtime coordinate — the key both the crypto link and the connection use. */
|
|
1382
|
-
declare function runtimeLinkId(coordinate: IRuntimeCoordinate): TTypeAndId;
|
|
1814
|
+
type TChannelAcceptorCases<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn> = TUnionToIntersection<TDomainAcceptorCases<TO_ACCEPTOR[number], TConn>>;
|
|
1815
|
+
/**
|
|
1816
|
+
* Register an acceptor handler's execution for a channel straight from its definition: the channel's
|
|
1817
|
+
* `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets
|
|
1818
|
+
* the primed request + the originating connection, as with
|
|
1819
|
+
* {@link AcceptorHandler.forConnectionDomainCases}). The domain list is taken from the channel,
|
|
1820
|
+
* never restated. Add the returned handler to the runtime alongside the acceptor handler:
|
|
1821
|
+
* ```ts
|
|
1822
|
+
* runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]);
|
|
1823
|
+
* ```
|
|
1824
|
+
*/
|
|
1825
|
+
declare function acceptChannelConnections<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn>(serverHandler: AcceptorHandler<TConn>, channel: IActionChannel<TO_ACCEPTOR, any>, cases: TChannelAcceptorCases<TO_ACCEPTOR, TConn>): ActionLocalHandler;
|
|
1826
|
+
interface IAcceptChannelOptions<TConn> {
|
|
1827
|
+
/**
|
|
1828
|
+
* Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
|
|
1829
|
+
* used to score return-path dispatch back to the right connection.
|
|
1830
|
+
*/
|
|
1831
|
+
clientEnv: RuntimeCoordinate;
|
|
1832
|
+
/**
|
|
1833
|
+
* One backing store for the acceptor's crypto identity *and* its trust-on-first-use verify-key pins
|
|
1834
|
+
* (their keys don't collide). Back it with persistent storage so identity + pins survive eviction.
|
|
1835
|
+
*/
|
|
1836
|
+
storageAdapter: StorageAdapter;
|
|
1837
|
+
/** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
|
|
1838
|
+
send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
|
|
1839
|
+
/**
|
|
1840
|
+
* The acceptor's crypto identity. Defaults to a fresh `ClientCryptoKeyLink` over `storageAdapter`.
|
|
1841
|
+
* Pass an existing link to share one identity across several acceptors on the same server (e.g. this
|
|
1842
|
+
* WebSocket acceptor and a secure-HTTP `createActionFetchHandler`), so they present the same keys.
|
|
1843
|
+
*/
|
|
1844
|
+
link?: ClientCryptoKeyLink;
|
|
1845
|
+
/** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */
|
|
1846
|
+
securityLevel?: ESecurityLevel | readonly ESecurityLevel[];
|
|
1847
|
+
/** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storageAdapter`. */
|
|
1848
|
+
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
1849
|
+
/** Timeout (ms) applied to acceptor-initiated actions awaiting a client response. */
|
|
1850
|
+
defaultTimeout?: number;
|
|
1851
|
+
}
|
|
1383
1852
|
/**
|
|
1384
|
-
*
|
|
1385
|
-
*
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1853
|
+
* Build the secure {@link AcceptorHandler} for a channel — the accept-in counterpart to
|
|
1854
|
+
* {@link connectChannel}. It folds in the same boilerplate as {@link createSecureAcceptorHandler} (the
|
|
1855
|
+
* `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storageAdapter`, the channel's codec +
|
|
1856
|
+
* dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel,
|
|
1857
|
+
* options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution:
|
|
1858
|
+
* ```ts
|
|
1859
|
+
* const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storageAdapter, send });
|
|
1860
|
+
* runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]);
|
|
1861
|
+
* ```
|
|
1388
1862
|
*/
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
remote: IRuntimeCoordinate;
|
|
1402
|
-
securityLevel: ESecurityLevel;
|
|
1403
|
-
/** For the `encrypted` level: material to restore the shared key after eviction (persist it). */
|
|
1404
|
-
encryptionKeyMaterial?: IHandshakeEncryptionKeyMaterial;
|
|
1863
|
+
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>;
|
|
1864
|
+
//#endregion
|
|
1865
|
+
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter.d.ts
|
|
1866
|
+
interface IHibernatableWsServerAdapterOptions<TConn> {
|
|
1867
|
+
/** The handler to drive (from `createSecureAcceptorHandler` or `createAcceptorHandler`). */
|
|
1868
|
+
handler: AcceptorHandler<TConn>;
|
|
1869
|
+
/** All currently-live connections — replayed on construction to rebuild bindings after a wake. */
|
|
1870
|
+
getConnections: () => TConn[];
|
|
1871
|
+
/** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */
|
|
1872
|
+
getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined;
|
|
1873
|
+
/** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */
|
|
1874
|
+
setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void;
|
|
1405
1875
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1876
|
+
/**
|
|
1877
|
+
* The neutral lifecycle surface for a duplex (push-capable) acceptor: feed it each inbound frame and tell
|
|
1878
|
+
* it when a connection goes away. Carrier-agnostic — a WebSocket, a WebRTC data channel, or any other
|
|
1879
|
+
* duplex connection drives the same two methods.
|
|
1880
|
+
*/
|
|
1881
|
+
interface IDuplexConnectionRouter<TConn> {
|
|
1882
|
+
/** Feed one inbound frame from a connection into the handler. */
|
|
1883
|
+
receive: (connection: TConn, frame: string | ArrayBuffer | Uint8Array) => void;
|
|
1884
|
+
/** Forget a connection (call on socket close/error). */
|
|
1885
|
+
drop: (connection: TConn) => void;
|
|
1409
1886
|
}
|
|
1410
|
-
|
|
1887
|
+
/**
|
|
1888
|
+
* Wire the hibernation lifecycle for an acceptor handler on a transport whose connections outlive process
|
|
1889
|
+
* eviction (e.g. a Durable Object's hibernatable WebSockets). It owns persistence end to end:
|
|
1890
|
+
* registers `setAttachment` as the handler's connection-bound callback and immediately replays every
|
|
1891
|
+
* live connection's stored binding via `getAttachment`, so results/pushes still route after a wake.
|
|
1892
|
+
*
|
|
1893
|
+
* Layered on top of the generic {@link AcceptorHandler} — it touches only the handler's neutral
|
|
1894
|
+
* `setOnConnectionBound` / `rehydrateConnection` / `receive` / `dropConnection` surface, so no
|
|
1895
|
+
* hibernation concern leaks into the handler itself.
|
|
1896
|
+
*
|
|
1897
|
+
* Construct it once when the handler is built, then forward connection events:
|
|
1898
|
+
* ```ts
|
|
1899
|
+
* const duplex = createHibernatableWsServerAdapter({ handler, getConnections, getAttachment, setAttachment });
|
|
1900
|
+
* // webSocketMessage(ws, msg) => duplex.receive(ws, msg);
|
|
1901
|
+
* // webSocketClose/Error(ws) => duplex.drop(ws);
|
|
1902
|
+
* ```
|
|
1903
|
+
*/
|
|
1904
|
+
declare function createHibernatableWsServerAdapter<TConn>(options: IHibernatableWsServerAdapterOptions<TConn>): IDuplexConnectionRouter<TConn>;
|
|
1905
|
+
//#endregion
|
|
1906
|
+
//#region src/ActionRuntime/Transport/Carrier/Carrier.types.d.ts
|
|
1907
|
+
/**
|
|
1908
|
+
* Carrier shapes — the only transport-specific surface a new protocol must implement. The secure
|
|
1909
|
+
* session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;
|
|
1910
|
+
* a carrier just moves frames. Two shapes capture every carrier:
|
|
1911
|
+
*
|
|
1912
|
+
* - {@link IDuplexCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,
|
|
1913
|
+
* Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client
|
|
1914
|
+
* pushes (the return path + broadcast).
|
|
1915
|
+
* - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push
|
|
1916
|
+
* (HTTP, and anything request/response-shaped). The reply rides the response to its own request.
|
|
1917
|
+
*
|
|
1918
|
+
* Frames are `string` (text — handshake control messages and JSON action frames) or binary
|
|
1919
|
+
* (`Uint8Array`/`ArrayBuffer` — the optimized binary wire / encrypted frames).
|
|
1920
|
+
*/
|
|
1921
|
+
type TFrame$1 = string | Uint8Array | ArrayBuffer;
|
|
1922
|
+
/**
|
|
1923
|
+
* A bidirectional, push-capable byte stream. Reduces every duplex carrier to "open, send bytes, receive
|
|
1924
|
+
* bytes, close" — a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe
|
|
1925
|
+
* all satisfy this, so the identical secure session runs over each.
|
|
1926
|
+
*/
|
|
1927
|
+
interface IDuplexCarrier {
|
|
1928
|
+
/** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */
|
|
1929
|
+
readonly ready: Promise<void>;
|
|
1930
|
+
/** Whether the carrier is currently open (a synchronous guard before `send`). */
|
|
1931
|
+
isOpen(): boolean;
|
|
1932
|
+
/** Write one frame to the peer. */
|
|
1933
|
+
send(frame: TFrame$1): void;
|
|
1411
1934
|
/**
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
* persistent / pre-provisioned implementation without touching the protocol.
|
|
1935
|
+
* Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`
|
|
1936
|
+
* receives every inbound frame; `onClose` fires when the carrier goes away.
|
|
1415
1937
|
*/
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1938
|
+
attach(handlers: {
|
|
1939
|
+
onMessage: (frame: TFrame$1) => void;
|
|
1940
|
+
onClose: () => void;
|
|
1941
|
+
onError?: (error: unknown) => void;
|
|
1942
|
+
}): void;
|
|
1943
|
+
/** Close the carrier deliberately (a teardown). */
|
|
1944
|
+
close(): void;
|
|
1945
|
+
/** Optional human-readable endpoint for the devtools route chip. */
|
|
1946
|
+
readonly label?: string;
|
|
1420
1947
|
}
|
|
1421
1948
|
/**
|
|
1422
|
-
*
|
|
1423
|
-
*
|
|
1424
|
-
*
|
|
1949
|
+
* A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a
|
|
1950
|
+
* frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the
|
|
1951
|
+
* HTTP transaction), so no correlation id is needed on the wire.
|
|
1425
1952
|
*/
|
|
1426
|
-
|
|
1953
|
+
interface IExchangeCarrier {
|
|
1954
|
+
/** Send one frame, await the single correlated reply frame. */
|
|
1955
|
+
exchange(frame: TFrame$1, opts?: {
|
|
1956
|
+
signal?: AbortSignal;
|
|
1957
|
+
}): Promise<TFrame$1>;
|
|
1958
|
+
/** Optional human-readable endpoint for the devtools route chip. */
|
|
1959
|
+
readonly label?: string;
|
|
1960
|
+
}
|
|
1961
|
+
type TCarrier = IDuplexCarrier | IExchangeCarrier;
|
|
1427
1962
|
/**
|
|
1428
|
-
*
|
|
1429
|
-
*
|
|
1430
|
-
*
|
|
1963
|
+
* A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.
|
|
1964
|
+
* Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and handed to
|
|
1965
|
+
* {@link secureTransport} / `LinkTransport` — so adding a new carrier is "write one of these", nothing
|
|
1966
|
+
* else.
|
|
1431
1967
|
*/
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1968
|
+
interface IDuplexCarrierSource {
|
|
1969
|
+
/** Open (or reuse) the carrier for an action. */
|
|
1970
|
+
open: (input: ITransportRouteActionParams) => IDuplexCarrier;
|
|
1971
|
+
/** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
|
|
1972
|
+
getCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
1973
|
+
/** Devtools route info for an action routed over this carrier. */
|
|
1974
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
1975
|
+
/** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`, `"memory"`). */
|
|
1976
|
+
readonly carrierLabel: string;
|
|
1438
1977
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1978
|
+
/**
|
|
1979
|
+
* The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an
|
|
1980
|
+
* {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by
|
|
1981
|
+
* `httpCarrier` and handed to {@link secureTransport} — adding a new request/reply protocol is "write
|
|
1982
|
+
* one of these". The `shape` tag lets {@link secureTransport} pick the duplex vs exchange transport.
|
|
1983
|
+
*/
|
|
1984
|
+
interface IExchangeCarrierSource {
|
|
1985
|
+
/** Discriminant so a generic factory can tell an exchange source from a duplex one. */
|
|
1986
|
+
readonly shape: "exchange";
|
|
1987
|
+
/** Open (or reuse) the carrier for an action. */
|
|
1988
|
+
open: (input: ITransportRouteActionParams) => IExchangeCarrier;
|
|
1989
|
+
/** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
|
|
1990
|
+
getCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
1991
|
+
/** Devtools route info for an action routed over this carrier. */
|
|
1992
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
1993
|
+
/** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
|
|
1994
|
+
readonly carrierLabel: string;
|
|
1995
|
+
}
|
|
1996
|
+
//#endregion
|
|
1997
|
+
//#region src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.d.ts
|
|
1998
|
+
/**
|
|
1999
|
+
* Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}
|
|
2000
|
+
* / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an
|
|
2001
|
+
* acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral
|
|
2002
|
+
* about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block
|
|
2003
|
+
* once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates
|
|
2004
|
+
* it.
|
|
2005
|
+
*
|
|
2006
|
+
* Two shapes mirror the connector side:
|
|
2007
|
+
*
|
|
2008
|
+
* - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can
|
|
2009
|
+
* push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step
|
|
2010
|
+
* (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.
|
|
2011
|
+
* - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push
|
|
2012
|
+
* (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to
|
|
2013
|
+
* upgrade.
|
|
2014
|
+
*/
|
|
2015
|
+
/**
|
|
2016
|
+
* Persistence + replay hooks for a duplex carrier whose connections outlive process eviction (e.g. a
|
|
2017
|
+
* Durable Object's hibernatable WebSockets). Optional — omit for a transport that never hibernates. The
|
|
2018
|
+
* same surface {@link createHibernatableWsServerAdapter} consumes, minus the handler it's bound to.
|
|
2019
|
+
*/
|
|
2020
|
+
interface IAcceptorHibernation<TConn> {
|
|
2021
|
+
/** All currently-live connections — replayed on build to rebuild bindings after a wake. */
|
|
2022
|
+
getConnections: () => TConn[];
|
|
2023
|
+
/** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */
|
|
2024
|
+
getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined;
|
|
2025
|
+
/** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */
|
|
2026
|
+
setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void;
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each
|
|
2030
|
+
* inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a
|
|
2031
|
+
* server with *several* duplex carriers routes each connection's traffic to the right one — you hold the
|
|
2032
|
+
* carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if
|
|
2033
|
+
* called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)
|
|
2034
|
+
*/
|
|
2035
|
+
interface IDuplexCarrierLifecycle<TConn> {
|
|
2036
|
+
/** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */
|
|
2037
|
+
receive(connection: TConn, frame: TFrame$1): void;
|
|
2038
|
+
/** Forget a connection on close/error. No-op until the carrier is served. */
|
|
2039
|
+
drop(connection: TConn): void;
|
|
2040
|
+
/** @internal `serveChannel` binds this carrier's live connection router here. */
|
|
2041
|
+
_activate(router: IDuplexConnectionRouter<TConn>): void;
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live
|
|
2045
|
+
* connection, how to perform the transport-specific upgrade that admits one, which requests are such
|
|
2046
|
+
* upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and
|
|
2047
|
+
* handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see
|
|
2048
|
+
* {@link IDuplexCarrierLifecycle}).
|
|
2049
|
+
*/
|
|
2050
|
+
interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {
|
|
2051
|
+
/** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */
|
|
2052
|
+
readonly shape: ETransportShape.duplex;
|
|
1448
2053
|
/**
|
|
1449
|
-
*
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
2054
|
+
* Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex
|
|
2055
|
+
* carrier: connections speak the channel's wire codec directly with a self-asserted identity — no
|
|
2056
|
+
* handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain
|
|
2057
|
+
* carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.
|
|
1453
2058
|
*/
|
|
1454
|
-
|
|
1455
|
-
/**
|
|
1456
|
-
|
|
2059
|
+
secure?: boolean;
|
|
2060
|
+
/** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
|
|
2061
|
+
send: (connection: TConn, frame: TFrame$1) => void;
|
|
2062
|
+
/**
|
|
2063
|
+
* Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a
|
|
2064
|
+
* Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that
|
|
2065
|
+
* is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).
|
|
2066
|
+
*/
|
|
2067
|
+
upgrade?: (request: Request, url: URL) => Response | Promise<Response>;
|
|
2068
|
+
/**
|
|
2069
|
+
* Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.
|
|
2070
|
+
* Only consulted when {@link upgrade} is present.
|
|
2071
|
+
*/
|
|
2072
|
+
isUpgrade?: (request: Request, url: URL) => boolean;
|
|
2073
|
+
/** Optional persistence + replay for connections that survive eviction (Durable Object hibernation). */
|
|
2074
|
+
hibernation?: IAcceptorHibernation<TConn>;
|
|
2075
|
+
/** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`). */
|
|
2076
|
+
readonly carrierLabel: string;
|
|
1457
2077
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
2078
|
+
/**
|
|
2079
|
+
* An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By
|
|
2080
|
+
* default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose
|
|
2081
|
+
* identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint
|
|
2082
|
+
* that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's
|
|
2083
|
+
* `plainTransport({ carrier: httpCarrier(...) })`. So a server can pair a secure duplex (WebSocket) with a
|
|
2084
|
+
* plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.
|
|
2085
|
+
*/
|
|
2086
|
+
interface IExchangeAcceptorCarrier {
|
|
2087
|
+
/** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */
|
|
2088
|
+
readonly shape: ETransportShape.exchange;
|
|
2089
|
+
/**
|
|
2090
|
+
* Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain
|
|
2091
|
+
* endpoint: the body is the raw action wire and the result is the response body — no handshake, token,
|
|
2092
|
+
* or encryption. A plain endpoint ignores the central crypto identity entirely.
|
|
2093
|
+
*/
|
|
2094
|
+
secure?: boolean;
|
|
2095
|
+
/** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */
|
|
2096
|
+
isActionPath?: (url: URL) => boolean;
|
|
2097
|
+
/**
|
|
2098
|
+
* CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the
|
|
2099
|
+
* permissive `*` set; pass `false` to attach no CORS headers at all.
|
|
2100
|
+
*/
|
|
2101
|
+
cors?: Record<string, string> | false;
|
|
2102
|
+
/** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */
|
|
2103
|
+
useErrorStatus?: boolean;
|
|
2104
|
+
/** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
|
|
2105
|
+
readonly carrierLabel: string;
|
|
2106
|
+
}
|
|
2107
|
+
type TAcceptorCarrier<TConn = unknown> = IDuplexAcceptorCarrier<TConn> | IExchangeAcceptorCarrier;
|
|
2108
|
+
/**
|
|
2109
|
+
* Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch
|
|
2110
|
+
* `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex
|
|
2111
|
+
* carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.
|
|
2112
|
+
*/
|
|
2113
|
+
declare function isExchangeAcceptorCarrier<TConn>(carrier: TAcceptorCarrier<TConn>): carrier is IExchangeAcceptorCarrier;
|
|
1463
2114
|
//#endregion
|
|
1464
|
-
//#region src/ActionRuntime/
|
|
2115
|
+
//#region src/ActionRuntime/Channel/serveChannel.d.ts
|
|
2116
|
+
interface IServeChannelOptions<TConn> {
|
|
2117
|
+
/**
|
|
2118
|
+
* Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
|
|
2119
|
+
* used to score return-path dispatch back to the right connection.
|
|
2120
|
+
*/
|
|
2121
|
+
clientEnv: RuntimeCoordinate;
|
|
2122
|
+
/**
|
|
2123
|
+
* One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins
|
|
2124
|
+
* (their keys don't collide). It is built once and shared across every carrier, so the WebSocket and the
|
|
2125
|
+
* secure-HTTP endpoint present the exact same verify/exchange keys and trust the same pinned clients.
|
|
2126
|
+
* Back it with persistent storage (e.g. a Durable Object's storage) so identity + pins survive eviction.
|
|
2127
|
+
*
|
|
2128
|
+
* Required only when at least one carrier is secure (the default). A fully-plain server (every carrier
|
|
2129
|
+
* `secure: false`) needs no storage and may omit it.
|
|
2130
|
+
*/
|
|
2131
|
+
storage?: StorageAdapter;
|
|
2132
|
+
/**
|
|
2133
|
+
* The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`.
|
|
2134
|
+
* Build them with `wsAcceptorCarrier` / `httpAcceptorCarrier`. Any number of duplex (push-capable)
|
|
2135
|
+
* carriers are supported (e.g. WebSocket + WebRTC), plus at most one exchange (request/reply) carrier;
|
|
2136
|
+
* all share one crypto identity and one runtime, and each result/push routes back over the carrier its
|
|
2137
|
+
* client connected on.
|
|
2138
|
+
*/
|
|
2139
|
+
carriers: readonly TAcceptorCarrier<TConn>[];
|
|
2140
|
+
/** Your execution handlers (e.g. the local handler holding the action cases). Registered for you. */
|
|
2141
|
+
handlers?: TActionRuntimeHandler[];
|
|
2142
|
+
/**
|
|
2143
|
+
* The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. Pass an
|
|
2144
|
+
* existing link only to share identity with acceptors built outside this call.
|
|
2145
|
+
*/
|
|
2146
|
+
link?: ClientCryptoKeyLink;
|
|
2147
|
+
/** Accepted level(s) for every carrier; defaults to negotiating any of none/authenticated/encrypted. */
|
|
2148
|
+
securityLevel?: ESecurityLevel | readonly ESecurityLevel[];
|
|
2149
|
+
/** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */
|
|
2150
|
+
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
2151
|
+
/** Timeout (ms) applied to server-initiated actions awaiting a client response. */
|
|
2152
|
+
defaultTimeout?: number;
|
|
2153
|
+
}
|
|
1465
2154
|
/**
|
|
1466
|
-
*
|
|
1467
|
-
*
|
|
1468
|
-
* the identity and, for the `encrypted` level, deriving a shared key that encrypts every action frame.
|
|
2155
|
+
* One server serving a secure channel over several carriers — the accept-in dual of `connectChannel`,
|
|
2156
|
+
* returned by {@link serveChannel}. Wire its surface straight to the host's request/socket events.
|
|
1469
2157
|
*/
|
|
1470
|
-
interface
|
|
1471
|
-
|
|
1472
|
-
|
|
2158
|
+
interface IChannelServer<TConn> {
|
|
2159
|
+
/**
|
|
2160
|
+
* The duplex acceptor handlers — one per duplex carrier, in carrier order (empty if none). For pushing,
|
|
2161
|
+
* prefer {@link pushToClient} (it resolves the owning handler); reach for these for cross-carrier work
|
|
2162
|
+
* like a per-handler `broadcast`.
|
|
2163
|
+
*/
|
|
2164
|
+
handlers: AcceptorHandler<TConn>[];
|
|
2165
|
+
/**
|
|
2166
|
+
* Unified request handler: answers the CORS preflight, performs the duplex upgrade for an upgrade
|
|
2167
|
+
* request, serves a secure-exchange action `POST`, else `404`. Forward the host's `fetch` straight to it.
|
|
2168
|
+
*/
|
|
2169
|
+
fetch: (request: Request) => Promise<Response>;
|
|
2170
|
+
/**
|
|
2171
|
+
* Convenience lifecycle for the *sole* duplex carrier — `receive` inbound frames, `drop` on close. Set
|
|
2172
|
+
* only when exactly one duplex carrier was provided; with several, feed each carrier handle directly
|
|
2173
|
+
* (every duplex carrier is its own lifecycle handle). `undefined` when there are zero or multiple.
|
|
2174
|
+
*/
|
|
2175
|
+
duplex?: IDuplexConnectionRouter<TConn>;
|
|
2176
|
+
/**
|
|
2177
|
+
* Push a server-initiated action to a connected client (the runtime is bound in, so unlike
|
|
2178
|
+
* {@link AcceptorHandler.pushToClient} you pass only the target + request). It routes through the duplex
|
|
2179
|
+
* carrier the target connected on. Throws if no duplex carrier currently holds the target.
|
|
2180
|
+
*/
|
|
2181
|
+
pushToClient: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(target: TConn | RuntimeCoordinate, request: ActionPayload_Request<DOM, ID>, options?: {
|
|
2182
|
+
timeout?: number;
|
|
2183
|
+
}) => RunningAction<DOM, ID>;
|
|
2184
|
+
}
|
|
2185
|
+
/**
|
|
2186
|
+
* Serve a secure channel over one or more carriers from a single call — the accept-in dual of
|
|
2187
|
+
* `connectChannel`. It builds the crypto identity (a {@link ClientCryptoKeyLink} + a storage-backed TOFU
|
|
2188
|
+
* resolver) and the security block (coordinate, dictionary version, accepted levels) *once* from
|
|
2189
|
+
* `(runtime, channel)` and fans them across every carrier, so the WebSocket and the secure-HTTP endpoint
|
|
2190
|
+
* can never drift apart. It registers your handlers (plus the duplex acceptor it builds) on the runtime,
|
|
2191
|
+
* wires hibernation when the duplex carrier declares it, and returns a single {@link IChannelServer} whose
|
|
2192
|
+
* `fetch` / `duplex` / `pushToClient` you forward straight to the host:
|
|
2193
|
+
* ```ts
|
|
2194
|
+
* const server = serveChannel(runtime, channel, {
|
|
2195
|
+
* clientEnv, storage,
|
|
2196
|
+
* carriers: [wsAcceptorCarrier({ send, upgrade, hibernation }), httpAcceptorCarrier()],
|
|
2197
|
+
* handlers: [localHandler],
|
|
2198
|
+
* });
|
|
2199
|
+
* // fetch(req) => server.fetch(req)
|
|
2200
|
+
* // webSocketMessage(conn, m) => server.duplex?.receive(conn, m)
|
|
2201
|
+
* // webSocketClose/Error(conn) => server.duplex?.drop(conn)
|
|
2202
|
+
* ```
|
|
2203
|
+
*
|
|
2204
|
+
* `TConn` (the live-connection token a duplex carrier hands back through `send`/`receive`/`drop`) is
|
|
2205
|
+
* inferred from the carriers — `WebSocket` for `wsAcceptorCarrier`, the data-channel type for a WebRTC
|
|
2206
|
+
* carrier, and so on — so it stays carrier-agnostic.
|
|
2207
|
+
*/
|
|
2208
|
+
declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TConn = unknown>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IServeChannelOptions<TConn>): IChannelServer<TConn>;
|
|
2209
|
+
//#endregion
|
|
2210
|
+
//#region src/ActionRuntime/Transport/SecureSession/exchangeAcceptor.d.ts
|
|
2211
|
+
/** Acceptor secure config for the exchange (HTTP) endpoint — same identity an `AcceptorHandler` uses. */
|
|
2212
|
+
interface IExchangeAcceptorSecurity {
|
|
2213
|
+
/** This acceptor's crypto identity (verify + exchange key pairs, optionally persisted). */
|
|
1473
2214
|
link: ClientCryptoKeyLink;
|
|
1474
|
-
/** This
|
|
2215
|
+
/** This acceptor's coordinate — its identity to clients during the handshake. */
|
|
1475
2216
|
localCoordinate: IRuntimeCoordinate;
|
|
1476
|
-
/** Wire dictionary version; the
|
|
2217
|
+
/** Wire dictionary version; the handshake rejects a client on a mismatch. */
|
|
1477
2218
|
dictionaryVersion: string;
|
|
2219
|
+
/** Accepted level(s) — a single level is strict, an array is a negotiable allowed set. */
|
|
2220
|
+
securityLevel: ESecurityLevel | readonly ESecurityLevel[];
|
|
2221
|
+
/** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */
|
|
2222
|
+
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
1478
2223
|
}
|
|
1479
|
-
interface
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
* `Uint8Array`/`ArrayBuffer` for optimized binary frames (e.g. msgpackr).
|
|
1484
|
-
*/
|
|
1485
|
-
outgoing: (input: ITransportRouteActionParams) => string | Uint8Array | ArrayBuffer;
|
|
1486
|
-
/**
|
|
1487
|
-
* Unpack an incoming frame back into the wire JSON object the runtime hydrates + validates.
|
|
1488
|
-
* Return `undefined` to defer to the connection's built-in JSON parser — this is how binary
|
|
1489
|
-
* adapters stay backward compatible with plain-JSON clients on the same socket.
|
|
1490
|
-
*/
|
|
1491
|
-
incoming?: (input: string | ArrayBuffer | Uint8Array | Blob) => TActionPayload_Any_JsonObject<any, any> | undefined;
|
|
1492
|
-
};
|
|
1493
|
-
ws: WebSocket;
|
|
1494
|
-
/** Optional authenticated/encrypted channel; the connection runs the handshake during init. */
|
|
1495
|
-
secureChannel?: IWsClientSecureChannel;
|
|
2224
|
+
interface IExchangeAcceptorConfig {
|
|
2225
|
+
security: IExchangeAcceptorSecurity;
|
|
2226
|
+
/** The runtime that executes an inbound action wire and produces its result. */
|
|
2227
|
+
runtime: ActionRuntime;
|
|
1496
2228
|
}
|
|
1497
|
-
interface IActionTransportInitialized_Ws extends IActionTransportInitialized<ITransportRouteActionParams, IActionTransportReadyData_Ws> {}
|
|
1498
|
-
interface IActionTransportDef_Ws extends IActionTransportDef<ETransportType.ws, IActionTransportInitialized_Ws> {}
|
|
1499
|
-
//#endregion
|
|
1500
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/createBinaryWsAdapter.d.ts
|
|
1501
2229
|
/**
|
|
1502
|
-
*
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1505
|
-
*
|
|
1506
|
-
* (
|
|
1507
|
-
*
|
|
1508
|
-
* No validation runs here: `incoming` blindly reconstructs the wire JSON shape and hands it back to
|
|
1509
|
-
* the connection, which flows into `ActionRuntime` → `domain.hydrateAnyAction()` where the Valibot
|
|
1510
|
-
* schemas validate it exactly as they would for a JSON frame.
|
|
1511
|
-
*
|
|
1512
|
-
* Both ends of the socket MUST construct the adapter with the same domains in the same order — the
|
|
1513
|
-
* integer dictionary is positional. Mismatched dictionaries will route to the wrong action.
|
|
2230
|
+
* Acceptor (accept-in) side of the secure exchange protocol — the HTTP counterpart to
|
|
2231
|
+
* {@link AcceptorSecureSession}. Each POST body is one {@link decodeExchangeRequest} envelope; the
|
|
2232
|
+
* acceptor drives the server handshake over the two `hs` POSTs (correlated by `hsid`, since stateless
|
|
2233
|
+
* requests can't rely on channel ordering), mints a session **token** on accept, and on every later `act`
|
|
2234
|
+
* POST resolves the session by token, decrypts the body (at `encrypted`), routes it through the runtime,
|
|
2235
|
+
* and returns the (encrypted) result inline as the reply.
|
|
1514
2236
|
*
|
|
1515
|
-
*
|
|
1516
|
-
*
|
|
2237
|
+
* Sessions and in-flight handshakes are held in memory — fine for a single-instance server. (Surviving a
|
|
2238
|
+
* Durable-Object eviction would persist each token's `keyMaterial` and re-derive the key on a miss, the
|
|
2239
|
+
* same primitive `AcceptorSecureSession.rehydrate` uses; left as a follow-up.)
|
|
1517
2240
|
*/
|
|
1518
|
-
declare
|
|
2241
|
+
declare class ExchangeAcceptor {
|
|
2242
|
+
private readonly _security;
|
|
2243
|
+
private readonly _runtime;
|
|
2244
|
+
private readonly _allowedLevels;
|
|
2245
|
+
private readonly _noneAllowed;
|
|
2246
|
+
private readonly _pendingHandshakes;
|
|
2247
|
+
private readonly _sessions;
|
|
2248
|
+
constructor(config: IExchangeAcceptorConfig);
|
|
2249
|
+
/** Process one POST body (an exchange envelope), returning the reply body to send back. */
|
|
2250
|
+
handlePost(body: string): Promise<string>;
|
|
2251
|
+
private _handleHandshake;
|
|
2252
|
+
private _handleAction;
|
|
2253
|
+
private _err;
|
|
2254
|
+
}
|
|
1519
2255
|
//#endregion
|
|
1520
|
-
//#region src/ActionRuntime/Handler/
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
2256
|
+
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/createActionFetchHandler.d.ts
|
|
2257
|
+
interface IActionFetchHandlerOptions {
|
|
2258
|
+
/**
|
|
2259
|
+
* CORS headers merged onto every response (a preflight `OPTIONS` is answered `204` with them).
|
|
2260
|
+
* Defaults to permissive `*`; pass `false` to attach no CORS headers at all.
|
|
2261
|
+
*/
|
|
2262
|
+
cors?: Record<string, string> | false;
|
|
2263
|
+
/** Which requests carry an action wire on `POST`. Default: pathname ends with `/action`. */
|
|
2264
|
+
isActionPath?: (url: URL) => boolean;
|
|
2265
|
+
/** Which requests are WebSocket upgrades. Default: pathname ends with `/ws`. */
|
|
2266
|
+
isWebSocketPath?: (url: URL) => boolean;
|
|
2267
|
+
/**
|
|
2268
|
+
* Whether a request is a WebSocket upgrade for this endpoint, given the whole request (not just the
|
|
2269
|
+
* URL). When set it *replaces* the default gate (an `Upgrade: websocket` header on an
|
|
2270
|
+
* {@link isWebSocketPath} match) — use it when the discriminant needs a header or method, not only the
|
|
2271
|
+
* path. Only consulted when {@link onWebSocketUpgrade} is present.
|
|
2272
|
+
*/
|
|
2273
|
+
isWebSocketUpgrade?: (request: Request, url: URL) => boolean;
|
|
2274
|
+
/**
|
|
2275
|
+
* Perform the transport-specific WebSocket upgrade (e.g. a Durable Object's
|
|
2276
|
+
* `new WebSocketPair()` + `ctx.acceptWebSocket()` returning a `101`). Omit for HTTP-only endpoints.
|
|
2277
|
+
* Its response is returned as-is — a `101` upgrade carries no CORS headers.
|
|
2278
|
+
*/
|
|
2279
|
+
onWebSocketUpgrade?: (request: Request, url: URL) => Response | Promise<Response>;
|
|
2280
|
+
/** Forwarded to `ActionPayload_Result.toHttpResponse` — use the error's HTTP status (default true). */
|
|
2281
|
+
useErrorStatus?: boolean;
|
|
2282
|
+
/**
|
|
2283
|
+
* Enable the secure exchange protocol (handshake + token sessions + body encryption) on the `/action`
|
|
2284
|
+
* endpoint, mirroring an `AcceptorHandler`'s `security`. The matching connector is
|
|
2285
|
+
* `secureTransport({ carrier: httpCarrier(...), securityLevel })`. When omitted, the endpoint speaks
|
|
2286
|
+
* today's plain protocol (the raw action wire is POSTed and the result is the response body).
|
|
2287
|
+
*/
|
|
2288
|
+
security?: IExchangeAcceptorSecurity;
|
|
1525
2289
|
}
|
|
1526
2290
|
/**
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1529
|
-
*
|
|
1530
|
-
*
|
|
1531
|
-
*
|
|
1532
|
-
* On top of everything {@link createBinaryWsAdapter} drops, a session also drops:
|
|
1533
|
-
* - **`cuid`** — replaced by a per-connection integer correlation id. The initiator maps it to its
|
|
1534
|
-
* real cuid; the responder echoes it; each side reconstructs the cuid from its own map. Correlation
|
|
1535
|
-
* only needs to be unique per socket, so a counter suffices.
|
|
1536
|
-
* - **`originClient` after the first request** — the first request each side sends carries its
|
|
1537
|
-
* identity; the peer remembers it and injects it into later frames. Replies omit it entirely (a
|
|
1538
|
-
* reply carries the initiator's own origin, which the initiator already knows).
|
|
1539
|
-
*
|
|
1540
|
-
* Both ends MUST build the factory from the same domains in the same order (positional dictionary).
|
|
1541
|
-
* Text frames still return `undefined` from `incoming`, so JSON clients remain interoperable.
|
|
2291
|
+
* Build the `fetch` handler a server/Durable-Object exposes for action traffic, folding in the
|
|
2292
|
+
* boilerplate every endpoint repeats: CORS (incl. the `OPTIONS` preflight), routing the `/action`
|
|
2293
|
+
* `POST` body through the runtime (`handleActionPayloadWire` → `waitForResultPayload` →
|
|
2294
|
+
* `toHttpResponse`), an optional WebSocket-upgrade hook, and a `404` fallback.
|
|
1542
2295
|
*
|
|
1543
|
-
*
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1546
|
-
*
|
|
2296
|
+
* It only touches web-standard `Request`/`Response`, so it stays transport-agnostic — the one
|
|
2297
|
+
* environment-specific bit (the WS upgrade) is injected via {@link IActionFetchHandlerOptions.onWebSocketUpgrade}:
|
|
2298
|
+
* ```ts
|
|
2299
|
+
* this.fetchHandler = createActionFetchHandler(this.runtime, {
|
|
2300
|
+
* onWebSocketUpgrade: () => {
|
|
2301
|
+
* const pair = new WebSocketPair();
|
|
2302
|
+
* this.ctx.acceptWebSocket(pair[1]);
|
|
2303
|
+
* return new Response(null, { status: 101, webSocket: pair[0] });
|
|
2304
|
+
* },
|
|
2305
|
+
* });
|
|
2306
|
+
* // async fetch(request) { return this.fetchHandler(request); }
|
|
2307
|
+
* ```
|
|
1547
2308
|
*/
|
|
1548
|
-
declare function
|
|
2309
|
+
declare function createActionFetchHandler(runtime: ActionRuntime, options?: IActionFetchHandlerOptions): (request: Request) => Promise<Response>;
|
|
1549
2310
|
//#endregion
|
|
1550
|
-
//#region src/ActionRuntime/Handler/
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
private _liveSocketUrl?;
|
|
1555
|
-
/** Sockets we closed on purpose (via `disconnect`), so their `close` event stays quiet. */
|
|
1556
|
-
private _intentionalCloses;
|
|
1557
|
-
constructor(def: Omit<IActionTransportDef_Ws, "type">, resolvers?: IActionTransportResolvers);
|
|
1558
|
-
protected _getCacheKey(_input: ITransportRouteActionParams): string;
|
|
1559
|
-
protected _processTransportStatus(input: ITransportRouteActionParams): TTransportStatusInfo<IActionTransportReadyData_Methods>;
|
|
1560
|
-
getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo | undefined;
|
|
1561
|
-
private _isSecure;
|
|
1562
|
-
private _awaitOpen;
|
|
1563
|
-
/** Non-secure connections finalize synchronously; secure ones run the handshake first. */
|
|
1564
|
-
private _finalize;
|
|
1565
|
-
_finalizeTransportMethods(wsData: IActionTransportReadyData_Ws): IActionTransportReadyData_Methods;
|
|
1566
|
-
/**
|
|
1567
|
-
* Secure path: a single message listener feeds the handshake until it completes, then routes action
|
|
1568
|
-
* frames (decrypting for the `encrypted` level). Frames that arrive in the gap between accept and
|
|
1569
|
-
* activation are buffered and flushed, so nothing is lost.
|
|
1570
|
-
*/
|
|
1571
|
-
private _finalizeSecureMethods;
|
|
1572
|
-
private _runClientHandshake;
|
|
1573
|
-
private _buildSendMethods;
|
|
1574
|
-
/** Decode (and, when encrypted, decrypt) one inbound action frame and hand it to the runtime. */
|
|
1575
|
-
private _handleIncomingActionFrame;
|
|
1576
|
-
/** Accept text + binary frames (ArrayBuffer / Uint8Array / Blob); Blobs are converted to a buffer. */
|
|
1577
|
-
private _normalizeFrame;
|
|
1578
|
-
private _captureSocketUrl;
|
|
1579
|
-
private _attachLifecycle;
|
|
1580
|
-
private _abortAll;
|
|
1581
|
-
}
|
|
1582
|
-
//#endregion
|
|
1583
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketTransport.d.ts
|
|
1584
|
-
interface IWebSocketTransportSharedOptions {
|
|
1585
|
-
/** Custom (de)serialization of action payloads on the wire (shared across all sockets). */
|
|
1586
|
-
formatMessage?: IActionTransportReadyData_Ws["formatMessage"];
|
|
1587
|
-
/**
|
|
1588
|
-
* Per-socket codec factory — called once for each socket so stateful codecs (e.g. the
|
|
1589
|
-
* `createBinaryWsSessionFactory` session, which holds per-connection correlation + identity state)
|
|
1590
|
-
* get their own instance and reset cleanly on reconnect. Takes precedence over `formatMessage`.
|
|
1591
|
-
*/
|
|
1592
|
-
createFormatMessage?: () => IActionTransportReadyData_Ws["formatMessage"];
|
|
1593
|
-
updateRunConfig?: TUpdateActionRunConfig;
|
|
2311
|
+
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/createSecureActionServer.d.ts
|
|
2312
|
+
interface ISecureAcceptorHandlerOptions<TConn> {
|
|
2313
|
+
/** The shared channel identity (codec + dictionary version) — same one the clients use. */
|
|
2314
|
+
channel: ISecureChannel;
|
|
1594
2315
|
/**
|
|
1595
|
-
*
|
|
1596
|
-
*
|
|
2316
|
+
* Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
|
|
2317
|
+
* used to route results/pushes back over this handler.
|
|
1597
2318
|
*/
|
|
1598
|
-
|
|
1599
|
-
/**
|
|
1600
|
-
|
|
2319
|
+
clientEnv: RuntimeCoordinate;
|
|
2320
|
+
/** This server's runtime — its coordinate is the server identity presented in the handshake. */
|
|
2321
|
+
runtime: ActionRuntime;
|
|
1601
2322
|
/**
|
|
1602
|
-
*
|
|
1603
|
-
*
|
|
2323
|
+
* One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins.
|
|
2324
|
+
* Their keys don't collide, so a single adapter is enough; back it with persistent storage (e.g. a
|
|
2325
|
+
* Durable Object's storage) so identity and pins survive eviction.
|
|
1604
2326
|
*/
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
/** Open (or reuse) the WebSocket for an action — keep it simple or derive it per action. */
|
|
1609
|
-
createWebSocket: (input: ITransportRouteActionParams) => WebSocket;
|
|
1610
|
-
}
|
|
1611
|
-
interface IWebSocketTransportAdvancedOptions extends IWebSocketTransportSharedOptions {
|
|
2327
|
+
storageAdapter: StorageAdapter;
|
|
2328
|
+
/** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
|
|
2329
|
+
send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
|
|
1612
2330
|
/**
|
|
1613
|
-
*
|
|
1614
|
-
*
|
|
1615
|
-
*
|
|
1616
|
-
*
|
|
2331
|
+
* The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storageAdapter`.
|
|
2332
|
+
* Pass an existing link to share one identity across several acceptors on the same server (e.g. a
|
|
2333
|
+
* WebSocket acceptor and a secure-HTTP {@link createActionFetchHandler}), so they present the same
|
|
2334
|
+
* verify/exchange keys — avoiding a divergent-key race when two fresh links initialize concurrently.
|
|
1617
2335
|
*/
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
/**
|
|
1626
|
-
* Reusable WebSocket transport definition. Create one with `WebSocketTransport.create({ createWebSocket })`
|
|
1627
|
-
* for the common case, or `WebSocketTransport.createAdvanced({ getTransport })` for full control over
|
|
1628
|
-
* readiness. The underlying socket is cached (via `getTransportCacheKey`) and reused across actions.
|
|
1629
|
-
*/
|
|
1630
|
-
declare class WebSocketTransport extends Transport<ETransportType.ws> {
|
|
1631
|
-
private readonly options;
|
|
1632
|
-
readonly type = ETransportType.ws;
|
|
1633
|
-
constructor(options: TWebSocketTransportOptions);
|
|
1634
|
-
static create(options: IWebSocketTransportSocketOptions): WebSocketTransport;
|
|
1635
|
-
static createAdvanced(options: IWebSocketTransportAdvancedOptions): WebSocketTransport;
|
|
1636
|
-
_createConnection(ctx: ITransportConnectionContext): WebSocketConnection;
|
|
1637
|
-
getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
|
|
1638
|
-
}
|
|
1639
|
-
//#endregion
|
|
1640
|
-
//#region src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/secureWsChannel.d.ts
|
|
1641
|
-
/** The per-connection binary session codec — built once per socket from the channel's domains. */
|
|
1642
|
-
type TChannelCodec = NonNullable<IActionTransportReadyData_Ws["formatMessage"]>;
|
|
1643
|
-
/**
|
|
1644
|
-
* The shared identity of a secure WebSocket channel: the wire dictionary version both ends check
|
|
1645
|
-
* during the handshake, plus the per-connection codec factory both ends build from the *same* domain
|
|
1646
|
-
* list. Define it once (typically in code shared by client and server) and hand it to
|
|
1647
|
-
* {@link createSecureWebSocketTransport} on the client and `createSecureActionServerHandler` on the
|
|
1648
|
-
* server, so the codec and version can never drift apart.
|
|
1649
|
-
*/
|
|
1650
|
-
interface ISecureWsChannel {
|
|
1651
|
-
/** Wire dictionary version — derived from the domains by default; the handshake rejects a mismatch. */
|
|
1652
|
-
dictionaryVersion: string;
|
|
1653
|
-
/** Per-connection session codec factory (call once per live connection). */
|
|
1654
|
-
createCodec: () => TChannelCodec;
|
|
1655
|
-
}
|
|
1656
|
-
/**
|
|
1657
|
-
* Bundle a secure channel's shared identity from its transported domains. Both ends MUST call this
|
|
1658
|
-
* with the same domains in the same order (the binary wire dictionary is positional). The
|
|
1659
|
-
* `dictionaryVersion` is derived from those domains unless you pin an explicit one.
|
|
1660
|
-
*/
|
|
1661
|
-
declare function defineSecureWsChannel(options: {
|
|
1662
|
-
/** Domains transported over this channel, in a stable order. Add new ones to the *end*. */domains: ActionDomain<any>[]; /** Pin a human-readable version instead of the derived hash (must match on both ends). */
|
|
1663
|
-
dictionaryVersion?: string; /** Tuning for the per-connection binary session (e.g. correlation TTL). */
|
|
1664
|
-
sessionOptions?: IBinaryWsSessionOptions;
|
|
1665
|
-
}): ISecureWsChannel;
|
|
1666
|
-
interface ISecureWebSocketTransportOptions {
|
|
1667
|
-
/** The shared channel identity (codec + dictionary version). */
|
|
1668
|
-
channel: ISecureWsChannel;
|
|
1669
|
-
/** This client's runtime — its coordinate is the authenticated identity sent in the handshake. */
|
|
1670
|
-
runtime: ActionRuntime;
|
|
1671
|
-
/** Backing store for this client's crypto identity (a stable verify key across reloads). */
|
|
1672
|
-
storageAdapter: StorageAdapter;
|
|
1673
|
-
/** The level this client requests; the server must allow it. */
|
|
1674
|
-
securityLevel: ESecurityLevel;
|
|
1675
|
-
/** Endpoint URL — drives both the socket and the per-endpoint cache key. */
|
|
1676
|
-
url: string;
|
|
1677
|
-
/** Override socket creation (defaults to a `new WebSocket(url)` with `binaryType = "arraybuffer"`). */
|
|
1678
|
-
createWebSocket?: (input: ITransportRouteActionParams) => WebSocket;
|
|
1679
|
-
/** Override the reuse key (defaults to `[url]`, so one socket is shared per endpoint). */
|
|
1680
|
-
getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
1681
|
-
updateRunConfig?: TUpdateActionRunConfig;
|
|
1682
|
-
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
2336
|
+
link?: ClientCryptoKeyLink;
|
|
2337
|
+
/** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */
|
|
2338
|
+
securityLevel?: ESecurityLevel | readonly ESecurityLevel[];
|
|
2339
|
+
/** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storageAdapter`. */
|
|
2340
|
+
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
2341
|
+
/** Timeout (ms) applied to server-initiated actions awaiting a client response. */
|
|
2342
|
+
defaultTimeout?: number;
|
|
1683
2343
|
}
|
|
1684
2344
|
/**
|
|
1685
|
-
* Build
|
|
1686
|
-
* creates the {@link ClientCryptoKeyLink}
|
|
1687
|
-
* `
|
|
1688
|
-
*
|
|
1689
|
-
*
|
|
2345
|
+
* Build an {@link AcceptorHandler} for the secure binary channel with the boilerplate folded in:
|
|
2346
|
+
* it creates the {@link ClientCryptoKeyLink} and the storage-backed TOFU resolver from a single
|
|
2347
|
+
* `storageAdapter`, installs the channel's per-connection codec, and assembles the `security` block
|
|
2348
|
+
* from the runtime coordinate + channel version (accepting all three levels by default).
|
|
2349
|
+
*
|
|
2350
|
+
* For a hibernatable transport (e.g. a Durable Object), pair it with
|
|
2351
|
+
* {@link createHibernatableWsServerAdapter} to wire persistence + replay.
|
|
1690
2352
|
*/
|
|
1691
|
-
declare function
|
|
2353
|
+
declare function createSecureAcceptorHandler<TConn = unknown>(options: ISecureAcceptorHandlerOptions<TConn>): AcceptorHandler<TConn>;
|
|
1692
2354
|
//#endregion
|
|
1693
|
-
//#region src/ActionRuntime/Handler/
|
|
2355
|
+
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore.d.ts
|
|
1694
2356
|
/**
|
|
1695
2357
|
* The composite value persisted to a connection's attachment: the consumer's own app state plus the
|
|
1696
|
-
* {@link
|
|
2358
|
+
* {@link AcceptorHandler} routing binding. Co-storing them in one slot means a transport whose
|
|
1697
2359
|
* sockets outlive process eviction (e.g. a Durable Object's hibernatable WebSocket) recovers both the
|
|
1698
2360
|
* application identity *and* the action routing from a single attachment after a wake — no storage reads.
|
|
1699
2361
|
*/
|
|
1700
2362
|
interface IConnectionAttachment<TApp> {
|
|
1701
2363
|
app?: TApp;
|
|
1702
|
-
binding?:
|
|
2364
|
+
binding?: IAcceptorConnectionBinding;
|
|
1703
2365
|
}
|
|
1704
|
-
interface
|
|
2366
|
+
interface IConnectionStateStoreOptions<TConn, TApp> {
|
|
1705
2367
|
/** Read a connection's raw attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
|
|
1706
2368
|
read: (connection: TConn) => unknown;
|
|
1707
2369
|
/** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
|
|
1708
2370
|
write: (connection: TConn, value: IConnectionAttachment<TApp>) => void;
|
|
1709
2371
|
/**
|
|
1710
2372
|
* All currently-live connections (e.g. `() => ctx.getWebSockets()`). Used to replay routing bindings
|
|
1711
|
-
* after a wake (via {@link
|
|
1712
|
-
* {@link
|
|
2373
|
+
* after a wake (via {@link createConnectionStateStore}) and to enumerate app state in
|
|
2374
|
+
* {@link ConnectionStateStore.entries}.
|
|
1713
2375
|
*/
|
|
1714
2376
|
getConnections: () => TConn[];
|
|
1715
2377
|
/**
|
|
@@ -1720,13 +2382,17 @@ interface IWsConnectionStateStoreOptions<TConn, TApp> {
|
|
|
1720
2382
|
schema?: StandardSchemaV1<unknown, TApp>;
|
|
1721
2383
|
}
|
|
1722
2384
|
/**
|
|
1723
|
-
* A typed per-connection state store that co-owns the app state and the
|
|
2385
|
+
* A typed per-connection state store that co-owns the app state and the acceptor handler's routing
|
|
1724
2386
|
* binding in one attachment, so neither the consumer nor the handler has to hand-merge the two. Create
|
|
1725
|
-
* it through {@link
|
|
1726
|
-
*
|
|
2387
|
+
* it through {@link createConnectionStateStore} (which also wires binding persistence and replays
|
|
2388
|
+
* surviving connections after a wake), then `get`/`set`/`clearApp` the app state directly.
|
|
2389
|
+
*
|
|
2390
|
+
* The mechanism is carrier-neutral — it only needs read/write/enumerate callbacks for the connection's
|
|
2391
|
+
* attachment — but it pays off on transports whose connections outlive process eviction (e.g. a
|
|
2392
|
+
* Durable Object's hibernatable WebSockets), which is why it lives beside the hibernation adapter.
|
|
1727
2393
|
*
|
|
1728
2394
|
* ```ts
|
|
1729
|
-
* const players = serverHandler
|
|
2395
|
+
* const players = createConnectionStateStore(serverHandler, {
|
|
1730
2396
|
* schema: vs_player,
|
|
1731
2397
|
* read: (ws) => ws.deserializeAttachment(),
|
|
1732
2398
|
* write: (ws, v) => ws.serializeAttachment(v),
|
|
@@ -1736,9 +2402,9 @@ interface IWsConnectionStateStoreOptions<TConn, TApp> {
|
|
|
1736
2402
|
* const player = players.get(ws);
|
|
1737
2403
|
* ```
|
|
1738
2404
|
*/
|
|
1739
|
-
declare class
|
|
2405
|
+
declare class ConnectionStateStore<TConn, TApp> {
|
|
1740
2406
|
private readonly options;
|
|
1741
|
-
constructor(options:
|
|
2407
|
+
constructor(options: IConnectionStateStoreOptions<TConn, TApp>);
|
|
1742
2408
|
/** The validated app state for a connection, or `null` if unset / invalid. */
|
|
1743
2409
|
get(connection: TConn): TApp | null;
|
|
1744
2410
|
/** Set the app state, preserving the runtime binding already pinned to the connection. */
|
|
@@ -1748,356 +2414,517 @@ declare class WsConnectionStateStore<TConn, TApp> {
|
|
|
1748
2414
|
/** Every live connection paired with its (validated) app state — for rebuilding in-memory state after a wake. */
|
|
1749
2415
|
entries(): [TConn, TApp | null][];
|
|
1750
2416
|
/** @internal Persist a freshly-bound connection's binding, preserving any app state already stored. */
|
|
1751
|
-
_persistBinding(connection: TConn, binding:
|
|
2417
|
+
_persistBinding(connection: TConn, binding: IAcceptorConnectionBinding): void;
|
|
1752
2418
|
/** @internal The persisted binding for a connection, if any (used to replay routing after a wake). */
|
|
1753
|
-
_readBinding(connection: TConn):
|
|
2419
|
+
_readBinding(connection: TConn): IAcceptorConnectionBinding | undefined;
|
|
1754
2420
|
private _readAttachment;
|
|
1755
2421
|
private _validateApp;
|
|
1756
2422
|
}
|
|
2423
|
+
/**
|
|
2424
|
+
* Build a per-connection {@link ConnectionStateStore} bound to an {@link AcceptorHandler}: it registers
|
|
2425
|
+
* itself as the handler's connection-bound persistence callback (so bindings are written without
|
|
2426
|
+
* overwriting app state) and immediately replays every live connection's stored binding via
|
|
2427
|
+
* {@link AcceptorHandler.rehydrateConnection} — so on a transport that resumes after eviction (e.g. a
|
|
2428
|
+
* Durable Object waking from hibernation) both the app identity and the action routing come back from a
|
|
2429
|
+
* single attachment, with no storage reads and no hand-rolled merge.
|
|
2430
|
+
*
|
|
2431
|
+
* Lives outside the handler so the generic {@link AcceptorHandler} stays free of any attachment/
|
|
2432
|
+
* hibernation concern — it exposes only the neutral `setOnConnectionBound` + `rehydrateConnection`
|
|
2433
|
+
* hooks this builder drives.
|
|
2434
|
+
*/
|
|
2435
|
+
declare function createConnectionStateStore<TConn, TApp>(handler: AcceptorHandler<TConn>, options: IConnectionStateStoreOptions<TConn, TApp>): ConnectionStateStore<TConn, TApp>;
|
|
1757
2436
|
//#endregion
|
|
1758
|
-
//#region src/ActionRuntime/Handler/
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
2437
|
+
//#region src/ActionRuntime/Handler/PeerLink/Connector/err_nice_external_client.d.ts
|
|
2438
|
+
declare const err_nice_external_client: import("@nice-code/error").NiceErrorDomain<{
|
|
2439
|
+
domain: string;
|
|
2440
|
+
allDomains: [string, string, "err_nice"];
|
|
2441
|
+
schema: {};
|
|
2442
|
+
}>;
|
|
2443
|
+
//#endregion
|
|
2444
|
+
//#region src/ActionRuntime/Transport/Carrier/duplex/inMemory/createInMemoryChannel.d.ts
|
|
2445
|
+
type TFrame = string | ArrayBuffer | Uint8Array;
|
|
2446
|
+
/** The peer (server) end of an in-memory pair — what you feed into an `AcceptorHandler`. */
|
|
2447
|
+
interface IInMemoryServerEndpoint {
|
|
2448
|
+
/** Write a frame back to the client end. */
|
|
2449
|
+
send(frame: TFrame): void;
|
|
2450
|
+
/** Register the inbound handler (frames the client sent). */
|
|
2451
|
+
onMessage(handler: (frame: TFrame) => void): void;
|
|
2452
|
+
/** Close the pair from this end. */
|
|
2453
|
+
close(): void;
|
|
2454
|
+
/** Notified when the pair closes (from either end). */
|
|
2455
|
+
onClose(handler: () => void): void;
|
|
2456
|
+
}
|
|
2457
|
+
interface IInMemoryChannelPair {
|
|
2458
|
+
/** The client end — pass as a {@link IDuplexCarrier} to a `LinkTransport`. */
|
|
2459
|
+
clientChannel: IDuplexCarrier;
|
|
2460
|
+
/** The server end — wire into an `AcceptorHandler` (`send` + `receive`). */
|
|
2461
|
+
serverEndpoint: IInMemoryServerEndpoint;
|
|
1778
2462
|
}
|
|
1779
2463
|
/**
|
|
1780
|
-
*
|
|
1781
|
-
* {@link
|
|
1782
|
-
*
|
|
1783
|
-
*
|
|
1784
|
-
*
|
|
2464
|
+
* Two cross-wired in-process byte channels — a loopback carrier with no socket. The client end is a
|
|
2465
|
+
* {@link IDuplexCarrier} you hand to a {@link LinkTransport}; the server end plugs into an
|
|
2466
|
+
* `AcceptorHandler` (`send: (_, f) => serverEndpoint.send(f)`, and `serverEndpoint.onMessage(f =>
|
|
2467
|
+
* handler.receive(conn, f))`). Frames are delivered on a microtask, so each side observes the other
|
|
2468
|
+
* asynchronously — exactly like a real transport — which makes this ideal for tests and for running
|
|
2469
|
+
* two runtimes in one process (or proving a non-WS carrier end to end).
|
|
1785
2470
|
*/
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
link: ClientCryptoKeyLink;
|
|
1795
|
-
/** This server's coordinate — its identity to clients during the handshake. */
|
|
1796
|
-
localCoordinate: IRuntimeCoordinate;
|
|
1797
|
-
/** Wire dictionary version; the handshake rejects a client on a mismatch. */
|
|
1798
|
-
dictionaryVersion: string;
|
|
1799
|
-
/** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */
|
|
1800
|
-
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
1801
|
-
}
|
|
1802
|
-
interface IActionServerHandlerBaseOptions<TConn> {
|
|
1803
|
-
/**
|
|
1804
|
-
* Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`).
|
|
1805
|
-
* The runtime's return-path dispatch scores incoming actions' `originClient` against this to pick
|
|
1806
|
-
* this handler for sending results/pushes back over the right channel.
|
|
1807
|
-
*/
|
|
1808
|
-
clientEnv: RuntimeCoordinate;
|
|
1809
|
-
/** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
|
|
1810
|
-
send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;
|
|
1811
|
-
/**
|
|
1812
|
-
* The runtime this handler belongs to. When set, {@link ActionServerHandler.broadcast} can be called
|
|
1813
|
-
* without threading a runtime through each call. Optional — `pushToClient` still takes one explicitly.
|
|
1814
|
-
*/
|
|
1815
|
-
runtime?: ActionRuntime;
|
|
1816
|
-
/** Timeout (ms) applied to server-initiated actions awaiting a client response. */
|
|
1817
|
-
defaultTimeout?: number;
|
|
1818
|
-
/**
|
|
1819
|
-
* Called once when a connection is first bound to a client identity. Use it to persist the binding
|
|
1820
|
-
* for transports that can resume after eviction — e.g. a Durable Object's hibernatable WebSocket:
|
|
1821
|
-
* `(ws, binding) => ws.serializeAttachment(binding)` — then replay it via {@link ActionServerHandler.rehydrateConnection}
|
|
1822
|
-
* when the channel comes back.
|
|
1823
|
-
*/
|
|
1824
|
-
onConnectionBound?: (connection: TConn, binding: IActionServerConnectionBinding) => void;
|
|
1825
|
-
/**
|
|
1826
|
-
* Enable the authenticated (optionally encrypted) handshake. When omitted, connections are trusted
|
|
1827
|
-
* as-is (identity self-asserted) — fine for dev / trusted networks.
|
|
1828
|
-
*/
|
|
1829
|
-
security?: IActionServerSecurity;
|
|
2471
|
+
declare function createInMemoryChannelPair(): IInMemoryChannelPair;
|
|
2472
|
+
//#endregion
|
|
2473
|
+
//#region src/ActionRuntime/Transport/Carrier/duplex/inMemory/inMemoryCarrier.d.ts
|
|
2474
|
+
interface IInMemoryCarrier {
|
|
2475
|
+
/** The connector end — pass as the `carrier` to {@link secureTransport} / `LinkTransport`. */
|
|
2476
|
+
carrier: IDuplexCarrierSource;
|
|
2477
|
+
/** The acceptor end — wire into an `AcceptorHandler` (`send` + `receive`). */
|
|
2478
|
+
serverEndpoint: IInMemoryServerEndpoint;
|
|
1830
2479
|
}
|
|
1831
2480
|
/**
|
|
1832
|
-
*
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
* leanest binary wire; the handler creates and caches one codec per connection.
|
|
2481
|
+
* A loopback duplex carrier with no socket — two cross-wired in-process ends. The connector end is an
|
|
2482
|
+
* {@link IDuplexCarrierSource} for {@link secureTransport}; the acceptor end plugs into an
|
|
2483
|
+
* `AcceptorHandler`. Ideal for tests and for running two runtimes in one process, or proving a
|
|
2484
|
+
* non-WS carrier end to end.
|
|
1837
2485
|
*/
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
} | {
|
|
1842
|
-
createFormatMessage: () => TActionChannelFormatMessage;
|
|
1843
|
-
formatMessage?: never;
|
|
1844
|
-
});
|
|
2486
|
+
declare function inMemoryCarrier(): IInMemoryCarrier;
|
|
2487
|
+
//#endregion
|
|
2488
|
+
//#region src/ActionRuntime/Transport/Carrier/duplex/rtc/rtcDataChannelByteChannel.d.ts
|
|
1845
2489
|
/**
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
1848
|
-
*
|
|
1849
|
-
* a result payload, or nothing (auto-wrapped as an empty success) — exactly like a local handler case.
|
|
2490
|
+
* The slice of the `RTCDataChannel` surface this adapter uses — declared structurally so it accepts the
|
|
2491
|
+
* browser `RTCDataChannel`, React-Native (`react-native-webrtc`), or any node-webrtc shim without a hard
|
|
2492
|
+
* dependency on a specific DOM/RN type. A real `RTCDataChannel` satisfies it as-is.
|
|
1850
2493
|
*/
|
|
1851
|
-
|
|
2494
|
+
interface IRtcDataChannelLike {
|
|
2495
|
+
readyState: string;
|
|
2496
|
+
binaryType: string;
|
|
2497
|
+
label?: string;
|
|
2498
|
+
send(data: string | ArrayBuffer | ArrayBufferView): void;
|
|
2499
|
+
close(): void;
|
|
2500
|
+
addEventListener(type: string, listener: (event: any) => void, options?: unknown): void;
|
|
2501
|
+
}
|
|
1852
2502
|
/**
|
|
1853
|
-
*
|
|
1854
|
-
* (
|
|
1855
|
-
*
|
|
1856
|
-
*
|
|
1857
|
-
* Add it alongside your local execution handler:
|
|
1858
|
-
* ```ts
|
|
1859
|
-
* const serverHandler = createServerHandler({ clientEnv, formatMessage, send: (ws, f) => ws.send(f) });
|
|
1860
|
-
* runtime.addHandlers([localHandler, serverHandler]);
|
|
1861
|
-
* // per inbound message (e.g. a Durable Object's webSocketMessage):
|
|
1862
|
-
* serverHandler.receive(ws, message);
|
|
1863
|
-
* ```
|
|
1864
|
-
*
|
|
1865
|
-
* Inbound requests route to your local handler; the runtime's return dispatch then calls this
|
|
1866
|
-
* handler back (it is an external handler keyed to `clientEnv`) to send the result to the originating
|
|
1867
|
-
* connection. The handler keeps a per-connection identity registry so each result lands on the right
|
|
1868
|
-
* socket, and remembers each connection's encoding so binary and JSON clients can share the channel.
|
|
2503
|
+
* Adapt a WebRTC `RTCDataChannel` to the carrier-agnostic {@link IDuplexCarrier}, so two browsers
|
|
2504
|
+
* (or two mobile apps) linked peer-to-peer — no server in the middle — run the *same* secure session as
|
|
2505
|
+
* a WebSocket. Hand it to `createSecureLinkTransport({ openChannel: () => rtcDataChannelByteChannel(dc) })`.
|
|
1869
2506
|
*
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
2507
|
+
* The data channel must already be created (its negotiation/signaling is the app's concern); this only
|
|
2508
|
+
* drives bytes over it. Binary frames are requested as `ArrayBuffer` so the binary session codec unpacks
|
|
2509
|
+
* them synchronously; a `Blob` (if the channel hands one back) is normalized to a buffer.
|
|
1872
2510
|
*/
|
|
1873
|
-
declare
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
setOnConnectionBound(onConnectionBound: (connection: TConn, binding: IActionServerConnectionBinding) => void): void;
|
|
1910
|
-
/**
|
|
1911
|
-
* Create a typed per-connection state store that co-owns the consumer's app state and this handler's
|
|
1912
|
-
* routing binding in one attachment. It registers itself as the connection-bound persistence callback
|
|
1913
|
-
* (so bindings are written without overwriting app state) and immediately replays every live
|
|
1914
|
-
* connection's stored binding via {@link rehydrateConnection} — so on a transport that resumes after
|
|
1915
|
-
* eviction (e.g. a Durable Object waking from hibernation) both the app identity and the action
|
|
1916
|
-
* routing come back from a single attachment, with no storage reads and no hand-rolled merge.
|
|
1917
|
-
*
|
|
1918
|
-
* This supersedes {@link createHibernatableWsServerAdapter} for app code that also pins its own state
|
|
1919
|
-
* to the connection. Construct it once when the handler is built, then `get`/`set` app state directly.
|
|
1920
|
-
*/
|
|
1921
|
-
createConnectionState<TApp>(options: IWsConnectionStateStoreOptions<TConn, TApp>): WsConnectionStateStore<TConn, TApp>;
|
|
1922
|
-
/**
|
|
1923
|
-
* Feed one inbound frame from a connection into the runtime. Decodes text or binary, binds the
|
|
1924
|
-
* connection to the requesting client's identity, then routes it (requests execute locally;
|
|
1925
|
-
* results/progress resolve pending server-initiated actions).
|
|
1926
|
-
*/
|
|
1927
|
-
receive(connection: TConn, frame: string | ArrayBuffer | Uint8Array): void;
|
|
1928
|
-
private _receivePlain;
|
|
1929
|
-
private _receiveSecure;
|
|
1930
|
-
private _completeServerHandshake;
|
|
1931
|
-
/**
|
|
1932
|
-
* Ensure an inbound request carries the client's identity and that this connection is bound to it,
|
|
1933
|
-
* so its result can be routed back. A session codec omits `originClient` after the first request, so
|
|
1934
|
-
* when it's missing we restore it from the (possibly rehydrated) binding instead. (Plain mode only;
|
|
1935
|
-
* secure mode binds the authenticated coordinate at handshake time.)
|
|
1936
|
-
*/
|
|
1937
|
-
private _resolveRequestIdentity;
|
|
1938
|
-
/**
|
|
1939
|
-
* Restore a connection→client binding without an inbound frame — for transports that resume after
|
|
1940
|
-
* eviction. Pair it with the {@link IActionServerHandlerOptions.onConnectionBound} hook: persist
|
|
1941
|
-
* the binding there, then replay each live connection here when the channel comes back (e.g. a
|
|
1942
|
-
* Durable Object iterating `ctx.getWebSockets()` as it wakes from hibernation).
|
|
1943
|
-
*/
|
|
1944
|
-
rehydrateConnection(connection: TConn, binding: IActionServerConnectionBinding): void;
|
|
1945
|
-
toHandlerRouteItem(): IActionRouteItemHandler;
|
|
1946
|
-
/** Forget a connection (call on socket close) so stale entries don't misroute later results. */
|
|
1947
|
-
dropConnection(connection: TConn): void;
|
|
1948
|
-
/** Live connection for a client coordinate, if currently registered. */
|
|
1949
|
-
getConnectionForClient(client: RuntimeCoordinate): TConn | undefined;
|
|
1950
|
-
/**
|
|
1951
|
-
* Send (and optionally await) a server-initiated action to a specific connected client. Pass the
|
|
1952
|
-
* connection token directly (e.g. the `ws`) or a client `RuntimeCoordinate` to look one up.
|
|
1953
|
-
*/
|
|
1954
|
-
pushToClient<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(runtime: ActionRuntime, target: TConn | RuntimeCoordinate, request: ActionPayload_Request<DOM, ID>, options?: {
|
|
1955
|
-
timeout?: number;
|
|
1956
|
-
}): RunningAction<DOM, ID>;
|
|
2511
|
+
declare function rtcDataChannelByteChannel(dc: IRtcDataChannelLike): IDuplexCarrier;
|
|
2512
|
+
//#endregion
|
|
2513
|
+
//#region src/ActionRuntime/Transport/Carrier/duplex/rtc/rtcCarrier.d.ts
|
|
2514
|
+
interface IRtcCarrierOptions {
|
|
2515
|
+
getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
2516
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
2517
|
+
}
|
|
2518
|
+
/**
|
|
2519
|
+
* A WebRTC {@link IDuplexCarrierSource} over an already-negotiated `RTCDataChannel` (signaling is the
|
|
2520
|
+
* app's concern). Hand it to {@link secureTransport} so two browsers/apps linked peer-to-peer run the
|
|
2521
|
+
* identical secure session as a WebSocket.
|
|
2522
|
+
*/
|
|
2523
|
+
declare function rtcCarrier(dataChannel: IRtcDataChannelLike, options?: IRtcCarrierOptions): IDuplexCarrierSource;
|
|
2524
|
+
//#endregion
|
|
2525
|
+
//#region src/ActionRuntime/Transport/Carrier/duplex/ws/err_nice_transport_ws.d.ts
|
|
2526
|
+
declare enum EErrId_NiceTransport_WebSocket {
|
|
2527
|
+
ws_disconnected = "ws_disconnected",
|
|
2528
|
+
ws_create_failed = "ws_create_failed",
|
|
2529
|
+
ws_error = "ws_error"
|
|
2530
|
+
}
|
|
2531
|
+
declare const err_nice_transport_ws: import("@nice-code/error").NiceErrorDomain<{
|
|
2532
|
+
domain: string;
|
|
2533
|
+
allDomains: [string, string, string, string, "err_nice"];
|
|
2534
|
+
schema: {
|
|
2535
|
+
ws_disconnected: import("@nice-code/error").INiceErrorIdMetadata<Record<string, never>, import("@nice-code/error").JSONSerializableValue>;
|
|
2536
|
+
ws_create_failed: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2537
|
+
originalError?: Error;
|
|
2538
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2539
|
+
ws_error: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2540
|
+
originalError?: Error;
|
|
2541
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2542
|
+
};
|
|
2543
|
+
}>;
|
|
2544
|
+
//#endregion
|
|
2545
|
+
//#region src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.d.ts
|
|
2546
|
+
interface IWsAcceptorCarrierOptions<TConn> {
|
|
1957
2547
|
/**
|
|
1958
|
-
*
|
|
1959
|
-
* the
|
|
1960
|
-
*
|
|
1961
|
-
* nothing, just like {@link ActionLocalHandler.forDomainActionCases}. Add the returned handler to the
|
|
1962
|
-
* runtime alongside this server handler:
|
|
1963
|
-
* ```ts
|
|
1964
|
-
* runtime.addHandlers([serverHandler.forConnectionDomainCases(domain, { … }), serverHandler]);
|
|
1965
|
-
* ```
|
|
2548
|
+
* Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint
|
|
2549
|
+
* — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption
|
|
2550
|
+
* (and `serveChannel` then needs no `storage` for this carrier).
|
|
1966
2551
|
*/
|
|
1967
|
-
|
|
2552
|
+
secure?: boolean;
|
|
2553
|
+
/** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */
|
|
2554
|
+
send: (connection: TConn, frame: TFrame$1) => void;
|
|
1968
2555
|
/**
|
|
1969
|
-
*
|
|
1970
|
-
*
|
|
1971
|
-
* `except` to skip the originating socket and `where` to filter by connection (e.g. read its
|
|
1972
|
-
* attachment for a role). Iterating bound connections (rather than every accepted socket) skips
|
|
1973
|
-
* sockets that are still mid-handshake and so can't yet receive a frame.
|
|
2556
|
+
* Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's
|
|
2557
|
+
* `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band.
|
|
1974
2558
|
*/
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
sendReturnPayload(payload: TActionPayload_Any_Instance<any, any>, config: {
|
|
1983
|
-
targetLocalRuntime: ActionRuntime;
|
|
1984
|
-
}): Promise<boolean>;
|
|
1985
|
-
handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
|
|
1986
|
-
private _dispatch;
|
|
1987
|
-
private _sendPayload;
|
|
1988
|
-
private _bindConnection;
|
|
1989
|
-
private _resolveConnection;
|
|
1990
|
-
private _resolveSingleConnection;
|
|
2559
|
+
upgrade?: (request: Request, url: URL) => Response | Promise<Response>;
|
|
2560
|
+
/** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */
|
|
2561
|
+
isUpgrade?: (request: Request, url: URL) => boolean;
|
|
2562
|
+
/** Persistence + replay hooks for hibernatable sockets (e.g. a Durable Object). */
|
|
2563
|
+
hibernation?: IAcceptorHibernation<TConn>;
|
|
2564
|
+
/** Override the devtools carrier-kind label (defaults to `"ws"`). */
|
|
2565
|
+
carrierLabel?: string;
|
|
1991
2566
|
}
|
|
1992
|
-
|
|
2567
|
+
/**
|
|
2568
|
+
* A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to
|
|
2569
|
+
* write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to
|
|
2570
|
+
* persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,
|
|
2571
|
+
* codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.
|
|
2572
|
+
*/
|
|
2573
|
+
declare function wsAcceptorCarrier<TConn = WebSocket>(options: IWsAcceptorCarrierOptions<TConn>): IDuplexAcceptorCarrier<TConn>;
|
|
1993
2574
|
//#endregion
|
|
1994
|
-
//#region src/ActionRuntime/
|
|
1995
|
-
interface
|
|
2575
|
+
//#region src/ActionRuntime/Transport/Carrier/duplex/ws/wsCarrier.d.ts
|
|
2576
|
+
interface IWsCarrierOptions {
|
|
2577
|
+
/** Override socket creation (defaults to a `new WebSocket(url)` with `binaryType = "arraybuffer"`). */
|
|
2578
|
+
createWebSocket?: (input: ITransportRouteActionParams) => WebSocket;
|
|
2579
|
+
/** Override the reuse key (defaults to `[url]`, so one socket is shared per endpoint). */
|
|
2580
|
+
getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
2581
|
+
/** Override the devtools route info for a specific action. */
|
|
2582
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
2583
|
+
}
|
|
2584
|
+
/**
|
|
2585
|
+
* A WebSocket {@link IDuplexCarrierSource}: opens an `arraybuffer` socket to `url` (cached per endpoint)
|
|
2586
|
+
* and adapts it to a carrier. Hand it to {@link secureTransport} (or `LinkTransport`) — the WebSocket is
|
|
2587
|
+
* now "just another carrier" under the shared secure session, with no WS-specific transport class.
|
|
2588
|
+
*/
|
|
2589
|
+
declare function wsCarrier(url: string, options?: IWsCarrierOptions): IDuplexCarrierSource;
|
|
2590
|
+
//#endregion
|
|
2591
|
+
//#region src/ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier.d.ts
|
|
2592
|
+
interface IHttpAcceptorCarrierOptions {
|
|
1996
2593
|
/**
|
|
1997
|
-
*
|
|
1998
|
-
*
|
|
2594
|
+
* Whether this endpoint runs the secure exchange protocol (default `true`). Pass `false` for a plain
|
|
2595
|
+
* endpoint — the body is the raw action wire and the result is the response body, the request/reply dual
|
|
2596
|
+
* of `plainTransport({ carrier: httpCarrier(...) })`. A plain endpoint ignores the crypto identity, so it
|
|
2597
|
+
* can sit alongside a secure WebSocket on the same server (e.g. a secure WS preferred, plain HTTP fallback).
|
|
1999
2598
|
*/
|
|
2000
|
-
|
|
2001
|
-
/** Which requests carry an action
|
|
2599
|
+
secure?: boolean;
|
|
2600
|
+
/** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */
|
|
2002
2601
|
isActionPath?: (url: URL) => boolean;
|
|
2003
|
-
/** Which requests are WebSocket upgrades. Default: pathname ends with `/ws`. */
|
|
2004
|
-
isWebSocketPath?: (url: URL) => boolean;
|
|
2005
2602
|
/**
|
|
2006
|
-
*
|
|
2007
|
-
*
|
|
2008
|
-
* Its response is returned as-is — a `101` upgrade carries no CORS headers.
|
|
2603
|
+
* CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the
|
|
2604
|
+
* permissive `*` set; pass `false` to attach no CORS headers at all.
|
|
2009
2605
|
*/
|
|
2010
|
-
|
|
2011
|
-
/**
|
|
2606
|
+
cors?: Record<string, string> | false;
|
|
2607
|
+
/** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */
|
|
2012
2608
|
useErrorStatus?: boolean;
|
|
2609
|
+
/** Override the devtools carrier-kind label (defaults to `"http"`). */
|
|
2610
|
+
carrierLabel?: string;
|
|
2013
2611
|
}
|
|
2014
2612
|
/**
|
|
2015
|
-
*
|
|
2016
|
-
*
|
|
2017
|
-
* `
|
|
2018
|
-
* `
|
|
2613
|
+
* An HTTP {@link IExchangeAcceptorCarrier}: the accept-in dual of {@link httpCarrier}. It serves the
|
|
2614
|
+
* secure exchange protocol (handshake → token session → encrypted frames) over web-standard
|
|
2615
|
+
* `Request`/`Response`. The crypto identity, runtime coordinate, dictionary version, and accepted security
|
|
2616
|
+
* levels are all supplied centrally by `serveChannel`, so this only needs to say which requests carry an
|
|
2617
|
+
* action envelope and how to answer CORS.
|
|
2618
|
+
*/
|
|
2619
|
+
declare function httpAcceptorCarrier(options?: IHttpAcceptorCarrierOptions): IExchangeAcceptorCarrier;
|
|
2620
|
+
//#endregion
|
|
2621
|
+
//#region src/ActionRuntime/Transport/Carrier/exchange/http/httpCarrier.d.ts
|
|
2622
|
+
/** The HTTP request an action is sent over (the body is supplied by the secure exchange session). */
|
|
2623
|
+
interface IHttpCarrierRequest {
|
|
2624
|
+
url: string;
|
|
2625
|
+
headers?: Record<string, string>;
|
|
2626
|
+
}
|
|
2627
|
+
/** The slice of `fetch` the carrier uses — a structural type so callers (and tests) needn't match the
|
|
2628
|
+
* full platform `fetch` (Bun's adds `preconnect`, etc.). The global `fetch` satisfies it. */
|
|
2629
|
+
type TCarrierFetch = (input: string, init?: RequestInit) => Promise<Response>;
|
|
2630
|
+
interface IHttpCarrierOptions {
|
|
2631
|
+
/** Override the reuse key (defaults to `[url]`, so one session is shared per endpoint). */
|
|
2632
|
+
getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
2633
|
+
/** Override the devtools route info for a specific action. */
|
|
2634
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
2635
|
+
/** Override `fetch` (e.g. to route to an in-memory handler in tests). Defaults to global `fetch`. */
|
|
2636
|
+
fetch?: TCarrierFetch;
|
|
2637
|
+
}
|
|
2638
|
+
/**
|
|
2639
|
+
* An HTTP {@link IExchangeCarrierSource}: each `exchange` POSTs one frame body to the action endpoint and
|
|
2640
|
+
* resolves with the response body as the single correlated reply. Hand it to {@link secureTransport} —
|
|
2641
|
+
* HTTP then runs the *same* secure session as a duplex carrier (handshake → token → encrypted frames),
|
|
2642
|
+
* the request/reply correlation provided for free by the HTTP transaction.
|
|
2019
2643
|
*
|
|
2020
|
-
*
|
|
2021
|
-
*
|
|
2022
|
-
* ```ts
|
|
2023
|
-
* this.fetchHandler = createActionFetchHandler(this.runtime, {
|
|
2024
|
-
* onWebSocketUpgrade: () => {
|
|
2025
|
-
* const pair = new WebSocketPair();
|
|
2026
|
-
* this.ctx.acceptWebSocket(pair[1]);
|
|
2027
|
-
* return new Response(null, { status: 101, webSocket: pair[0] });
|
|
2028
|
-
* },
|
|
2029
|
-
* });
|
|
2030
|
-
* // async fetch(request) { return this.fetchHandler(request); }
|
|
2031
|
-
* ```
|
|
2644
|
+
* `createRequest` derives the URL/headers per action (keep it simple with `() => ({ url })`). The body is
|
|
2645
|
+
* the session's responsibility, so it is never built here.
|
|
2032
2646
|
*/
|
|
2033
|
-
declare function
|
|
2647
|
+
declare function httpCarrier(createRequest: (input: ITransportRouteActionParams) => IHttpCarrierRequest, options?: IHttpCarrierOptions): IExchangeCarrierSource;
|
|
2034
2648
|
//#endregion
|
|
2035
|
-
//#region src/ActionRuntime/
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2649
|
+
//#region src/ActionRuntime/Transport/codec/createBinaryWireAdapter.d.ts
|
|
2650
|
+
/**
|
|
2651
|
+
* Builds a *stateless* `formatMessage` pipeline for {@link LinkTransport}, packing action
|
|
2652
|
+
* payloads into a compact msgpackr binary frame instead of JSON. The `domain`/`id` route collapses to
|
|
2653
|
+
* a single integer drawn from a shared dictionary; `form`/`type`, the recomputable
|
|
2654
|
+
* `inputHash`/`outputHash`, and the per-frame `context.routing`/`context.timeCreated` are all dropped
|
|
2655
|
+
* (see {@link ENVELOPE}).
|
|
2656
|
+
*
|
|
2657
|
+
* No validation runs here: `incoming` blindly reconstructs the wire JSON shape and hands it back to
|
|
2658
|
+
* the connection, which flows into `ActionRuntime` → `domain.hydrateAnyAction()` where the Valibot
|
|
2659
|
+
* schemas validate it exactly as they would for a JSON frame.
|
|
2660
|
+
*
|
|
2661
|
+
* Both ends of the socket MUST construct the adapter with the same domains in the same order — the
|
|
2662
|
+
* integer dictionary is positional. Mismatched dictionaries will route to the wrong action.
|
|
2663
|
+
*
|
|
2664
|
+
* Because `incoming` returns `undefined` for text frames, a binary server can still serve plain-JSON
|
|
2665
|
+
* clients on the same runtime (the connection falls back to its built-in JSON parser).
|
|
2666
|
+
*/
|
|
2667
|
+
declare function createBinaryWireAdapter(domains: ActionDomain<any>[]): IActionWireFormat;
|
|
2668
|
+
//#endregion
|
|
2669
|
+
//#region src/ActionRuntime/Transport/crypto/actionFrameCrypto.d.ts
|
|
2670
|
+
/**
|
|
2671
|
+
* Async AES-GCM transform for the `encrypted` security level. It wraps the opaque binary frame a
|
|
2672
|
+
* session codec produces (it does NOT look inside it), encrypting on the way out and decrypting on the
|
|
2673
|
+
* way in with the shared key established by the handshake.
|
|
2674
|
+
*
|
|
2675
|
+
* It is deliberately separate from the (synchronous) session `formatMessage`: WebCrypto is always
|
|
2676
|
+
* Promise-based, so encryption has to happen at the transport's async I/O boundary — the connection
|
|
2677
|
+
* encrypts after `session.outgoing()` and decrypts before `session.incoming()`. The `authenticated`
|
|
2678
|
+
* and `none` levels use no crypto transform at all (frames go out as the session produced them).
|
|
2679
|
+
*
|
|
2680
|
+
* Wire shape of an encrypted frame: `pack([nonceBytes, ciphertextBytes])` — msgpack carries the two
|
|
2681
|
+
* binary fields with a couple of bytes of overhead, no base64 inflation.
|
|
2682
|
+
*/
|
|
2683
|
+
interface IActionFrameCrypto {
|
|
2684
|
+
/** Encrypt one session frame for sending. */
|
|
2685
|
+
encryptFrame(frame: Uint8Array): Promise<Uint8Array>;
|
|
2686
|
+
/** Decrypt one received frame back to the session frame. Throws on a non-binary / malformed /
|
|
2687
|
+
* tampered frame — the caller (transport) decides how to react (drop / close). */
|
|
2688
|
+
decryptFrame(frame: string | ArrayBuffer | Uint8Array): Promise<Uint8Array>;
|
|
2689
|
+
}
|
|
2690
|
+
interface IActionFrameCryptoConfig {
|
|
2691
|
+
link: ClientCryptoKeyLink;
|
|
2692
|
+
/** The handshake-established link id for the remote (key + connection-registry id). */
|
|
2693
|
+
linkedClientId: TTypeAndId;
|
|
2694
|
+
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Build the encrypt/decrypt transform for a connection whose handshake settled on the `encrypted`
|
|
2697
|
+
* level. Keyed by the link + `linkedClientId`, so it reuses the cached shared AES-GCM key.
|
|
2698
|
+
*/
|
|
2699
|
+
declare function createActionFrameCrypto({
|
|
2700
|
+
link,
|
|
2701
|
+
linkedClientId
|
|
2702
|
+
}: IActionFrameCryptoConfig): IActionFrameCrypto;
|
|
2703
|
+
//#endregion
|
|
2704
|
+
//#region src/ActionRuntime/Transport/Exchange/TransportExchange.types.d.ts
|
|
2705
|
+
interface IActionTransportReadyData_Exchange extends IActionTransportReadyData_Base {
|
|
2706
|
+
/** The live request/reply carrier this connection drives its session over. */
|
|
2707
|
+
carrier: IExchangeCarrier;
|
|
2708
|
+
/** Optional authenticated/encrypted config; the handshake runs once at bring-up when set. */
|
|
2709
|
+
secureChannel?: ISecureClientConfig;
|
|
2710
|
+
}
|
|
2711
|
+
interface IActionTransportInitialized_Exchange extends IActionTransportInitialized<ITransportRouteActionParams, IActionTransportReadyData_Exchange> {}
|
|
2712
|
+
interface IActionTransportDef_Exchange extends IActionTransportDef<ETransportShape.exchange, IActionTransportInitialized_Exchange> {}
|
|
2713
|
+
//#endregion
|
|
2714
|
+
//#region src/ActionRuntime/Transport/Exchange/ExchangeConnection.d.ts
|
|
2715
|
+
/**
|
|
2716
|
+
* Carrier-agnostic live connection for the exchange (request → single reply) shape — the HTTP
|
|
2717
|
+
* counterpart to {@link LinkConnection}. It owns only the bring-up (run the secure handshake on first
|
|
2718
|
+
* use); the request/reply lifecycle + crypto live in the shared `establishExchangeSession`.
|
|
2719
|
+
*/
|
|
2720
|
+
declare class ExchangeConnection extends TransportConnection<ETransportShape.exchange, ITransportRouteActionParams, IActionTransportReadyData_Exchange, IActionTransportInitialized_Exchange, IActionTransportDef_Exchange> {
|
|
2721
|
+
constructor(def: Omit<IActionTransportDef_Exchange, "type">);
|
|
2722
|
+
protected _getCacheKey(input: ITransportRouteActionParams): string;
|
|
2723
|
+
protected _needsAsyncBringUp(data: IActionTransportReadyData_Exchange): boolean;
|
|
2724
|
+
protected _finalizeReady(data: IActionTransportReadyData_Exchange): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods>;
|
|
2725
|
+
_finalizeTransportMethods(data: IActionTransportReadyData_Exchange): IActionTransportReadyData_Methods;
|
|
2726
|
+
private _sessionContext;
|
|
2727
|
+
}
|
|
2728
|
+
//#endregion
|
|
2729
|
+
//#region src/ActionRuntime/Transport/Exchange/ExchangeTransport.d.ts
|
|
2730
|
+
interface IExchangeTransportOptions {
|
|
2731
|
+
/** Open (or reuse) the exchange carrier for an action — e.g. `httpCarrier(...).open`. */
|
|
2732
|
+
openCarrier: (input: ITransportRouteActionParams) => IExchangeCarrier;
|
|
2733
|
+
/** Secure config; when set (and `securityLevel !== none`) the handshake runs once at bring-up. */
|
|
2734
|
+
security?: ISecureClientConfig;
|
|
2735
|
+
updateRunConfig?: TUpdateActionRunConfig;
|
|
2736
|
+
/** Keys identifying a reusable session, so one carrier is shared across actions to the same peer. */
|
|
2737
|
+
getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
2738
|
+
/** Short label for the devtools chip (defaults to "exchange"). */
|
|
2739
|
+
label?: string;
|
|
2740
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
2741
|
+
}
|
|
2742
|
+
/**
|
|
2743
|
+
* A carrier-agnostic exchange (request → single reply) transport: it drives nice-action's secure session
|
|
2744
|
+
* over any {@link IExchangeCarrier} (HTTP being the one built-in). The duplex counterpart is
|
|
2745
|
+
* {@link LinkTransport}; this is the no-push half — its reply rides the response to its own request, so it
|
|
2746
|
+
* can't deliver an unsolicited frame (the runtime never picks it for the return path).
|
|
2747
|
+
*/
|
|
2748
|
+
declare class ExchangeTransport extends Transport<ETransportShape.exchange> {
|
|
2749
|
+
private readonly options;
|
|
2750
|
+
readonly type = ETransportShape.exchange;
|
|
2751
|
+
constructor(options: IExchangeTransportOptions);
|
|
2752
|
+
static create(options: IExchangeTransportOptions): ExchangeTransport;
|
|
2753
|
+
_createConnection(_ctx: ITransportConnectionContext): ExchangeConnection;
|
|
2754
|
+
getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
|
|
2755
|
+
}
|
|
2756
|
+
//#endregion
|
|
2757
|
+
//#region src/ActionRuntime/Transport/err_nice_transport.d.ts
|
|
2758
|
+
declare enum EErrId_NiceTransport {
|
|
2759
|
+
timeout = "timeout",
|
|
2760
|
+
not_found = "not_found",
|
|
2761
|
+
unsupported = "unsupported",
|
|
2762
|
+
initialization_failed = "initialization_failed",
|
|
2763
|
+
send_failed = "send_failed",
|
|
2764
|
+
invalid_action_response = "invalid_action_response"
|
|
2765
|
+
}
|
|
2766
|
+
declare const err_nice_transport: import("@nice-code/error").NiceErrorDomain<{
|
|
2767
|
+
domain: string;
|
|
2768
|
+
allDomains: [string, string, string, "err_nice"];
|
|
2769
|
+
schema: {
|
|
2770
|
+
timeout: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2771
|
+
timeout: number;
|
|
2772
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2773
|
+
not_found: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2774
|
+
actionId: string;
|
|
2775
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2776
|
+
unsupported: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2777
|
+
transportShapes: ETransportShape[];
|
|
2778
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2779
|
+
initialization_failed: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2780
|
+
actionId: string;
|
|
2781
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2782
|
+
send_failed: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2783
|
+
actionState: string;
|
|
2784
|
+
actionId: string;
|
|
2785
|
+
httpStatusCode?: number;
|
|
2786
|
+
message?: string;
|
|
2787
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2788
|
+
invalid_action_response: import("@nice-code/error").INiceErrorIdMetadata<{
|
|
2789
|
+
actionId: string;
|
|
2790
|
+
}, import("@nice-code/error").JSONSerializableValue>;
|
|
2791
|
+
};
|
|
2792
|
+
}>;
|
|
2793
|
+
//#endregion
|
|
2794
|
+
//#region src/ActionRuntime/Transport/Link/TransportLink.types.d.ts
|
|
2795
|
+
/** The per-connection codec (positional binary wire / JSON fallback) the carrier's session uses. */
|
|
2796
|
+
type TLinkFormatMessage = IActionWireFormat;
|
|
2797
|
+
interface IActionTransportReadyData_Link extends IActionTransportReadyData_Base {
|
|
2798
|
+
/** The live carrier this connection drives its session over. */
|
|
2799
|
+
channel: IDuplexCarrier;
|
|
2800
|
+
formatMessage?: TLinkFormatMessage;
|
|
2801
|
+
/** Optional authenticated/encrypted channel; the connection runs the handshake during init. */
|
|
2802
|
+
secureChannel?: ISecureClientConfig;
|
|
2803
|
+
}
|
|
2804
|
+
interface IActionTransportInitialized_Link extends IActionTransportInitialized<ITransportRouteActionParams, IActionTransportReadyData_Link> {}
|
|
2805
|
+
interface IActionTransportDef_Link extends IActionTransportDef<ETransportShape.duplex, IActionTransportInitialized_Link> {}
|
|
2806
|
+
//#endregion
|
|
2807
|
+
//#region src/ActionRuntime/Transport/Link/LinkConnection.d.ts
|
|
2808
|
+
/**
|
|
2809
|
+
* Carrier-agnostic live connection. It owns only the *bring-up* (open the carrier, then run the secure
|
|
2810
|
+
* session); the session itself — handshake, frame crypto, codec, send/receive — lives in the shared
|
|
2811
|
+
* {@link finalizeSecureLinkMethods}/{@link finalizePlainLinkMethods}, so a WebSocket, a WebRTC data
|
|
2812
|
+
* channel, a Bluetooth characteristic, and an in-memory pipe all run the identical secure layer.
|
|
2813
|
+
*/
|
|
2814
|
+
declare class LinkConnection extends TransportConnection<ETransportShape.duplex, ITransportRouteActionParams, IActionTransportReadyData_Link, IActionTransportInitialized_Link, IActionTransportDef_Link> {
|
|
2815
|
+
private resolvers;
|
|
2816
|
+
constructor(def: Omit<IActionTransportDef_Link, "type">, resolvers?: IActionTransportResolvers);
|
|
2817
|
+
protected _getCacheKey(input: ITransportRouteActionParams): string;
|
|
2818
|
+
protected _needsAsyncBringUp(): boolean;
|
|
2819
|
+
protected _awaitCarrierReady(data: IActionTransportReadyData_Link): Promise<void>;
|
|
2820
|
+
protected _finalizeReady(data: IActionTransportReadyData_Link): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods>;
|
|
2821
|
+
private _sessionContext;
|
|
2822
|
+
_finalizeTransportMethods(data: IActionTransportReadyData_Link): IActionTransportReadyData_Methods;
|
|
2823
|
+
}
|
|
2824
|
+
//#endregion
|
|
2825
|
+
//#region src/ActionRuntime/Transport/Link/LinkTransport.d.ts
|
|
2826
|
+
interface ILinkTransportOptions {
|
|
2039
2827
|
/**
|
|
2040
|
-
*
|
|
2041
|
-
*
|
|
2828
|
+
* Open (or reuse) the carrier for an action — a WebSocket adapter, a WebRTC data channel, a Bluetooth
|
|
2829
|
+
* characteristic, an in-memory pipe, anything that satisfies {@link IDuplexCarrier}.
|
|
2042
2830
|
*/
|
|
2043
|
-
|
|
2044
|
-
/**
|
|
2045
|
-
|
|
2831
|
+
openChannel: (input: ITransportRouteActionParams) => IDuplexCarrier;
|
|
2832
|
+
/** Shared codec for every channel (stateless). */
|
|
2833
|
+
formatMessage?: TLinkFormatMessage;
|
|
2046
2834
|
/**
|
|
2047
|
-
*
|
|
2048
|
-
*
|
|
2049
|
-
* Durable Object's storage) so identity and pins survive eviction.
|
|
2835
|
+
* Per-channel codec factory — called once per opened channel so stateful codecs (e.g. the binary
|
|
2836
|
+
* session) get their own instance. Takes precedence over `formatMessage`.
|
|
2050
2837
|
*/
|
|
2051
|
-
|
|
2052
|
-
/**
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2838
|
+
createFormatMessage?: () => TLinkFormatMessage;
|
|
2839
|
+
/** Secure-channel config; when set (and `securityLevel !== none`) the handshake runs on init. */
|
|
2840
|
+
security?: ISecureClientConfig;
|
|
2841
|
+
updateRunConfig?: TUpdateActionRunConfig;
|
|
2842
|
+
/** Keys identifying a reusable channel, so one carrier is shared across actions to the same peer. */
|
|
2843
|
+
getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
2844
|
+
/** Short label for the devtools chip (defaults to "link"). */
|
|
2845
|
+
label?: string;
|
|
2846
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
2060
2847
|
}
|
|
2061
2848
|
/**
|
|
2062
|
-
*
|
|
2063
|
-
*
|
|
2064
|
-
* `
|
|
2065
|
-
*
|
|
2066
|
-
*
|
|
2067
|
-
* For a hibernatable transport (e.g. a Durable Object), pair it with
|
|
2068
|
-
* {@link createHibernatableWsServerAdapter} to wire persistence + replay.
|
|
2849
|
+
* A carrier-agnostic transport: it drives nice-action's secure session + action routing over any
|
|
2850
|
+
* {@link IDuplexCarrier}. The WebSocket transport is the special case that opens a `WebSocket`;
|
|
2851
|
+
* this opens whatever `openChannel` returns, so the identical secure layer works over WebRTC, Bluetooth,
|
|
2852
|
+
* or an in-memory pipe. Reported with an overridable carrier label in the devtools (defaults to "link").
|
|
2069
2853
|
*/
|
|
2070
|
-
declare
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
getAttachment: (connection: TConn) => IActionServerConnectionBinding | undefined;
|
|
2078
|
-
/** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */
|
|
2079
|
-
setAttachment: (connection: TConn, binding: IActionServerConnectionBinding) => void;
|
|
2854
|
+
declare class LinkTransport extends Transport<ETransportShape.duplex> {
|
|
2855
|
+
private readonly options;
|
|
2856
|
+
readonly type = ETransportShape.duplex;
|
|
2857
|
+
constructor(options: ILinkTransportOptions);
|
|
2858
|
+
static create(options: ILinkTransportOptions): LinkTransport;
|
|
2859
|
+
_createConnection(ctx: ITransportConnectionContext): LinkConnection;
|
|
2860
|
+
getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
|
|
2080
2861
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
/**
|
|
2085
|
-
|
|
2862
|
+
//#endregion
|
|
2863
|
+
//#region src/ActionRuntime/Transport/plainTransport.d.ts
|
|
2864
|
+
interface IPlainTransportOptions {
|
|
2865
|
+
/**
|
|
2866
|
+
* How to reach the peer with no security layer. A duplex carrier (`wsCarrier(url)`,
|
|
2867
|
+
* `rtcCarrier(dc)`, `inMemoryCarrier().carrier`) builds a push-capable {@link LinkTransport}; an
|
|
2868
|
+
* exchange carrier (`httpCarrier(...)`) builds a request/reply {@link ExchangeTransport}.
|
|
2869
|
+
*/
|
|
2870
|
+
carrier: IDuplexCarrierSource | IExchangeCarrierSource;
|
|
2871
|
+
/**
|
|
2872
|
+
* Codec for a *duplex* carrier (a duplex link frames the action wire itself). Required for duplex;
|
|
2873
|
+
* ignored for an exchange carrier, which JSON-encodes the action wire in its envelope.
|
|
2874
|
+
*/
|
|
2875
|
+
formatMessage?: TLinkFormatMessage;
|
|
2876
|
+
/** Per-channel codec factory for stateful duplex codecs (e.g. the binary session). */
|
|
2877
|
+
createFormatMessage?: () => TLinkFormatMessage;
|
|
2878
|
+
updateRunConfig?: TUpdateActionRunConfig;
|
|
2879
|
+
/** Override the devtools chip label (defaults to the carrier's `carrierLabel`). */
|
|
2880
|
+
label?: string;
|
|
2086
2881
|
}
|
|
2087
2882
|
/**
|
|
2088
|
-
*
|
|
2089
|
-
*
|
|
2090
|
-
*
|
|
2091
|
-
*
|
|
2092
|
-
*
|
|
2093
|
-
*
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2883
|
+
* The plain (no-handshake, no-crypto) sibling of {@link secureTransport}: swap the carrier to change
|
|
2884
|
+
* protocol, with no security layer. Over an {@link IExchangeCarrierSource} it builds an
|
|
2885
|
+
* {@link ExchangeTransport} that POSTs the bare action wire and completes inline (HTTP is just
|
|
2886
|
+
* `carrier: httpCarrier(...)`); over an {@link IDuplexCarrierSource} it builds a push-capable
|
|
2887
|
+
* {@link LinkTransport} with the given codec. HTTP is therefore no longer a bespoke transport class —
|
|
2888
|
+
* it is "just another carrier", exactly like a WebSocket under {@link secureTransport}.
|
|
2889
|
+
*/
|
|
2890
|
+
declare function plainTransport(options: IPlainTransportOptions & {
|
|
2891
|
+
carrier: IExchangeCarrierSource;
|
|
2892
|
+
}): ExchangeTransport;
|
|
2893
|
+
declare function plainTransport(options: IPlainTransportOptions & {
|
|
2894
|
+
carrier: IDuplexCarrierSource;
|
|
2895
|
+
}): LinkTransport;
|
|
2896
|
+
//#endregion
|
|
2897
|
+
//#region src/ActionRuntime/Transport/secureTransport.d.ts
|
|
2898
|
+
interface ISecureTransportOptions {
|
|
2899
|
+
/** The shared channel identity (per-connection codec + dictionary version) — same one both ends use. */
|
|
2900
|
+
channel: ISecureChannel;
|
|
2901
|
+
/** This client's runtime — its coordinate is the authenticated identity sent in the handshake. */
|
|
2902
|
+
runtime: ActionRuntime;
|
|
2903
|
+
/** Backing store for this client's crypto identity (a stable verify key across reloads). */
|
|
2904
|
+
storageAdapter: StorageAdapter;
|
|
2905
|
+
/** The level this client requests; the peer must allow it. */
|
|
2906
|
+
securityLevel: ESecurityLevel;
|
|
2907
|
+
/**
|
|
2908
|
+
* How to reach the peer. A duplex carrier (`wsCarrier(url)`, `rtcCarrier(dc)`,
|
|
2909
|
+
* `inMemoryCarrier().carrier`) runs the push-capable session; an exchange carrier (`httpCarrier(...)`)
|
|
2910
|
+
* runs the request/reply session over the same handshake + crypto.
|
|
2911
|
+
*/
|
|
2912
|
+
carrier: IDuplexCarrierSource | IExchangeCarrierSource;
|
|
2913
|
+
}
|
|
2914
|
+
/**
|
|
2915
|
+
* The one secure-transport factory — swap the carrier to change protocol. Folds in the boilerplate (the
|
|
2916
|
+
* {@link ClientCryptoKeyLink} from `storageAdapter`, the `security` block from the runtime coordinate +
|
|
2917
|
+
* channel version) and drives it over whatever carrier you pass: a {@link IDuplexCarrierSource} builds a
|
|
2918
|
+
* push-capable {@link LinkTransport} (WS is just `carrier: wsCarrier(url)`), an
|
|
2919
|
+
* {@link IExchangeCarrierSource} builds a request/reply {@link ExchangeTransport} (HTTP, with the same
|
|
2920
|
+
* authentication/encryption). Replaces the old `createSecureWebSocketTransport` / `createSecureLinkTransport`.
|
|
2099
2921
|
*/
|
|
2100
|
-
declare function
|
|
2922
|
+
declare function secureTransport(options: ISecureTransportOptions & {
|
|
2923
|
+
carrier: IDuplexCarrierSource;
|
|
2924
|
+
}): LinkTransport;
|
|
2925
|
+
declare function secureTransport(options: ISecureTransportOptions & {
|
|
2926
|
+
carrier: IExchangeCarrierSource;
|
|
2927
|
+
}): ExchangeTransport;
|
|
2101
2928
|
//#endregion
|
|
2102
2929
|
//#region src/errors/err_nice_action.d.ts
|
|
2103
2930
|
declare enum EErrId_NiceAction {
|
|
@@ -2198,7 +3025,7 @@ declare const err_nice_action: import("@nice-code/error").NiceErrorDomain<{
|
|
|
2198
3025
|
//#region src/utils/decodeActionFrame.d.ts
|
|
2199
3026
|
/**
|
|
2200
3027
|
* Minimal codec shape needed to turn an incoming channel frame back into action wire JSON. Matches
|
|
2201
|
-
* the `formatMessage` object the WebSocket transport (and `
|
|
3028
|
+
* the `formatMessage` object the WebSocket transport (and `createBinaryWireAdapter`) provide.
|
|
2202
3029
|
*/
|
|
2203
3030
|
interface IActionFrameDecoder {
|
|
2204
3031
|
incoming?: (frame: string | ArrayBuffer | Uint8Array | Blob) => TActionPayload_Any_JsonObject<any, any> | undefined;
|
|
@@ -2207,7 +3034,7 @@ interface IActionFrameDecoder {
|
|
|
2207
3034
|
* Decode a single inbound channel frame (text or binary) into validated action wire JSON, or
|
|
2208
3035
|
* `undefined` if it isn't a recognisable action payload.
|
|
2209
3036
|
*
|
|
2210
|
-
* Shared by the WebSocket transport's message listener and the server-side `
|
|
3037
|
+
* Shared by the WebSocket transport's message listener and the server-side `AcceptorHandler` so
|
|
2211
3038
|
* both decode identically: a binary `decoder.incoming` (e.g. msgpackr) takes precedence, and plain
|
|
2212
3039
|
* text frames fall back to JSON — keeping binary and JSON clients interoperable on one channel.
|
|
2213
3040
|
*/
|
|
@@ -2237,8 +3064,8 @@ interface IActionPayload_Base<DT extends EActionPayloadType, DOM extends IAction
|
|
|
2237
3064
|
readonly type: DT;
|
|
2238
3065
|
readonly context: ActionContext<DOM, ID>;
|
|
2239
3066
|
}
|
|
2240
|
-
type IActionRouteItemHandler = IActionHandler_Local_Json | (
|
|
2241
|
-
|
|
3067
|
+
type IActionRouteItemHandler = IActionHandler_Local_Json | (IActionHandler_Peer_Json & {
|
|
3068
|
+
transShape: ETransportShape;
|
|
2242
3069
|
transOrd: number;
|
|
2243
3070
|
transInfo?: ITransportRouteInfo;
|
|
2244
3071
|
});
|
|
@@ -2324,5 +3151,5 @@ interface IActionPayload_Result_JsonObject<DOM extends IActionDomain = IActionDo
|
|
|
2324
3151
|
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>;
|
|
2325
3152
|
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>;
|
|
2326
3153
|
//#endregion
|
|
2327
|
-
export {
|
|
2328
|
-
//# sourceMappingURL=ActionPayload.types-
|
|
3154
|
+
export { EErrId_NiceTransport_WebSocket as $, IClientVerifyKeyResolver as $n, TTransportedValue as $r, IAcceptorConnectionBinding as $t, ILinkTransportOptions as A, ITransportStatusInfo_Ready as An, ActionPayload_Result as Ar, IDuplexCarrier as At, IActionFrameCryptoConfig as B, TSendActionDataMethod as Bn, TActionDomainChildDef as Br, IActionChannel as Bt, decodeActionFrame as C, ITransportMethod_SendActionData_Input as Cn, IRunningActionUpdate_Abort as Cr, IServeChannelOptions as Ct, secureTransport as D, ITransportStatusInfo_Base as Dn, TRunningActionUpdate as Dr, IExchangeAcceptorCarrier as Dt, ISecureTransportOptions as E, ITransportRouteInfo as En, IRunningActionUpdate_Success as Er, IDuplexAcceptorCarrier as Et, err_nice_transport as F, TOnResolveAnyIncomingActionData_Json as Fn, TRuntimeCoordinateEnvId as Fr, TFrame$1 as Ft, TCarrierFetch as G, TTransportStatusInfo_GetTransport_Output as Gn, TPossibleDomainId as Gr, acceptChannelConnections as Gt, createBinaryWireAdapter as H, TTransportCache as Hn, TDomainActionId as Hr, TChannelAcceptorCases as Ht, ExchangeTransport as I, TOnResolveIncomingRequest as In, TRuntimeCoordinateStringId as Ir, IDuplexConnectionRouter as It, httpAcceptorCarrier as J, Transport as Jn, EActionResponseMode as Jr, ISecureChannel as Jt, httpCarrier as K, TUpdateActionRunConfig as Kn, TPossibleDomainIdList as Kr, connectChannel as Kt, IExchangeTransportOptions as L, TOnResolveIncomingRequestJson as Ln, IActionDomain as Lr, IHibernatableWsServerAdapterOptions as Lt, IActionTransportReadyData_Link as M, IUpdateActionRunConfig_Output as Mn, IRuntimeCoordinateSpecifics as Mr, IExchangeCarrier as Mt, TLinkFormatMessage as N, TGetTransportFn as Nn, IRuntimeFullCoordinates as Nr, IExchangeCarrierSource as Nt, IPlainTransportOptions as O, ITransportStatusInfo_Failed as On, TRunningActionUpdateFinished as Or, TAcceptorCarrier as Ot, EErrId_NiceTransport as P, TOnResolveAnyIncomingActionData as Pn, RuntimeCoordinate as Pr, TCarrier as Pt, wsAcceptorCarrier as Q, IClientVerifyKeyResolveInput as Qn, TActionSerializationDefinition as Qr, AcceptorHandler as Qt, IActionTransportReadyData_Exchange as R, TOnResolveIncomingResponse as Rn, IActionDomainChildOptions as Rr, createHibernatableWsServerAdapter as Rt, IActionFrameDecoder as S, ITransportDispatchAction as Sn, ERunningActionUpdateType as Sr, IChannelServer as St, err_nice_action as T, ITransportRouteClientParams as Tn, IRunningActionUpdate_Started as Tr, IAcceptorHibernation as Tt, IHttpCarrierOptions as U, TTransportInitializationFinishedInfo as Un, TInferInputFromSchema as Ur, TChannelPushHandlers as Ut, createActionFrameCrypto as V, TSendReturnDataMethod as Vn, TActionDomainSchema as Vr, IConnectChannelOptions as Vt, IHttpCarrierRequest as W, TTransportStatusInfo as Wn, TInferOutputFromSchema as Wr, acceptChannel as Wt, wsCarrier as X, ESecurityLevel as Xn, actionSchema as Xr, IBinaryWireSessionOptions as Xt, IWsCarrierOptions as Y, EHandshakeMessageType as Yn, TInferActionError as Yr, defineSecureChannel as Yt, IWsAcceptorCarrierOptions as Z, IClientHandshakeConfig as Zn, TActionSchemaOptions as Zr, createBinaryWireSessionFactory as Zt, TActionProgress as _, IActionTransportReady as _n, ActionPayload_Request as _r, IActionFetchHandlerOptions as _t, IActionPayload_Data_Base as a, IActionWireFormat as an, createInMemoryTofuVerifyKeyResolver as ar, IInMemoryCarrier as at, isActionPayload_Request_JsonObject as b, IActionTransportResolvers as bn, ERunningActionFinishedType as br, IExchangeAcceptorConfig as bt, IActionPayload_Request_JsonObject as c, ActionDomain as cn, decodeHandshakeMessage as cr, IInMemoryServerEndpoint as ct, IActionProgress_Custom as d, ConnectorHandler as dn, PeerLinkHandler as dr, ConnectionStateStore as dt, IAcceptorHandlerOptions as en, IHandshakeEncryptionKeyMaterial as er, err_nice_transport_ws as et, IActionProgress_None as f, createConnectorHandler as fn, ActionLocalHandler as fr, IConnectionAttachment as ft, TActionPayload_Any_JsonObject as g, IActionTransportInitialized as gn, ActionPayload_Progress as gr, createSecureAcceptorHandler as gt, TActionPayload_Any_Instance as h, IActionTransportDef as hn, RunningAction as hr, ISecureAcceptorHandlerOptions as ht, IActionPayload_Base_JsonObject as i, createAcceptorHandler as in, createClientHandshake as ir, rtcDataChannelByteChannel as it, LinkTransport as j, ITransportStatusInfo_Unsupported as jn, IRuntimeCoordinate as jr, IDuplexCarrierSource as jt, plainTransport as k, ITransportStatusInfo_Initializing as kn, TRunningActionUpdateListener as kr, isExchangeAcceptorCarrier as kt, IActionPayload_Result as l, ActionRootDomain as ln, encodeHandshakeMessage as lr, createInMemoryChannelPair as lt, IActionRouteItemHandler as m, ETransportStatus as mn, MaybePromise as mr, createConnectionStateStore as mt, EActionProgressType as n, TActionChannelFormatMessage as nn, IServerHandshakeConfig as nr, rtcCarrier as nt, IActionPayload_Progress as o, createActionRootDomain as on, createServerHandshake as or, inMemoryCarrier as ot, IActionProgress_Percentage as p, ETransportShape as pn, createLocalHandler as pr, IConnectionStateStoreOptions as pt, IHttpAcceptorCarrierOptions as q, ITransportConnectionContext as qn, ActionSchema as qr, defineChannel as qt, IActionPayload_Base as r, TActionConnectionEncoding as rn, THandshakeMessage as rr, IRtcDataChannelLike as rt, IActionPayload_Progress_JsonObject as s, ActionCore as sn, createStorageTofuVerifyKeyResolver as sr, IInMemoryChannelPair as st, EActionPayloadType as t, TAcceptorConnectionCaseFn as tn, IHandshakeResult as tr, IRtcCarrierOptions as tt, IActionPayload_Result_JsonObject as u, ActionRuntime as un, runtimeLinkId as ur, err_nice_external_client as ut, TActionResultOutcome as v, IActionTransportReadyData_Base as vn, IActionCore as vr, createActionFetchHandler as vt, EErrId_NiceAction as w, ITransportRouteActionParams as wn, IRunningActionUpdate_Progress as wr, serveChannel as wt, isActionPayload_Any_JsonObject as x, ISecureClientConfig as xn, ERunningActionState as xr, IExchangeAcceptorSecurity as xt, isActionPayload_Result_JsonObject as y, IActionTransportReadyData_Methods as yn, IActionCore_JsonObject as yr, ExchangeAcceptor as yt, IActionFrameCrypto as z, TOnResolveIncomingResponseJson as zn, IActionRootDomain as zr, IAcceptChannelOptions as zt };
|
|
3155
|
+
//# sourceMappingURL=ActionPayload.types-BchJrBIX.d.mts.map
|