@procedures/client 0.2.3 → 0.4.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/CHANGELOG.md +94 -0
- package/README.md +77 -44
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/client/transport.d.ts.map +1 -1
- package/dist/adapters/http/client/transport.js +35 -3
- package/dist/adapters/http/client/transport.js.map +1 -1
- package/dist/adapters/http/client/types.d.ts +3 -2
- package/dist/adapters/http/client/types.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.d.ts +2 -2
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +54 -22
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/adapters/http/shared/headers.d.ts +1 -1
- package/dist/adapters/http/shared/headers.d.ts.map +1 -1
- package/dist/adapters/http/shared/headers.js +7 -6
- package/dist/adapters/http/shared/headers.js.map +1 -1
- package/dist/adapters/local/client/transport.d.ts +3 -0
- package/dist/adapters/local/client/transport.d.ts.map +1 -1
- package/dist/adapters/local/client/transport.js +67 -6
- package/dist/adapters/local/client/transport.js.map +1 -1
- package/dist/adapters/local/client/types.d.ts +15 -0
- package/dist/adapters/local/client/types.d.ts.map +1 -1
- package/dist/adapters/local/client/types.js.map +1 -1
- package/dist/adapters/mock/client/transport.d.ts +3 -2
- package/dist/adapters/mock/client/transport.d.ts.map +1 -1
- package/dist/adapters/mock/client/transport.js +8 -5
- package/dist/adapters/mock/client/transport.js.map +1 -1
- package/dist/adapters/mock/client/types.d.ts +7 -6
- package/dist/adapters/mock/client/types.d.ts.map +1 -1
- package/dist/adapters/port/transport.d.ts +62 -0
- package/dist/adapters/port/transport.d.ts.map +1 -0
- package/dist/adapters/port/transport.js +94 -0
- package/dist/adapters/port/transport.js.map +1 -0
- package/dist/adapters/websocket/client/index.d.ts +1 -1
- package/dist/adapters/websocket/client/index.d.ts.map +1 -1
- package/dist/adapters/websocket/client/index.js.map +1 -1
- package/dist/adapters/websocket/client/node.d.ts +30 -0
- package/dist/adapters/websocket/client/node.d.ts.map +1 -0
- package/dist/adapters/websocket/client/node.js +32 -0
- package/dist/adapters/websocket/client/node.js.map +1 -0
- package/dist/adapters/websocket/client/transport.d.ts +47 -7
- package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.js +229 -75
- package/dist/adapters/websocket/client/transport.js.map +1 -1
- package/dist/adapters/websocket/client/types.d.ts +71 -21
- package/dist/adapters/websocket/client/types.d.ts.map +1 -1
- package/dist/adapters/websocket/client/types.js.map +1 -1
- package/dist/adapters/websocket/server/transport.d.ts +65 -24
- package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/server/transport.js +192 -263
- package/dist/adapters/websocket/server/transport.js.map +1 -1
- package/dist/adapters/websocket/server/types.d.ts +20 -1
- package/dist/adapters/websocket/server/types.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.d.ts +129 -0
- package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -0
- package/dist/adapters/websocket/shared/rpc.js +425 -0
- package/dist/adapters/websocket/shared/rpc.js.map +1 -0
- package/dist/browser.d.ts +29 -7
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +26 -3
- package/dist/browser.js.map +1 -1
- package/dist/client/call-types.d.ts +10 -10
- package/dist/client/call-types.d.ts.map +1 -1
- package/dist/client/call-types.js +1 -6
- package/dist/client/call-types.js.map +1 -1
- package/dist/client/client.d.ts +6 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +23 -9
- package/dist/client/client.js.map +1 -1
- package/dist/client/deadline.d.ts +38 -0
- package/dist/client/deadline.d.ts.map +1 -0
- package/dist/client/deadline.js +60 -0
- package/dist/client/deadline.js.map +1 -0
- package/dist/client/errors/factory.d.ts.map +1 -1
- package/dist/client/errors/factory.js +11 -7
- package/dist/client/errors/factory.js.map +1 -1
- package/dist/client/errors/http-errors.d.ts.map +1 -1
- package/dist/client/errors/http-errors.js +6 -0
- package/dist/client/errors/http-errors.js.map +1 -1
- package/dist/client/errors/index.d.ts +1 -1
- package/dist/client/errors/index.js +1 -1
- package/dist/client/errors/registry.d.ts +2 -1
- package/dist/client/errors/registry.d.ts.map +1 -1
- package/dist/client/errors/registry.js +72 -22
- package/dist/client/errors/registry.js.map +1 -1
- package/dist/client/index.d.ts +9 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +8 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/middleware/cache.d.ts +19 -5
- package/dist/client/middleware/cache.d.ts.map +1 -1
- package/dist/client/middleware/cache.js +106 -18
- package/dist/client/middleware/cache.js.map +1 -1
- package/dist/client/middleware/circuit-breaker.d.ts +28 -10
- package/dist/client/middleware/circuit-breaker.d.ts.map +1 -1
- package/dist/client/middleware/circuit-breaker.js +35 -24
- package/dist/client/middleware/circuit-breaker.js.map +1 -1
- package/dist/client/middleware/contexts.d.ts +16 -10
- package/dist/client/middleware/contexts.d.ts.map +1 -1
- package/dist/client/middleware/contexts.js.map +1 -1
- package/dist/client/middleware/items.d.ts +31 -2
- package/dist/client/middleware/items.d.ts.map +1 -1
- package/dist/client/middleware/items.js +86 -6
- package/dist/client/middleware/items.js.map +1 -1
- package/dist/client/middleware/lru.d.ts +2 -0
- package/dist/client/middleware/lru.d.ts.map +1 -1
- package/dist/client/middleware/lru.js +11 -0
- package/dist/client/middleware/lru.js.map +1 -1
- package/dist/client/middleware/rate-limit.d.ts +7 -6
- package/dist/client/middleware/rate-limit.d.ts.map +1 -1
- package/dist/client/middleware/rate-limit.js +9 -7
- package/dist/client/middleware/rate-limit.js.map +1 -1
- package/dist/client/middleware/resilience.d.ts +61 -0
- package/dist/client/middleware/resilience.d.ts.map +1 -0
- package/dist/client/middleware/resilience.js +59 -0
- package/dist/client/middleware/resilience.js.map +1 -0
- package/dist/client/middleware/retry.d.ts +45 -11
- package/dist/client/middleware/retry.d.ts.map +1 -1
- package/dist/client/middleware/retry.js +102 -22
- package/dist/client/middleware/retry.js.map +1 -1
- package/dist/client/middleware/timeout.d.ts +15 -12
- package/dist/client/middleware/timeout.d.ts.map +1 -1
- package/dist/client/middleware/timeout.js +48 -46
- package/dist/client/middleware/timeout.js.map +1 -1
- package/dist/client/middleware/tracing.d.ts.map +1 -1
- package/dist/client/middleware/tracing.js +11 -3
- package/dist/client/middleware/tracing.js.map +1 -1
- package/dist/client/types.d.ts +9 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js.map +1 -1
- package/dist/clock.d.ts +19 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +18 -0
- package/dist/clock.js.map +1 -0
- package/dist/duration.d.ts +37 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +44 -0
- package/dist/duration.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/machines.d.ts +90 -0
- package/dist/machines.d.ts.map +1 -0
- package/dist/machines.js +127 -0
- package/dist/machines.js.map +1 -0
- package/dist/node.d.ts +11 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +10 -0
- package/dist/node.js.map +1 -0
- package/dist/procedures/capability.d.ts +3 -6
- package/dist/procedures/capability.d.ts.map +1 -1
- package/dist/procedures/capability.js +7 -8
- package/dist/procedures/capability.js.map +1 -1
- package/dist/procedures/collection/index.d.ts +3 -3
- package/dist/procedures/collection/index.d.ts.map +1 -1
- package/dist/procedures/collection/index.js +1 -1
- package/dist/procedures/collection/index.js.map +1 -1
- package/dist/procedures/collection/procedures.d.ts.map +1 -1
- package/dist/procedures/collection/procedures.js +49 -1
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/collection/schemas.d.ts +33 -0
- package/dist/procedures/collection/schemas.d.ts.map +1 -1
- package/dist/procedures/collection/schemas.js +68 -0
- package/dist/procedures/collection/schemas.js.map +1 -1
- package/dist/procedures/collection/storage.d.ts +37 -0
- package/dist/procedures/collection/storage.d.ts.map +1 -1
- package/dist/procedures/core/array.d.ts +1 -1
- package/dist/procedures/core/array.d.ts.map +1 -1
- package/dist/procedures/core/array.js +81 -29
- package/dist/procedures/core/array.js.map +1 -1
- package/dist/procedures/core/index.d.ts.map +1 -1
- package/dist/procedures/core/index.js +17 -2
- package/dist/procedures/core/index.js.map +1 -1
- package/dist/procedures/core/logic.js +1 -1
- package/dist/procedures/core/logic.js.map +1 -1
- package/dist/procedures/core/math.d.ts.map +1 -1
- package/dist/procedures/core/math.js +5 -1
- package/dist/procedures/core/math.js.map +1 -1
- package/dist/procedures/core/meta.d.ts.map +1 -1
- package/dist/procedures/core/meta.js +43 -8
- package/dist/procedures/core/meta.js.map +1 -1
- package/dist/procedures/core/object.d.ts.map +1 -1
- package/dist/procedures/core/object.js +42 -13
- package/dist/procedures/core/object.js.map +1 -1
- package/dist/procedures/core/operands.d.ts.map +1 -1
- package/dist/procedures/core/operands.js +6 -1
- package/dist/procedures/core/operands.js.map +1 -1
- package/dist/procedures/core/string.d.ts.map +1 -1
- package/dist/procedures/core/string.js +4 -2
- package/dist/procedures/core/string.js.map +1 -1
- package/dist/procedures/core/type.d.ts.map +1 -1
- package/dist/procedures/core/type.js +4 -2
- package/dist/procedures/core/type.js.map +1 -1
- package/dist/procedures/define-procedure.d.ts +19 -7
- package/dist/procedures/define-procedure.d.ts.map +1 -1
- package/dist/procedures/define-procedure.js +57 -17
- package/dist/procedures/define-procedure.js.map +1 -1
- package/dist/procedures/discovery.d.ts +4 -6
- package/dist/procedures/discovery.d.ts.map +1 -1
- package/dist/procedures/discovery.js +4 -6
- package/dist/procedures/discovery.js.map +1 -1
- package/dist/procedures/effects.d.ts +16 -0
- package/dist/procedures/effects.d.ts.map +1 -1
- package/dist/procedures/effects.js +33 -0
- package/dist/procedures/effects.js.map +1 -1
- package/dist/procedures/idempotency.d.ts +73 -0
- package/dist/procedures/idempotency.d.ts.map +1 -0
- package/dist/procedures/idempotency.js +131 -0
- package/dist/procedures/idempotency.js.map +1 -0
- package/dist/procedures/index.d.ts +7 -3
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +6 -2
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/interceptors.d.ts +17 -4
- package/dist/procedures/interceptors.d.ts.map +1 -1
- package/dist/procedures/interceptors.js +18 -7
- package/dist/procedures/interceptors.js.map +1 -1
- package/dist/procedures/invoke.d.ts +15 -3
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +8 -3
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/manifest.d.ts +83 -0
- package/dist/procedures/manifest.d.ts.map +1 -0
- package/dist/procedures/manifest.js +90 -0
- package/dist/procedures/manifest.js.map +1 -0
- package/dist/procedures/ref.d.ts +4 -2
- package/dist/procedures/ref.d.ts.map +1 -1
- package/dist/procedures/ref.js +2 -1
- package/dist/procedures/ref.js.map +1 -1
- package/dist/procedures/runtime-programs.d.ts +19 -0
- package/dist/procedures/runtime-programs.d.ts.map +1 -0
- package/dist/procedures/runtime-programs.js +24 -0
- package/dist/procedures/runtime-programs.js.map +1 -0
- package/dist/procedures/types.d.ts +16 -0
- package/dist/procedures/types.d.ts.map +1 -1
- package/dist/procedures/types.js.map +1 -1
- package/dist/protocol.d.ts +47 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +68 -0
- package/dist/protocol.js.map +1 -0
- package/dist/server/host-policy.d.ts +4 -3
- package/dist/server/host-policy.d.ts.map +1 -1
- package/dist/server/host-policy.js +6 -3
- package/dist/server/host-policy.js.map +1 -1
- package/dist/server/host-procedures.d.ts +31 -0
- package/dist/server/host-procedures.d.ts.map +1 -0
- package/dist/server/host-procedures.js +75 -0
- package/dist/server/host-procedures.js.map +1 -0
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/procedure-server.d.ts +112 -5
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +273 -19
- package/dist/server/procedure-server.js.map +1 -1
- package/dist/server/request-auth.d.ts +19 -0
- package/dist/server/request-auth.d.ts.map +1 -0
- package/dist/server/request-auth.js +32 -0
- package/dist/server/request-auth.js.map +1 -0
- package/dist/server/types.d.ts +32 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/timers.d.ts +60 -0
- package/dist/timers.d.ts.map +1 -0
- package/dist/timers.js +33 -0
- package/dist/timers.js.map +1 -0
- package/dist/tracing/traceparent.d.ts +26 -0
- package/dist/tracing/traceparent.d.ts.map +1 -0
- package/dist/tracing/traceparent.js +53 -0
- package/dist/tracing/traceparent.js.map +1 -0
- package/package.json +11 -4
- package/src/adapters/http/client/transport.ts +31 -3
- package/src/adapters/http/client/types.ts +3 -2
- package/src/adapters/http/server/transport.ts +61 -26
- package/src/adapters/http/shared/headers.ts +7 -6
- package/src/adapters/local/client/transport.ts +74 -7
- package/src/adapters/local/client/types.ts +18 -0
- package/src/adapters/mock/client/transport.ts +9 -6
- package/src/adapters/mock/client/types.ts +7 -6
- package/src/adapters/port/transport.ts +124 -0
- package/src/adapters/websocket/client/index.ts +1 -0
- package/src/adapters/websocket/client/node.ts +34 -0
- package/src/adapters/websocket/client/transport.ts +251 -81
- package/src/adapters/websocket/client/types.ts +81 -21
- package/src/adapters/websocket/server/transport.ts +199 -301
- package/src/adapters/websocket/server/types.ts +20 -1
- package/src/adapters/websocket/shared/rpc.ts +453 -0
- package/src/browser.ts +51 -3
- package/src/client/call-types.ts +10 -14
- package/src/client/client.ts +28 -9
- package/src/client/deadline.ts +64 -0
- package/src/client/errors/README.md +2 -0
- package/src/client/errors/factory.ts +12 -8
- package/src/client/errors/http-errors.ts +5 -0
- package/src/client/errors/index.ts +1 -1
- package/src/client/errors/registry.ts +79 -22
- package/src/client/index.ts +21 -3
- package/src/client/middleware/cache.ts +120 -23
- package/src/client/middleware/circuit-breaker.ts +65 -35
- package/src/client/middleware/contexts.ts +17 -10
- package/src/client/middleware/items.ts +101 -6
- package/src/client/middleware/lru.ts +12 -0
- package/src/client/middleware/rate-limit.ts +9 -9
- package/src/client/middleware/resilience.ts +81 -0
- package/src/client/middleware/retry.ts +146 -34
- package/src/client/middleware/timeout.ts +57 -55
- package/src/client/middleware/tracing.ts +13 -4
- package/src/client/types.ts +9 -1
- package/src/clock.ts +21 -0
- package/src/duration.ts +56 -0
- package/src/index.ts +4 -0
- package/src/machines.ts +186 -0
- package/src/node.ts +11 -0
- package/src/procedures/capability.ts +7 -8
- package/src/procedures/collection/index.ts +9 -1
- package/src/procedures/collection/procedures.ts +57 -0
- package/src/procedures/collection/schemas.ts +110 -0
- package/src/procedures/collection/storage.ts +38 -0
- package/src/procedures/core/array.ts +79 -31
- package/src/procedures/core/index.ts +17 -2
- package/src/procedures/core/logic.ts +1 -1
- package/src/procedures/core/math.ts +5 -1
- package/src/procedures/core/meta.ts +45 -8
- package/src/procedures/core/object.ts +48 -14
- package/src/procedures/core/operands.ts +7 -0
- package/src/procedures/core/string.ts +4 -2
- package/src/procedures/core/type.ts +4 -2
- package/src/procedures/define-procedure.ts +73 -20
- package/src/procedures/discovery.ts +4 -6
- package/src/procedures/effects.ts +35 -0
- package/src/procedures/idempotency.ts +152 -0
- package/src/procedures/index.ts +18 -2
- package/src/procedures/interceptors.ts +28 -11
- package/src/procedures/invoke.ts +19 -5
- package/src/procedures/manifest.ts +158 -0
- package/src/procedures/ref.ts +4 -2
- package/src/procedures/runtime-programs.ts +27 -0
- package/src/procedures/types.ts +16 -0
- package/src/protocol.ts +108 -0
- package/src/server/host-policy.ts +8 -5
- package/src/server/host-procedures.ts +102 -0
- package/src/server/index.ts +5 -2
- package/src/server/procedure-server.ts +328 -12
- package/src/server/request-auth.ts +35 -0
- package/src/server/types.ts +37 -0
- package/src/timers.ts +82 -0
- package/src/tracing/traceparent.ts +61 -0
- package/dist/procedures/cli/announce.d.ts +0 -67
- package/dist/procedures/cli/announce.d.ts.map +0 -1
- package/dist/procedures/cli/announce.js +0 -392
- package/dist/procedures/cli/announce.js.map +0 -1
- package/dist/procedures/cli/discover.d.ts +0 -66
- package/dist/procedures/cli/discover.d.ts.map +0 -1
- package/dist/procedures/cli/discover.js +0 -310
- package/dist/procedures/cli/discover.js.map +0 -1
- package/dist/procedures/cli/index.d.ts +0 -13
- package/dist/procedures/cli/index.d.ts.map +0 -1
- package/dist/procedures/cli/index.js +0 -203
- package/dist/procedures/cli/index.js.map +0 -1
- package/dist/procedures/cli/init.d.ts +0 -17
- package/dist/procedures/cli/init.d.ts.map +0 -1
- package/dist/procedures/cli/init.js +0 -336
- package/dist/procedures/cli/init.js.map +0 -1
- package/src/procedures/cli/announce.ts +0 -470
- package/src/procedures/cli/discover.ts +0 -390
- package/src/procedures/cli/index.ts +0 -231
- package/src/procedures/cli/init.ts +0 -370
|
@@ -6,9 +6,20 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Transport, Message, ResponseItem } from "../../../client/types.js";
|
|
9
|
-
import type { WebSocketTransportOptions, WebSocketMessage, ServerRequestHandler, EventHandler } from "./types.js";
|
|
9
|
+
import type { WebSocketTransportOptions, WebSocketMessage, ServerRequestHandler, EventHandler, WebSocketClass } from "./types.js";
|
|
10
10
|
import { WebSocketState } from "./types.js";
|
|
11
11
|
import { withoutInternalKeys } from "../../metadata.js";
|
|
12
|
+
import { IncomingCalls } from "../shared/rpc.js";
|
|
13
|
+
import type { RequestHandler, ServerResponse } from "../../../server/types.js";
|
|
14
|
+
import { methodToPath } from "../../../server/method.js";
|
|
15
|
+
import { optionalMs } from "../../../duration.js";
|
|
16
|
+
import { PROTOCOL_VERSION, protocolVersionOf } from "../../../protocol.js";
|
|
17
|
+
import { TIMER_DEFAULTS } from "../../../timers.js";
|
|
18
|
+
import { realClock, type Cancel, type Clock } from "../../../clock.js";
|
|
19
|
+
import { heartbeat, type Heartbeat } from "@mark1russell7/service/heartbeat";
|
|
20
|
+
import { backoffDelay } from "@mark1russell7/service/backoff";
|
|
21
|
+
import type { MachineTransition } from "../../../machines.js";
|
|
22
|
+
import { WEBSOCKET_CONNECTION_MACHINE } from "../../../machines.js";
|
|
12
23
|
|
|
13
24
|
/**
|
|
14
25
|
* Pending request waiting for response.
|
|
@@ -64,7 +75,7 @@ interface PendingRequest {
|
|
|
64
75
|
/** The response items of the request, in order */
|
|
65
76
|
queue: ItemQueue<ResponseItem<unknown>>;
|
|
66
77
|
/** The request timeout: it runs until the first frame arrives */
|
|
67
|
-
timeout?:
|
|
78
|
+
timeout?: Cancel | undefined;
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
/**
|
|
@@ -85,6 +96,23 @@ interface PendingRequest {
|
|
|
85
96
|
* const client = new Client({ transport });
|
|
86
97
|
* ```
|
|
87
98
|
*/
|
|
99
|
+
/** The `readyState` of an open socket. The global `WebSocket` and the `ws` package use the same number. */
|
|
100
|
+
const OPEN = 1;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The error handler of a socket that the transport left. A `WebSocket` of the `ws` package that
|
|
104
|
+
* closes during its handshake emits an error. Without a handler, that error ends the process.
|
|
105
|
+
*/
|
|
106
|
+
const ignoreError = (): void => undefined;
|
|
107
|
+
|
|
108
|
+
/** This function removes the handlers of the transport from a socket, and keeps `ignoreError`. */
|
|
109
|
+
function detach(ws: WebSocket): void {
|
|
110
|
+
ws.onopen = null;
|
|
111
|
+
ws.onmessage = null;
|
|
112
|
+
ws.onclose = null;
|
|
113
|
+
ws.onerror = ignoreError;
|
|
114
|
+
}
|
|
115
|
+
|
|
88
116
|
function errorItem<TRes>(id: string, code: string, message: string, retryable: boolean): ResponseItem<TRes> {
|
|
89
117
|
return { id, status: { type: "error", code, message, retryable }, payload: null as TRes, metadata: {} };
|
|
90
118
|
}
|
|
@@ -93,6 +121,59 @@ function abortedItem<TRes>(id: string): ResponseItem<TRes> {
|
|
|
93
121
|
return errorItem<TRes>(id, "ABORTED", "Request was aborted", false);
|
|
94
122
|
}
|
|
95
123
|
|
|
124
|
+
/**
|
|
125
|
+
* The handler of the calls of the server without a `serve` option: the older `onServerRequest`
|
|
126
|
+
* function (it gets the path and the input), or else a `NOT_FOUND` answer.
|
|
127
|
+
*/
|
|
128
|
+
function legacyHandler(onServerRequest: ServerRequestHandler | undefined): RequestHandler {
|
|
129
|
+
return {
|
|
130
|
+
async handle(request): Promise<ServerResponse> {
|
|
131
|
+
if (!onServerRequest) {
|
|
132
|
+
return {
|
|
133
|
+
id: request.id,
|
|
134
|
+
status: { type: "error", code: "NOT_FOUND", message: "This client serves no procedures", retryable: false },
|
|
135
|
+
metadata: {},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const payload = await onServerRequest(methodToPath(request.method), request.payload);
|
|
140
|
+
return { id: request.id, status: { type: "success", code: 200 }, payload, metadata: {} };
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return {
|
|
143
|
+
id: request.id,
|
|
144
|
+
status: { type: "error", code: "HANDLER_ERROR", message: error instanceof Error ? error.message : String(error), retryable: false },
|
|
145
|
+
metadata: {},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The delay before a reconnect try (from 1): an exponential backoff up to `maxDelay`, with a
|
|
154
|
+
* random part. With `jitter: 1` (full jitter, the default), the delay is a random time from 0 to
|
|
155
|
+
* the backoff, so the clients of a server that restarted do not come back at the same moments
|
|
156
|
+
* (roadmap R3). The backoff is `backoffDelay` of `@mark1russell7/service/backoff` (roadmap R37).
|
|
157
|
+
*/
|
|
158
|
+
export function reconnectDelay(
|
|
159
|
+
attempt: number,
|
|
160
|
+
options: { initialDelay: number; backoffMultiplier: number; maxDelay: number; jitter: number },
|
|
161
|
+
random: () => number = Math.random,
|
|
162
|
+
): number {
|
|
163
|
+
return Math.round(
|
|
164
|
+
backoffDelay(
|
|
165
|
+
{
|
|
166
|
+
base: options.initialDelay,
|
|
167
|
+
factor: options.backoffMultiplier,
|
|
168
|
+
cap: options.maxDelay,
|
|
169
|
+
jitter: { kind: "subtract", fraction: options.jitter },
|
|
170
|
+
},
|
|
171
|
+
attempt,
|
|
172
|
+
random,
|
|
173
|
+
),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
96
177
|
export class WebSocketTransport implements Transport {
|
|
97
178
|
readonly name = "websocket";
|
|
98
179
|
|
|
@@ -106,6 +187,7 @@ export class WebSocketTransport implements Transport {
|
|
|
106
187
|
initialDelay: number;
|
|
107
188
|
maxDelay: number;
|
|
108
189
|
backoffMultiplier: number;
|
|
190
|
+
jitter: number;
|
|
109
191
|
};
|
|
110
192
|
connectionTimeout: number;
|
|
111
193
|
requestTimeout: number;
|
|
@@ -114,12 +196,14 @@ export class WebSocketTransport implements Transport {
|
|
|
114
196
|
enabled: boolean;
|
|
115
197
|
interval: number;
|
|
116
198
|
timeout: number;
|
|
199
|
+
misses: number;
|
|
117
200
|
};
|
|
118
201
|
onConnect: (() => void) | undefined;
|
|
119
202
|
onDisconnect: ((reason?: string) => void) | undefined;
|
|
120
203
|
onReconnecting: ((attempt: number) => void) | undefined;
|
|
121
204
|
onError: ((error: Error) => void) | undefined;
|
|
122
205
|
onServerRequest: ServerRequestHandler | undefined;
|
|
206
|
+
serve: RequestHandler | undefined;
|
|
123
207
|
onEvent: EventHandler | undefined;
|
|
124
208
|
};
|
|
125
209
|
/** The requests in progress, by wire id */
|
|
@@ -135,9 +219,21 @@ export class WebSocketTransport implements Transport {
|
|
|
135
219
|
/** Set by close(): the connection is being closed on purpose, so it must not reconnect */
|
|
136
220
|
private closing = false;
|
|
137
221
|
private reconnectAttempts = 0;
|
|
138
|
-
private reconnectTimer:
|
|
139
|
-
|
|
140
|
-
private
|
|
222
|
+
private reconnectTimer: Cancel | undefined = undefined;
|
|
223
|
+
/** The heartbeat of the connection: `heartbeat` of `@mark1russell7/service/heartbeat` (roadmap R37) */
|
|
224
|
+
private heartbeat: Heartbeat | undefined = undefined;
|
|
225
|
+
/** The clock of each timer of the transport (roadmap R35) */
|
|
226
|
+
private readonly clock: Clock;
|
|
227
|
+
/** The class of the sockets, or undefined for the global `WebSocket` (finding of roadmap R36) */
|
|
228
|
+
private readonly webSocketClass: WebSocketClass | undefined;
|
|
229
|
+
/** The observer of the changes of the state (roadmap R35) */
|
|
230
|
+
private readonly transitionHandler: ((transition: MachineTransition) => void) | undefined;
|
|
231
|
+
/** The protocol version of the server, from its hello, or undefined (an older server) (R25) */
|
|
232
|
+
private serverProtocol: number | undefined;
|
|
233
|
+
/** The calls that the server sends on the current socket, served by `serve` (roadmap R24) */
|
|
234
|
+
private incoming: IncomingCalls | undefined;
|
|
235
|
+
/** The time limit of the handshake of the socket that opens now */
|
|
236
|
+
private handshakeTimer: Cancel | undefined = undefined;
|
|
141
237
|
|
|
142
238
|
constructor(options: WebSocketTransportOptions) {
|
|
143
239
|
this.options = {
|
|
@@ -145,40 +241,54 @@ export class WebSocketTransport implements Transport {
|
|
|
145
241
|
reconnect: {
|
|
146
242
|
enabled: options.reconnect?.enabled ?? true,
|
|
147
243
|
maxAttempts: options.reconnect?.maxAttempts ?? Infinity,
|
|
148
|
-
initialDelay: options.reconnect?.initialDelay ??
|
|
149
|
-
maxDelay: options.reconnect?.maxDelay ??
|
|
244
|
+
initialDelay: optionalMs(options.reconnect?.initialDelay, "reconnect.initialDelay") ?? TIMER_DEFAULTS.webSocketClient.reconnectInitialDelayMs,
|
|
245
|
+
maxDelay: optionalMs(options.reconnect?.maxDelay, "reconnect.maxDelay") ?? TIMER_DEFAULTS.webSocketClient.reconnectMaxDelayMs,
|
|
150
246
|
backoffMultiplier: options.reconnect?.backoffMultiplier ?? 1.5,
|
|
247
|
+
jitter: Math.min(1, Math.max(0, options.reconnect?.jitter ?? 1)),
|
|
151
248
|
},
|
|
152
|
-
connectionTimeout: options.connectionTimeout ??
|
|
153
|
-
requestTimeout: options.requestTimeout ??
|
|
249
|
+
connectionTimeout: optionalMs(options.connectionTimeout, "connectionTimeout") ?? TIMER_DEFAULTS.webSocketClient.connectionTimeoutMs,
|
|
250
|
+
requestTimeout: optionalMs(options.requestTimeout, "requestTimeout") ?? TIMER_DEFAULTS.webSocketClient.requestTimeoutMs,
|
|
154
251
|
streamWindow: Math.max(2, options.streamWindow ?? 64),
|
|
155
252
|
heartbeat: {
|
|
156
253
|
enabled: options.heartbeat?.enabled ?? true,
|
|
157
|
-
interval: options.heartbeat?.interval ??
|
|
158
|
-
timeout: options.heartbeat?.timeout ??
|
|
254
|
+
interval: optionalMs(options.heartbeat?.interval, "heartbeat.interval") ?? TIMER_DEFAULTS.webSocketClient.heartbeatIntervalMs,
|
|
255
|
+
timeout: optionalMs(options.heartbeat?.timeout, "heartbeat.timeout") ?? TIMER_DEFAULTS.webSocketClient.heartbeatTimeoutMs,
|
|
256
|
+
misses: Math.max(1, options.heartbeat?.misses ?? TIMER_DEFAULTS.webSocketClient.heartbeatMisses),
|
|
159
257
|
},
|
|
160
258
|
onConnect: options.onConnect,
|
|
161
259
|
onDisconnect: options.onDisconnect,
|
|
162
260
|
onReconnecting: options.onReconnecting,
|
|
163
261
|
onError: options.onError,
|
|
164
262
|
onServerRequest: options.onServerRequest,
|
|
263
|
+
serve: options.serve,
|
|
165
264
|
onEvent: options.onEvent,
|
|
166
265
|
};
|
|
266
|
+
this.clock = options.clock ?? realClock;
|
|
267
|
+
this.webSocketClass = options.webSocket;
|
|
268
|
+
this.transitionHandler = options.onTransition;
|
|
167
269
|
|
|
168
270
|
// Auto-connect
|
|
169
271
|
this.connect();
|
|
170
272
|
}
|
|
171
273
|
|
|
172
|
-
/**
|
|
173
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Change the state, and tell the callers that wait for a connection. `on` is the event of
|
|
276
|
+
* `WEBSOCKET_CONNECTION_MACHINE`: a change goes to `onTransition` (roadmap R35).
|
|
277
|
+
*/
|
|
278
|
+
private setState(state: WebSocketState, on: string): void {
|
|
279
|
+
const from = this.state;
|
|
174
280
|
this.state = state;
|
|
281
|
+
if (from !== state) {
|
|
282
|
+
this.transitionHandler?.({ machine: WEBSOCKET_CONNECTION_MACHINE.id, instance: this.name, from, on, to: state, at: this.clock.now() });
|
|
283
|
+
}
|
|
175
284
|
for (const listener of [...this.stateListeners]) listener();
|
|
176
285
|
}
|
|
177
286
|
|
|
178
287
|
/**
|
|
179
288
|
* Connect to WebSocket server.
|
|
180
289
|
*/
|
|
181
|
-
|
|
290
|
+
/** `on` is the event of the machine: the first connect, or the end of a backoff. */
|
|
291
|
+
private connect(on: "connect" | "reconnect-due" = "connect"): void {
|
|
182
292
|
if (this.closing) {
|
|
183
293
|
return;
|
|
184
294
|
}
|
|
@@ -186,17 +296,36 @@ export class WebSocketTransport implements Transport {
|
|
|
186
296
|
return;
|
|
187
297
|
}
|
|
188
298
|
|
|
189
|
-
this.setState(WebSocketState.CONNECTING);
|
|
299
|
+
this.setState(WebSocketState.CONNECTING, on);
|
|
190
300
|
|
|
191
301
|
try {
|
|
192
|
-
const
|
|
302
|
+
const WebSocketClass = (this.webSocketClass ?? WebSocket) as typeof WebSocket;
|
|
303
|
+
const ws = new WebSocketClass(this.options.url);
|
|
193
304
|
this.ws = ws;
|
|
305
|
+
// The calls of the server on this socket (roadmap R24)
|
|
306
|
+
this.incoming = this.servedCallsOf(ws);
|
|
307
|
+
|
|
308
|
+
// The handshake has a time limit: an address that never answers does not keep the socket
|
|
309
|
+
// in CONNECTING (roadmap R3)
|
|
310
|
+
this.clearHandshakeTimer();
|
|
311
|
+
this.handshakeTimer = this.clock.after(this.options.connectionTimeout, () => {
|
|
312
|
+
this.handshakeTimer = undefined;
|
|
313
|
+
if (this.ws === ws && this.state === WebSocketState.CONNECTING) {
|
|
314
|
+
console.error(`[${this.name}] Handshake timeout after ${this.options.connectionTimeout}ms`);
|
|
315
|
+
this.dropConnection("Handshake timeout");
|
|
316
|
+
}
|
|
317
|
+
});
|
|
194
318
|
|
|
195
319
|
// Connection opened
|
|
196
320
|
ws.onopen = () => {
|
|
197
321
|
if (this.ws !== ws) return;
|
|
322
|
+
this.clearHandshakeTimer();
|
|
198
323
|
this.reconnectAttempts = 0;
|
|
199
|
-
this
|
|
324
|
+
// The protocol version of this client (roadmap R25). An older server answers with an
|
|
325
|
+
// error frame, which the client ignores.
|
|
326
|
+
this.serverProtocol = undefined;
|
|
327
|
+
ws.send(JSON.stringify({ id: "hello", type: "hello", protocol: PROTOCOL_VERSION }));
|
|
328
|
+
this.setState(WebSocketState.CONNECTED, "open");
|
|
200
329
|
console.error(`[${this.name}] Connected to ${this.options.url}`);
|
|
201
330
|
|
|
202
331
|
if (this.options.onConnect) {
|
|
@@ -235,6 +364,10 @@ export class WebSocketTransport implements Transport {
|
|
|
235
364
|
if (this.options.onError) {
|
|
236
365
|
this.options.onError(error);
|
|
237
366
|
}
|
|
367
|
+
// A failed handshake: the WebSocket of Node 22 gives no close event after this error, and
|
|
368
|
+
// stays in CONNECTING. The transport closes it now, so the reconnect starts at once
|
|
369
|
+
// (roadmap R30: before, each try waited for the time limit of the handshake).
|
|
370
|
+
if (this.state === WebSocketState.CONNECTING) this.dropConnection("Connection failed");
|
|
238
371
|
};
|
|
239
372
|
} catch (error) {
|
|
240
373
|
console.error(`[${this.name}] Failed to create WebSocket:`, error);
|
|
@@ -252,12 +385,13 @@ export class WebSocketTransport implements Transport {
|
|
|
252
385
|
const ws = this.ws;
|
|
253
386
|
this.ws = null;
|
|
254
387
|
if (ws) {
|
|
255
|
-
ws
|
|
256
|
-
ws.onmessage = null;
|
|
257
|
-
ws.onclose = null;
|
|
258
|
-
ws.onerror = null;
|
|
388
|
+
detach(ws);
|
|
259
389
|
try {
|
|
260
|
-
ws.close()
|
|
390
|
+
// A socket of the ws package ends at once. close() waits for the answer of a peer that can
|
|
391
|
+
// be dead (finding of roadmap R36).
|
|
392
|
+
const terminate = (ws as { terminate?: () => void }).terminate;
|
|
393
|
+
if (typeof terminate === "function") terminate.call(ws);
|
|
394
|
+
else ws.close();
|
|
261
395
|
} catch {
|
|
262
396
|
// The socket is unusable: nothing more to do
|
|
263
397
|
}
|
|
@@ -269,9 +403,7 @@ export class WebSocketTransport implements Transport {
|
|
|
269
403
|
private failPending(reason: string): void {
|
|
270
404
|
for (const pending of this.pendingRequests.values()) {
|
|
271
405
|
pending.queue.fail(new Error(reason));
|
|
272
|
-
|
|
273
|
-
clearTimeout(pending.timeout);
|
|
274
|
-
}
|
|
406
|
+
pending.timeout?.();
|
|
275
407
|
}
|
|
276
408
|
this.pendingRequests.clear();
|
|
277
409
|
}
|
|
@@ -281,11 +413,14 @@ export class WebSocketTransport implements Transport {
|
|
|
281
413
|
*/
|
|
282
414
|
private handleClose(reason?: string): void {
|
|
283
415
|
this.stopHeartbeat();
|
|
416
|
+
this.incoming?.abortAll();
|
|
417
|
+
this.incoming = undefined;
|
|
418
|
+
this.clearHandshakeTimer();
|
|
284
419
|
|
|
285
420
|
// An intentional close() must not reconnect (BUGS-2026-07 H6)
|
|
286
421
|
const reconnect = this.options.reconnect;
|
|
287
422
|
const willReconnect = !this.closing && reconnect.enabled && this.reconnectAttempts < reconnect.maxAttempts;
|
|
288
|
-
this.setState(willReconnect ? WebSocketState.RECONNECTING : WebSocketState.DISCONNECTED);
|
|
423
|
+
this.setState(willReconnect ? WebSocketState.RECONNECTING : WebSocketState.DISCONNECTED, willReconnect ? "drop" : "give-up");
|
|
289
424
|
|
|
290
425
|
console.error(`[${this.name}] Disconnected${reason ? `: ${reason}` : ""}`);
|
|
291
426
|
|
|
@@ -305,17 +440,13 @@ export class WebSocketTransport implements Transport {
|
|
|
305
440
|
*/
|
|
306
441
|
private scheduleReconnect(): void {
|
|
307
442
|
if (this.reconnectTimer) {
|
|
308
|
-
|
|
443
|
+
this.reconnectTimer();
|
|
309
444
|
}
|
|
310
445
|
|
|
311
446
|
this.reconnectAttempts++;
|
|
312
|
-
this.setState(WebSocketState.RECONNECTING);
|
|
447
|
+
this.setState(WebSocketState.RECONNECTING, "drop");
|
|
313
448
|
|
|
314
|
-
const
|
|
315
|
-
const delay = Math.min(
|
|
316
|
-
reconnect.initialDelay * Math.pow(reconnect.backoffMultiplier, this.reconnectAttempts - 1),
|
|
317
|
-
reconnect.maxDelay
|
|
318
|
-
);
|
|
449
|
+
const delay = reconnectDelay(this.reconnectAttempts, this.options.reconnect);
|
|
319
450
|
|
|
320
451
|
console.error(`[${this.name}] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
321
452
|
|
|
@@ -323,64 +454,95 @@ export class WebSocketTransport implements Transport {
|
|
|
323
454
|
this.options.onReconnecting(this.reconnectAttempts);
|
|
324
455
|
}
|
|
325
456
|
|
|
326
|
-
this.reconnectTimer =
|
|
457
|
+
this.reconnectTimer = this.clock.after(delay, () => {
|
|
327
458
|
this.reconnectTimer = undefined;
|
|
328
459
|
// connect() returns at once in RECONNECTING only for CONNECTING/CONNECTED
|
|
329
|
-
this.connect();
|
|
330
|
-
}
|
|
460
|
+
this.connect("reconnect-due");
|
|
461
|
+
});
|
|
331
462
|
}
|
|
332
463
|
|
|
333
464
|
/**
|
|
334
|
-
* Start heartbeat
|
|
465
|
+
* Start the heartbeat of the connection: a ping each interval on the clock, one ping at a time.
|
|
466
|
+
* A ping without a pong at its timeout is a miss. The connection drops after `misses` misses in
|
|
467
|
+
* sequence. A ping whose send throws is a miss too.
|
|
335
468
|
*/
|
|
336
469
|
private startHeartbeat(): void {
|
|
337
470
|
this.stopHeartbeat();
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
471
|
+
this.heartbeat = heartbeat({
|
|
472
|
+
interval: this.options.heartbeat.interval,
|
|
473
|
+
timeout: this.options.heartbeat.timeout,
|
|
474
|
+
misses: this.options.heartbeat.misses,
|
|
475
|
+
clock: this.clock,
|
|
476
|
+
due: () => this.state === WebSocketState.CONNECTED && this.ws !== null,
|
|
477
|
+
ping: () => {
|
|
343
478
|
const ping: WebSocketMessage = {
|
|
344
479
|
id: `ping-${Date.now()}`,
|
|
345
480
|
type: "ping",
|
|
346
481
|
};
|
|
347
|
-
this.ws
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
this.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}, this.options.heartbeat.timeout);
|
|
355
|
-
}
|
|
356
|
-
}, this.options.heartbeat.interval);
|
|
482
|
+
this.ws?.send(JSON.stringify(ping));
|
|
483
|
+
},
|
|
484
|
+
onDead: () => {
|
|
485
|
+
console.warn(`[${this.name}] Heartbeat timeout - dropping connection`);
|
|
486
|
+
this.dropConnection("Heartbeat timeout");
|
|
487
|
+
},
|
|
488
|
+
});
|
|
357
489
|
}
|
|
358
490
|
|
|
359
|
-
/**
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
clearInterval(this.heartbeatTimer);
|
|
365
|
-
this.heartbeatTimer = undefined;
|
|
366
|
-
}
|
|
367
|
-
if (this.heartbeatTimeout) {
|
|
368
|
-
clearTimeout(this.heartbeatTimeout);
|
|
369
|
-
this.heartbeatTimeout = undefined;
|
|
491
|
+
/** Stop the time limit of the handshake. */
|
|
492
|
+
private clearHandshakeTimer(): void {
|
|
493
|
+
if (this.handshakeTimer) {
|
|
494
|
+
this.handshakeTimer();
|
|
495
|
+
this.handshakeTimer = undefined;
|
|
370
496
|
}
|
|
371
497
|
}
|
|
372
498
|
|
|
499
|
+
/** Stop the heartbeat: its ticks, its timeout and its count of misses. */
|
|
500
|
+
private stopHeartbeat(): void {
|
|
501
|
+
this.heartbeat?.stop();
|
|
502
|
+
this.heartbeat = undefined;
|
|
503
|
+
}
|
|
504
|
+
|
|
373
505
|
/**
|
|
374
506
|
* Handle incoming message.
|
|
375
507
|
*/
|
|
508
|
+
/**
|
|
509
|
+
* The calls of the server on a socket, served by the `serve` option (or by `onServerRequest`, or
|
|
510
|
+
* with `NOT_FOUND`), with the same frames as the calls of the client (roadmap R24).
|
|
511
|
+
*/
|
|
512
|
+
private servedCallsOf(ws: WebSocket): IncomingCalls {
|
|
513
|
+
const handler = this.options.serve ?? legacyHandler(this.options.onServerRequest);
|
|
514
|
+
return new IncomingCalls({
|
|
515
|
+
handler,
|
|
516
|
+
send: (frame) => ws.send(JSON.stringify(frame)),
|
|
517
|
+
isOpen: () => this.ws === ws && ws.readyState === OPEN,
|
|
518
|
+
// A call of the server that this client opened: the client trusts it, so it has no
|
|
519
|
+
// credentials (a ProcedureServer applies no token to it)
|
|
520
|
+
requestOf: (frame, signal) => ({
|
|
521
|
+
id: frame.id,
|
|
522
|
+
method: frame.method!,
|
|
523
|
+
payload: frame.payload === undefined ? {} : frame.payload,
|
|
524
|
+
metadata: withoutInternalKeys(frame.metadata),
|
|
525
|
+
signal,
|
|
526
|
+
}),
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
376
530
|
private handleMessage(message: WebSocketMessage): void {
|
|
531
|
+
// The hello of the server, or the error of an older server that does not know it (R25)
|
|
532
|
+
if (message.id === "hello" && (message.type === "hello" || message.type === "error")) {
|
|
533
|
+
if (message.type === "hello") this.serverProtocol = protocolVersionOf(message.protocol);
|
|
534
|
+
else if (message.error?.code === "PROTOCOL_VERSION") this.options.onError?.(new Error(message.error.message));
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
// A call of the server, its cancel or its credit (roadmap R24)
|
|
538
|
+
if ((message.type === "request" || message.type === "cancel" || message.type === "credit") && this.incoming?.receive(message)) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
377
542
|
|
|
378
543
|
// Handle pong (heartbeat response)
|
|
379
544
|
if (message.type === "pong") {
|
|
380
|
-
|
|
381
|
-
clearTimeout(this.heartbeatTimeout);
|
|
382
|
-
this.heartbeatTimeout = undefined;
|
|
383
|
-
}
|
|
545
|
+
this.heartbeat?.answer();
|
|
384
546
|
return;
|
|
385
547
|
}
|
|
386
548
|
|
|
@@ -421,7 +583,7 @@ export class WebSocketTransport implements Transport {
|
|
|
421
583
|
|
|
422
584
|
// The first frame arrived: the request timeout ends. A stream can stay open longer.
|
|
423
585
|
if (pending.timeout) {
|
|
424
|
-
|
|
586
|
+
pending.timeout();
|
|
425
587
|
pending.timeout = undefined;
|
|
426
588
|
}
|
|
427
589
|
|
|
@@ -539,7 +701,7 @@ export class WebSocketTransport implements Transport {
|
|
|
539
701
|
|
|
540
702
|
const queue = new ItemQueue<ResponseItem<unknown>>();
|
|
541
703
|
const pending: PendingRequest = { messageId: message.id, queue };
|
|
542
|
-
pending.timeout =
|
|
704
|
+
pending.timeout = this.clock.after(this.options.requestTimeout, () => {
|
|
543
705
|
if (this.pendingRequests.get(wireId) !== pending) return;
|
|
544
706
|
this.pendingRequests.delete(wireId);
|
|
545
707
|
this.sendCancel(wireId);
|
|
@@ -548,13 +710,13 @@ export class WebSocketTransport implements Transport {
|
|
|
548
710
|
// so a retry middleware must not repeat it (deep dive TRN-8)
|
|
549
711
|
queue.push(errorItem(message.id, "TIMEOUT", `Request timeout after ${this.options.requestTimeout}ms`, false));
|
|
550
712
|
queue.end();
|
|
551
|
-
}
|
|
713
|
+
});
|
|
552
714
|
this.pendingRequests.set(wireId, pending);
|
|
553
715
|
|
|
554
716
|
const onAbort = (): void => {
|
|
555
717
|
if (this.pendingRequests.get(wireId) !== pending) return;
|
|
556
718
|
this.pendingRequests.delete(wireId);
|
|
557
|
-
|
|
719
|
+
pending.timeout?.();
|
|
558
720
|
this.sendCancel(wireId);
|
|
559
721
|
queue.push(abortedItem(message.id));
|
|
560
722
|
queue.end();
|
|
@@ -579,7 +741,7 @@ export class WebSocketTransport implements Transport {
|
|
|
579
741
|
}
|
|
580
742
|
} finally {
|
|
581
743
|
message.signal?.removeEventListener("abort", onAbort);
|
|
582
|
-
|
|
744
|
+
pending.timeout?.();
|
|
583
745
|
if (this.pendingRequests.get(wireId) === pending) {
|
|
584
746
|
// The reader stopped before the end: the server stops the stream
|
|
585
747
|
this.pendingRequests.delete(wireId);
|
|
@@ -619,7 +781,7 @@ export class WebSocketTransport implements Transport {
|
|
|
619
781
|
|
|
620
782
|
return new Promise((resolve, reject) => {
|
|
621
783
|
const finish = (error?: Error): void => {
|
|
622
|
-
|
|
784
|
+
timeout();
|
|
623
785
|
this.stateListeners.delete(check);
|
|
624
786
|
signal?.removeEventListener("abort", onAbort);
|
|
625
787
|
if (error) reject(error);
|
|
@@ -631,7 +793,7 @@ export class WebSocketTransport implements Transport {
|
|
|
631
793
|
};
|
|
632
794
|
// The caller aborted: send() then gives the ABORTED item
|
|
633
795
|
const onAbort = (): void => finish();
|
|
634
|
-
const timeout =
|
|
796
|
+
const timeout = this.clock.after(this.options.connectionTimeout, () => finish(new Error("Connection timeout")));
|
|
635
797
|
this.stateListeners.add(check);
|
|
636
798
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
637
799
|
});
|
|
@@ -691,11 +853,14 @@ export class WebSocketTransport implements Transport {
|
|
|
691
853
|
*/
|
|
692
854
|
async close(): Promise<void> {
|
|
693
855
|
this.closing = true;
|
|
694
|
-
this.setState(WebSocketState.DISCONNECTING);
|
|
856
|
+
this.setState(WebSocketState.DISCONNECTING, "close");
|
|
695
857
|
this.stopHeartbeat();
|
|
858
|
+
this.clearHandshakeTimer();
|
|
859
|
+
this.incoming?.abortAll();
|
|
860
|
+
this.incoming = undefined;
|
|
696
861
|
|
|
697
862
|
if (this.reconnectTimer) {
|
|
698
|
-
|
|
863
|
+
this.reconnectTimer();
|
|
699
864
|
this.reconnectTimer = undefined;
|
|
700
865
|
}
|
|
701
866
|
|
|
@@ -703,20 +868,25 @@ export class WebSocketTransport implements Transport {
|
|
|
703
868
|
if (this.ws) {
|
|
704
869
|
const ws = this.ws;
|
|
705
870
|
this.ws = null;
|
|
706
|
-
ws
|
|
707
|
-
ws.onmessage = null;
|
|
708
|
-
ws.onclose = null;
|
|
709
|
-
ws.onerror = null;
|
|
871
|
+
detach(ws);
|
|
710
872
|
ws.close();
|
|
711
873
|
}
|
|
712
874
|
|
|
713
875
|
this.failPending("WebSocket connection closed");
|
|
714
|
-
this.setState(WebSocketState.DISCONNECTED);
|
|
876
|
+
this.setState(WebSocketState.DISCONNECTED, "closed");
|
|
715
877
|
if (hadSocket && this.options.onDisconnect) {
|
|
716
878
|
this.options.onDisconnect("closed");
|
|
717
879
|
}
|
|
718
880
|
}
|
|
719
881
|
|
|
882
|
+
/**
|
|
883
|
+
* The protocol version of the server: from its hello, or undefined before the hello or for a
|
|
884
|
+
* server older than protocol v1 (roadmap R25).
|
|
885
|
+
*/
|
|
886
|
+
getServerProtocol(): number | undefined {
|
|
887
|
+
return this.serverProtocol;
|
|
888
|
+
}
|
|
889
|
+
|
|
720
890
|
/**
|
|
721
891
|
* Get current connection state.
|
|
722
892
|
*/
|