@mark1russell7/service 0.4.1 → 0.5.1
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 +358 -18
- package/build/builder-QVxu1WcL.d.ts +204 -0
- package/build/http.d.ts +277 -0
- package/build/http.js +782 -0
- package/build/http.js.map +1 -0
- package/build/index.d.ts +253 -6
- package/build/index.js +1506 -166
- package/build/index.js.map +1 -1
- package/build/knobs-BxG-c7Zb.js +39 -0
- package/build/knobs-BxG-c7Zb.js.map +1 -0
- package/build/{builder-DnfUDW9u.d.ts → lifecycle-DtJZW09J.d.ts} +272 -181
- package/build/redis.d.ts +2 -1
- package/build/testing.d.ts +98 -4
- package/build/testing.js +321 -8
- package/build/testing.js.map +1 -1
- package/package.json +10 -3
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { n as Clock } from "./clock-D-38uH_z.js";
|
|
2
|
+
import { B as Gate, D as Outcome, E as CheckFn, F as CooperativeFloor, H as Phase, I as Coupling, J as Poll, L as Dependency, N as Check, R as ExitCodes, T as StartOptions, U as RepeatSignal, V as MessageTrigger, W as ServiceDescription, X as Timing, d as TransportImpl, k as Trace, n as HealthServer, nt as OperationOf, o as PhaseWork, pt as ProcessTrigger, r as Lifecycle, s as RunOptions, st as ExitTrigger, t as DependencyHandle, x as Health, xt as Operation } from "./lifecycle-DtJZW09J.js";
|
|
3
|
+
import { Extent, Quantity } from "@mark1russell7/quantity";
|
|
4
|
+
//#region src/profiles.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* A profile is a part of a declaration as data: policies, phases and the rules of the shutdown.
|
|
7
|
+
* `svc.profile(profile)` declares each field, as its method does. The runtime has no defaults, and
|
|
8
|
+
* a profile is not a default, because the caller gives it. The caller can change each field with a
|
|
9
|
+
* spread. `describe()` then shows the values of the profile.
|
|
10
|
+
*
|
|
11
|
+
* svc.profile({ ...kubernetes({ grace: s(60), preStop: s(5) }), onRepeatSignal: "exit" });
|
|
12
|
+
*/
|
|
13
|
+
interface Profile {
|
|
14
|
+
readonly readiness?: Timing | undefined;
|
|
15
|
+
readonly liveness?: Timing | undefined;
|
|
16
|
+
readonly startup?: Quantity | undefined;
|
|
17
|
+
readonly couplings?: readonly Coupling[] | undefined;
|
|
18
|
+
/** These are the phases of the shutdown, in their sequence. */
|
|
19
|
+
readonly phases?: readonly Phase[] | undefined;
|
|
20
|
+
readonly cooperativeWindow?: Extent | undefined;
|
|
21
|
+
readonly cooperativeFloor?: CooperativeFloor | undefined;
|
|
22
|
+
readonly onRepeatSignal?: RepeatSignal | undefined;
|
|
23
|
+
readonly triggers?: readonly ProcessTrigger[] | undefined;
|
|
24
|
+
/** These messages of the IPC channel start the shutdown, as `svc.trigger("ipc-message", message)` declares each one. */
|
|
25
|
+
readonly messages?: readonly MessageTrigger[] | undefined;
|
|
26
|
+
readonly exitCodes?: ExitCodes | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* This profile is for a service on the computer of a developer. No load balancer routes to it,
|
|
30
|
+
* thus it has no readiness hold. Its caps sum to 10 s, and a second signal exits at once. It
|
|
31
|
+
* declares no checks, thus the caller declares readiness, liveness and the startup budget.
|
|
32
|
+
*/
|
|
33
|
+
declare const localDev: Profile;
|
|
34
|
+
/**
|
|
35
|
+
* This profile is for a background host on the computer of a developer, under a supervisor or a
|
|
36
|
+
* command that stops it. It has the caps of `localDev`. A repeat signal has no effect, thus the
|
|
37
|
+
* host ends its drain. It has no process trigger: a background host has no parent on stdin.
|
|
38
|
+
*/
|
|
39
|
+
declare const daemon: Profile;
|
|
40
|
+
/**
|
|
41
|
+
* This profile is for a small process: a command that runs one time, or a child process on stdio.
|
|
42
|
+
* It has no checks and no probes, and its policies gate nothing. The cooperative abort occurs at the
|
|
43
|
+
* start of the drain, thus a first Ctrl-C stops the work at once. A second signal exits at once.
|
|
44
|
+
* The exit codes tell the shell how the process stopped: 130 for SIGINT, 143 for SIGTERM and 131
|
|
45
|
+
* for SIGQUIT. A stdio child also declares `triggers: ["stdin-close"]`.
|
|
46
|
+
*/
|
|
47
|
+
declare const ephemeral: Profile;
|
|
48
|
+
/** These are the settings of a pod that the profile `kubernetes` derives its caps from. */
|
|
49
|
+
interface PodTiming {
|
|
50
|
+
/** This time is `terminationGracePeriodSeconds`: from the deletion of the pod to SIGKILL. */
|
|
51
|
+
readonly grace: Quantity;
|
|
52
|
+
/** This time is the preStop sleep, or 0 for no preStop hook. SIGTERM comes after it. */
|
|
53
|
+
readonly preStop: Quantity;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* This function gives the profile of a pod in Kubernetes. The budget is the grace less the preStop
|
|
57
|
+
* sleep: the time from SIGTERM to SIGKILL. Each stage gets a part of the budget, rounded down to
|
|
58
|
+
* the millisecond.
|
|
59
|
+
* - The parts in thirtieths are 2, 5, 15, 2, 1, 2 and 1, in the sequence of the stages. Thus 2
|
|
60
|
+
* thirtieths of the budget stay as a margin before SIGKILL.
|
|
61
|
+
* - The cooperative window is 5 thirtieths of the budget.
|
|
62
|
+
* - The kubelet sends SIGTERM one time, thus a repeat signal has no effect.
|
|
63
|
+
*
|
|
64
|
+
* At the defaults of Kubernetes (a grace of 30 s and no preStop hook), the caps are 2, 5, 15, 2, 1,
|
|
65
|
+
* 2 and 1 s. Thus the sum of the caps and the preStop sleep fits in the grace (O10). The window is
|
|
66
|
+
* shorter than the drain, which is shorter than the budget (O16).
|
|
67
|
+
*/
|
|
68
|
+
declare const kubernetes: (pod: PodTiming) => Profile;
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/watchdog.d.ts
|
|
71
|
+
/**
|
|
72
|
+
* A watchdog spec declares a watchdog of the event loop. The service declares it as a check whose
|
|
73
|
+
* function the watchdog gives: the process's own wedge, which a liveness window reads.
|
|
74
|
+
*/
|
|
75
|
+
interface WatchdogSpec {
|
|
76
|
+
readonly name: string;
|
|
77
|
+
/** This time is the interval of the tick of the watchdog. */
|
|
78
|
+
readonly tick: Quantity;
|
|
79
|
+
/** This time is the most that a tick can be late before the loop counts as blocked. */
|
|
80
|
+
readonly threshold: Quantity;
|
|
81
|
+
/** This gate is the policy that the check controls, or null for a check that only reports. */
|
|
82
|
+
readonly gates: Gate | null;
|
|
83
|
+
readonly poll: Poll;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A watchdog measures how late its own tick comes. A blocked event loop delays each timer, thus a
|
|
87
|
+
* tick that comes later than the threshold shows the block. Its states are `watching`, `blocked`
|
|
88
|
+
* and `stopped`.
|
|
89
|
+
*/
|
|
90
|
+
interface Watchdog {
|
|
91
|
+
/** This function is the check: it fails when a tick since the last poll came late, or the loop is still blocked. */
|
|
92
|
+
readonly check: () => Outcome;
|
|
93
|
+
/** This time is the most that a tick was late, in milliseconds, since the start. */
|
|
94
|
+
readonly worst: number;
|
|
95
|
+
readonly blocked: boolean;
|
|
96
|
+
stop(): void;
|
|
97
|
+
}
|
|
98
|
+
/** This function starts a watchdog on a clock. */
|
|
99
|
+
declare const watchdog: (clock: Clock, spec: WatchdogSpec, trace?: Trace) => Watchdog;
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/builder.d.ts
|
|
102
|
+
/**
|
|
103
|
+
* A service is composed with plain method uses before it starts. The builder is imperative, and
|
|
104
|
+
* it holds static knowledge. The service does not use an `init` function that gives registrations.
|
|
105
|
+
*
|
|
106
|
+
* const svc = service("app");
|
|
107
|
+
* svc.dependency("weather", {
|
|
108
|
+
* system: null,
|
|
109
|
+
* knobs: { request_timeout },
|
|
110
|
+
* operations: { "GET /v1/forecast": { idempotent: true } },
|
|
111
|
+
* });
|
|
112
|
+
* svc.controller("forecast").requires("weather").route("GET /api/forecast", { idempotent: true });
|
|
113
|
+
* svc.check({ name: "weather.round-trip", dependency: "weather", … }, roundTrip);
|
|
114
|
+
* svc.describe(); // the whole graph as data, with nothing started
|
|
115
|
+
* const health = await svc.start();
|
|
116
|
+
*
|
|
117
|
+
* The graph has one source, thus its parts cannot disagree. A dependency or a check can join
|
|
118
|
+
* after the start. It starts tripped, and the export is partial. The runtime has no defaults of
|
|
119
|
+
* its own. Thus `describe()` refuses a service that does not declare a policy, its startup budget
|
|
120
|
+
* or its shutdown, and it tells which. The shutdown includes what a repeat signal does.
|
|
121
|
+
*
|
|
122
|
+
* `start()` starts only the health runtime. `run()` starts the whole lifecycle: the startup in its
|
|
123
|
+
* sequence, the exit signals, the capped phases of the shutdown, and the exit of the process.
|
|
124
|
+
*/
|
|
125
|
+
interface ControllerBuilder {
|
|
126
|
+
/** This method declares the dependencies that its routes need. The controller is ready only while they are ready. */
|
|
127
|
+
requires(...dependencies: string[]): ControllerBuilder;
|
|
128
|
+
/** This method declares a route that the controller serves, and if its callers can send it again. */
|
|
129
|
+
route(route: string, operation: Operation): ControllerBuilder;
|
|
130
|
+
/**
|
|
131
|
+
* This method declares a route pattern, the operations that its requests can be, and the function
|
|
132
|
+
* that picks the operation of each request. A pattern with one operation needs no function.
|
|
133
|
+
*/
|
|
134
|
+
pattern(pattern: string, operations: Readonly<Record<string, Operation>>, operationOf?: OperationOf): ControllerBuilder;
|
|
135
|
+
}
|
|
136
|
+
/** A service builder composes a service, gives it as data, and starts it. */
|
|
137
|
+
interface ServiceBuilder {
|
|
138
|
+
readonly name: string;
|
|
139
|
+
/** This method declares a dependency, and the handle that connects to it and closes it, if it has one. */
|
|
140
|
+
dependency(name: string, spec: Omit<Dependency, "name">, handle?: DependencyHandle): ServiceBuilder;
|
|
141
|
+
/** This method declares a server, and its implementation. `run()` needs the implementation. */
|
|
142
|
+
server(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;
|
|
143
|
+
consumer(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;
|
|
144
|
+
worker(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;
|
|
145
|
+
/** This method gives the server of the probes. It starts first and closes last. */
|
|
146
|
+
healthServer(server: HealthServer): ServiceBuilder;
|
|
147
|
+
/** This method adds work to a declared phase of the shutdown: the owner of the work registers it. */
|
|
148
|
+
onPhase(phase: string, work: PhaseWork): ServiceBuilder;
|
|
149
|
+
/** This method declares a watchdog of the event loop, as a check on the state of the process. */
|
|
150
|
+
watchdog(spec: WatchdogSpec): ServiceBuilder;
|
|
151
|
+
controller(name: string): ControllerBuilder;
|
|
152
|
+
/** This method declares a check, and the function that measures its step. `start()` needs the function. */
|
|
153
|
+
check(check: Check, run?: CheckFn): ServiceBuilder;
|
|
154
|
+
readiness(timing: Timing): ServiceBuilder;
|
|
155
|
+
liveness(timing: Timing): ServiceBuilder;
|
|
156
|
+
startup(budget: Quantity): ServiceBuilder;
|
|
157
|
+
couple(coupling: Coupling): ServiceBuilder;
|
|
158
|
+
phase(name: string, cap: Quantity): ServiceBuilder;
|
|
159
|
+
cooperativeWindow(window: Extent): ServiceBuilder;
|
|
160
|
+
/** This method declares the earliest time of the cooperative abort: half the cap of the drain, or no floor. */
|
|
161
|
+
cooperativeFloor(floor: CooperativeFloor): ServiceBuilder;
|
|
162
|
+
/** This method declares what an exit signal does while the shutdown runs: nothing, or an exit at once. */
|
|
163
|
+
onRepeatSignal(policy: RepeatSignal): ServiceBuilder;
|
|
164
|
+
/** This method declares process triggers that start the shutdown, in addition to the exit signals. */
|
|
165
|
+
trigger(...triggers: ProcessTrigger[]): ServiceBuilder;
|
|
166
|
+
/**
|
|
167
|
+
* This method declares a message of the IPC channel that starts the shutdown, as data. With `as`,
|
|
168
|
+
* the message is that exit signal: `svc.trigger("ipc-message", { match: "shutdown", as: "SIGTERM" })`.
|
|
169
|
+
*/
|
|
170
|
+
trigger(trigger: "ipc-message", message: MessageTrigger): ServiceBuilder;
|
|
171
|
+
/** This method declares the exit code of a clean shutdown that a trigger starts, in place of 0. */
|
|
172
|
+
exitCode(trigger: ExitTrigger, code: number): ServiceBuilder;
|
|
173
|
+
/** This method declares each field of a profile, as the method of that field does. */
|
|
174
|
+
profile(profile: Profile): ServiceBuilder;
|
|
175
|
+
/** This method gives the service as data, and it starts nothing. */
|
|
176
|
+
describe(): ServiceDescription;
|
|
177
|
+
/**
|
|
178
|
+
* This method closes the composition, starts the health runtime, and gives it after one warm-up
|
|
179
|
+
* sweep of each check. A part that joins after it starts tripped, and it makes the export
|
|
180
|
+
* partial. A second use gives the same runtime.
|
|
181
|
+
*/
|
|
182
|
+
start(options?: StartOptions): Promise<Health>;
|
|
183
|
+
/**
|
|
184
|
+
* This method starts the whole lifecycle and gives it when its startup ends: the service operates,
|
|
185
|
+
* or its startup failed and it shuts down. It refuses a declaration that it cannot start: a transport
|
|
186
|
+
* without its implementation, or a stage with work and no declared phase.
|
|
187
|
+
*/
|
|
188
|
+
run(options?: RunOptions): Promise<Lifecycle>;
|
|
189
|
+
/**
|
|
190
|
+
* This value is the health runtime from the time that `start()` begins, or null before. Thus the
|
|
191
|
+
* probes can answer during the warm-up sweep: the startup probe fails until the sweep ends.
|
|
192
|
+
*/
|
|
193
|
+
readonly health: Health | null;
|
|
194
|
+
/**
|
|
195
|
+
* This value is the lifecycle from the time that `run()` begins, or null before. Its request
|
|
196
|
+
* tracking operates while the transports start, before `run()` gives it.
|
|
197
|
+
*/
|
|
198
|
+
readonly lifecycle: Lifecycle | null;
|
|
199
|
+
}
|
|
200
|
+
/** This function starts the composition of a service. */
|
|
201
|
+
declare const service: (name: string) => ServiceBuilder;
|
|
202
|
+
//#endregion
|
|
203
|
+
export { WatchdogSpec as a, Profile as c, kubernetes as d, localDev as f, Watchdog as i, daemon as l, ServiceBuilder as n, watchdog as o, service as r, PodTiming as s, ControllerBuilder as t, ephemeral as u };
|
|
204
|
+
//# sourceMappingURL=builder-QVxu1WcL.d.ts.map
|
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-DtJZW09J.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
|