@actuallab/rpc 13.0.4 → 13.0.14
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/dist/index.cjs +83 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -6
- package/dist/index.d.ts +60 -6
- package/dist/index.js +83 -38
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -558,6 +558,48 @@ declare const IncreasingSeqCompressor: {
|
|
|
558
558
|
readonly deserialize: (data: Uint8Array) => number[];
|
|
559
559
|
};
|
|
560
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Configurable timing limits for RPC connection lifecycle. Mirrors .NET's
|
|
563
|
+
* `RpcLimits` (src/ActualLab.Rpc/Configuration/RpcLimits.cs).
|
|
564
|
+
*
|
|
565
|
+
* Override paths, from broadest to narrowest:
|
|
566
|
+
* - **Process-wide**: mutate fields on {@link RpcLimits.Default} (affects
|
|
567
|
+
* every hub that hasn't been given its own instance — hubs default to
|
|
568
|
+
* using the shared `Default`, so mutations propagate live), or replace
|
|
569
|
+
* `RpcLimits.Default = new RpcLimits({ … })` (affects hubs constructed
|
|
570
|
+
* *after* the replacement).
|
|
571
|
+
* - **Per-hub**: assign `hub.limits = new RpcLimits({ … })` before peers
|
|
572
|
+
* are created from that hub.
|
|
573
|
+
* - **Per-peer**: set the matching `*Ms` field on the peer directly
|
|
574
|
+
* (e.g. `peer.keepAliveTimeoutMs = 60_000`) before `start()`.
|
|
575
|
+
*
|
|
576
|
+
* Peers snapshot the relevant values from `hub.limits` at construction —
|
|
577
|
+
* later mutations to `hub.limits` don't retroactively affect already-built
|
|
578
|
+
* peers; that's by design and matches the .NET semantics.
|
|
579
|
+
*/
|
|
580
|
+
declare class RpcLimits {
|
|
581
|
+
/** Process-wide default. Hubs read from this when they're constructed
|
|
582
|
+
* unless caller assigns a custom instance to `hub.limits`. */
|
|
583
|
+
static Default: RpcLimits;
|
|
584
|
+
/** Max time to wait for the WebSocket to enter the OPEN state. On a
|
|
585
|
+
* hung connect (mobile after network change, half-open after sleep)
|
|
586
|
+
* the browser can take ~2 min to emit `onerror`/`onclose`; without
|
|
587
|
+
* this cap the reconnect loop blocks for that whole window. */
|
|
588
|
+
connectTimeoutMs: number;
|
|
589
|
+
/** Max time to wait for the server's handshake response after WS opens. */
|
|
590
|
+
handshakeTimeoutMs: number;
|
|
591
|
+
/** Outbound `$sys.KeepAlive` send period. */
|
|
592
|
+
keepAlivePeriodMs: number;
|
|
593
|
+
/** Reaper threshold — connection is force-closed (and the reconnect
|
|
594
|
+
* loop takes over) if no inbound `$sys.KeepAlive` has been seen for
|
|
595
|
+
* this long. Sized to tolerate a complete server stall plus most of
|
|
596
|
+
* one keepalive cycle: the worst-case age of `_lastKeepAliveAt` is
|
|
597
|
+
* `keepAlivePeriodMs + stall_duration`, so with these defaults the
|
|
598
|
+
* tolerated stall is ~15 s. */
|
|
599
|
+
keepAliveTimeoutMs: number;
|
|
600
|
+
constructor(overrides?: Partial<RpcLimits>);
|
|
601
|
+
}
|
|
602
|
+
|
|
561
603
|
declare class RpcClientPeerReconnectDelayer extends RetryDelayer {
|
|
562
604
|
constructor();
|
|
563
605
|
}
|
|
@@ -663,6 +705,13 @@ declare class RpcHub {
|
|
|
663
705
|
/** Shared reconnect delayer used by every client peer in this hub. Swap in
|
|
664
706
|
* a custom subclass (e.g. app-level signal-gated) before peers start. */
|
|
665
707
|
reconnectDelayer: RpcClientPeerReconnectDelayer;
|
|
708
|
+
/** Connection-lifecycle timing limits. Peers constructed against this hub
|
|
709
|
+
* read their initial `*Ms` field values from this instance; later
|
|
710
|
+
* mutations affect only peers built afterward (existing peers can be
|
|
711
|
+
* retuned by setting their `*Ms` fields directly). Defaults to the
|
|
712
|
+
* process-wide {@link RpcLimits.Default} so a single global override
|
|
713
|
+
* propagates to every hub that hasn't replaced this. */
|
|
714
|
+
limits: RpcLimits;
|
|
666
715
|
/** URL used by {@link defaultPeer} to resolve / create the default client
|
|
667
716
|
* peer. Must be set before the first {@link defaultPeer} access. */
|
|
668
717
|
defaultPeerUrl: string | undefined;
|
|
@@ -746,8 +795,6 @@ declare class RpcSharedObjectTracker {
|
|
|
746
795
|
|
|
747
796
|
/** WebSocket close code sent by the server when the client's serialization format is unsupported. */
|
|
748
797
|
declare const RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
|
|
749
|
-
/** Maximum time to wait for the server's handshake response after opening the socket. */
|
|
750
|
-
declare const HANDSHAKE_TIMEOUT_MS = 10000;
|
|
751
798
|
/** Builds the WebSocket connection URL for an RpcClientPeer. */
|
|
752
799
|
type RpcConnectionUrlResolver = (peer: RpcClientPeer) => string | Promise<string>;
|
|
753
800
|
/** Default connection URL provider — sets `clientId` and `f` query parameters,
|
|
@@ -802,9 +849,11 @@ declare abstract class RpcPeer {
|
|
|
802
849
|
/** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet
|
|
803
850
|
* for the current connection. Useful for diagnostics. */
|
|
804
851
|
private _lastKeepAliveAt;
|
|
805
|
-
/** Outbound keep-alive send period
|
|
852
|
+
/** Outbound keep-alive send period. Initialized from `hub.limits` at
|
|
853
|
+
* construction; set directly to override on a single peer. */
|
|
806
854
|
keepAlivePeriodMs: number;
|
|
807
|
-
/** Inbound keep-alive silence tolerance
|
|
855
|
+
/** Inbound keep-alive silence tolerance. Initialized from `hub.limits`
|
|
856
|
+
* at construction; set directly to override on a single peer. */
|
|
808
857
|
keepAliveTimeoutMs: number;
|
|
809
858
|
protected constructor(hub: RpcHub, ref: string);
|
|
810
859
|
get connection(): RpcConnection | undefined;
|
|
@@ -886,8 +935,13 @@ declare class RpcClientPeer extends RpcPeer {
|
|
|
886
935
|
* `Cookie` headers to the WS upgrade, which the browser `WebSocket`
|
|
887
936
|
* constructor can't do). Must be set before `start()`. */
|
|
888
937
|
webSocketFactory: ((url: string) => WebSocketLike) | undefined;
|
|
938
|
+
/** Max time to wait for the WebSocket to reach OPEN before aborting the
|
|
939
|
+
* connection attempt. Initialized from `hub.limits` at construction;
|
|
940
|
+
* set directly to override on a single peer. */
|
|
941
|
+
connectTimeoutMs: number;
|
|
889
942
|
/** Max time to wait for the server's handshake response before aborting
|
|
890
|
-
* the connection attempt.
|
|
943
|
+
* the connection attempt. Initialized from `hub.limits` at construction;
|
|
944
|
+
* set directly to override on a single peer. */
|
|
891
945
|
handshakeTimeoutMs: number;
|
|
892
946
|
readonly peerChanged: EventHandlerSet<void>;
|
|
893
947
|
/** Fired when the server rejects the connection due to an unsupported serialization format (close code 4010). */
|
|
@@ -1342,4 +1396,4 @@ declare function xxh3_64str(input: string, seed?: bigint): bigint;
|
|
|
1342
1396
|
*/
|
|
1343
1397
|
declare function computeMethodHash(methodName: string): number;
|
|
1344
1398
|
|
|
1345
|
-
export { ARG_DELIMITER, ENVELOPE_DELIMITER, FRAME_DELIMITER,
|
|
1399
|
+
export { ARG_DELIMITER, ENVELOPE_DELIMITER, FRAME_DELIMITER, type IRpcObject, IncreasingSeqCompressor, type MethodMeta, RPC_CLOSE_CODE_UNSUPPORTED_FORMAT, type RemoteHandshake, type RpcCallOptions, RpcCallStage, RpcCallTypeId, RpcClientPeer, RpcClientPeerReconnectDelayer, type RpcConnection, RpcConnectionState, type RpcConnectionUrlResolver, type RpcDeserializedMessage, type RpcDispatchContext, RpcError, RpcHub, RpcInboundCall, RpcInboundCallTracker, RpcJsonSerializationFormat, RpcLimits, type RpcLogScope, type RpcMessage, RpcMessageChannelConnection, RpcMessagePackCompactSerializationFormat, RpcMessagePackSerializationFormat, type RpcMethodDef, type RpcMethodDefInput, RpcMethodRegistry, type RpcObjectId, RpcObjectKind, RpcOutboundCall, RpcOutboundCallTracker, RpcPeer, type RpcPeerFactory, RpcPeerRefBuilder, type RpcPeerState, RpcPeerStateKind, RpcPeerStateMonitor, type RpcReceivedMessage, RpcRemoteExecutionMode, RpcRemoteObjectTracker, RpcSerializationFormat, RpcSerializationFormatResolver, RpcServerPeer, type RpcServiceDef, RpcServiceHost, type RpcServiceImpl, RpcSharedObjectTracker, RpcStream, type RpcStreamOptions, type RpcStreamRef, RpcStreamSender, type RpcStreamSource, RpcSystemCallHandler, RpcSystemCallSender, RpcSystemCalls, RpcType, RpcWebSocketConnection, type RpcWireData, type ServiceMeta, type WebSocketLike, type WebSocketServer, WebSocketState, computeMethodHash, createBinaryEncoder, createMessageChannelPair, createRpcClient, defaultBinaryDecoder, defaultBinaryEncoder, defaultConnectionUrlResolver, defineRpcService, deserializeBinaryMessage, deserializeCompactBinaryMessage, deserializeMessage, getLogs, getMethodsMeta, getServiceMeta, getStateDescription, isConnected, likelyConnected, parseStreamRef, resolveStreamRefs, rpcMethod, rpcService, serializeBinaryFrame, serializeBinaryMessage, serializeCompactBinaryMessage, serializeFrame, serializeMessage, splitBinaryFrame, splitCompactBinaryFrame, splitFrame, wireMethodName, xxh3_64, xxh3_64str };
|
package/dist/index.d.ts
CHANGED
|
@@ -558,6 +558,48 @@ declare const IncreasingSeqCompressor: {
|
|
|
558
558
|
readonly deserialize: (data: Uint8Array) => number[];
|
|
559
559
|
};
|
|
560
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Configurable timing limits for RPC connection lifecycle. Mirrors .NET's
|
|
563
|
+
* `RpcLimits` (src/ActualLab.Rpc/Configuration/RpcLimits.cs).
|
|
564
|
+
*
|
|
565
|
+
* Override paths, from broadest to narrowest:
|
|
566
|
+
* - **Process-wide**: mutate fields on {@link RpcLimits.Default} (affects
|
|
567
|
+
* every hub that hasn't been given its own instance — hubs default to
|
|
568
|
+
* using the shared `Default`, so mutations propagate live), or replace
|
|
569
|
+
* `RpcLimits.Default = new RpcLimits({ … })` (affects hubs constructed
|
|
570
|
+
* *after* the replacement).
|
|
571
|
+
* - **Per-hub**: assign `hub.limits = new RpcLimits({ … })` before peers
|
|
572
|
+
* are created from that hub.
|
|
573
|
+
* - **Per-peer**: set the matching `*Ms` field on the peer directly
|
|
574
|
+
* (e.g. `peer.keepAliveTimeoutMs = 60_000`) before `start()`.
|
|
575
|
+
*
|
|
576
|
+
* Peers snapshot the relevant values from `hub.limits` at construction —
|
|
577
|
+
* later mutations to `hub.limits` don't retroactively affect already-built
|
|
578
|
+
* peers; that's by design and matches the .NET semantics.
|
|
579
|
+
*/
|
|
580
|
+
declare class RpcLimits {
|
|
581
|
+
/** Process-wide default. Hubs read from this when they're constructed
|
|
582
|
+
* unless caller assigns a custom instance to `hub.limits`. */
|
|
583
|
+
static Default: RpcLimits;
|
|
584
|
+
/** Max time to wait for the WebSocket to enter the OPEN state. On a
|
|
585
|
+
* hung connect (mobile after network change, half-open after sleep)
|
|
586
|
+
* the browser can take ~2 min to emit `onerror`/`onclose`; without
|
|
587
|
+
* this cap the reconnect loop blocks for that whole window. */
|
|
588
|
+
connectTimeoutMs: number;
|
|
589
|
+
/** Max time to wait for the server's handshake response after WS opens. */
|
|
590
|
+
handshakeTimeoutMs: number;
|
|
591
|
+
/** Outbound `$sys.KeepAlive` send period. */
|
|
592
|
+
keepAlivePeriodMs: number;
|
|
593
|
+
/** Reaper threshold — connection is force-closed (and the reconnect
|
|
594
|
+
* loop takes over) if no inbound `$sys.KeepAlive` has been seen for
|
|
595
|
+
* this long. Sized to tolerate a complete server stall plus most of
|
|
596
|
+
* one keepalive cycle: the worst-case age of `_lastKeepAliveAt` is
|
|
597
|
+
* `keepAlivePeriodMs + stall_duration`, so with these defaults the
|
|
598
|
+
* tolerated stall is ~15 s. */
|
|
599
|
+
keepAliveTimeoutMs: number;
|
|
600
|
+
constructor(overrides?: Partial<RpcLimits>);
|
|
601
|
+
}
|
|
602
|
+
|
|
561
603
|
declare class RpcClientPeerReconnectDelayer extends RetryDelayer {
|
|
562
604
|
constructor();
|
|
563
605
|
}
|
|
@@ -663,6 +705,13 @@ declare class RpcHub {
|
|
|
663
705
|
/** Shared reconnect delayer used by every client peer in this hub. Swap in
|
|
664
706
|
* a custom subclass (e.g. app-level signal-gated) before peers start. */
|
|
665
707
|
reconnectDelayer: RpcClientPeerReconnectDelayer;
|
|
708
|
+
/** Connection-lifecycle timing limits. Peers constructed against this hub
|
|
709
|
+
* read their initial `*Ms` field values from this instance; later
|
|
710
|
+
* mutations affect only peers built afterward (existing peers can be
|
|
711
|
+
* retuned by setting their `*Ms` fields directly). Defaults to the
|
|
712
|
+
* process-wide {@link RpcLimits.Default} so a single global override
|
|
713
|
+
* propagates to every hub that hasn't replaced this. */
|
|
714
|
+
limits: RpcLimits;
|
|
666
715
|
/** URL used by {@link defaultPeer} to resolve / create the default client
|
|
667
716
|
* peer. Must be set before the first {@link defaultPeer} access. */
|
|
668
717
|
defaultPeerUrl: string | undefined;
|
|
@@ -746,8 +795,6 @@ declare class RpcSharedObjectTracker {
|
|
|
746
795
|
|
|
747
796
|
/** WebSocket close code sent by the server when the client's serialization format is unsupported. */
|
|
748
797
|
declare const RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
|
|
749
|
-
/** Maximum time to wait for the server's handshake response after opening the socket. */
|
|
750
|
-
declare const HANDSHAKE_TIMEOUT_MS = 10000;
|
|
751
798
|
/** Builds the WebSocket connection URL for an RpcClientPeer. */
|
|
752
799
|
type RpcConnectionUrlResolver = (peer: RpcClientPeer) => string | Promise<string>;
|
|
753
800
|
/** Default connection URL provider — sets `clientId` and `f` query parameters,
|
|
@@ -802,9 +849,11 @@ declare abstract class RpcPeer {
|
|
|
802
849
|
/** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet
|
|
803
850
|
* for the current connection. Useful for diagnostics. */
|
|
804
851
|
private _lastKeepAliveAt;
|
|
805
|
-
/** Outbound keep-alive send period
|
|
852
|
+
/** Outbound keep-alive send period. Initialized from `hub.limits` at
|
|
853
|
+
* construction; set directly to override on a single peer. */
|
|
806
854
|
keepAlivePeriodMs: number;
|
|
807
|
-
/** Inbound keep-alive silence tolerance
|
|
855
|
+
/** Inbound keep-alive silence tolerance. Initialized from `hub.limits`
|
|
856
|
+
* at construction; set directly to override on a single peer. */
|
|
808
857
|
keepAliveTimeoutMs: number;
|
|
809
858
|
protected constructor(hub: RpcHub, ref: string);
|
|
810
859
|
get connection(): RpcConnection | undefined;
|
|
@@ -886,8 +935,13 @@ declare class RpcClientPeer extends RpcPeer {
|
|
|
886
935
|
* `Cookie` headers to the WS upgrade, which the browser `WebSocket`
|
|
887
936
|
* constructor can't do). Must be set before `start()`. */
|
|
888
937
|
webSocketFactory: ((url: string) => WebSocketLike) | undefined;
|
|
938
|
+
/** Max time to wait for the WebSocket to reach OPEN before aborting the
|
|
939
|
+
* connection attempt. Initialized from `hub.limits` at construction;
|
|
940
|
+
* set directly to override on a single peer. */
|
|
941
|
+
connectTimeoutMs: number;
|
|
889
942
|
/** Max time to wait for the server's handshake response before aborting
|
|
890
|
-
* the connection attempt.
|
|
943
|
+
* the connection attempt. Initialized from `hub.limits` at construction;
|
|
944
|
+
* set directly to override on a single peer. */
|
|
891
945
|
handshakeTimeoutMs: number;
|
|
892
946
|
readonly peerChanged: EventHandlerSet<void>;
|
|
893
947
|
/** Fired when the server rejects the connection due to an unsupported serialization format (close code 4010). */
|
|
@@ -1342,4 +1396,4 @@ declare function xxh3_64str(input: string, seed?: bigint): bigint;
|
|
|
1342
1396
|
*/
|
|
1343
1397
|
declare function computeMethodHash(methodName: string): number;
|
|
1344
1398
|
|
|
1345
|
-
export { ARG_DELIMITER, ENVELOPE_DELIMITER, FRAME_DELIMITER,
|
|
1399
|
+
export { ARG_DELIMITER, ENVELOPE_DELIMITER, FRAME_DELIMITER, type IRpcObject, IncreasingSeqCompressor, type MethodMeta, RPC_CLOSE_CODE_UNSUPPORTED_FORMAT, type RemoteHandshake, type RpcCallOptions, RpcCallStage, RpcCallTypeId, RpcClientPeer, RpcClientPeerReconnectDelayer, type RpcConnection, RpcConnectionState, type RpcConnectionUrlResolver, type RpcDeserializedMessage, type RpcDispatchContext, RpcError, RpcHub, RpcInboundCall, RpcInboundCallTracker, RpcJsonSerializationFormat, RpcLimits, type RpcLogScope, type RpcMessage, RpcMessageChannelConnection, RpcMessagePackCompactSerializationFormat, RpcMessagePackSerializationFormat, type RpcMethodDef, type RpcMethodDefInput, RpcMethodRegistry, type RpcObjectId, RpcObjectKind, RpcOutboundCall, RpcOutboundCallTracker, RpcPeer, type RpcPeerFactory, RpcPeerRefBuilder, type RpcPeerState, RpcPeerStateKind, RpcPeerStateMonitor, type RpcReceivedMessage, RpcRemoteExecutionMode, RpcRemoteObjectTracker, RpcSerializationFormat, RpcSerializationFormatResolver, RpcServerPeer, type RpcServiceDef, RpcServiceHost, type RpcServiceImpl, RpcSharedObjectTracker, RpcStream, type RpcStreamOptions, type RpcStreamRef, RpcStreamSender, type RpcStreamSource, RpcSystemCallHandler, RpcSystemCallSender, RpcSystemCalls, RpcType, RpcWebSocketConnection, type RpcWireData, type ServiceMeta, type WebSocketLike, type WebSocketServer, WebSocketState, computeMethodHash, createBinaryEncoder, createMessageChannelPair, createRpcClient, defaultBinaryDecoder, defaultBinaryEncoder, defaultConnectionUrlResolver, defineRpcService, deserializeBinaryMessage, deserializeCompactBinaryMessage, deserializeMessage, getLogs, getMethodsMeta, getServiceMeta, getStateDescription, isConnected, likelyConnected, parseStreamRef, resolveStreamRefs, rpcMethod, rpcService, serializeBinaryFrame, serializeBinaryMessage, serializeCompactBinaryMessage, serializeFrame, serializeMessage, splitBinaryFrame, splitCompactBinaryFrame, splitFrame, wireMethodName, xxh3_64, xxh3_64str };
|
package/dist/index.js
CHANGED
|
@@ -471,7 +471,7 @@ var RpcWebSocketConnection = class {
|
|
|
471
471
|
return this._ws.readyState === WebSocketState.OPEN;
|
|
472
472
|
}
|
|
473
473
|
get whenConnected() {
|
|
474
|
-
return this._connected
|
|
474
|
+
return this._connected;
|
|
475
475
|
}
|
|
476
476
|
send(serializedMessage) {
|
|
477
477
|
this._sendRaw(serializedMessage);
|
|
@@ -1043,7 +1043,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1043
1043
|
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.
|
|
1044
1044
|
*/
|
|
1045
1045
|
async writeFrom(source) {
|
|
1046
|
-
await this._started
|
|
1046
|
+
await this._started;
|
|
1047
1047
|
if (this._ended) return;
|
|
1048
1048
|
const iterator = source[Symbol.asyncIterator]();
|
|
1049
1049
|
this._iterator = iterator;
|
|
@@ -1242,7 +1242,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1242
1242
|
async _waitAckReady() {
|
|
1243
1243
|
if (this._acks.length > 0 || this._ended) return;
|
|
1244
1244
|
this._whenAckReady = new PromiseSource3();
|
|
1245
|
-
await this._whenAckReady
|
|
1245
|
+
await this._whenAckReady;
|
|
1246
1246
|
this._whenAckReady = null;
|
|
1247
1247
|
}
|
|
1248
1248
|
// -- IRpcObject --
|
|
@@ -1564,7 +1564,7 @@ var RpcStream = class _RpcStream {
|
|
|
1564
1564
|
return { value: void 0, done: true };
|
|
1565
1565
|
}
|
|
1566
1566
|
this._consumerWaiting = new PromiseSource4();
|
|
1567
|
-
await this._consumerWaiting
|
|
1567
|
+
await this._consumerWaiting;
|
|
1568
1568
|
}
|
|
1569
1569
|
},
|
|
1570
1570
|
return: () => {
|
|
@@ -2020,7 +2020,7 @@ var RpcSharedObjectTracker = class {
|
|
|
2020
2020
|
};
|
|
2021
2021
|
|
|
2022
2022
|
// src/rpc-peer.ts
|
|
2023
|
-
import { EventHandlerSet as EventHandlerSet3, PromiseSource as PromiseSource5 } from "@actuallab/core";
|
|
2023
|
+
import { EventHandlerSet as EventHandlerSet3, PromiseSource as PromiseSource5, TimeoutError, withTimeout } from "@actuallab/core";
|
|
2024
2024
|
|
|
2025
2025
|
// src/rpc-serialization-format.ts
|
|
2026
2026
|
import { Decoder as Decoder3 } from "@msgpack/msgpack";
|
|
@@ -2184,9 +2184,6 @@ function yieldToEventLoop() {
|
|
|
2184
2184
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
2185
2185
|
}
|
|
2186
2186
|
var RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
|
|
2187
|
-
var HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
2188
|
-
var KEEP_ALIVE_PERIOD_MS = 15e3;
|
|
2189
|
-
var KEEP_ALIVE_TIMEOUT_MS = 55e3;
|
|
2190
2187
|
var { debugLog: debugLog2, infoLog, warnLog: warnLog6, errorLog: errorLog2 } = getLogs("RpcPeer");
|
|
2191
2188
|
var defaultConnectionUrlResolver = (peer) => {
|
|
2192
2189
|
const formatKey = peer.serializationFormat.key;
|
|
@@ -2248,13 +2245,17 @@ var RpcPeer = class {
|
|
|
2248
2245
|
/** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet
|
|
2249
2246
|
* for the current connection. Useful for diagnostics. */
|
|
2250
2247
|
_lastKeepAliveAt = 0;
|
|
2251
|
-
/** Outbound keep-alive send period
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2248
|
+
/** Outbound keep-alive send period. Initialized from `hub.limits` at
|
|
2249
|
+
* construction; set directly to override on a single peer. */
|
|
2250
|
+
keepAlivePeriodMs;
|
|
2251
|
+
/** Inbound keep-alive silence tolerance. Initialized from `hub.limits`
|
|
2252
|
+
* at construction; set directly to override on a single peer. */
|
|
2253
|
+
keepAliveTimeoutMs;
|
|
2255
2254
|
constructor(hub, ref) {
|
|
2256
2255
|
this.hub = hub;
|
|
2257
2256
|
this.ref = ref;
|
|
2257
|
+
this.keepAlivePeriodMs = hub.limits.keepAlivePeriodMs;
|
|
2258
|
+
this.keepAliveTimeoutMs = hub.limits.keepAliveTimeoutMs;
|
|
2258
2259
|
}
|
|
2259
2260
|
get connection() {
|
|
2260
2261
|
return this._connection;
|
|
@@ -2347,7 +2348,7 @@ var RpcPeer = class {
|
|
|
2347
2348
|
this._sendWireData(outboundCall.serializedWireData);
|
|
2348
2349
|
};
|
|
2349
2350
|
this.connectionStateChanged.add(stateHandler);
|
|
2350
|
-
outboundCall.result.
|
|
2351
|
+
outboundCall.result.then(() => this.connectionStateChanged.remove(stateHandler)).catch(() => this.connectionStateChanged.remove(stateHandler));
|
|
2351
2352
|
}
|
|
2352
2353
|
const signal = options?.signal;
|
|
2353
2354
|
if (signal !== void 0) {
|
|
@@ -2360,7 +2361,7 @@ var RpcPeer = class {
|
|
|
2360
2361
|
}
|
|
2361
2362
|
};
|
|
2362
2363
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
2363
|
-
outboundCall.result.
|
|
2364
|
+
outboundCall.result.then(() => signal.removeEventListener("abort", onAbort)).catch(() => signal.removeEventListener("abort", onAbort));
|
|
2364
2365
|
}
|
|
2365
2366
|
return outboundCall;
|
|
2366
2367
|
}
|
|
@@ -2573,9 +2574,14 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2573
2574
|
* `Cookie` headers to the WS upgrade, which the browser `WebSocket`
|
|
2574
2575
|
* constructor can't do). Must be set before `start()`. */
|
|
2575
2576
|
webSocketFactory;
|
|
2577
|
+
/** Max time to wait for the WebSocket to reach OPEN before aborting the
|
|
2578
|
+
* connection attempt. Initialized from `hub.limits` at construction;
|
|
2579
|
+
* set directly to override on a single peer. */
|
|
2580
|
+
connectTimeoutMs;
|
|
2576
2581
|
/** Max time to wait for the server's handshake response before aborting
|
|
2577
|
-
* the connection attempt.
|
|
2578
|
-
|
|
2582
|
+
* the connection attempt. Initialized from `hub.limits` at construction;
|
|
2583
|
+
* set directly to override on a single peer. */
|
|
2584
|
+
handshakeTimeoutMs;
|
|
2579
2585
|
peerChanged = new EventHandlerSet3();
|
|
2580
2586
|
/** Fired when the server rejects the connection due to an unsupported serialization format (close code 4010). */
|
|
2581
2587
|
unsupportedFormat = new EventHandlerSet3();
|
|
@@ -2606,6 +2612,8 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2606
2612
|
*/
|
|
2607
2613
|
constructor(hub, url, mustStart = true) {
|
|
2608
2614
|
super(hub, url);
|
|
2615
|
+
this.connectTimeoutMs = hub.limits.connectTimeoutMs;
|
|
2616
|
+
this.handshakeTimeoutMs = hub.limits.handshakeTimeoutMs;
|
|
2609
2617
|
this.clientId = guidToBase64Url(this.id);
|
|
2610
2618
|
const resolver = RpcSerializationFormatResolver.Default;
|
|
2611
2619
|
this.serializationFormat = resolver.get(parseFormatFromUrl(url) ?? resolver.defaultFormatKey);
|
|
@@ -2675,14 +2683,27 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2675
2683
|
whenClosed.resolve(ev);
|
|
2676
2684
|
closedController.abort();
|
|
2677
2685
|
});
|
|
2678
|
-
const closedRejection = whenClosed.
|
|
2686
|
+
const closedRejection = whenClosed.then(() => {
|
|
2679
2687
|
throw new Error("Connection failed");
|
|
2680
2688
|
});
|
|
2681
2689
|
closedRejection.catch(() => {
|
|
2682
2690
|
});
|
|
2683
2691
|
this._pendingHandshake = new PromiseSource5();
|
|
2684
2692
|
this.setupConnection(conn);
|
|
2685
|
-
|
|
2693
|
+
const connectTimeoutMs = this.connectTimeoutMs;
|
|
2694
|
+
try {
|
|
2695
|
+
await withTimeout(
|
|
2696
|
+
Promise.race([conn.whenConnected, closedRejection]),
|
|
2697
|
+
connectTimeoutMs,
|
|
2698
|
+
"Connect timeout"
|
|
2699
|
+
);
|
|
2700
|
+
} catch (e) {
|
|
2701
|
+
if (e instanceof TimeoutError) {
|
|
2702
|
+
warnLog6?.log(`'${this.ref}': Connect timed out after ${connectTimeoutMs}ms`);
|
|
2703
|
+
conn.close();
|
|
2704
|
+
}
|
|
2705
|
+
throw e;
|
|
2706
|
+
}
|
|
2686
2707
|
this._setConnectionState(2 /* Handshaking */);
|
|
2687
2708
|
try {
|
|
2688
2709
|
this.hub.systemCallSender.handshake(
|
|
@@ -2697,29 +2718,20 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2697
2718
|
throw e;
|
|
2698
2719
|
}
|
|
2699
2720
|
const handshakeTimeoutMs = this.handshakeTimeoutMs;
|
|
2700
|
-
let handshakeTimer;
|
|
2701
|
-
const handshakeTimeout = new Promise((_, reject) => {
|
|
2702
|
-
handshakeTimer = setTimeout(() => {
|
|
2703
|
-
reject(new Error("Handshake timeout"));
|
|
2704
|
-
}, handshakeTimeoutMs);
|
|
2705
|
-
});
|
|
2706
|
-
handshakeTimeout.catch(() => {
|
|
2707
|
-
});
|
|
2708
2721
|
let remoteHandshake;
|
|
2709
2722
|
try {
|
|
2710
|
-
remoteHandshake = await
|
|
2711
|
-
this._pendingHandshake
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2723
|
+
remoteHandshake = await withTimeout(
|
|
2724
|
+
Promise.race([this._pendingHandshake, closedRejection]),
|
|
2725
|
+
handshakeTimeoutMs,
|
|
2726
|
+
"Handshake timeout"
|
|
2727
|
+
);
|
|
2715
2728
|
} catch (e) {
|
|
2716
|
-
if (e instanceof
|
|
2729
|
+
if (e instanceof TimeoutError) {
|
|
2717
2730
|
warnLog6?.log(`'${this.ref}': Handshake timed out after ${handshakeTimeoutMs}ms`);
|
|
2718
2731
|
conn.close();
|
|
2719
2732
|
}
|
|
2720
2733
|
throw e;
|
|
2721
2734
|
} finally {
|
|
2722
|
-
if (handshakeTimer !== void 0) clearTimeout(handshakeTimer);
|
|
2723
2735
|
this._pendingHandshake = void 0;
|
|
2724
2736
|
}
|
|
2725
2737
|
this._remoteHandshakeIndex = remoteHandshake.Index ?? 0;
|
|
@@ -2738,7 +2750,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2738
2750
|
this._setConnectionState(3 /* Connected */);
|
|
2739
2751
|
this._tryIndex = 0;
|
|
2740
2752
|
await this._reconnect(isPeerChanged, closedController.signal);
|
|
2741
|
-
await whenClosed
|
|
2753
|
+
await whenClosed;
|
|
2742
2754
|
} catch (e) {
|
|
2743
2755
|
debugLog2?.log(`'${this.ref}': run() iteration failed:`, e);
|
|
2744
2756
|
}
|
|
@@ -2878,7 +2890,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2878
2890
|
signal: closedSignal
|
|
2879
2891
|
}
|
|
2880
2892
|
);
|
|
2881
|
-
const result = await reconnectCall.result
|
|
2893
|
+
const result = await reconnectCall.result;
|
|
2882
2894
|
const bytes = _toBytes2(result);
|
|
2883
2895
|
if (bytes === null) return new Set(eligible.map((c) => c.callId));
|
|
2884
2896
|
return new Set(IncreasingSeqCompressor.deserialize(bytes));
|
|
@@ -2951,6 +2963,32 @@ function _toBytes2(value) {
|
|
|
2951
2963
|
return null;
|
|
2952
2964
|
}
|
|
2953
2965
|
|
|
2966
|
+
// src/rpc-limits.ts
|
|
2967
|
+
var RpcLimits = class _RpcLimits {
|
|
2968
|
+
/** Process-wide default. Hubs read from this when they're constructed
|
|
2969
|
+
* unless caller assigns a custom instance to `hub.limits`. */
|
|
2970
|
+
static Default = new _RpcLimits();
|
|
2971
|
+
/** Max time to wait for the WebSocket to enter the OPEN state. On a
|
|
2972
|
+
* hung connect (mobile after network change, half-open after sleep)
|
|
2973
|
+
* the browser can take ~2 min to emit `onerror`/`onclose`; without
|
|
2974
|
+
* this cap the reconnect loop blocks for that whole window. */
|
|
2975
|
+
connectTimeoutMs = 1e4;
|
|
2976
|
+
/** Max time to wait for the server's handshake response after WS opens. */
|
|
2977
|
+
handshakeTimeoutMs = 1e4;
|
|
2978
|
+
/** Outbound `$sys.KeepAlive` send period. */
|
|
2979
|
+
keepAlivePeriodMs = 1e4;
|
|
2980
|
+
/** Reaper threshold — connection is force-closed (and the reconnect
|
|
2981
|
+
* loop takes over) if no inbound `$sys.KeepAlive` has been seen for
|
|
2982
|
+
* this long. Sized to tolerate a complete server stall plus most of
|
|
2983
|
+
* one keepalive cycle: the worst-case age of `_lastKeepAliveAt` is
|
|
2984
|
+
* `keepAlivePeriodMs + stall_duration`, so with these defaults the
|
|
2985
|
+
* tolerated stall is ~15 s. */
|
|
2986
|
+
keepAliveTimeoutMs = 25e3;
|
|
2987
|
+
constructor(overrides) {
|
|
2988
|
+
if (overrides) Object.assign(this, overrides);
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
|
|
2954
2992
|
// src/rpc-client-peer-reconnect-delayer.ts
|
|
2955
2993
|
import { RetryDelaySeq, RetryDelayer } from "@actuallab/core";
|
|
2956
2994
|
var RpcClientPeerReconnectDelayer = class extends RetryDelayer {
|
|
@@ -3565,6 +3603,13 @@ var RpcHub = class {
|
|
|
3565
3603
|
/** Shared reconnect delayer used by every client peer in this hub. Swap in
|
|
3566
3604
|
* a custom subclass (e.g. app-level signal-gated) before peers start. */
|
|
3567
3605
|
reconnectDelayer = new RpcClientPeerReconnectDelayer();
|
|
3606
|
+
/** Connection-lifecycle timing limits. Peers constructed against this hub
|
|
3607
|
+
* read their initial `*Ms` field values from this instance; later
|
|
3608
|
+
* mutations affect only peers built afterward (existing peers can be
|
|
3609
|
+
* retuned by setting their `*Ms` fields directly). Defaults to the
|
|
3610
|
+
* process-wide {@link RpcLimits.Default} so a single global override
|
|
3611
|
+
* propagates to every hub that hasn't replaced this. */
|
|
3612
|
+
limits = RpcLimits.Default;
|
|
3568
3613
|
/** URL used by {@link defaultPeer} to resolve / create the default client
|
|
3569
3614
|
* peer. Must be set before the first {@link defaultPeer} access. */
|
|
3570
3615
|
defaultPeerUrl;
|
|
@@ -3730,7 +3775,7 @@ var RpcHub = class {
|
|
|
3730
3775
|
throw new Error("Outbound call failed: not connected and AwaitForConnection is not set.");
|
|
3731
3776
|
const callArgs = args.slice(0, methodDef.argCount);
|
|
3732
3777
|
const outboundCall = peer.call(wireName, callArgs);
|
|
3733
|
-
const result = await outboundCall.result
|
|
3778
|
+
const result = await outboundCall.result;
|
|
3734
3779
|
const ref = parseStreamRef(result);
|
|
3735
3780
|
if (!ref)
|
|
3736
3781
|
throw new Error(
|
|
@@ -3747,7 +3792,7 @@ var RpcHub = class {
|
|
|
3747
3792
|
throw new Error("Outbound call failed: not connected and AwaitForConnection is not set.");
|
|
3748
3793
|
const callArgs = args.slice(0, methodDef.argCount);
|
|
3749
3794
|
const outboundCall = peer.call(wireName, callArgs, callOptions);
|
|
3750
|
-
const result = await outboundCall.result
|
|
3795
|
+
const result = await outboundCall.result;
|
|
3751
3796
|
return resolveStreamRefs(result, peer);
|
|
3752
3797
|
};
|
|
3753
3798
|
}
|
|
@@ -4012,7 +4057,6 @@ export {
|
|
|
4012
4057
|
ARG_DELIMITER,
|
|
4013
4058
|
ENVELOPE_DELIMITER,
|
|
4014
4059
|
FRAME_DELIMITER,
|
|
4015
|
-
HANDSHAKE_TIMEOUT_MS,
|
|
4016
4060
|
IncreasingSeqCompressor,
|
|
4017
4061
|
RPC_CLOSE_CODE_UNSUPPORTED_FORMAT,
|
|
4018
4062
|
RpcCallStage,
|
|
@@ -4025,6 +4069,7 @@ export {
|
|
|
4025
4069
|
RpcInboundCall,
|
|
4026
4070
|
RpcInboundCallTracker,
|
|
4027
4071
|
RpcJsonSerializationFormat,
|
|
4072
|
+
RpcLimits,
|
|
4028
4073
|
RpcMessageChannelConnection,
|
|
4029
4074
|
RpcMessagePackCompactSerializationFormat,
|
|
4030
4075
|
RpcMessagePackSerializationFormat,
|