@mark1russell7/service 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +268 -18
- package/build/builder-B_q-JeW8.d.ts +197 -0
- package/build/http.d.ts +277 -0
- package/build/http.js +789 -0
- package/build/http.js.map +1 -0
- package/build/index.d.ts +15 -5
- package/build/index.js +745 -159
- package/build/index.js.map +1 -1
- package/build/{builder-DnfUDW9u.d.ts → lifecycle-DIj4YGmO.d.ts} +238 -181
- package/build/redis.d.ts +2 -1
- package/build/testing.d.ts +92 -4
- package/build/testing.js +302 -8
- package/build/testing.js.map +1 -1
- package/package.json +10 -3
package/build/http.d.ts
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { n as Clock } from "./clock-D-38uH_z.js";
|
|
2
|
+
import { d as TransportImpl, m as RequestEnd, n as HealthServer, r as Lifecycle, v as RequestStream, x as Health, y as RequestWork } from "./lifecycle-DIj4YGmO.js";
|
|
3
|
+
import { Extent, Quantity } from "@mark1russell7/quantity";
|
|
4
|
+
import { ListenOptions } from "node:net";
|
|
5
|
+
import { IncomingMessage, Server, ServerEventMap, ServerResponse } from "node:http";
|
|
6
|
+
//#region src/http/transport.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* These are the knobs of an HTTP transport, by the names of the ordering law. The transport has no
|
|
9
|
+
* defaults, thus each knob is necessary. The declaration of the server gets the same record.
|
|
10
|
+
*/
|
|
11
|
+
interface HttpKnobs {
|
|
12
|
+
/**
|
|
13
|
+
* This time is the real idle close of the server, which O01 compares with the idle time of a
|
|
14
|
+
* client. Node closes an idle connection at `keepAliveTimeout` plus `keepAliveTimeoutBuffer`.
|
|
15
|
+
* Thus the transport sets `keepAliveTimeout` to this time less the buffer of the server.
|
|
16
|
+
* A time without a bound stops the timer.
|
|
17
|
+
*/
|
|
18
|
+
readonly server_idle_close: Extent;
|
|
19
|
+
/**
|
|
20
|
+
* This time is the interval of the sweep of idle connections, from the stop of the intake until the
|
|
21
|
+
* close. A time without a bound gives one sweep, at the stop of the intake.
|
|
22
|
+
*/
|
|
23
|
+
readonly server_idle_sweep_interval: Extent;
|
|
24
|
+
/** This time goes in the `Retry-After` of each 503 that the transport gives after its intake stops. */
|
|
25
|
+
readonly server_retry_after: Quantity;
|
|
26
|
+
/** The other knobs of the server go to the declaration. The transport does not read them. */
|
|
27
|
+
readonly [knob: string]: Extent;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* These are the knobs that the transport gives the declaration: the knobs of the caller, and the two
|
|
31
|
+
* times that the transport found. Thus the declaration shows the three times of the idle close.
|
|
32
|
+
*/
|
|
33
|
+
interface HttpTransportKnobs extends HttpKnobs {
|
|
34
|
+
/** This time is the hint that each response sends (`Keep-Alive: timeout=…`), in whole seconds. A time without a bound tells that no hint goes. */
|
|
35
|
+
readonly server_idle_close_hint: Extent;
|
|
36
|
+
/** This time is the `keepAliveTimeoutBuffer` of the server, which Node adds to the timer and not to the hint. */
|
|
37
|
+
readonly server_idle_close_buffer: Quantity;
|
|
38
|
+
}
|
|
39
|
+
/** A mount stage is the stage of the shutdown at which a mount stops: the stop of the intake, or the close. */
|
|
40
|
+
type MountStage = "stop intake" | "close transports";
|
|
41
|
+
/** A mount context gives each handler the signals of the shutdown, the lifecycle and the time of a retry. */
|
|
42
|
+
interface MountContext {
|
|
43
|
+
/** This signal aborts when the intake stops. A long stream ends in its own protocol at it. */
|
|
44
|
+
readonly intake: AbortSignal;
|
|
45
|
+
/** This signal aborts at the cooperative abort of the drain, or at the close. The work in flight stops at it. */
|
|
46
|
+
readonly signal: AbortSignal;
|
|
47
|
+
/** This lifecycle is the lifecycle that the transport got at its start, or null before the start. */
|
|
48
|
+
readonly life: Lifecycle | null;
|
|
49
|
+
/** This time is the knob `server_retry_after`: the `Retry-After` of each 503 of the handlers. */
|
|
50
|
+
readonly retryAfter: Quantity;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A request handler answers a request and gives true, or it gives false and does not touch the
|
|
54
|
+
* response. Then the next handler gets the request. `until` tells when the handler stops.
|
|
55
|
+
*/
|
|
56
|
+
interface RequestHandler {
|
|
57
|
+
(req: IncomingMessage, res: ServerResponse, context: MountContext): boolean | Promise<boolean>;
|
|
58
|
+
readonly until?: MountStage | undefined;
|
|
59
|
+
}
|
|
60
|
+
type UpgradeArgs = ServerEventMap["upgrade"];
|
|
61
|
+
/** An upgrade socket is the stream of an upgraded connection, as the `upgrade` event of Node gives it. */
|
|
62
|
+
type UpgradeSocket = UpgradeArgs[1];
|
|
63
|
+
/** An upgrade handler takes an upgraded connection, for example a WebSocket server in its `noServer` mode. */
|
|
64
|
+
interface UpgradeHandler {
|
|
65
|
+
(req: IncomingMessage, socket: UpgradeSocket, head: UpgradeArgs[2], context: MountContext): void;
|
|
66
|
+
readonly until?: MountStage | undefined;
|
|
67
|
+
}
|
|
68
|
+
/** An upgrade match is a path, without its query, or a function that tells if a handler takes a request. */
|
|
69
|
+
type UpgradeMatch = string | ((req: IncomingMessage) => boolean);
|
|
70
|
+
/** These options change one mount. */
|
|
71
|
+
interface MountOptions {
|
|
72
|
+
/**
|
|
73
|
+
* This stage is when the mount stops. Without it, the stage of the handler applies, and then
|
|
74
|
+
* `stop intake`. A mount until `close transports` keeps the listener open through the drain.
|
|
75
|
+
*/
|
|
76
|
+
readonly until?: MountStage | undefined;
|
|
77
|
+
}
|
|
78
|
+
/** These options make an HTTP transport. */
|
|
79
|
+
interface HttpTransportOptions {
|
|
80
|
+
/**
|
|
81
|
+
* These are the listen options of `start()`, or a function that makes the server listen. Without
|
|
82
|
+
* them, `start()` takes a server that listens already.
|
|
83
|
+
*/
|
|
84
|
+
readonly listen?: ListenOptions | ((server: Server) => Promise<void> | void) | undefined;
|
|
85
|
+
/** This clock is the clock of the lifecycle. The deadline of the drain and the sweep use it. */
|
|
86
|
+
readonly clock?: Clock | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* This function tells if a response can keep its connection open. Without it, the transport reads
|
|
89
|
+
* `life.keepAlive()` of the lifecycle of its start. The transport reads it when the headers of
|
|
90
|
+
* each response go, and false gives `Connection: close`.
|
|
91
|
+
*/
|
|
92
|
+
readonly keepAlive?: (() => boolean) | undefined;
|
|
93
|
+
/** This function receives each error of a handler. The transport then answers 500, or it destroys the response. */
|
|
94
|
+
readonly onError?: ((error: unknown, req: IncomingMessage) => void) | undefined;
|
|
95
|
+
}
|
|
96
|
+
/** An HTTP transport is a server of `node:http` as a transport of the lifecycle, with a list of mounts on its listener. */
|
|
97
|
+
interface HttpTransport extends TransportImpl {
|
|
98
|
+
readonly server: Server;
|
|
99
|
+
/** These knobs go to the declaration of the server: the knobs of the caller, the hint and the buffer. */
|
|
100
|
+
readonly knobs: HttpTransportKnobs;
|
|
101
|
+
/** This method adds a request handler at the end of the list, and gives the function that removes it. */
|
|
102
|
+
mount(handler: RequestHandler, options?: MountOptions): () => void;
|
|
103
|
+
/** This method adds an upgrade handler for a path, and gives the function that removes it. */
|
|
104
|
+
upgrade(match: UpgradeMatch, handler: UpgradeHandler, options?: MountOptions): () => void;
|
|
105
|
+
/** This count is the number of open connections, idle connections and upgraded connections too. */
|
|
106
|
+
readonly connections: number;
|
|
107
|
+
/** This flag tells if the transport stopped its intake. */
|
|
108
|
+
readonly intakeStopped: boolean;
|
|
109
|
+
}
|
|
110
|
+
/** This function gives the path of a request target, without its query and fragment. */
|
|
111
|
+
export declare const pathOf: (url: string | undefined) => string;
|
|
112
|
+
/**
|
|
113
|
+
* This function makes a server of `node:http` a transport of the lifecycle (S6 A8 and A13). It is not
|
|
114
|
+
* a framework: it has no routes, no body parser and no middleware.
|
|
115
|
+
* - The count of work in flight comes from events: each response until its `close`, and each upgraded
|
|
116
|
+
* connection until its `close`.
|
|
117
|
+
* - `stopIntake` closes the listener, and new connections get a refusal. It closes the idle connections
|
|
118
|
+
* at once and at each `server_idle_sweep_interval`. Each later response gets `Connection: close`.
|
|
119
|
+
* - `drain` waits until no work is in flight, or until the deadline. It gives the cooperative abort to
|
|
120
|
+
* the handlers through their context.
|
|
121
|
+
* - `close` destroys each connection. A second close gives the same promise.
|
|
122
|
+
* - The mounts share one port. The first request handler that gives true answers. After the stop of
|
|
123
|
+
* the intake, only the mounts until `close transports` answer, and each other request gets a 503.
|
|
124
|
+
*/
|
|
125
|
+
export declare const httpTransport: (server: Server, knobs: HttpKnobs, options?: HttpTransportOptions) => HttpTransport;
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/http/probes.d.ts
|
|
128
|
+
/** A probe response is the status, the headers and the body of one probe, for any HTTP framework or none. */
|
|
129
|
+
interface ProbeResponse {
|
|
130
|
+
readonly status: number;
|
|
131
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
132
|
+
readonly body: string;
|
|
133
|
+
}
|
|
134
|
+
/** A health source is the health runtime, or a function that gives it when it exists. */
|
|
135
|
+
type HealthSource = Health | (() => Health | null | undefined);
|
|
136
|
+
/**
|
|
137
|
+
* This function gives the response of a probe path from the cache of the health runtime. For a path
|
|
138
|
+
* that is not a probe, it gives null. The paths are `/startup`, `/readiness`, `/liveness` and
|
|
139
|
+
* `/report` after the prefix. `/readiness/<controller>` gives the readiness of one controller.
|
|
140
|
+
*/
|
|
141
|
+
export declare const probeResponse: (health: Health | null | undefined, method: string | undefined, path: string, prefix?: string) => ProbeResponse | null;
|
|
142
|
+
/** These options mount the probes on the port of the application. */
|
|
143
|
+
interface ProbeHandlerOptions {
|
|
144
|
+
/** This path goes before each probe path, for example `/health`. It starts with `/`, or it is empty. */
|
|
145
|
+
readonly prefix: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* This function gives a request handler that answers the probes on the port of the application. It
|
|
149
|
+
* answers until `close transports`, thus its mount keeps the listener open through the drain. A path
|
|
150
|
+
* that is not a probe goes to the next handler. Each 503 gets the `Retry-After` of the context.
|
|
151
|
+
*/
|
|
152
|
+
export declare const probeHandler: (health: HealthSource, options: ProbeHandlerOptions) => RequestHandler;
|
|
153
|
+
/** These options make a probe server: the listen options of its port, the prefix of its paths, and the time of a retry. */
|
|
154
|
+
interface ProbeServerOptions extends ListenOptions {
|
|
155
|
+
/**
|
|
156
|
+
* This time goes in the `Retry-After` of each 503 of a probe, as on the port of the application.
|
|
157
|
+
* The probe server has no defaults, thus the knob is necessary.
|
|
158
|
+
*/
|
|
159
|
+
readonly server_retry_after: Quantity;
|
|
160
|
+
/** This path goes before each probe path. The default is no prefix, because the port has only the probes. */
|
|
161
|
+
readonly prefix?: string | undefined;
|
|
162
|
+
/** This server answers the probes. Without it, the probe server makes a server of `node:http`. */
|
|
163
|
+
readonly server?: Server | undefined;
|
|
164
|
+
}
|
|
165
|
+
/** A probe server is a health server on its own port. Its server is visible for the address of the port. */
|
|
166
|
+
interface ProbeServer extends HealthServer {
|
|
167
|
+
readonly server: Server;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* This function makes a health server on a port of its own (S6 decision D5). It starts first and
|
|
171
|
+
* closes last, thus the probes answer through the whole drain. Each probe answers from the cache, and
|
|
172
|
+
* each 503 gets the `Retry-After` of `server_retry_after`.
|
|
173
|
+
*/
|
|
174
|
+
export declare const probeServer: (options: ProbeServerOptions) => ProbeServer;
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/http/outcome.d.ts
|
|
177
|
+
/** An outcome response is the status, the headers and the body of a request, for any HTTP framework or none. */
|
|
178
|
+
interface OutcomeResponse {
|
|
179
|
+
readonly status: number;
|
|
180
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
181
|
+
readonly body: string | Uint8Array;
|
|
182
|
+
}
|
|
183
|
+
/** A response part is what a hook gives for one value or one failure. The parts that it does not give keep the values of the mapping. */
|
|
184
|
+
interface ResponsePart {
|
|
185
|
+
readonly status?: number | undefined;
|
|
186
|
+
readonly headers?: Readonly<Record<string, string>> | undefined;
|
|
187
|
+
readonly body?: string | Uint8Array | undefined;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* A stream format writes the items of a stream and its end in the body, as they come. An end after
|
|
191
|
+
* the first item goes in the body, because the status went with the first item.
|
|
192
|
+
*/
|
|
193
|
+
interface StreamFormat<T = unknown> {
|
|
194
|
+
readonly contentType: string;
|
|
195
|
+
/** This function gives the bytes of one item. */
|
|
196
|
+
item(value: T): string | Uint8Array;
|
|
197
|
+
/** This function gives the bytes of the end, the last part of the body. */
|
|
198
|
+
end(end: RequestEnd): string | Uint8Array;
|
|
199
|
+
}
|
|
200
|
+
/** These options map a request to a response. */
|
|
201
|
+
interface OutcomeOptions<T> {
|
|
202
|
+
/**
|
|
203
|
+
* This value tells if the response can keep its connection open, as `life.keepAlive()` does. False
|
|
204
|
+
* gives `Connection: close`. A function is read when the headers go.
|
|
205
|
+
*/
|
|
206
|
+
readonly keepAlive: boolean | (() => boolean);
|
|
207
|
+
/** This time goes in the `Retry-After` of each 503, in whole seconds, rounded up. It is the knob `server_retry_after`. */
|
|
208
|
+
readonly retryAfter: Quantity;
|
|
209
|
+
/**
|
|
210
|
+
* This function gives the response of the value of a unary request. Without it, the mapping gives
|
|
211
|
+
* 200 and the value as JSON, or 204 for no value.
|
|
212
|
+
*/
|
|
213
|
+
readonly done?: ((value: T) => ResponsePart) | undefined;
|
|
214
|
+
/**
|
|
215
|
+
* This function gives the response of a failure of the route before the first byte. Without it,
|
|
216
|
+
* the mapping gives 500 with no detail, because an error can hold private data.
|
|
217
|
+
*/
|
|
218
|
+
readonly failed?: ((error: unknown) => ResponsePart) | undefined;
|
|
219
|
+
/**
|
|
220
|
+
* This format makes the response a stream: the status and each item go as they come, and the end
|
|
221
|
+
* goes in the body. Without it, the response is unary: one status and the one value.
|
|
222
|
+
*/
|
|
223
|
+
readonly stream?: StreamFormat<T> | undefined;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* This function gives an end as a record for a body: its kind and the count of its items. A cut and
|
|
227
|
+
* a refusal also give their cause, and if the caller can send the request again. A failure gives
|
|
228
|
+
* no detail.
|
|
229
|
+
*/
|
|
230
|
+
export declare const endRecord: (end: RequestEnd) => Readonly<Record<string, unknown>>;
|
|
231
|
+
/** This format writes each item and the end as one line of JSON (NDJSON). */
|
|
232
|
+
export declare const ndjson: StreamFormat;
|
|
233
|
+
/** This format writes each item as an event of server-sent events, and the end as the event `end`. */
|
|
234
|
+
export declare const sse: StreamFormat;
|
|
235
|
+
/**
|
|
236
|
+
* This function maps a unary request to its response, from its items and its end (S6 A12 and A13).
|
|
237
|
+
* - One value and the end `done`: 200 with the value, or the response of the `done` hook.
|
|
238
|
+
* - No value and the end `done`: 204. More than one value: 500, because the route is not a stream.
|
|
239
|
+
* - `refused`: 503. The work did not start, thus the caller can send it again. The service did not
|
|
240
|
+
* start, it stopped its intake, or a controller is not ready.
|
|
241
|
+
* - `cut`: 503 for a route whose callers can send it again (S6 A12), and 500 for each other route.
|
|
242
|
+
* - `failed`: 500 with no detail, or the response of the `failed` hook. A failure of the route is not a 503.
|
|
243
|
+
*
|
|
244
|
+
* Each 503 gets `Retry-After` (RFC 9110), and a 500 gets none. Each response gets `Connection: close`
|
|
245
|
+
* while the service cannot keep the connection.
|
|
246
|
+
*/
|
|
247
|
+
export declare const outcomeResponse: <T>(items: readonly T[], end: RequestEnd, options: OutcomeOptions<T>) => OutcomeResponse;
|
|
248
|
+
/**
|
|
249
|
+
* This function writes the stream of a request to a response of `node:http`, and gives its end. It
|
|
250
|
+
* gives null when the client closed the connection first. Then the work stops through `return()`.
|
|
251
|
+
* - Unary (no format): it waits for the end, and it writes the status and the body of `outcomeResponse`.
|
|
252
|
+
* - Stream: the status 200 goes with the first item, then each item goes as it comes, then the end
|
|
253
|
+
* goes in the body (in-band). The next item waits until the client reads, thus a slow client
|
|
254
|
+
* slows the work. An end before the first item gets a status, as a unary end does.
|
|
255
|
+
*/
|
|
256
|
+
export declare const writeOutcome: <T>(res: ServerResponse, stream: RequestStream<T>, options: OutcomeOptions<T>) => Promise<RequestEnd | null>;
|
|
257
|
+
/** A route of a handler is the name of the route that a controller declares, or a function that gives it from the request. */
|
|
258
|
+
type HandledRoute = string | ((req: IncomingMessage) => string | null);
|
|
259
|
+
/** A handled work does the work of one request of `node:http`. Its signal aborts when the shutdown or the client stops the request. */
|
|
260
|
+
type HandledWork<T> = (req: IncomingMessage, signal: AbortSignal) => RequestWork<T>;
|
|
261
|
+
/** These options bind a request of `node:http` to the request tracking of the service. */
|
|
262
|
+
interface HandleOptions<T> extends Omit<OutcomeOptions<T>, "keepAlive" | "retryAfter"> {
|
|
263
|
+
/** This function gives the detail of a request, for the function of a route pattern. */
|
|
264
|
+
readonly detail?: ((req: IncomingMessage) => unknown) | undefined;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* This function gives a request handler that does each request of a route through `life.request`
|
|
268
|
+
* (S6 A12 and A13). The transport gives the lifecycle and `server_retry_after` through the context.
|
|
269
|
+
* - A route name takes a request whose method and path are that name, for example `POST /orders`.
|
|
270
|
+
* A function gives the name of a route or of a pattern, or null for a request of another handler.
|
|
271
|
+
* - Before the start of the transport, each request gets a retryable 503.
|
|
272
|
+
* - The response is unary, or a stream with the option `stream`, as `writeOutcome` writes it.
|
|
273
|
+
*/
|
|
274
|
+
export declare const handle: <T>(controller: string, route: HandledRoute, work: HandledWork<T>, options?: HandleOptions<T>) => RequestHandler;
|
|
275
|
+
//#endregion
|
|
276
|
+
export type { HandleOptions, HandledRoute, HandledWork, HealthSource, HttpKnobs, HttpTransport, HttpTransportKnobs, HttpTransportOptions, MountContext, MountOptions, MountStage, OutcomeOptions, OutcomeResponse, ProbeHandlerOptions, ProbeResponse, ProbeServer, ProbeServerOptions, RequestHandler, ResponsePart, StreamFormat, UpgradeHandler, UpgradeMatch, UpgradeSocket };
|
|
277
|
+
//# sourceMappingURL=http.d.ts.map
|