@mark1russell7/service 0.5.12 → 0.5.13

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.
@@ -0,0 +1,275 @@
1
+ import { Y as Poll, wt as Operation } from "./lifecycle-fgYfxrHK.js";
2
+ import { C as MANIFESTATIONS, D as firstDetector, E as Situation, O as unwatched, S as Detector, T as Meeting, _ as Sent, a as ClientKnobs, b as retryProblems, c as ClientSpec, d as Factory, f as ReadyWait, g as SendOptions, h as STARTING_POLL, i as ClientJoin, l as ClientStep, m as RetrySchedule, n as CheckOptions, o as ClientOptions, p as RetryKnobs, r as Client, s as ClientSocket, t as Attempt, u as DialFn, v as readyWait, w as Manifestation, x as retrySchedule, y as retryBackoff } from "./core-BPLRDcUJ.js";
3
+ import { n as statusLineJudge, t as Judge } from "./dial-DA8vJfh2.js";
4
+ import { Extent, Quantity } from "@mark1russell7/quantity";
5
+ import { Machine } from "@mark1russell7/algebra";
6
+ //#region src/websocket/connection.d.ts
7
+ /**
8
+ * These are the listeners of one connection. The connection holds each event until the client
9
+ * listens. Thus an event that comes between the creation and the listen does not go away.
10
+ */
11
+ interface WsListeners {
12
+ /** This event tells that the upgrade ended with 101, and the connection can send. */
13
+ open(): void;
14
+ /** This event gives one message of the server. A text message is UTF-8 in the buffer. */
15
+ message(data: Buffer, binary: boolean): void;
16
+ /** This event gives the payload of one pong frame. */
17
+ pong(data: Buffer): void;
18
+ /** This event tells that the connection closed, with the code and the reason of the close frame. Code 1006 tells that no close frame came. */
19
+ close(code: number, reason: string): void;
20
+ /** This event gives an error of the connection. A close follows it. */
21
+ error(error: Error): void;
22
+ }
23
+ /**
24
+ * A connection is the part of a `WebSocket` of ws 8 that the wrapper uses. A fake for tests has
25
+ * each part too. Thus the package depends on no type of ws.
26
+ */
27
+ interface WsConnection {
28
+ /** This method gives the listeners of the connection. The connection then gives each event that it holds. */
29
+ listen(listeners: WsListeners): void;
30
+ /** This method writes one message. The callback gets an error when the write fails. It throws while the connection is not open. */
31
+ send(data: string | Buffer, callback: (error?: Error) => void): void;
32
+ /** This method writes one ping frame with this payload. It throws while the connection is not open. */
33
+ ping(data: Buffer): void;
34
+ /** This method writes a close frame, and waits for the close frame of the server. It destroys nothing. */
35
+ close(code: number, reason: string): void;
36
+ /** This method destroys the socket at once. The cells measured 0.3 ms to 1 ms from the call to the close of the socket. */
37
+ terminate(): void;
38
+ /** This field holds the socket after the upgrade, or undefined before it. The join reads its addresses. */
39
+ readonly socket: ClientSocket | undefined;
40
+ /** This text is the subprotocol that the server chose, or "" when it chose none. */
41
+ readonly protocol: string;
42
+ }
43
+ /**
44
+ * These are the options that the wrapper gives `new WebSocket(url, protocols, options)` of ws. Each
45
+ * time comes from the declaration. ws gives the options that it does not read to `net.connect`.
46
+ */
47
+ interface WsDriverOptions {
48
+ /** This time bounds the TCP connect and the upgrade, in milliseconds. */
49
+ readonly handshakeTimeout: number;
50
+ /** ws answers each ping of the server with a pong. The heartbeat of the server then finds the client alive. */
51
+ readonly autoPong: true;
52
+ /** This time bounds the close handshake of ws, in milliseconds. The wrapper gives its own bound, `close_bound`. */
53
+ readonly closeTimeout: number;
54
+ /** ws sets no keepalive on its socket. The wrapper turns it on only when the declaration gives an idle time. */
55
+ readonly keepAlive: boolean;
56
+ /** This time is the idle time of TCP keepalive, in milliseconds. Node truncates it to whole seconds. */
57
+ readonly keepAliveInitialDelay?: number | undefined;
58
+ /** These headers go with the upgrade request, for example `authorization`. */
59
+ readonly headers?: Readonly<Record<string, string>> | undefined;
60
+ }
61
+ /** These are the arguments of `new WebSocket(url, protocols, options)`. */
62
+ interface WsConnectOptions {
63
+ readonly url: string;
64
+ readonly protocols: readonly string[];
65
+ readonly options: WsDriverOptions;
66
+ }
67
+ /** A connection factory makes one connection from its options. The connection opens later, and tells it with the event `open`. */
68
+ type WsFactory = Factory<WsConnectOptions, WsConnection>;
69
+ /** This type is the part of a `WebSocket` of ws 8 that the adapter uses. */
70
+ interface WsDriver {
71
+ on(event: string, listener: (...args: never[]) => void): unknown;
72
+ send(data: string | Buffer, callback: (error?: Error | null) => void): void;
73
+ ping(data: Buffer): void;
74
+ close(code: number, reason: string): void;
75
+ terminate(): void;
76
+ readonly protocol: string;
77
+ }
78
+ /**
79
+ * This function adapts a `WebSocket` of ws to a connection. It listens to `error` at once, because
80
+ * in Node an error with no listener throws. It takes the socket from the event `upgrade`.
81
+ */
82
+ export declare const adaptWs: (ws: WsDriver) => WsConnection;
83
+ /**
84
+ * This factory makes a `WebSocket` of ws. It loads ws only when a client connects, thus a service
85
+ * that does not use WebSocket does not need it. The specifier is a value, thus the package needs no types of ws.
86
+ */
87
+ export declare const nodeWs: WsFactory;
88
+ //#endregion
89
+ //#region src/websocket/client.d.ts
90
+ /**
91
+ * These are the knobs of the client, each a time, by the names of the ordering law. The runtime has
92
+ * no defaults, thus each knob is necessary. A bound that nobody gives is "infinite", not zero. The
93
+ * constant `WS_CLIENT_STARTING_KNOBS` gives a declaration from the WS cells. The knobs that each client has come from the core.
94
+ */
95
+ interface WsKnobs extends ClientKnobs, RetryKnobs {
96
+ /** This time bounds the TCP connect and the upgrade (`handshakeTimeout` of ws). The wrapper also bounds the open with it. */
97
+ readonly connect_bound: Quantity;
98
+ /** This time bounds each call: a request until its reply, and a ping until its pong. The wrapper has this timer, because ws has none. */
99
+ readonly request_timeout: Quantity;
100
+ /** This time is the interval of the ping frames of the heartbeat, from the start of one to the start of the next. "infinite" sends no ping. */
101
+ readonly heartbeat_interval: Extent;
102
+ /** This time bounds each ping of the heartbeat. A ping with no pong in this time is a miss. */
103
+ readonly heartbeat_timeout: Quantity;
104
+ /** This time is the first delay before a new try to connect. The delay doubles after each try. */
105
+ readonly retry_base: Quantity;
106
+ /** This time is the most that one delay can be, before its jitter. */
107
+ readonly retry_cap: Quantity;
108
+ /** This time is the idle time of TCP keepalive (`keepAliveInitialDelay`), in whole seconds. "infinite" keeps keepalive off, as ws does. */
109
+ readonly keepalive_idle: Extent;
110
+ /** The client does not replace its connection at an age, thus this knob must be "infinite". */
111
+ readonly max_connection_age: Extent;
112
+ /** This time bounds the close at the shutdown: the calls in flight, then the close handshake. At the bound, `terminate()` destroys the socket. */
113
+ readonly close_bound: Quantity;
114
+ }
115
+ /** A reply is the id of the request that a message answers, and the error of the server when the reply tells one. */
116
+ interface WsReply {
117
+ readonly id: string;
118
+ readonly error?: string | undefined;
119
+ }
120
+ /**
121
+ * The replies of a protocol tell which message of the server answers which request. WebSocket has no
122
+ * reply of its own: each application protocol puts an id in its messages, for example JSON-RPC 2.0.
123
+ */
124
+ interface WsReplies {
125
+ /** This function reads one message of the server. It gives its reply, or null for a message that answers no request. */
126
+ reply(data: Buffer, binary: boolean): WsReply | null;
127
+ }
128
+ /** A WebSocket client spec is the declaration of one WebSocket client: where it connects, and its knobs. */
129
+ interface WsClientSpec extends ClientSpec<WsKnobs> {
130
+ /** This path is the path of the upgrade request. The default is "/". */
131
+ readonly path?: string | undefined;
132
+ /** These names are the subprotocols that the client offers (`Sec-WebSocket-Protocol`). */
133
+ readonly protocols?: readonly string[] | undefined;
134
+ /** These headers go with the upgrade request and with the dial, for example `authorization`. */
135
+ readonly headers?: Readonly<Record<string, string>> | undefined;
136
+ /** This count is the number of heartbeat misses in sequence that end the connection. The cells used 1. */
137
+ readonly heartbeatMisses: number;
138
+ /** These replies match a message of the server to its request. Without them, the client sends no request. */
139
+ readonly replies?: WsReplies | undefined;
140
+ /**
141
+ * These operations are the calls of the service, by name, with their idempotency. A message does
142
+ * not tell if the server removes its duplicates. Thus the client sends again only a call that this table names, and a ping.
143
+ */
144
+ readonly operations?: ClientSpec<WsKnobs>["operations"];
145
+ }
146
+ /** These options make a client. Without them, it uses ws, the real clock, `Math.random`, and a dial with the upgrade request. */
147
+ interface WsClientOptions extends ClientOptions<WsFactory> {
148
+ readonly random?: (() => number) | undefined;
149
+ /**
150
+ * This function waits one turn of the event loop. The heartbeat waits it before it counts a miss,
151
+ * thus a pong that is in the socket counts first. The default is `setImmediate`.
152
+ */
153
+ readonly turn?: (() => Promise<void>) | undefined;
154
+ }
155
+ /** A join is one generation of the client: one connection, when its upgrade ended with 101 (D1, D2). WebSocket gives no id of a connection. */
156
+ interface WsJoin extends ClientJoin {
157
+ /** This URL is the URL of the connection. */
158
+ readonly url: string;
159
+ /** This text is the subprotocol that the server chose, or "" when it chose none. */
160
+ readonly protocol: string;
161
+ }
162
+ /** A request is one message to the server, with the id that its reply gives back. */
163
+ interface WsRequest {
164
+ readonly id: string;
165
+ /** This data goes as one message. A string goes as a text message, and a buffer as a binary message. */
166
+ readonly data: string | Buffer;
167
+ }
168
+ /** A response is the message of the server that answered a request. */
169
+ interface WsResponse {
170
+ readonly data: Buffer;
171
+ readonly binary: boolean;
172
+ }
173
+ /** A message listener receives each message of the server that answers no request. */
174
+ type WsMessageListener = (data: Buffer, binary: boolean) => void;
175
+ /** A WebSocket client is a dependency of a service: its declaration, its handle, its checks and its calls. */
176
+ interface WsClient extends Client<ClientStep, WsJoin> {
177
+ /** This text is the state of the client, as `WS_CLIENT_MACHINE` names it. */
178
+ readonly status: string;
179
+ /**
180
+ * This method sends a request, and waits for its reply. A reply is `ok`, or a certain error when it
181
+ * tells one. The client sends again only an idempotent operation, one time at most, after an outcome that is not certain.
182
+ */
183
+ request(operation: string, call: WsRequest, options?: SendOptions): Promise<Sent<WsResponse>>;
184
+ /** This method sends a ping frame, and waits for a pong. A pong is `ok`. A ping is idempotent, thus the client can send it again. */
185
+ ping(options?: SendOptions): Promise<Sent<void>>;
186
+ /** This method sends one message with no reply. It gives true when ws took the message, and false when the client is not ready. */
187
+ send(data: string | Buffer): boolean;
188
+ /** This method adds a listener of the messages that answer no request. It gives the function that removes it. */
189
+ onMessage(listener: WsMessageListener): () => void;
190
+ }
191
+ /** These operations are in each table: a ping frame is idempotent (RFC 6455 §5.5.2). */
192
+ export declare const WS_OPERATIONS: Readonly<Record<string, Operation>>;
193
+ /** This function gives the problems of a spec: the knobs that the client cannot use. */
194
+ export declare const specProblems: (name: string, spec: WsClientSpec) => string[];
195
+ /** This function gives the URL of a spec. The client speaks ws:// only. */
196
+ export declare const wsUrl: (spec: WsClientSpec) => string;
197
+ /** This function gives the arguments of `new WebSocket` from a spec. Each timer of ws comes from the declaration. */
198
+ export declare const wsOptions: (spec: WsClientSpec) => WsConnectOptions;
199
+ /** This function gives the bytes of the dial: the upgrade request of RFC 6455 §4.1, with the subprotocols and the headers of the spec. */
200
+ export declare const upgradeRequest: (spec: Pick<WsClientSpec, "host" | "port" | "path" | "protocols" | "headers">) => string;
201
+ /** This judge reads the status line of the answer to the upgrade. Only 101 passes: a WebSocket server takes the upgrade. */
202
+ export declare const upgradeJudge: Judge;
203
+ /** This function makes the dial of a spec: a new TCP connection, the upgrade request, and a wait for 101. The dial then destroys its socket. */
204
+ export declare const wsDial: (spec: Pick<WsClientSpec, "path" | "protocols" | "headers">) => DialFn;
205
+ /**
206
+ * This function makes a WebSocket client, a dependency of a service over ws 8.
207
+ * - It has one connection as one generation. The end of the connection ends the generation. The
208
+ * client then connects again by itself, after a backoff.
209
+ * - Its calls are a request until its reply, and a ping until its pong. Each call has a timer of
210
+ * the wrapper, because ws has none.
211
+ * - Its heartbeat sends a ping frame each interval, one at a time. A ping with no pong in its timeout
212
+ * is a miss. At the last miss, `terminate()` destroys the socket at once, and the client connects again.
213
+ * - It tells the outcome of a call in three values, and it settles each call in flight at the end of its generation (C8, C10).
214
+ * - It records the URL and the ends of the socket of each connection at its open (D1).
215
+ *
216
+ * These parts come from the core of the clients: the outcomes, the second try and the wait for a ready
217
+ * client. The backoff and its schedule, the calls in flight, the bounded close and the checks come from
218
+ * the core too. The heartbeat is the heartbeat of the package. This entry gives what ws does in its own way.
219
+ */
220
+ export declare const wsClient: (name: string, spec: WsClientSpec, options?: WsClientOptions) => WsClient;
221
+ //#endregion
222
+ //#region src/websocket/machine.d.ts
223
+ /**
224
+ * The WebSocket client of the service as a `mark1russell7.machine/1` document. It is one generation
225
+ * at a time: one `WebSocket` of ws 8. The states:
226
+ * - `ready`: the upgrade ended with 101, and the client sends each call. Its heartbeat sends a ping frame each interval.
227
+ * - `failing`: no generation is open. The client connects again after its backoff.
228
+ * - `closing`: the calls in flight, then the close handshake, in `close_bound`. At the bound, `terminate()`.
229
+ *
230
+ * The end of the connection and the last miss of the heartbeat end the generation. A test records
231
+ * the transitions of the client and replays them against this document.
232
+ */
233
+ export declare const WS_CLIENT_MACHINE: Machine;
234
+ //#endregion
235
+ //#region src/websocket/meets.d.ts
236
+ /**
237
+ * This function gives the manifestations that a WebSocket client can meet, and the detectors of
238
+ * each, from its knobs. The WS cells measured them on ws 8:
239
+ * - At a heartbeat of 2 s, a timeout of 1 s and 1 miss, the drop came 0.999 s to 1.0035 s after the
240
+ * ping that got no pong. The slowest of 495 runs took 1.003761 s.
241
+ * - `terminate()` closes the socket 0.3 ms to 1 ms after the drop. The close of the global WebSocket
242
+ * only writes a close frame, and its socket stayed ESTABLISHED.
243
+ * - The kernel of a frozen server acknowledges each ping, thus only the heartbeat finds it.
244
+ * - At `tcp_retries2 = 3`, the kernel gave up before a pong timeout of 10 s.
245
+ * - Without a heartbeat, the client waited for the keepalive of undici: 70 s. ws sets no keepalive.
246
+ */
247
+ export declare const wsMeetings: (name: string, knobs: WsKnobs, misses: number) => Meeting[];
248
+ //#endregion
249
+ //#region src/websocket/starting.d.ts
250
+ /**
251
+ * This poll is the poll of the checks that `WS_CLIENT_STARTING_KNOBS` fits. It is `STARTING_POLL` of
252
+ * the core: each 5 s with a jitter of 20 %, and a timeout of 4 s (B6, O12). The poll does not depend on the driver.
253
+ */
254
+ export declare const WS_CLIENT_STARTING_POLL: Poll;
255
+ /** This count is the heartbeat misses to start from: 1, as in the WS cells. The drop came 0.999 s to 1.0035 s after the ping with no pong. */
256
+ export declare const WS_CLIENT_STARTING_MISSES = 1;
257
+ /**
258
+ * These knobs are a declaration to start from, from what the WS cells measured on ws 8. The runtime
259
+ * has no defaults: a service gives these knobs, or its own. Each value holds the borders that the cells measured:
260
+ * - `connect_bound` 5 s: the connect timeout of DP44. ws sets no handshake timeout by default.
261
+ * - `request_timeout` 9.5 s: in the slowest poll of 10 s (O13).
262
+ * - `heartbeat_interval` 4 s: in the WST cells, a ping each 4 s kept a flow through a router that
263
+ * forgets it at 5 s. A ping each 6 s did not (O03).
264
+ * - `heartbeat_timeout` 3 s: a stall of 1 s with a pong timeout of 3 s dropped nothing. The detection is 4 s + 3 s = 7 s.
265
+ * - `retry_base` 0.5 s and `retry_cap` 4 s: the reconnect of the cells, from 500 ms, doubling to 4 s.
266
+ * The client adds a jitter up to the base. Thus the most delay is 4.5 s, in the slowest poll of 10 s.
267
+ * - `keepalive_idle` "infinite": ws sets no keepalive, and the heartbeat refreshes an idle flow.
268
+ * - `max_connection_age` "infinite": the client does not replace its connection at an age.
269
+ * - `close_bound` 2 s: a close frame to a silent server gets no answer. The `closeTimeout` of ws is 30 s.
270
+ * - `retrans_giveup_time` 924.6 s: the give-up of Linux at `tcp_retries2 = 15`.
271
+ */
272
+ export declare const WS_CLIENT_STARTING_KNOBS: WsKnobs;
273
+ //#endregion
274
+ export { type Attempt, type CheckOptions, type Client, type ClientJoin, type ClientKnobs, type ClientOptions, type ClientSocket, type ClientSpec, type ClientStep, type Detector, type DialFn, type Factory, type Judge, MANIFESTATIONS, type Manifestation, type Meeting, type ReadyWait, type RetryKnobs, type RetrySchedule, STARTING_POLL, type SendOptions, type Sent, type Situation, type WsClient, type WsClientOptions, type WsClientSpec, type WsConnectOptions, type WsConnection, type WsDriverOptions, type WsFactory, type WsJoin, type WsKnobs, type WsListeners, type WsMessageListener, type WsReplies, type WsReply, type WsRequest, type WsResponse, firstDetector, readyWait, retryBackoff, retryProblems, retrySchedule, statusLineJudge, unwatched };
275
+ //# sourceMappingURL=websocket.d.ts.map