@mark1russell7/service 0.5.5 → 0.5.7
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 +14 -2
- package/build/http.d.ts +2 -1
- package/build/http.js +38 -16
- package/build/http.js.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.js +7 -5
- package/build/index.js.map +1 -1
- package/build/{knobs-BZAsgb5H.js → knobs-D2ClI68I.js} +21 -2
- package/build/knobs-D2ClI68I.js.map +1 -0
- package/build/limiter-DCDdzVwe.d.ts +45 -0
- package/build/limiter-pkBZUp-t.js +402 -0
- package/build/limiter-pkBZUp-t.js.map +1 -0
- package/build/limiter.d.ts +2 -1
- package/build/limiter.js +2 -2
- package/package.json +2 -2
- package/build/knobs-BZAsgb5H.js.map +0 -1
- package/build/limiter-wv0uXDk9.js +0 -126
- package/build/limiter-wv0uXDk9.js.map +0 -1
package/README.md
CHANGED
|
@@ -65,6 +65,7 @@ The format of a description is `mark1russell7.service/1`.
|
|
|
65
65
|
- **A phase fails only for its own work.** At the cap of the drain, a transport that waits only for the bytes of its answers did its work. Then the cap is not a failure, and the exit code stays 0.
|
|
66
66
|
- **The answers that did not go.** The event of that cap (`phase.cap`) gives the cause and the count, for example `answers not flushed: 2`. The close of the transports then destroys those sockets.
|
|
67
67
|
- **Work that did not end.** A call whose end did not come, a transport that did not end its drain, or a step of an owner still fails the phase. The exit code is then 1, as before.
|
|
68
|
+
- **Only the work of the drain.** The drain waits only for the streams that `life.request` of its lifecycle made, because only its cut ends them. A stream of another lifecycle, or of none, does not hold the drain. The record of the drain gives their count, for example `streams outside the lifecycle: 1`. Thus each exit code is the exit code of 0.5.4.
|
|
68
69
|
- **Exit codes.** `life.shutdown(cause, { code })` gives the exit code of the work, a whole number from 0 to 255. The first request with a code sets it. `svc.exitCode(trigger, code)` gives the code of a shutdown that a trigger starts, for example 130 for SIGINT. A code that is not 0 stays after an error. A code of 0, or no code, becomes 1 after an error.
|
|
69
70
|
- **The output.** The stage `flush the output` comes last. The owners of the phase write their last output first, for example a log or a trace exporter. Then the `ProcessPort` writes the buffered output of stdout and stderr, in the cap of the stage. Without this phase, the process exits without a wait, as in 0.4.
|
|
70
71
|
- **The hold.** The lifecycle holds its process from its start to its exit, through `ProcessPort.hold`, as a listening socket does. The hold is one timer of the event loop, and the exit releases it. The other timers of the runtime still do not keep the process alive (A14).
|
|
@@ -73,7 +74,9 @@ The format of a description is `mark1russell7.service/1`.
|
|
|
73
74
|
|
|
74
75
|
## The machines
|
|
75
76
|
|
|
76
|
-
The machines of the runtime are `mark1russell7.machine/1` documents, which `@mark1russell7/algebra` reads. They are the gate, the latch (a debounce), the window, the poller, the exit manager and the watchdog. `drainMachine(description)` gives the drain of a declaration, with one state for each phase.
|
|
77
|
+
The machines of the runtime are `mark1russell7.machine/1` documents, which `@mark1russell7/algebra` reads. They are the gate, the latch (a debounce), the window, the poller, the exit manager and the watchdog. `drainMachine(description)` gives the drain of a declaration, with one state for each phase. The keyed limiter has its own documents.
|
|
78
|
+
|
|
79
|
+
Each machine gives its transitions to `run({ record })` or `start({ record })`. `replay(doc, events)` gives each transition that the document does not permit. It is the replay of `@mark1russell7/algebra`, which reads a machine of any name.
|
|
77
80
|
|
|
78
81
|
The tests replay the real runtime against each document. They also compare each timer of the documents, from the algebra, with the time that the runtime keeps.
|
|
79
82
|
|
|
@@ -317,6 +320,8 @@ A keyed limiter gives each key its own limit, for example each tenant or each pr
|
|
|
317
320
|
| `idleSpan(policy)`, `longestRetryAfter(policy)`, `retryAfterSeconds(retryAfter)` | The times of a policy, and `Retry-After` in whole seconds |
|
|
318
321
|
| `limiterProblems(name, spec)` | The problems of a spec that `describe()` and `keyedLimiter` refuse |
|
|
319
322
|
| `LIMITER_RULES` | The rules O29 and O30 of the law, as data. Their identifiers are provisional. |
|
|
323
|
+
| `TOKEN_BUCKET_MACHINE`, `SLIDING_WINDOW_MACHINE`, `limiterMachine(policy)` | The machine of one key of each policy, as a `mark1russell7.machine/1` document |
|
|
324
|
+
| `limiterStateOf(rule, state, now)` | The state of a key of a store, as its document names it: `new`, `open` or `limited` |
|
|
320
325
|
|
|
321
326
|
- **No defaults.** A spec has a key, a policy, a store and a bound on its keys. `describe()` refuses a spec without its rate, its burst, its limit, its window, its store, the failure policy of a shared store or its bound. The error names the part.
|
|
322
327
|
- **The key.** `key` tells what the key of each take is, for example `tenant`. The caller gives each key to `take(key)`.
|
|
@@ -342,6 +347,11 @@ A keyed limiter gives each key its own limit, for example each tenant or each pr
|
|
|
342
347
|
- **O30.** The least delay of a caller before a retry (`retry_delay@caller`) is at least the longest `Retry-After` of the policy. Otherwise a caller that does not read the header sends again too early.
|
|
343
348
|
- **A closed failure.** The `retryAfter` of the cause `store` is the longest `retryAfter` of the policy. Thus O29 and O30 apply to it too.
|
|
344
349
|
- **The symbols.** `retry_after@limiter` is the longest `Retry-After` of the policy in whole seconds: one interval of the bucket, or one window. `retry_after_at_cap@limiter` is the longest at the cap, `idleSpan` in whole seconds.
|
|
350
|
+
- **The machine of a key.** Each policy has a document of one key, which `@mark1russell7/algebra` reads. A key is `new` (the store holds no count, or a count equal to a new key), `open` (its next take is permitted) or `limited` (its next take is refused).
|
|
351
|
+
- **The timers of a token bucket.** `token` fires at most `1 / rate` after the take that used the last token. That time is the longest `Retry-After`. `full` fires from one to `burst` intervals after a permitted take: the bucket is full, and the key is new again.
|
|
352
|
+
- **The timers of a sliding window.** `oldest` fires at most one window after the take that filled the window. `last` fires one window after the last permitted take, and the key is new again.
|
|
353
|
+
- **The other transitions.** A refused take leaves a limited key limited. A new key at the cap stays new, with the cause `cap`. A failure of a shared store leaves each state as it is.
|
|
354
|
+
- **The replay.** The tests drive the runtime on a fake clock and replay each take and each change of state against the document. The scenarios reach each transition. The times of the runtime are inside the bounds of the timers that the algebra gives.
|
|
345
355
|
|
|
346
356
|
```ts
|
|
347
357
|
import { keyedLimiter, memoryStore, realClock } from "@mark1russell7/service";
|
|
@@ -387,6 +397,7 @@ if (!decision.allowed) return limitedResponse(decision); // 429, with Retry-Afte
|
|
|
387
397
|
- **The stop of the intake.** `stopIntake()` closes the listener, thus each new connection gets a refusal. It closes the idle connections at once, and again at each `server_idle_sweep_interval`. Each later response gets `Connection: close`, and each later request gets a retryable 503.
|
|
388
398
|
- **The drain and the close.** `drain(deadline, signal)` ends when no work is in flight, or at the deadline. It gives the cooperative abort to the handlers. `close()` destroys each connection, the upgraded connections too. A second close gives the same promise.
|
|
389
399
|
- **The answers at the deadline.** At its deadline, the drain also waits for each answer that is to come or to go out. That is a response that `writeOutcome` writes, or an ended response whose bytes did not go.
|
|
400
|
+
- **The streams of the lifecycle.** A response of `writeOutcome` holds the drain only when `life.request` of the lifecycle of the transport made its stream. `handle` gives such a stream. Another stream does not hold the drain, and the drain counts it.
|
|
390
401
|
- **Why the drain waits.** The cut of the requests and the deadline of the transport come at the same time, in either sequence. Thus each cut call gets its 503, its 500 or its in-band end before the close.
|
|
391
402
|
- **The bound of that wait.** The cap of the phase bounds the wait. A client that does not read its answer holds the drain to the cap. That cap is not a failure: its event gives `answers not flushed` and the count. A response that is open for another reason does not hold the drain.
|
|
392
403
|
- **The lifecycle.** The transport gets the lifecycle at its start (`start(signal, life)`), before the warm-up. Each handler gets it in its context (`context.life`), with `server_retry_after` (`context.retryAfter`).
|
|
@@ -618,7 +629,8 @@ for await (const event of stream) {
|
|
|
618
629
|
- **The options of the runner.** These tests cover the requests of a transport while the startup runs, the policy of a repeat signal, the profiles and the process triggers. They also cover the exit codes, a first Ctrl-C that aborts at once, and the flush of the output.
|
|
619
630
|
- **The composition.** Two components, each with a transport, a dependency and a check, share one service and one `run()`. The parts start and stop in their sequence, and the process exits one time.
|
|
620
631
|
- **The process port.** The port of Node, on a fake host: the signals, the end of stdin and the close of the IPC channel. The tests also cover the messages, the hold, the flush and the exit. The lifecycle holds its fake process from its start to its exit.
|
|
621
|
-
- **The HTTP edge.** The transport, the mounts, the upgrades, the probes and `handle` run on fake servers and the fake clock, with no socket. A service under `run()` drains a held request, and its probes answer through the drain. A request during the warm-up gets 503, and a stream ends in-band after its first item.
|
|
632
|
+
- **The HTTP edge.** The transport, the mounts, the upgrades, the probes and `handle` run on fake servers and the fake clock, with no socket. A service under `run()` drains a held request, and its probes answer through the drain. A request during the warm-up gets 503, and a stream ends in-band after its first item.
|
|
633
|
+
- **The drain of the HTTP edge (SYS-29).** Each call that the deadline cuts gets its answer before its socket closes, with the cut first and with the transport first. Answers that do not go out by the cap give exit 0, and work that did not end by the cap gives exit 1. A stream outside the lifecycle does not hold the drain, and the record of the drain counts it.
|
|
622
634
|
- **The headers of the outcomes.** Each path of the request tracker gets its status. Each 503 gets `Retry-After`: the refusals, the retryable cut and the cut of a unary stream. No 500 gets it.
|
|
623
635
|
- **The headers on the port.** The transport adds `Retry-After` to each 503 of a probe and of a handler that writes its own status, in each form of `writeHead`. It takes `Retry-After` from each 500. The probe server gives its 503s the time of its knob.
|
|
624
636
|
- **The conformance of a transport.** A reference transport passes each case, and each of nine mutations of it fails the case of its part. The HTTP transport passes each case on a fake server, for its requests and its upgrades.
|
package/build/http.d.ts
CHANGED
|
@@ -120,7 +120,7 @@ export declare const pathOf: (url: string | undefined) => string;
|
|
|
120
120
|
* - `drain` waits until no work is in flight, or until the deadline. It gives the cooperative abort to
|
|
121
121
|
* the handlers through their context. At the deadline, it also waits for each answer that is to come
|
|
122
122
|
* or to go out (SYS-29). The cap of the phase bounds that wait. Answers that did not go out by the
|
|
123
|
-
* cap are not a failure of the phase.
|
|
123
|
+
* cap are not a failure of the phase. A stream outside the lifecycle of the transport does not hold it.
|
|
124
124
|
* - `close` destroys each connection. A second close gives the same promise.
|
|
125
125
|
* - The mounts share one port. The first request handler that gives true answers. After the stop of
|
|
126
126
|
* the intake, only the mounts until `close transports` answer, and each other request gets a 503.
|
|
@@ -264,6 +264,7 @@ export declare const limitedResponse: (refused: Refused) => OutcomeResponse;
|
|
|
264
264
|
* slows the work. An end before the first item gets a status, as a unary end does.
|
|
265
265
|
* - The answer to come: until it writes the end, the response holds the drain of `httpTransport` past
|
|
266
266
|
* its deadline. The cut of the drain gives that end, and the drain waits until the end goes out.
|
|
267
|
+
* Only a stream of `life.request` of the lifecycle of the transport holds the drain.
|
|
267
268
|
*/
|
|
268
269
|
export declare const writeOutcome: <T>(res: ServerResponse, stream: RequestStream<T>, options: OutcomeOptions<T>) => Promise<RequestEnd | null>;
|
|
269
270
|
/** A route of a handler is the name of the route that a controller declares, or a function that gives it from the request. */
|
package/build/http.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import { realClock } from "./clock.js";
|
|
2
2
|
import { s as retryAfterSeconds } from "./policy-CgS4y94U.js";
|
|
3
|
-
import { i as holdLate, r as millisecondsOf$1 } from "./knobs-
|
|
3
|
+
import { c as ownerOf, i as holdLate, o as noteOutside, r as millisecondsOf$1, s as ownStream } from "./knobs-D2ClI68I.js";
|
|
4
4
|
import { valueIn } from "@mark1russell7/quantity";
|
|
5
5
|
import { createServer } from "node:http";
|
|
6
6
|
//#region src/http/answers.ts
|
|
7
|
-
const marked = /* @__PURE__ */ new
|
|
7
|
+
const marked = /* @__PURE__ */ new WeakMap();
|
|
8
8
|
const watchers = /* @__PURE__ */ new Set();
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* This function marks a response that waits for the end of its request. `owner` is the lifecycle
|
|
11
|
+
* that made the stream, or null. It gives the function that removes the mark.
|
|
12
|
+
*/
|
|
13
|
+
const markAnswer = (res, owner) => {
|
|
14
|
+
marked.set(res, owner);
|
|
12
15
|
return () => {
|
|
13
16
|
if (!marked.delete(res)) return;
|
|
14
17
|
for (const watcher of [...watchers]) watcher();
|
|
15
18
|
};
|
|
16
19
|
};
|
|
17
|
-
/**
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* This function gives the mark of a response: the lifecycle that made its stream. It gives null for a
|
|
22
|
+
* stream outside each lifecycle, and undefined for a response with no mark.
|
|
23
|
+
*/
|
|
24
|
+
const answerOwner = (res) => marked.get(res);
|
|
19
25
|
/** This function starts `watcher` after each removal of a mark. It gives the function that stops it. */
|
|
20
26
|
const watchAnswers = (watcher) => {
|
|
21
27
|
watchers.add(watcher);
|
|
@@ -94,7 +100,7 @@ const STATUS_TEXT = {
|
|
|
94
100
|
* - `drain` waits until no work is in flight, or until the deadline. It gives the cooperative abort to
|
|
95
101
|
* the handlers through their context. At the deadline, it also waits for each answer that is to come
|
|
96
102
|
* or to go out (SYS-29). The cap of the phase bounds that wait. Answers that did not go out by the
|
|
97
|
-
* cap are not a failure of the phase.
|
|
103
|
+
* cap are not a failure of the phase. A stream outside the lifecycle of the transport does not hold it.
|
|
98
104
|
* - `close` destroys each connection. A second close gives the same promise.
|
|
99
105
|
* - The mounts share one port. The first request handler that gives true answers. After the stop of
|
|
100
106
|
* the intake, only the mounts until `close transports` answer, and each other request gets a 503.
|
|
@@ -152,23 +158,29 @@ const httpTransport = (server, knobs, options = {}) => {
|
|
|
152
158
|
* This function gives what holds a drain after its deadline. An answer is unflushed when its handler
|
|
153
159
|
* ended it and its bytes did not all go. It is unflushed too while `writeOutcome` waits for a client
|
|
154
160
|
* that does not read. A call runs while `writeOutcome` waits for the end of its request, which the
|
|
155
|
-
* cut of the drain gives.
|
|
161
|
+
* cut of the drain gives. Only a stream of the lifecycle of the transport counts, because only its
|
|
162
|
+
* cut ends the stream. A response that is open for another reason does not hold the drain.
|
|
156
163
|
*/
|
|
157
164
|
const lateHold = () => {
|
|
158
165
|
let running = 0;
|
|
159
166
|
let unflushed = 0;
|
|
167
|
+
let outside = 0;
|
|
160
168
|
for (const res of responses) {
|
|
161
169
|
if (res.destroyed) continue;
|
|
162
170
|
if (res.writableEnded) {
|
|
163
171
|
if (!res.writableFinished) unflushed += 1;
|
|
164
|
-
|
|
165
|
-
if (res.writableNeedDrain) unflushed += 1;
|
|
166
|
-
else running += 1;
|
|
172
|
+
continue;
|
|
167
173
|
}
|
|
174
|
+
const owner = answerOwner(res);
|
|
175
|
+
if (owner === void 0) continue;
|
|
176
|
+
if (life === null || owner !== life) outside += 1;
|
|
177
|
+
else if (res.writableNeedDrain) unflushed += 1;
|
|
178
|
+
else running += 1;
|
|
168
179
|
}
|
|
169
180
|
return {
|
|
170
181
|
running,
|
|
171
|
-
unflushed
|
|
182
|
+
unflushed,
|
|
183
|
+
outside
|
|
172
184
|
};
|
|
173
185
|
};
|
|
174
186
|
const answering = () => {
|
|
@@ -393,7 +405,16 @@ const httpTransport = (server, knobs, options = {}) => {
|
|
|
393
405
|
const reached = () => {
|
|
394
406
|
late = true;
|
|
395
407
|
unwatch = watchAnswers(check);
|
|
396
|
-
if (life !== null)
|
|
408
|
+
if (life !== null) {
|
|
409
|
+
unhold = holdLate(life, () => {
|
|
410
|
+
const { running, unflushed } = lateHold();
|
|
411
|
+
return {
|
|
412
|
+
running,
|
|
413
|
+
unflushed
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
noteOutside(life, lateHold().outside);
|
|
417
|
+
}
|
|
397
418
|
check();
|
|
398
419
|
};
|
|
399
420
|
drainers.add(check);
|
|
@@ -745,6 +766,7 @@ const writeWhole = (res, response) => {
|
|
|
745
766
|
* slows the work. An end before the first item gets a status, as a unary end does.
|
|
746
767
|
* - The answer to come: until it writes the end, the response holds the drain of `httpTransport` past
|
|
747
768
|
* its deadline. The cut of the drain gives that end, and the drain waits until the end goes out.
|
|
769
|
+
* Only a stream of `life.request` of the lifecycle of the transport holds the drain.
|
|
748
770
|
*/
|
|
749
771
|
const writeOutcome = async (res, stream, options) => {
|
|
750
772
|
const iterator = stream[Symbol.asyncIterator]();
|
|
@@ -773,7 +795,7 @@ const writeOutcome = async (res, stream, options) => {
|
|
|
773
795
|
const drained = new Promise((resolve) => res.once("drain", () => resolve("drained")));
|
|
774
796
|
return await Promise.race([drained, closed]) === "drained";
|
|
775
797
|
};
|
|
776
|
-
const answered = markAnswer(res);
|
|
798
|
+
const answered = markAnswer(res, ownerOf(stream));
|
|
777
799
|
try {
|
|
778
800
|
const format = options.stream;
|
|
779
801
|
if (format === void 0) {
|
|
@@ -866,7 +888,7 @@ const handle = (controller, route, work, options = {}) => {
|
|
|
866
888
|
return true;
|
|
867
889
|
}
|
|
868
890
|
const given = options.detail === void 0 ? {} : { detail: options.detail(req) };
|
|
869
|
-
await writeOutcome(res, life.request(controller, name, (signal) => work(req, signal), given), written);
|
|
891
|
+
await writeOutcome(res, ownStream(life.request(controller, name, (signal) => work(req, signal), given), life), written);
|
|
870
892
|
return true;
|
|
871
893
|
};
|
|
872
894
|
};
|
package/build/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","names":["knobMilliseconds"],"sources":["../src/http/answers.ts","../src/http/transport.ts","../src/http/probes.ts","../src/http/outcome.ts"],"sourcesContent":["// The answers that the request tracking still writes (SYS-29). `writeOutcome` marks each response\n// while it waits for the end of its request. At the deadline of its drain, the transport waits for\n// each marked response: the cut of the drain gives that end at the same time.\n\nconst marked = new WeakSet<object>();\nconst watchers = new Set<() => void>();\n\n/** This function marks a response that waits for the end of its request. It gives the function that removes the mark. */\nexport const markAnswer = (res: object): (() => void) => {\n marked.add(res);\n return () => {\n if (!marked.delete(res)) return;\n for (const watcher of [...watchers]) watcher();\n };\n};\n\n/** This function tells if a response waits for the end of its request. */\nexport const awaitsAnswer = (res: object): boolean => marked.has(res);\n\n/** This function starts `watcher` after each removal of a mark. It gives the function that stops it. */\nexport const watchAnswers = (watcher: () => void): (() => void) => {\n watchers.add(watcher);\n return () => {\n watchers.delete(watcher);\n };\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport type { IncomingMessage, Server, ServerEventMap, ServerResponse } from \"node:http\";\nimport type { ListenOptions, Socket } from \"node:net\";\nimport type { Cancel, Clock } from \"../clock.ts\";\nimport { realClock } from \"../clock.ts\";\nimport type { LateHold } from \"../drains.ts\";\nimport { holdLate } from \"../drains.ts\";\nimport { millisecondsOf as knobMilliseconds } from \"../knobs.ts\";\nimport type { Lifecycle, TransportImpl } from \"../lifecycle.ts\";\nimport { awaitsAnswer, watchAnswers } from \"./answers.ts\";\n\n/**\n * These are the knobs of an HTTP transport, by the names of the ordering law. The transport has no\n * defaults, thus each knob is necessary. The declaration of the server gets the same record.\n */\nexport interface HttpKnobs {\n /**\n * This time is the real idle close of the server, which O01 compares with the idle time of a\n * client. Node closes an idle connection at `keepAliveTimeout` plus `keepAliveTimeoutBuffer`.\n * Thus the transport sets `keepAliveTimeout` to this time less the buffer of the server.\n * A time without a bound stops the timer.\n */\n readonly server_idle_close: Extent;\n /**\n * This time is the interval of the sweep of idle connections, from the stop of the intake until the\n * close. A time without a bound gives one sweep, at the stop of the intake.\n */\n readonly server_idle_sweep_interval: Extent;\n /** This time goes in the `Retry-After` of each 503 that the transport gives after its intake stops. */\n readonly server_retry_after: Quantity;\n /** The other knobs of the server go to the declaration. The transport does not read them. */\n readonly [knob: string]: Extent;\n}\n\n/**\n * These are the knobs that the transport gives the declaration: the knobs of the caller, and the two\n * times that the transport found. Thus the declaration shows the three times of the idle close.\n */\nexport interface HttpTransportKnobs extends HttpKnobs {\n /** 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. */\n readonly server_idle_close_hint: Extent;\n /** This time is the `keepAliveTimeoutBuffer` of the server, which Node adds to the timer and not to the hint. */\n readonly server_idle_close_buffer: Quantity;\n}\n\n/** A mount stage is the stage of the shutdown at which a mount stops: the stop of the intake, or the close. */\nexport type MountStage = \"stop intake\" | \"close transports\";\n\n/** A mount context gives each handler the signals of the shutdown, the lifecycle and the time of a retry. */\nexport interface MountContext {\n /** This signal aborts when the intake stops. A long stream ends in its own protocol at it. */\n readonly intake: AbortSignal;\n /** This signal aborts at the cooperative abort of the drain, or at the close. The work in flight stops at it. */\n readonly signal: AbortSignal;\n /** This lifecycle is the lifecycle that the transport got at its start, or null before the start. */\n readonly life: Lifecycle | null;\n /** This time is the knob `server_retry_after`: the `Retry-After` of each 503 of the handlers. */\n readonly retryAfter: Quantity;\n}\n\n/**\n * A request handler answers a request and gives true, or it gives false and does not touch the\n * response. Then the next handler gets the request. `until` tells when the handler stops.\n */\nexport interface RequestHandler {\n (req: IncomingMessage, res: ServerResponse, context: MountContext): boolean | Promise<boolean>;\n readonly until?: MountStage | undefined;\n}\n\ntype UpgradeArgs = ServerEventMap[\"upgrade\"];\n\n/** An upgrade socket is the stream of an upgraded connection, as the `upgrade` event of Node gives it. */\nexport type UpgradeSocket = UpgradeArgs[1];\n\n/** An upgrade handler takes an upgraded connection, for example a WebSocket server in its `noServer` mode. */\nexport interface UpgradeHandler {\n (req: IncomingMessage, socket: UpgradeSocket, head: UpgradeArgs[2], context: MountContext): void;\n readonly until?: MountStage | undefined;\n}\n\n/** An upgrade match is a path, without its query, or a function that tells if a handler takes a request. */\nexport type UpgradeMatch = string | ((req: IncomingMessage) => boolean);\n\n/** These options change one mount. */\nexport interface MountOptions {\n /**\n * This stage is when the mount stops. Without it, the stage of the handler applies, and then\n * `stop intake`. A mount until `close transports` keeps the listener open through the drain.\n */\n readonly until?: MountStage | undefined;\n}\n\n/** These options make an HTTP transport. */\nexport interface HttpTransportOptions {\n /**\n * These are the listen options of `start()`, or a function that makes the server listen. Without\n * them, `start()` takes a server that listens already.\n */\n readonly listen?: ListenOptions | ((server: Server) => Promise<void> | void) | undefined;\n /** This clock is the clock of the lifecycle. The deadline of the drain and the sweep use it. */\n readonly clock?: Clock | undefined;\n /**\n * This function tells if a response can keep its connection open. Without it, the transport reads\n * `life.keepAlive()` of the lifecycle of its start. The transport reads it when the headers of\n * each response go, and false gives `Connection: close`.\n */\n readonly keepAlive?: (() => boolean) | undefined;\n /** This function receives each error of a handler. The transport then answers 500, or it destroys the response. */\n readonly onError?: ((error: unknown, req: IncomingMessage) => void) | undefined;\n}\n\n/** An HTTP transport is a server of `node:http` as a transport of the lifecycle, with a list of mounts on its listener. */\nexport interface HttpTransport extends TransportImpl {\n readonly server: Server;\n /** These knobs go to the declaration of the server: the knobs of the caller, the hint and the buffer. */\n readonly knobs: HttpTransportKnobs;\n /** This method adds a request handler at the end of the list, and gives the function that removes it. */\n mount(handler: RequestHandler, options?: MountOptions): () => void;\n /** This method adds an upgrade handler for a path, and gives the function that removes it. */\n upgrade(match: UpgradeMatch, handler: UpgradeHandler, options?: MountOptions): () => void;\n /** This count is the number of open connections, idle connections and upgraded connections too. */\n readonly connections: number;\n /** This flag tells if the transport stopped its intake. */\n readonly intakeStopped: boolean;\n}\n\ninterface RequestMount {\n readonly handler: RequestHandler;\n readonly until: MountStage;\n}\n\ninterface UpgradeMount {\n readonly matches: (req: IncomingMessage) => boolean;\n readonly handler: UpgradeHandler;\n readonly until: MountStage;\n}\n\n/** This function gives the path of a request target, without its query and fragment. */\nexport const pathOf = (url: string | undefined): string => {\n const raw = url ?? \"/\";\n const end = raw.search(/[?#]/);\n const path = end === -1 ? raw : raw.slice(0, end);\n if (path.startsWith(\"/\")) return path;\n // an absolute target, as a proxy sends it\n try {\n return new URL(raw).pathname;\n } catch {\n return path;\n }\n};\n\n/** This function gives a knob in milliseconds, or infinity for a time without a bound. */\nconst millisecondsOf = (knobs: Readonly<Record<string, Extent | undefined>>, name: string, owner = \"httpTransport\"): number =>\n knobMilliseconds(knobs, name, owner);\n\n/**\n * This function gives the `Retry-After` of the knob `server_retry_after`, in whole seconds, rounded\n * up. The knob is necessary, and it has a bound.\n */\nexport const retryAfterOf = (knobs: Readonly<Record<string, Extent | undefined>>, owner: string): string => {\n const retryAfter = millisecondsOf(knobs, \"server_retry_after\", owner);\n if (!Number.isFinite(retryAfter)) throw new Error(\"the knob server_retry_after has no bound: a Retry-After needs a time\");\n return String(Math.ceil(retryAfter / 1000));\n};\n\n/** This function tells if the headers of a call of `writeHead` name a header, an object or a flat list of names and values. */\nconst names = (headers: unknown, name: string): boolean => {\n if (Array.isArray(headers)) return headers.some((each, at) => at % 2 === 0 && String(each).toLowerCase() === name);\n return typeof headers === \"object\" && headers !== null && Object.keys(headers).some((key) => key.toLowerCase() === name);\n};\n\n/** This function gives the headers of a call of `writeHead` without one header. */\nconst without = (headers: unknown, name: string): unknown => {\n if (Array.isArray(headers)) {\n const kept: unknown[] = [];\n for (let at = 0; at + 1 < headers.length; at += 2)\n if (String(headers[at]).toLowerCase() !== name) kept.push(headers[at], headers[at + 1]);\n return kept;\n }\n if (typeof headers !== \"object\" || headers === null) return headers;\n return Object.fromEntries(Object.entries(headers).filter(([key]) => key.toLowerCase() !== name));\n};\n\n/**\n * This function applies the rule of `Retry-After` to a call of `writeHead`, and gives its arguments.\n * A 503 without `Retry-After` gets the time of `server_retry_after` (RFC 9110). A 500 loses each\n * `Retry-After`, from the call and from the response.\n */\nconst retryRule = (res: ServerResponse, args: unknown[], retryAfter: string): unknown[] => {\n const status = typeof args[0] === \"number\" ? args[0] : res.statusCode;\n const at = typeof args[1] === \"string\" ? 2 : 1;\n if (status === 503 && !res.hasHeader(\"retry-after\") && !names(args[at], \"retry-after\")) res.setHeader(\"retry-after\", retryAfter);\n if (status !== 500) return args;\n res.removeHeader(\"retry-after\");\n if (!names(args[at], \"retry-after\")) return args;\n const given = [...args];\n given[at] = without(args[at], \"retry-after\");\n return given;\n};\n\nconst STATUS_TEXT: Readonly<Record<number, string>> = { 404: \"Not Found\", 500: \"Internal Server Error\", 503: \"Service Unavailable\" };\n\n/**\n * This function makes a server of `node:http` a transport of the lifecycle (S6 A8 and A13). It is not\n * a framework: it has no routes, no body parser and no middleware.\n * - The count of work in flight comes from events: each response until its `close`, and each upgraded\n * connection until its `close`.\n * - `stopIntake` closes the listener, and new connections get a refusal. It closes the idle connections\n * at once and at each `server_idle_sweep_interval`. Each later response gets `Connection: close`.\n * - `drain` waits until no work is in flight, or until the deadline. It gives the cooperative abort to\n * the handlers through their context. At the deadline, it also waits for each answer that is to come\n * or to go out (SYS-29). The cap of the phase bounds that wait. Answers that did not go out by the\n * cap are not a failure of the phase.\n * - `close` destroys each connection. A second close gives the same promise.\n * - The mounts share one port. The first request handler that gives true answers. After the stop of\n * the intake, only the mounts until `close transports` answer, and each other request gets a 503.\n */\nexport const httpTransport = (server: Server, knobs: HttpKnobs, options: HttpTransportOptions = {}): HttpTransport => {\n const clock = options.clock ?? realClock;\n const idleClose = millisecondsOf(knobs, \"server_idle_close\");\n const sweepEvery = millisecondsOf(knobs, \"server_idle_sweep_interval\");\n if (sweepEvery === 0)\n throw new Error(\"the knob server_idle_sweep_interval is zero: a sweep needs an interval above zero\");\n const retryAfterHeader = retryAfterOf(knobs, \"httpTransport\");\n for (const derived of [\"server_idle_close_hint\", \"server_idle_close_buffer\"])\n if ((knobs as Readonly<Record<string, Extent | undefined>>)[derived] !== undefined)\n throw new Error(`the knob ${derived} comes from the transport: give only server_idle_close`);\n if (server.listenerCount(\"request\") > 0 || server.listenerCount(\"upgrade\") > 0)\n throw new Error(\"the server has a request or upgrade listener of its own: mount it as a handler\");\n // Node closes an idle connection at keepAliveTimeout + keepAliveTimeoutBuffer, and its hint is\n // keepAliveTimeout. The buffer stays, thus the hint is less than the real close. An older Node has no buffer\n const buffer = (server as { keepAliveTimeoutBuffer?: number }).keepAliveTimeoutBuffer ?? 0;\n if (Number.isFinite(idleClose) && idleClose <= buffer)\n throw new Error(\n `the knob server_idle_close is ${idleClose} ms, at or below the keepAliveTimeoutBuffer of the server, ${buffer} ms: ` +\n \"the server closes an idle connection at keepAliveTimeout + keepAliveTimeoutBuffer\",\n );\n // 0 stops the keep-alive timer and the hint in Node\n server.keepAliveTimeout = Number.isFinite(idleClose) ? idleClose - buffer : 0;\n const declared: HttpTransportKnobs = {\n ...knobs,\n server_idle_close_hint: Number.isFinite(idleClose)\n ? { value: Math.floor(server.keepAliveTimeout / 1000), unit: \"s\" }\n : { value: \"infinite\", unit: \"s\" },\n server_idle_close_buffer: { value: buffer, unit: \"ms\" },\n };\n\n const requestMounts: RequestMount[] = [];\n const upgradeMounts: UpgradeMount[] = [];\n const responses = new Set<ServerResponse>();\n const upgraded = new Set<UpgradeSocket>();\n const sockets = new Set<Socket>();\n // the check of each drain: it ends its drain when nothing holds it\n const drainers = new Set<() => void>();\n const closedWaiters = new Set<() => void>();\n const intake = new AbortController();\n const cooperative = new AbortController();\n // the lifecycle comes at the start, thus the context reads it at each request\n let life: Lifecycle | null = null;\n const context: MountContext = {\n intake: intake.signal,\n signal: cooperative.signal,\n get life() {\n return life;\n },\n retryAfter: knobs.server_retry_after,\n };\n let intakeOpen = true;\n let listenerOpen = server.listening;\n let serverClosed = !server.listening;\n let sweep: Cancel | null = null;\n let closing: Promise<void> | null = null;\n\n const inFlight = (): number => responses.size + upgraded.size;\n /**\n * This function gives what holds a drain after its deadline. An answer is unflushed when its handler\n * ended it and its bytes did not all go. It is unflushed too while `writeOutcome` waits for a client\n * that does not read. A call runs while `writeOutcome` waits for the end of its request, which the\n * cut of the drain gives. A response that is open for another reason does not hold the drain.\n */\n const lateHold = (): LateHold => {\n let running = 0;\n let unflushed = 0;\n for (const res of responses) {\n if (res.destroyed) continue;\n if (res.writableEnded) {\n if (!res.writableFinished) unflushed += 1;\n } else if (awaitsAnswer(res)) {\n if (res.writableNeedDrain) unflushed += 1;\n else running += 1;\n }\n }\n return { running, unflushed };\n };\n const answering = (): boolean => {\n const hold = lateHold();\n return hold.running + hold.unflushed > 0;\n };\n const report = (error: unknown, req: IncomingMessage): void => {\n try {\n options.onError?.(error, req);\n } catch {\n // an error of the error hook does not stop the transport\n }\n };\n const settled = (): void => {\n for (const check of [...drainers]) check();\n if (closing !== null && inFlight() === 0 && sockets.size === 0 && serverClosed)\n for (const done of [...closedWaiters]) done();\n };\n // before its start, a transport has no lifecycle: a server that listens already keeps its connections\n const keepsAlive = (): boolean => intakeOpen && (options.keepAlive?.() ?? life?.keepAlive() ?? true);\n const live = <T extends { readonly until: MountStage }>(mounts: readonly T[]): T[] =>\n intakeOpen ? [...mounts] : mounts.filter((mount) => mount.until === \"close transports\");\n const throughDrain = (): boolean =>\n requestMounts.some((mount) => mount.until === \"close transports\") ||\n upgradeMounts.some((mount) => mount.until === \"close transports\");\n const closeListener = (): void => {\n if (!listenerOpen) return;\n listenerOpen = false;\n server.close();\n };\n const startSweep = (): void => {\n if (!Number.isFinite(sweepEvery) || closing !== null) return;\n sweep = clock.after(sweepEvery, () => {\n sweep = null;\n if (closing !== null) return;\n server.closeIdleConnections();\n startSweep();\n });\n };\n\n /**\n * This function watches the headers of each response when they go, whatever wrote them. Each 503\n * gets `Retry-After`, and a 500 gets none. The response closes its connection while the transport\n * cannot keep it.\n */\n const watchHeaders = (res: ServerResponse): void => {\n const writeHead = res.writeHead as (...args: unknown[]) => ServerResponse;\n res.writeHead = ((...args: unknown[]): ServerResponse => {\n if (res.headersSent) return writeHead.apply(res, args);\n if (!keepsAlive()) {\n res.shouldKeepAlive = false;\n res.setHeader(\"connection\", \"close\");\n }\n return writeHead.apply(res, retryRule(res, args, retryAfterHeader));\n }) as ServerResponse[\"writeHead\"];\n };\n const answer = (res: ServerResponse, status: number, headers: Readonly<Record<string, string>> = {}): void => {\n if (res.headersSent) {\n res.destroy();\n return;\n }\n const body = STATUS_TEXT[status] ?? \"\";\n res.writeHead(status, { \"content-type\": \"text/plain; charset=utf-8\", \"content-length\": String(Buffer.byteLength(body)), ...headers });\n res.end(body);\n };\n const refuse = (res: ServerResponse): void => answer(res, 503, { \"retry-after\": retryAfterHeader, connection: \"close\" });\n\n const route = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const mounts = live(requestMounts);\n try {\n for (const mount of mounts) {\n const given = mount.handler(req, res, context);\n const taken = typeof given === \"boolean\" ? given : await given;\n // a handler that wrote its response took the request, whatever it gave\n if (taken || res.headersSent || res.writableEnded) return;\n }\n } catch (error) {\n report(error, req);\n if (!res.writableEnded) answer(res, 500);\n return;\n }\n if (intakeOpen) answer(res, 404);\n else refuse(res);\n };\n\n const onRequest = (req: IncomingMessage, res: ServerResponse): void => {\n responses.add(res);\n // the bytes of an answer went: a drain after its deadline can end\n res.once(\"finish\", settled);\n res.once(\"close\", () => {\n responses.delete(res);\n settled();\n });\n watchHeaders(res);\n void route(req, res);\n };\n\n const onUpgrade = (req: IncomingMessage, socket: UpgradeSocket, head: UpgradeArgs[2]): void => {\n socket.on(\"error\", () => {});\n const mount = live(upgradeMounts).find((each) => each.matches(req));\n if (mount === undefined) {\n const status = intakeOpen ? 404 : 503;\n const extra = intakeOpen ? \"\" : `Retry-After: ${retryAfterHeader}\\r\\n`;\n socket.end(`HTTP/1.1 ${status} ${STATUS_TEXT[status]!}\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n${extra}\\r\\n`);\n return;\n }\n if (socket.destroyed) return;\n upgraded.add(socket);\n socket.once(\"close\", () => {\n upgraded.delete(socket);\n settled();\n });\n try {\n mount.handler(req, socket, head, context);\n } catch (error) {\n report(error, req);\n socket.destroy();\n }\n };\n\n const onConnection = (socket: Socket): void => {\n sockets.add(socket);\n socket.once(\"close\", () => {\n sockets.delete(socket);\n settled();\n });\n };\n\n server.on(\"request\", onRequest);\n server.on(\"upgrade\", onUpgrade);\n server.on(\"connection\", onConnection);\n server.on(\"listening\", () => {\n listenerOpen = true;\n serverClosed = false;\n });\n server.on(\"close\", () => {\n listenerOpen = false;\n serverClosed = true;\n settled();\n });\n\n const remover = <T>(list: T[], entry: T) => (): void => {\n const at = list.indexOf(entry);\n if (at >= 0) list.splice(at, 1);\n // the last mount through the drain went: the listener closes as the stop of the intake asks\n if (!intakeOpen && closing === null && !throughDrain()) closeListener();\n };\n\n return {\n server,\n knobs: declared,\n get connections() {\n return sockets.size;\n },\n get intakeStopped() {\n return !intakeOpen;\n },\n mount(handler, mountOptions = {}) {\n const entry: RequestMount = { handler, until: mountOptions.until ?? handler.until ?? \"stop intake\" };\n requestMounts.push(entry);\n return remover(requestMounts, entry);\n },\n upgrade(match, handler, mountOptions = {}) {\n const matches = typeof match === \"string\" ? (req: IncomingMessage) => pathOf(req.url) === match : match;\n const entry: UpgradeMount = { matches, handler, until: mountOptions.until ?? handler.until ?? \"stop intake\" };\n upgradeMounts.push(entry);\n return remover(upgradeMounts, entry);\n },\n async start(_signal, given) {\n life = given;\n if (closing !== null) throw new Error(\"the transport closed: a closed transport does not start again\");\n // a listener that another owner adds after the transport answers too, thus the start refuses it\n if (server.listenerCount(\"request\") > 1 || server.listenerCount(\"upgrade\") > 1)\n throw new Error(\"the server has a request or upgrade listener of its own: mount it as a handler\");\n if (server.listening) return;\n const listen = options.listen;\n if (listen === undefined) throw new Error(\"the server does not listen, and the transport has no listen options\");\n if (typeof listen === \"function\") {\n await listen(server);\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const failed = (error: Error): void => {\n server.off(\"listening\", listened);\n reject(error);\n };\n const listened = (): void => {\n server.off(\"error\", failed);\n resolve();\n };\n server.once(\"error\", failed);\n server.once(\"listening\", listened);\n server.listen(listen);\n });\n },\n stopIntake() {\n if (!intakeOpen) return;\n intakeOpen = false;\n intake.abort(new Error(\"the transport stopped its intake\"));\n if (!throughDrain()) closeListener();\n server.closeIdleConnections();\n startSweep();\n },\n drain(deadline, signal) {\n const abort = (): void => {\n if (!cooperative.signal.aborted) cooperative.abort(signal.reason);\n };\n if (signal.aborted) abort();\n else signal.addEventListener(\"abort\", abort, { once: true });\n // nothing in flight: the drain ends at once, with no timer\n if (inFlight() === 0) {\n signal.removeEventListener(\"abort\", abort);\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n let cancel: Cancel = () => {};\n let unwatch: () => void = () => {};\n let unhold: () => void = () => {};\n let late = false;\n let ended = false;\n // before the deadline, the work in flight holds the drain. After it, only an answer to come or to go out holds it\n const check = (): void => {\n if (ended || (inFlight() > 0 && (!late || answering()))) return;\n ended = true;\n drainers.delete(check);\n cancel();\n unwatch();\n unhold();\n signal.removeEventListener(\"abort\", abort);\n resolve();\n };\n // the cut of the requests comes at the same time, before or after this timer: the drain waits for its answers in each case\n const reached = (): void => {\n late = true;\n unwatch = watchAnswers(check);\n // the lifecycle reads the hold at the cap: answers that do not go out are not a failure of the phase\n if (life !== null) unhold = holdLate(life, lateHold);\n check();\n };\n drainers.add(check);\n if (deadline <= clock.now()) reached();\n else cancel = clock.after(deadline - clock.now(), reached);\n });\n },\n inFlight,\n close() {\n if (closing !== null) return closing;\n intakeOpen = false;\n if (!intake.signal.aborted) intake.abort(new Error(\"the transport closed\"));\n if (!cooperative.signal.aborted) cooperative.abort(new Error(\"the transport closed\"));\n sweep?.();\n sweep = null;\n closing = new Promise<void>((resolve) => {\n closedWaiters.add(resolve);\n });\n closeListener();\n server.closeAllConnections();\n for (const socket of [...upgraded]) socket.destroy();\n for (const socket of [...sockets]) socket.destroy();\n settled();\n return closing;\n },\n };\n};\n","import type { IncomingMessage, Server, ServerResponse } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { ListenOptions, Socket } from \"node:net\";\nimport type { Health, ProbeKind } from \"../health.ts\";\nimport type { HealthServer } from \"../lifecycle.ts\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport type { MountContext, RequestHandler } from \"./transport.ts\";\nimport { pathOf, retryAfterOf } from \"./transport.ts\";\n\n/** A probe response is the status, the headers and the body of one probe, for any HTTP framework or none. */\nexport interface ProbeResponse {\n readonly status: number;\n readonly headers: Readonly<Record<string, string>>;\n readonly body: string;\n}\n\n/** A health source is the health runtime, or a function that gives it when it exists. */\nexport type HealthSource = Health | (() => Health | null | undefined);\n\nconst KINDS: readonly ProbeKind[] = [\"startup\", \"readiness\", \"liveness\", \"report\"];\n\nconst prefixProblem = (prefix: string): string | null =>\n prefix === \"\" || (prefix.startsWith(\"/\") && !prefix.endsWith(\"/\"))\n ? null\n : `the probe prefix \"${prefix}\" does not start with \"/\", or it ends with \"/\"`;\n\nconst json = (status: number, body: unknown): ProbeResponse => ({\n status,\n headers: { \"content-type\": \"application/health+json\", \"cache-control\": \"no-store\" },\n body: JSON.stringify(body),\n});\n\n/**\n * This function gives the response of a probe path from the cache of the health runtime. For a path\n * that is not a probe, it gives null. The paths are `/startup`, `/readiness`, `/liveness` and\n * `/report` after the prefix. `/readiness/<controller>` gives the readiness of one controller.\n */\nexport const probeResponse = (\n health: Health | null | undefined,\n method: string | undefined,\n path: string,\n prefix = \"\",\n): ProbeResponse | null => {\n if (!path.startsWith(`${prefix}/`)) return null;\n const parts = path.slice(prefix.length + 1).split(\"/\");\n const kind = KINDS.find((each) => each === parts[0]);\n if (kind === undefined) return null;\n const controller = parts[1];\n if (parts.length > 2 || (controller !== undefined && (kind !== \"readiness\" || controller === \"\"))) return null;\n if (method !== \"GET\" && method !== \"HEAD\")\n return { status: 405, headers: { allow: \"GET, HEAD\", \"content-length\": \"0\" }, body: \"\" };\n // before the runtime exists, each probe fails: the service has not started\n if (health === null || health === undefined) return json(503, { status: \"fail\", probe: kind });\n try {\n const answer = health.probe(kind, controller === undefined ? undefined : decodeURIComponent(controller));\n return { ...json(answer.status, answer.body), headers: { \"content-type\": answer.contentType, \"cache-control\": \"no-store\" } };\n } catch {\n // a controller that the service does not have\n return json(404, { status: \"fail\", probe: kind, controller });\n }\n};\n\n/** This function writes a probe response. A 503 gets `Retry-After`, when the writer has the time of a retry. */\nconst write = (req: IncomingMessage, res: ServerResponse, response: ProbeResponse, retryAfter: string | undefined): void => {\n const retry = response.status === 503 && retryAfter !== undefined ? { \"retry-after\": retryAfter } : {};\n res.writeHead(response.status, { ...response.headers, ...retry, \"content-length\": String(Buffer.byteLength(response.body)) });\n res.end(req.method === \"HEAD\" ? undefined : response.body);\n};\n\n/** These options mount the probes on the port of the application. */\nexport interface ProbeHandlerOptions {\n /** This path goes before each probe path, for example `/health`. It starts with `/`, or it is empty. */\n readonly prefix: string;\n}\n\n/**\n * This function gives a request handler that answers the probes on the port of the application. It\n * answers until `close transports`, thus its mount keeps the listener open through the drain. A path\n * that is not a probe goes to the next handler. Each 503 gets the `Retry-After` of the context.\n */\nexport const probeHandler = (health: HealthSource, options: ProbeHandlerOptions): RequestHandler => {\n const problem = prefixProblem(options.prefix);\n if (problem !== null) throw new Error(problem);\n const handler = (req: IncomingMessage, res: ServerResponse, context?: MountContext): boolean => {\n const runtime = typeof health === \"function\" ? health() : health;\n const response = probeResponse(runtime, req.method, pathOf(req.url), options.prefix);\n if (response === null) return false;\n // the transport gives server_retry_after through the context, and it adds the header to each 503 too\n const retryAfter = context?.retryAfter === undefined ? undefined : retryAfterOf({ server_retry_after: context.retryAfter }, \"probeHandler\");\n write(req, res, response, retryAfter);\n return true;\n };\n return Object.assign(handler, { until: \"close transports\" as const });\n};\n\n/** These options make a probe server: the listen options of its port, the prefix of its paths, and the time of a retry. */\nexport interface ProbeServerOptions extends ListenOptions {\n /**\n * This time goes in the `Retry-After` of each 503 of a probe, as on the port of the application.\n * The probe server has no defaults, thus the knob is necessary.\n */\n readonly server_retry_after: Quantity;\n /** This path goes before each probe path. The default is no prefix, because the port has only the probes. */\n readonly prefix?: string | undefined;\n /** This server answers the probes. Without it, the probe server makes a server of `node:http`. */\n readonly server?: Server | undefined;\n}\n\n/** A probe server is a health server on its own port. Its server is visible for the address of the port. */\nexport interface ProbeServer extends HealthServer {\n readonly server: Server;\n}\n\n/**\n * This function makes a health server on a port of its own (S6 decision D5). It starts first and\n * closes last, thus the probes answer through the whole drain. Each probe answers from the cache, and\n * each 503 gets the `Retry-After` of `server_retry_after`.\n */\nexport const probeServer = (options: ProbeServerOptions): ProbeServer => {\n const { prefix = \"\", server = createServer(), server_retry_after, ...listen } = options;\n const retryAfter = retryAfterOf({ server_retry_after }, \"probeServer\");\n const problem = prefixProblem(prefix);\n if (problem !== null) throw new Error(problem);\n if (server.listenerCount(\"request\") > 0) throw new Error(\"the probe server has a request listener of its own\");\n let health: Health | null = null;\n let closing: Promise<void> | null = null;\n const sockets = new Set<Socket>();\n server.on(\"connection\", (socket: Socket) => {\n sockets.add(socket);\n socket.once(\"close\", () => sockets.delete(socket));\n });\n server.on(\"request\", (req: IncomingMessage, res: ServerResponse) => {\n const response = probeResponse(health, req.method, pathOf(req.url), prefix);\n write(req, res, response ?? { status: 404, headers: { \"content-type\": \"text/plain; charset=utf-8\" }, body: \"Not Found\" }, retryAfter);\n });\n return {\n server,\n listen(runtime) {\n health = runtime;\n if (server.listening) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const failed = (error: Error): void => {\n server.off(\"listening\", listened);\n reject(error);\n };\n const listened = (): void => {\n server.off(\"error\", failed);\n resolve();\n };\n server.once(\"error\", failed);\n server.once(\"listening\", listened);\n server.listen(listen);\n });\n },\n close() {\n if (closing !== null) return closing;\n closing = new Promise<void>((resolve) => {\n if (!server.listening) {\n resolve();\n return;\n }\n server.close(() => resolve());\n server.closeAllConnections();\n for (const socket of [...sockets]) socket.destroy();\n });\n return closing;\n },\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { Refused } from \"../limiter/policy.ts\";\nimport { retryAfterSeconds } from \"../limiter/policy.ts\";\nimport type { RequestEnd, RequestEvent, RequestOptions, RequestStream, RequestWork } from \"../requests.ts\";\nimport { markAnswer } from \"./answers.ts\";\nimport type { MountContext, RequestHandler } from \"./transport.ts\";\nimport { pathOf } from \"./transport.ts\";\n\n/** An outcome response is the status, the headers and the body of a request, for any HTTP framework or none. */\nexport interface OutcomeResponse {\n readonly status: number;\n readonly headers: Readonly<Record<string, string>>;\n readonly body: string | Uint8Array;\n}\n\n/** 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. */\nexport interface ResponsePart {\n readonly status?: number | undefined;\n readonly headers?: Readonly<Record<string, string>> | undefined;\n readonly body?: string | Uint8Array | undefined;\n}\n\n/**\n * A stream format writes the items of a stream and its end in the body, as they come. An end after\n * the first item goes in the body, because the status went with the first item.\n */\nexport interface StreamFormat<T = unknown> {\n readonly contentType: string;\n /** This function gives the bytes of one item. */\n item(value: T): string | Uint8Array;\n /** This function gives the bytes of the end, the last part of the body. */\n end(end: RequestEnd): string | Uint8Array;\n}\n\n/** These options map a request to a response. */\nexport interface OutcomeOptions<T> {\n /**\n * This value tells if the response can keep its connection open, as `life.keepAlive()` does. False\n * gives `Connection: close`. A function is read when the headers go.\n */\n readonly keepAlive: boolean | (() => boolean);\n /** This time goes in the `Retry-After` of each 503, in whole seconds, rounded up. It is the knob `server_retry_after`. */\n readonly retryAfter: Quantity;\n /**\n * This function gives the response of the value of a unary request. Without it, the mapping gives\n * 200 and the value as JSON, or 204 for no value.\n */\n readonly done?: ((value: T) => ResponsePart) | undefined;\n /**\n * This function gives the response of a failure of the route before the first byte. Without it,\n * the mapping gives 500 with no detail, because an error can hold private data.\n */\n readonly failed?: ((error: unknown) => ResponsePart) | undefined;\n /**\n * This format makes the response a stream: the status and each item go as they come, and the end\n * goes in the body. Without it, the response is unary: one status and the one value.\n */\n readonly stream?: StreamFormat<T> | undefined;\n}\n\nconst JSON_TYPE = \"application/json; charset=utf-8\";\n\n/**\n * This function gives an end as a record for a body: its kind and the count of its items. A cut and\n * a refusal also give their cause, and if the caller can send the request again. A failure gives\n * no detail.\n */\nexport const endRecord = (end: RequestEnd): Readonly<Record<string, unknown>> => {\n if (end.kind === \"refused\" || end.kind === \"cut\")\n return { end: end.kind, delivered: end.delivered, retryable: end.retryable, cause: end.cause };\n return { end: end.kind, delivered: end.delivered };\n};\n\n/** This format writes each item and the end as one line of JSON (NDJSON). */\nexport const ndjson: StreamFormat = {\n contentType: \"application/x-ndjson; charset=utf-8\",\n item: (value) => `${JSON.stringify(value)}\\n`,\n end: (end) => `${JSON.stringify(endRecord(end))}\\n`,\n};\n\n/** This format writes each item as an event of server-sent events, and the end as the event `end`. */\nexport const sse: StreamFormat = {\n contentType: \"text/event-stream; charset=utf-8\",\n item: (value) => `data: ${JSON.stringify(value)}\\n\\n`,\n end: (end) => `event: end\\ndata: ${JSON.stringify(endRecord(end))}\\n\\n`,\n};\n\nconst valueResponse = (value: unknown): OutcomeResponse => {\n if (value === undefined) return { status: 204, headers: {}, body: \"\" };\n if (typeof value === \"string\") return { status: 200, headers: { \"content-type\": \"text/plain; charset=utf-8\" }, body: value };\n if (value instanceof Uint8Array) return { status: 200, headers: { \"content-type\": \"application/octet-stream\" }, body: value };\n return { status: 200, headers: { \"content-type\": JSON_TYPE }, body: JSON.stringify(value) };\n};\n\nconst merge = (base: OutcomeResponse, part: ResponsePart | undefined): OutcomeResponse =>\n part === undefined\n ? base\n : { status: part.status ?? base.status, headers: { ...base.headers, ...part.headers }, body: part.body ?? base.body };\n\nconst keeps = (keepAlive: boolean | (() => boolean)): boolean => (typeof keepAlive === \"function\" ? keepAlive() : keepAlive);\n\n/**\n * This function applies the rules of the headers to a response. Each 503 gets `Retry-After`, and a\n * 500 gets none. The response closes its connection while the service cannot keep it.\n */\nconst ruled = (response: OutcomeResponse, retryAfter: Quantity, keepAlive: boolean | (() => boolean)): OutcomeResponse => {\n const headers: Record<string, string> = { ...response.headers };\n if (response.status === 503 && headers[\"retry-after\"] === undefined)\n headers[\"retry-after\"] = String(Math.max(0, Math.ceil(valueIn(retryAfter, \"s\"))));\n if (response.status === 500) delete headers[\"retry-after\"];\n if (!keeps(keepAlive)) headers[\"connection\"] = \"close\";\n return { ...response, headers };\n};\n\n/** This function gives the status response of an end before the first byte of the body. */\nconst endStatus = <T>(end: RequestEnd, options: OutcomeOptions<T>): OutcomeResponse => {\n const body = JSON.stringify(endRecord(end));\n switch (end.kind) {\n case \"refused\":\n return { status: 503, headers: { \"content-type\": JSON_TYPE }, body };\n case \"cut\":\n // a cut after the first item has no status of its own, but no byte went: its route decides\n return { status: end.status ?? (end.retryable ? 503 : 500), headers: { \"content-type\": JSON_TYPE }, body };\n case \"failed\":\n return merge({ status: 500, headers: { \"content-type\": JSON_TYPE }, body }, options.failed?.(end.error));\n case \"done\":\n return { status: 204, headers: {}, body: \"\" };\n }\n};\n\n/**\n * This function maps a unary request to its response, from its items and its end (S6 A12 and A13).\n * - One value and the end `done`: 200 with the value, or the response of the `done` hook.\n * - No value and the end `done`: 204. More than one value: 500, because the route is not a stream.\n * - `refused`: 503. The work did not start, thus the caller can send it again. The service did not\n * start, it stopped its intake, or a controller is not ready.\n * - `cut`: 503 for a route whose callers can send it again (S6 A12), and 500 for each other route.\n * - `failed`: 500 with no detail, or the response of the `failed` hook. A failure of the route is not a 503.\n *\n * Each 503 gets `Retry-After` (RFC 9110), and a 500 gets none. Each response gets `Connection: close`\n * while the service cannot keep the connection.\n */\nexport const outcomeResponse = <T>(items: readonly T[], end: RequestEnd, options: OutcomeOptions<T>): OutcomeResponse => {\n let response: OutcomeResponse;\n if (end.kind !== \"done\") response = endStatus(end, options);\n else if (items.length === 1) response = merge(valueResponse(items[0]), options.done?.(items[0]!));\n else if (items.length === 0) response = endStatus(end, options);\n else\n response = endStatus(\n { kind: \"failed\", error: new Error(`the work gave ${items.length} values, and a unary response has one`), delivered: items.length },\n options,\n );\n return ruled(response, options.retryAfter, options.keepAlive);\n};\n\n/**\n * This function gives the response of a take that a keyed limiter refused: 429 (RFC 6585), with\n * `Retry-After` in whole seconds, rounded up (RFC 9110). The body tells the cause (`limit`, `cap` or\n * `store`) and the same whole seconds, but not the failure of a store. The work did not start, thus\n * the caller can send it again.\n */\nexport const limitedResponse = (refused: Refused): OutcomeResponse => {\n const seconds = retryAfterSeconds(refused.retryAfter);\n return {\n status: 429,\n headers: { \"content-type\": JSON_TYPE, \"retry-after\": String(seconds) },\n body: JSON.stringify({ end: \"refused\", delivered: 0, retryable: true, cause: `the limiter refused the take: ${refused.cause}`, retryAfter: seconds }),\n };\n};\n\n/** This function writes a whole response of `node:http`. A response whose headers went gets no second status: it is destroyed. */\nconst writeWhole = (res: ServerResponse, response: OutcomeResponse): void => {\n if (res.headersSent) {\n res.destroy();\n return;\n }\n if (response.headers[\"connection\"] === \"close\") res.shouldKeepAlive = false;\n // a 204 has no body, thus no length\n if (response.status === 204) {\n res.writeHead(204, response.headers);\n res.end();\n return;\n }\n const length = typeof response.body === \"string\" ? Buffer.byteLength(response.body) : response.body.byteLength;\n res.writeHead(response.status, { ...response.headers, \"content-length\": String(length) });\n res.end(response.body);\n};\n\n/**\n * This function writes the stream of a request to a response of `node:http`, and gives its end. It\n * gives null when the client closed the connection first. Then the work stops through `return()`.\n * - Unary (no format): it waits for the end, and it writes the status and the body of `outcomeResponse`.\n * - Stream: the status 200 goes with the first item, then each item goes as it comes, then the end\n * goes in the body (in-band). The next item waits until the client reads, thus a slow client\n * slows the work. An end before the first item gets a status, as a unary end does.\n * - The answer to come: until it writes the end, the response holds the drain of `httpTransport` past\n * its deadline. The cut of the drain gives that end, and the drain waits until the end goes out.\n */\nexport const writeOutcome = async <T>(res: ServerResponse, stream: RequestStream<T>, options: OutcomeOptions<T>): Promise<RequestEnd | null> => {\n const iterator = stream[Symbol.asyncIterator]();\n let gone: () => void = () => {};\n const closed = new Promise<\"gone\">((resolve) => {\n gone = () => resolve(\"gone\");\n });\n // a close before the end of the response is a client that went away\n const onClose = (): void => {\n if (!res.writableFinished) gone();\n };\n res.once(\"close\", onClose);\n if (res.destroyed) gone();\n const leave = async (): Promise<null> => {\n await iterator.return?.();\n return null;\n };\n const next = async (): Promise<RequestEvent<T> | \"gone\"> => {\n const step = await Promise.race([iterator.next(), closed]);\n if (step === \"gone\") return step;\n if (step.done === true) throw new Error(\"the stream of the request ended without its end\");\n return step.value;\n };\n /** This function writes one part of the body, and waits while the client reads slower than the work. */\n const send = async (chunk: string | Uint8Array): Promise<boolean> => {\n if (res.write(chunk)) return true;\n const drained = new Promise<\"drained\">((resolve) => res.once(\"drain\", () => resolve(\"drained\")));\n return (await Promise.race([drained, closed])) === \"drained\";\n };\n const answered = markAnswer(res);\n try {\n const format = options.stream;\n if (format === undefined) {\n const items: T[] = [];\n for (;;) {\n const event = await next();\n if (event === \"gone\") return await leave();\n if (event.kind === \"item\") {\n items.push(event.value);\n continue;\n }\n writeWhole(res, outcomeResponse(items, event, options));\n return event;\n }\n }\n const first = await next();\n if (first === \"gone\") return await leave();\n if (first.kind !== \"item\") {\n // an empty stream that ended well gives 200 and its end. Each other end before the first item gets a status\n if (first.kind === \"done\")\n writeWhole(res, ruled({ status: 200, headers: { \"content-type\": format.contentType, \"cache-control\": \"no-store\" }, body: format.end(first) }, options.retryAfter, options.keepAlive));\n else writeWhole(res, outcomeResponse([], first, options));\n return first;\n }\n if (res.headersSent) {\n res.destroy();\n return await leave();\n }\n const head = ruled({ status: 200, headers: { \"content-type\": format.contentType, \"cache-control\": \"no-store\" }, body: \"\" }, options.retryAfter, options.keepAlive);\n if (head.headers[\"connection\"] === \"close\") res.shouldKeepAlive = false;\n res.writeHead(200, head.headers);\n if (!(await send(format.item(first.value)))) return await leave();\n for (;;) {\n const event = await next();\n if (event === \"gone\") return await leave();\n if (event.kind === \"item\") {\n if (!(await send(format.item(event.value)))) return await leave();\n continue;\n }\n // the end after the first item is in-band: the status went with the first item\n res.end(format.end(event));\n return event;\n }\n } finally {\n res.off(\"close\", onClose);\n answered();\n }\n};\n\n/** A route of a handler is the name of the route that a controller declares, or a function that gives it from the request. */\nexport type HandledRoute = string | ((req: IncomingMessage) => string | null);\n\n/** A handled work does the work of one request of `node:http`. Its signal aborts when the shutdown or the client stops the request. */\nexport type HandledWork<T> = (req: IncomingMessage, signal: AbortSignal) => RequestWork<T>;\n\n/** These options bind a request of `node:http` to the request tracking of the service. */\nexport interface HandleOptions<T> extends Omit<OutcomeOptions<T>, \"keepAlive\" | \"retryAfter\"> {\n /** This function gives the detail of a request, for the function of a route pattern. */\n readonly detail?: ((req: IncomingMessage) => unknown) | undefined;\n}\n\n/**\n * This function gives a request handler that does each request of a route through `life.request`\n * (S6 A12 and A13). The transport gives the lifecycle and `server_retry_after` through the context.\n * - A route name takes a request whose method and path are that name, for example `POST /orders`.\n * A function gives the name of a route or of a pattern, or null for a request of another handler.\n * - Before the start of the transport, each request gets a retryable 503.\n * - The response is unary, or a stream with the option `stream`, as `writeOutcome` writes it.\n */\nexport const handle = <T>(controller: string, route: HandledRoute, work: HandledWork<T>, options: HandleOptions<T> = {}): RequestHandler => {\n const nameOf = (req: IncomingMessage): string | null => {\n if (typeof route === \"function\") return route(req);\n return `${req.method ?? \"GET\"} ${pathOf(req.url)}` === route ? route : null;\n };\n return async (req: IncomingMessage, res: ServerResponse, context: MountContext): Promise<boolean> => {\n const name = nameOf(req);\n if (name === null) return false;\n const life = context.life;\n const written: OutcomeOptions<T> = {\n ...options,\n keepAlive: () => life?.keepAlive() ?? false,\n retryAfter: context.retryAfter,\n };\n if (life === null) {\n const refused: RequestEnd = { kind: \"refused\", status: 503, retryable: true, cause: \"the transport has not started\", delivered: 0 };\n writeWhole(res, outcomeResponse<T>([], refused, written));\n return true;\n }\n const given: RequestOptions = options.detail === undefined ? {} : { detail: options.detail(req) };\n await writeOutcome(res, life.request(controller, name, (signal) => work(req, signal), given), written);\n return true;\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http.js","names":["knobMilliseconds"],"sources":["../src/http/answers.ts","../src/http/transport.ts","../src/http/probes.ts","../src/http/outcome.ts"],"sourcesContent":["// The answers that the request tracking still writes (SYS-29). `writeOutcome` marks each response\n// while it waits for the end of its request, with the lifecycle that made the stream. At the deadline\n// of its drain, the transport waits for each response of its own lifecycle: the cut of the drain gives\n// that end at the same time. A stream of another lifecycle, or of none, does not hold the drain.\n\nconst marked = new WeakMap<object, object | null>();\nconst watchers = new Set<() => void>();\n\n/**\n * This function marks a response that waits for the end of its request. `owner` is the lifecycle\n * that made the stream, or null. It gives the function that removes the mark.\n */\nexport const markAnswer = (res: object, owner: object | null): (() => void) => {\n marked.set(res, owner);\n return () => {\n if (!marked.delete(res)) return;\n for (const watcher of [...watchers]) watcher();\n };\n};\n\n/**\n * This function gives the mark of a response: the lifecycle that made its stream. It gives null for a\n * stream outside each lifecycle, and undefined for a response with no mark.\n */\nexport const answerOwner = (res: object): object | null | undefined => marked.get(res);\n\n/** This function starts `watcher` after each removal of a mark. It gives the function that stops it. */\nexport const watchAnswers = (watcher: () => void): (() => void) => {\n watchers.add(watcher);\n return () => {\n watchers.delete(watcher);\n };\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport type { IncomingMessage, Server, ServerEventMap, ServerResponse } from \"node:http\";\nimport type { ListenOptions, Socket } from \"node:net\";\nimport type { Cancel, Clock } from \"../clock.ts\";\nimport { realClock } from \"../clock.ts\";\nimport type { LateHold } from \"../drains.ts\";\nimport { holdLate, noteOutside } from \"../drains.ts\";\nimport { millisecondsOf as knobMilliseconds } from \"../knobs.ts\";\nimport type { Lifecycle, TransportImpl } from \"../lifecycle.ts\";\nimport { answerOwner, watchAnswers } from \"./answers.ts\";\n\n/**\n * These are the knobs of an HTTP transport, by the names of the ordering law. The transport has no\n * defaults, thus each knob is necessary. The declaration of the server gets the same record.\n */\nexport interface HttpKnobs {\n /**\n * This time is the real idle close of the server, which O01 compares with the idle time of a\n * client. Node closes an idle connection at `keepAliveTimeout` plus `keepAliveTimeoutBuffer`.\n * Thus the transport sets `keepAliveTimeout` to this time less the buffer of the server.\n * A time without a bound stops the timer.\n */\n readonly server_idle_close: Extent;\n /**\n * This time is the interval of the sweep of idle connections, from the stop of the intake until the\n * close. A time without a bound gives one sweep, at the stop of the intake.\n */\n readonly server_idle_sweep_interval: Extent;\n /** This time goes in the `Retry-After` of each 503 that the transport gives after its intake stops. */\n readonly server_retry_after: Quantity;\n /** The other knobs of the server go to the declaration. The transport does not read them. */\n readonly [knob: string]: Extent;\n}\n\n/**\n * These are the knobs that the transport gives the declaration: the knobs of the caller, and the two\n * times that the transport found. Thus the declaration shows the three times of the idle close.\n */\nexport interface HttpTransportKnobs extends HttpKnobs {\n /** 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. */\n readonly server_idle_close_hint: Extent;\n /** This time is the `keepAliveTimeoutBuffer` of the server, which Node adds to the timer and not to the hint. */\n readonly server_idle_close_buffer: Quantity;\n}\n\n/** A mount stage is the stage of the shutdown at which a mount stops: the stop of the intake, or the close. */\nexport type MountStage = \"stop intake\" | \"close transports\";\n\n/** A mount context gives each handler the signals of the shutdown, the lifecycle and the time of a retry. */\nexport interface MountContext {\n /** This signal aborts when the intake stops. A long stream ends in its own protocol at it. */\n readonly intake: AbortSignal;\n /** This signal aborts at the cooperative abort of the drain, or at the close. The work in flight stops at it. */\n readonly signal: AbortSignal;\n /** This lifecycle is the lifecycle that the transport got at its start, or null before the start. */\n readonly life: Lifecycle | null;\n /** This time is the knob `server_retry_after`: the `Retry-After` of each 503 of the handlers. */\n readonly retryAfter: Quantity;\n}\n\n/**\n * A request handler answers a request and gives true, or it gives false and does not touch the\n * response. Then the next handler gets the request. `until` tells when the handler stops.\n */\nexport interface RequestHandler {\n (req: IncomingMessage, res: ServerResponse, context: MountContext): boolean | Promise<boolean>;\n readonly until?: MountStage | undefined;\n}\n\ntype UpgradeArgs = ServerEventMap[\"upgrade\"];\n\n/** An upgrade socket is the stream of an upgraded connection, as the `upgrade` event of Node gives it. */\nexport type UpgradeSocket = UpgradeArgs[1];\n\n/** An upgrade handler takes an upgraded connection, for example a WebSocket server in its `noServer` mode. */\nexport interface UpgradeHandler {\n (req: IncomingMessage, socket: UpgradeSocket, head: UpgradeArgs[2], context: MountContext): void;\n readonly until?: MountStage | undefined;\n}\n\n/** An upgrade match is a path, without its query, or a function that tells if a handler takes a request. */\nexport type UpgradeMatch = string | ((req: IncomingMessage) => boolean);\n\n/** These options change one mount. */\nexport interface MountOptions {\n /**\n * This stage is when the mount stops. Without it, the stage of the handler applies, and then\n * `stop intake`. A mount until `close transports` keeps the listener open through the drain.\n */\n readonly until?: MountStage | undefined;\n}\n\n/** These options make an HTTP transport. */\nexport interface HttpTransportOptions {\n /**\n * These are the listen options of `start()`, or a function that makes the server listen. Without\n * them, `start()` takes a server that listens already.\n */\n readonly listen?: ListenOptions | ((server: Server) => Promise<void> | void) | undefined;\n /** This clock is the clock of the lifecycle. The deadline of the drain and the sweep use it. */\n readonly clock?: Clock | undefined;\n /**\n * This function tells if a response can keep its connection open. Without it, the transport reads\n * `life.keepAlive()` of the lifecycle of its start. The transport reads it when the headers of\n * each response go, and false gives `Connection: close`.\n */\n readonly keepAlive?: (() => boolean) | undefined;\n /** This function receives each error of a handler. The transport then answers 500, or it destroys the response. */\n readonly onError?: ((error: unknown, req: IncomingMessage) => void) | undefined;\n}\n\n/** An HTTP transport is a server of `node:http` as a transport of the lifecycle, with a list of mounts on its listener. */\nexport interface HttpTransport extends TransportImpl {\n readonly server: Server;\n /** These knobs go to the declaration of the server: the knobs of the caller, the hint and the buffer. */\n readonly knobs: HttpTransportKnobs;\n /** This method adds a request handler at the end of the list, and gives the function that removes it. */\n mount(handler: RequestHandler, options?: MountOptions): () => void;\n /** This method adds an upgrade handler for a path, and gives the function that removes it. */\n upgrade(match: UpgradeMatch, handler: UpgradeHandler, options?: MountOptions): () => void;\n /** This count is the number of open connections, idle connections and upgraded connections too. */\n readonly connections: number;\n /** This flag tells if the transport stopped its intake. */\n readonly intakeStopped: boolean;\n}\n\ninterface RequestMount {\n readonly handler: RequestHandler;\n readonly until: MountStage;\n}\n\ninterface UpgradeMount {\n readonly matches: (req: IncomingMessage) => boolean;\n readonly handler: UpgradeHandler;\n readonly until: MountStage;\n}\n\n/** This function gives the path of a request target, without its query and fragment. */\nexport const pathOf = (url: string | undefined): string => {\n const raw = url ?? \"/\";\n const end = raw.search(/[?#]/);\n const path = end === -1 ? raw : raw.slice(0, end);\n if (path.startsWith(\"/\")) return path;\n // an absolute target, as a proxy sends it\n try {\n return new URL(raw).pathname;\n } catch {\n return path;\n }\n};\n\n/** This function gives a knob in milliseconds, or infinity for a time without a bound. */\nconst millisecondsOf = (knobs: Readonly<Record<string, Extent | undefined>>, name: string, owner = \"httpTransport\"): number =>\n knobMilliseconds(knobs, name, owner);\n\n/**\n * This function gives the `Retry-After` of the knob `server_retry_after`, in whole seconds, rounded\n * up. The knob is necessary, and it has a bound.\n */\nexport const retryAfterOf = (knobs: Readonly<Record<string, Extent | undefined>>, owner: string): string => {\n const retryAfter = millisecondsOf(knobs, \"server_retry_after\", owner);\n if (!Number.isFinite(retryAfter)) throw new Error(\"the knob server_retry_after has no bound: a Retry-After needs a time\");\n return String(Math.ceil(retryAfter / 1000));\n};\n\n/** This function tells if the headers of a call of `writeHead` name a header, an object or a flat list of names and values. */\nconst names = (headers: unknown, name: string): boolean => {\n if (Array.isArray(headers)) return headers.some((each, at) => at % 2 === 0 && String(each).toLowerCase() === name);\n return typeof headers === \"object\" && headers !== null && Object.keys(headers).some((key) => key.toLowerCase() === name);\n};\n\n/** This function gives the headers of a call of `writeHead` without one header. */\nconst without = (headers: unknown, name: string): unknown => {\n if (Array.isArray(headers)) {\n const kept: unknown[] = [];\n for (let at = 0; at + 1 < headers.length; at += 2)\n if (String(headers[at]).toLowerCase() !== name) kept.push(headers[at], headers[at + 1]);\n return kept;\n }\n if (typeof headers !== \"object\" || headers === null) return headers;\n return Object.fromEntries(Object.entries(headers).filter(([key]) => key.toLowerCase() !== name));\n};\n\n/**\n * This function applies the rule of `Retry-After` to a call of `writeHead`, and gives its arguments.\n * A 503 without `Retry-After` gets the time of `server_retry_after` (RFC 9110). A 500 loses each\n * `Retry-After`, from the call and from the response.\n */\nconst retryRule = (res: ServerResponse, args: unknown[], retryAfter: string): unknown[] => {\n const status = typeof args[0] === \"number\" ? args[0] : res.statusCode;\n const at = typeof args[1] === \"string\" ? 2 : 1;\n if (status === 503 && !res.hasHeader(\"retry-after\") && !names(args[at], \"retry-after\")) res.setHeader(\"retry-after\", retryAfter);\n if (status !== 500) return args;\n res.removeHeader(\"retry-after\");\n if (!names(args[at], \"retry-after\")) return args;\n const given = [...args];\n given[at] = without(args[at], \"retry-after\");\n return given;\n};\n\nconst STATUS_TEXT: Readonly<Record<number, string>> = { 404: \"Not Found\", 500: \"Internal Server Error\", 503: \"Service Unavailable\" };\n\n/**\n * This function makes a server of `node:http` a transport of the lifecycle (S6 A8 and A13). It is not\n * a framework: it has no routes, no body parser and no middleware.\n * - The count of work in flight comes from events: each response until its `close`, and each upgraded\n * connection until its `close`.\n * - `stopIntake` closes the listener, and new connections get a refusal. It closes the idle connections\n * at once and at each `server_idle_sweep_interval`. Each later response gets `Connection: close`.\n * - `drain` waits until no work is in flight, or until the deadline. It gives the cooperative abort to\n * the handlers through their context. At the deadline, it also waits for each answer that is to come\n * or to go out (SYS-29). The cap of the phase bounds that wait. Answers that did not go out by the\n * cap are not a failure of the phase. A stream outside the lifecycle of the transport does not hold it.\n * - `close` destroys each connection. A second close gives the same promise.\n * - The mounts share one port. The first request handler that gives true answers. After the stop of\n * the intake, only the mounts until `close transports` answer, and each other request gets a 503.\n */\nexport const httpTransport = (server: Server, knobs: HttpKnobs, options: HttpTransportOptions = {}): HttpTransport => {\n const clock = options.clock ?? realClock;\n const idleClose = millisecondsOf(knobs, \"server_idle_close\");\n const sweepEvery = millisecondsOf(knobs, \"server_idle_sweep_interval\");\n if (sweepEvery === 0)\n throw new Error(\"the knob server_idle_sweep_interval is zero: a sweep needs an interval above zero\");\n const retryAfterHeader = retryAfterOf(knobs, \"httpTransport\");\n for (const derived of [\"server_idle_close_hint\", \"server_idle_close_buffer\"])\n if ((knobs as Readonly<Record<string, Extent | undefined>>)[derived] !== undefined)\n throw new Error(`the knob ${derived} comes from the transport: give only server_idle_close`);\n if (server.listenerCount(\"request\") > 0 || server.listenerCount(\"upgrade\") > 0)\n throw new Error(\"the server has a request or upgrade listener of its own: mount it as a handler\");\n // Node closes an idle connection at keepAliveTimeout + keepAliveTimeoutBuffer, and its hint is\n // keepAliveTimeout. The buffer stays, thus the hint is less than the real close. An older Node has no buffer\n const buffer = (server as { keepAliveTimeoutBuffer?: number }).keepAliveTimeoutBuffer ?? 0;\n if (Number.isFinite(idleClose) && idleClose <= buffer)\n throw new Error(\n `the knob server_idle_close is ${idleClose} ms, at or below the keepAliveTimeoutBuffer of the server, ${buffer} ms: ` +\n \"the server closes an idle connection at keepAliveTimeout + keepAliveTimeoutBuffer\",\n );\n // 0 stops the keep-alive timer and the hint in Node\n server.keepAliveTimeout = Number.isFinite(idleClose) ? idleClose - buffer : 0;\n const declared: HttpTransportKnobs = {\n ...knobs,\n server_idle_close_hint: Number.isFinite(idleClose)\n ? { value: Math.floor(server.keepAliveTimeout / 1000), unit: \"s\" }\n : { value: \"infinite\", unit: \"s\" },\n server_idle_close_buffer: { value: buffer, unit: \"ms\" },\n };\n\n const requestMounts: RequestMount[] = [];\n const upgradeMounts: UpgradeMount[] = [];\n const responses = new Set<ServerResponse>();\n const upgraded = new Set<UpgradeSocket>();\n const sockets = new Set<Socket>();\n // the check of each drain: it ends its drain when nothing holds it\n const drainers = new Set<() => void>();\n const closedWaiters = new Set<() => void>();\n const intake = new AbortController();\n const cooperative = new AbortController();\n // the lifecycle comes at the start, thus the context reads it at each request\n let life: Lifecycle | null = null;\n const context: MountContext = {\n intake: intake.signal,\n signal: cooperative.signal,\n get life() {\n return life;\n },\n retryAfter: knobs.server_retry_after,\n };\n let intakeOpen = true;\n let listenerOpen = server.listening;\n let serverClosed = !server.listening;\n let sweep: Cancel | null = null;\n let closing: Promise<void> | null = null;\n\n const inFlight = (): number => responses.size + upgraded.size;\n /**\n * This function gives what holds a drain after its deadline. An answer is unflushed when its handler\n * ended it and its bytes did not all go. It is unflushed too while `writeOutcome` waits for a client\n * that does not read. A call runs while `writeOutcome` waits for the end of its request, which the\n * cut of the drain gives. Only a stream of the lifecycle of the transport counts, because only its\n * cut ends the stream. A response that is open for another reason does not hold the drain.\n */\n const lateHold = (): LateHold & { readonly outside: number } => {\n let running = 0;\n let unflushed = 0;\n let outside = 0;\n for (const res of responses) {\n if (res.destroyed) continue;\n if (res.writableEnded) {\n if (!res.writableFinished) unflushed += 1;\n continue;\n }\n const owner = answerOwner(res);\n if (owner === undefined) continue;\n if (life === null || owner !== life) outside += 1;\n else if (res.writableNeedDrain) unflushed += 1;\n else running += 1;\n }\n return { running, unflushed, outside };\n };\n const answering = (): boolean => {\n const hold = lateHold();\n return hold.running + hold.unflushed > 0;\n };\n const report = (error: unknown, req: IncomingMessage): void => {\n try {\n options.onError?.(error, req);\n } catch {\n // an error of the error hook does not stop the transport\n }\n };\n const settled = (): void => {\n for (const check of [...drainers]) check();\n if (closing !== null && inFlight() === 0 && sockets.size === 0 && serverClosed)\n for (const done of [...closedWaiters]) done();\n };\n // before its start, a transport has no lifecycle: a server that listens already keeps its connections\n const keepsAlive = (): boolean => intakeOpen && (options.keepAlive?.() ?? life?.keepAlive() ?? true);\n const live = <T extends { readonly until: MountStage }>(mounts: readonly T[]): T[] =>\n intakeOpen ? [...mounts] : mounts.filter((mount) => mount.until === \"close transports\");\n const throughDrain = (): boolean =>\n requestMounts.some((mount) => mount.until === \"close transports\") ||\n upgradeMounts.some((mount) => mount.until === \"close transports\");\n const closeListener = (): void => {\n if (!listenerOpen) return;\n listenerOpen = false;\n server.close();\n };\n const startSweep = (): void => {\n if (!Number.isFinite(sweepEvery) || closing !== null) return;\n sweep = clock.after(sweepEvery, () => {\n sweep = null;\n if (closing !== null) return;\n server.closeIdleConnections();\n startSweep();\n });\n };\n\n /**\n * This function watches the headers of each response when they go, whatever wrote them. Each 503\n * gets `Retry-After`, and a 500 gets none. The response closes its connection while the transport\n * cannot keep it.\n */\n const watchHeaders = (res: ServerResponse): void => {\n const writeHead = res.writeHead as (...args: unknown[]) => ServerResponse;\n res.writeHead = ((...args: unknown[]): ServerResponse => {\n if (res.headersSent) return writeHead.apply(res, args);\n if (!keepsAlive()) {\n res.shouldKeepAlive = false;\n res.setHeader(\"connection\", \"close\");\n }\n return writeHead.apply(res, retryRule(res, args, retryAfterHeader));\n }) as ServerResponse[\"writeHead\"];\n };\n const answer = (res: ServerResponse, status: number, headers: Readonly<Record<string, string>> = {}): void => {\n if (res.headersSent) {\n res.destroy();\n return;\n }\n const body = STATUS_TEXT[status] ?? \"\";\n res.writeHead(status, { \"content-type\": \"text/plain; charset=utf-8\", \"content-length\": String(Buffer.byteLength(body)), ...headers });\n res.end(body);\n };\n const refuse = (res: ServerResponse): void => answer(res, 503, { \"retry-after\": retryAfterHeader, connection: \"close\" });\n\n const route = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const mounts = live(requestMounts);\n try {\n for (const mount of mounts) {\n const given = mount.handler(req, res, context);\n const taken = typeof given === \"boolean\" ? given : await given;\n // a handler that wrote its response took the request, whatever it gave\n if (taken || res.headersSent || res.writableEnded) return;\n }\n } catch (error) {\n report(error, req);\n if (!res.writableEnded) answer(res, 500);\n return;\n }\n if (intakeOpen) answer(res, 404);\n else refuse(res);\n };\n\n const onRequest = (req: IncomingMessage, res: ServerResponse): void => {\n responses.add(res);\n // the bytes of an answer went: a drain after its deadline can end\n res.once(\"finish\", settled);\n res.once(\"close\", () => {\n responses.delete(res);\n settled();\n });\n watchHeaders(res);\n void route(req, res);\n };\n\n const onUpgrade = (req: IncomingMessage, socket: UpgradeSocket, head: UpgradeArgs[2]): void => {\n socket.on(\"error\", () => {});\n const mount = live(upgradeMounts).find((each) => each.matches(req));\n if (mount === undefined) {\n const status = intakeOpen ? 404 : 503;\n const extra = intakeOpen ? \"\" : `Retry-After: ${retryAfterHeader}\\r\\n`;\n socket.end(`HTTP/1.1 ${status} ${STATUS_TEXT[status]!}\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n${extra}\\r\\n`);\n return;\n }\n if (socket.destroyed) return;\n upgraded.add(socket);\n socket.once(\"close\", () => {\n upgraded.delete(socket);\n settled();\n });\n try {\n mount.handler(req, socket, head, context);\n } catch (error) {\n report(error, req);\n socket.destroy();\n }\n };\n\n const onConnection = (socket: Socket): void => {\n sockets.add(socket);\n socket.once(\"close\", () => {\n sockets.delete(socket);\n settled();\n });\n };\n\n server.on(\"request\", onRequest);\n server.on(\"upgrade\", onUpgrade);\n server.on(\"connection\", onConnection);\n server.on(\"listening\", () => {\n listenerOpen = true;\n serverClosed = false;\n });\n server.on(\"close\", () => {\n listenerOpen = false;\n serverClosed = true;\n settled();\n });\n\n const remover = <T>(list: T[], entry: T) => (): void => {\n const at = list.indexOf(entry);\n if (at >= 0) list.splice(at, 1);\n // the last mount through the drain went: the listener closes as the stop of the intake asks\n if (!intakeOpen && closing === null && !throughDrain()) closeListener();\n };\n\n return {\n server,\n knobs: declared,\n get connections() {\n return sockets.size;\n },\n get intakeStopped() {\n return !intakeOpen;\n },\n mount(handler, mountOptions = {}) {\n const entry: RequestMount = { handler, until: mountOptions.until ?? handler.until ?? \"stop intake\" };\n requestMounts.push(entry);\n return remover(requestMounts, entry);\n },\n upgrade(match, handler, mountOptions = {}) {\n const matches = typeof match === \"string\" ? (req: IncomingMessage) => pathOf(req.url) === match : match;\n const entry: UpgradeMount = { matches, handler, until: mountOptions.until ?? handler.until ?? \"stop intake\" };\n upgradeMounts.push(entry);\n return remover(upgradeMounts, entry);\n },\n async start(_signal, given) {\n life = given;\n if (closing !== null) throw new Error(\"the transport closed: a closed transport does not start again\");\n // a listener that another owner adds after the transport answers too, thus the start refuses it\n if (server.listenerCount(\"request\") > 1 || server.listenerCount(\"upgrade\") > 1)\n throw new Error(\"the server has a request or upgrade listener of its own: mount it as a handler\");\n if (server.listening) return;\n const listen = options.listen;\n if (listen === undefined) throw new Error(\"the server does not listen, and the transport has no listen options\");\n if (typeof listen === \"function\") {\n await listen(server);\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const failed = (error: Error): void => {\n server.off(\"listening\", listened);\n reject(error);\n };\n const listened = (): void => {\n server.off(\"error\", failed);\n resolve();\n };\n server.once(\"error\", failed);\n server.once(\"listening\", listened);\n server.listen(listen);\n });\n },\n stopIntake() {\n if (!intakeOpen) return;\n intakeOpen = false;\n intake.abort(new Error(\"the transport stopped its intake\"));\n if (!throughDrain()) closeListener();\n server.closeIdleConnections();\n startSweep();\n },\n drain(deadline, signal) {\n const abort = (): void => {\n if (!cooperative.signal.aborted) cooperative.abort(signal.reason);\n };\n if (signal.aborted) abort();\n else signal.addEventListener(\"abort\", abort, { once: true });\n // nothing in flight: the drain ends at once, with no timer\n if (inFlight() === 0) {\n signal.removeEventListener(\"abort\", abort);\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n let cancel: Cancel = () => {};\n let unwatch: () => void = () => {};\n let unhold: () => void = () => {};\n let late = false;\n let ended = false;\n // before the deadline, the work in flight holds the drain. After it, only an answer to come or to go out holds it\n const check = (): void => {\n if (ended || (inFlight() > 0 && (!late || answering()))) return;\n ended = true;\n drainers.delete(check);\n cancel();\n unwatch();\n unhold();\n signal.removeEventListener(\"abort\", abort);\n resolve();\n };\n // the cut of the requests comes at the same time, before or after this timer: the drain waits for its answers in each case\n const reached = (): void => {\n late = true;\n unwatch = watchAnswers(check);\n // the lifecycle reads the hold at the cap: answers that do not go out are not a failure of the phase\n if (life !== null) {\n unhold = holdLate(life, () => {\n const { running, unflushed } = lateHold();\n return { running, unflushed };\n });\n // a stream outside the lifecycle does not hold the drain: the record of the phase gives the count\n noteOutside(life, lateHold().outside);\n }\n check();\n };\n drainers.add(check);\n if (deadline <= clock.now()) reached();\n else cancel = clock.after(deadline - clock.now(), reached);\n });\n },\n inFlight,\n close() {\n if (closing !== null) return closing;\n intakeOpen = false;\n if (!intake.signal.aborted) intake.abort(new Error(\"the transport closed\"));\n if (!cooperative.signal.aborted) cooperative.abort(new Error(\"the transport closed\"));\n sweep?.();\n sweep = null;\n closing = new Promise<void>((resolve) => {\n closedWaiters.add(resolve);\n });\n closeListener();\n server.closeAllConnections();\n for (const socket of [...upgraded]) socket.destroy();\n for (const socket of [...sockets]) socket.destroy();\n settled();\n return closing;\n },\n };\n};\n","import type { IncomingMessage, Server, ServerResponse } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { ListenOptions, Socket } from \"node:net\";\nimport type { Health, ProbeKind } from \"../health.ts\";\nimport type { HealthServer } from \"../lifecycle.ts\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport type { MountContext, RequestHandler } from \"./transport.ts\";\nimport { pathOf, retryAfterOf } from \"./transport.ts\";\n\n/** A probe response is the status, the headers and the body of one probe, for any HTTP framework or none. */\nexport interface ProbeResponse {\n readonly status: number;\n readonly headers: Readonly<Record<string, string>>;\n readonly body: string;\n}\n\n/** A health source is the health runtime, or a function that gives it when it exists. */\nexport type HealthSource = Health | (() => Health | null | undefined);\n\nconst KINDS: readonly ProbeKind[] = [\"startup\", \"readiness\", \"liveness\", \"report\"];\n\nconst prefixProblem = (prefix: string): string | null =>\n prefix === \"\" || (prefix.startsWith(\"/\") && !prefix.endsWith(\"/\"))\n ? null\n : `the probe prefix \"${prefix}\" does not start with \"/\", or it ends with \"/\"`;\n\nconst json = (status: number, body: unknown): ProbeResponse => ({\n status,\n headers: { \"content-type\": \"application/health+json\", \"cache-control\": \"no-store\" },\n body: JSON.stringify(body),\n});\n\n/**\n * This function gives the response of a probe path from the cache of the health runtime. For a path\n * that is not a probe, it gives null. The paths are `/startup`, `/readiness`, `/liveness` and\n * `/report` after the prefix. `/readiness/<controller>` gives the readiness of one controller.\n */\nexport const probeResponse = (\n health: Health | null | undefined,\n method: string | undefined,\n path: string,\n prefix = \"\",\n): ProbeResponse | null => {\n if (!path.startsWith(`${prefix}/`)) return null;\n const parts = path.slice(prefix.length + 1).split(\"/\");\n const kind = KINDS.find((each) => each === parts[0]);\n if (kind === undefined) return null;\n const controller = parts[1];\n if (parts.length > 2 || (controller !== undefined && (kind !== \"readiness\" || controller === \"\"))) return null;\n if (method !== \"GET\" && method !== \"HEAD\")\n return { status: 405, headers: { allow: \"GET, HEAD\", \"content-length\": \"0\" }, body: \"\" };\n // before the runtime exists, each probe fails: the service has not started\n if (health === null || health === undefined) return json(503, { status: \"fail\", probe: kind });\n try {\n const answer = health.probe(kind, controller === undefined ? undefined : decodeURIComponent(controller));\n return { ...json(answer.status, answer.body), headers: { \"content-type\": answer.contentType, \"cache-control\": \"no-store\" } };\n } catch {\n // a controller that the service does not have\n return json(404, { status: \"fail\", probe: kind, controller });\n }\n};\n\n/** This function writes a probe response. A 503 gets `Retry-After`, when the writer has the time of a retry. */\nconst write = (req: IncomingMessage, res: ServerResponse, response: ProbeResponse, retryAfter: string | undefined): void => {\n const retry = response.status === 503 && retryAfter !== undefined ? { \"retry-after\": retryAfter } : {};\n res.writeHead(response.status, { ...response.headers, ...retry, \"content-length\": String(Buffer.byteLength(response.body)) });\n res.end(req.method === \"HEAD\" ? undefined : response.body);\n};\n\n/** These options mount the probes on the port of the application. */\nexport interface ProbeHandlerOptions {\n /** This path goes before each probe path, for example `/health`. It starts with `/`, or it is empty. */\n readonly prefix: string;\n}\n\n/**\n * This function gives a request handler that answers the probes on the port of the application. It\n * answers until `close transports`, thus its mount keeps the listener open through the drain. A path\n * that is not a probe goes to the next handler. Each 503 gets the `Retry-After` of the context.\n */\nexport const probeHandler = (health: HealthSource, options: ProbeHandlerOptions): RequestHandler => {\n const problem = prefixProblem(options.prefix);\n if (problem !== null) throw new Error(problem);\n const handler = (req: IncomingMessage, res: ServerResponse, context?: MountContext): boolean => {\n const runtime = typeof health === \"function\" ? health() : health;\n const response = probeResponse(runtime, req.method, pathOf(req.url), options.prefix);\n if (response === null) return false;\n // the transport gives server_retry_after through the context, and it adds the header to each 503 too\n const retryAfter = context?.retryAfter === undefined ? undefined : retryAfterOf({ server_retry_after: context.retryAfter }, \"probeHandler\");\n write(req, res, response, retryAfter);\n return true;\n };\n return Object.assign(handler, { until: \"close transports\" as const });\n};\n\n/** These options make a probe server: the listen options of its port, the prefix of its paths, and the time of a retry. */\nexport interface ProbeServerOptions extends ListenOptions {\n /**\n * This time goes in the `Retry-After` of each 503 of a probe, as on the port of the application.\n * The probe server has no defaults, thus the knob is necessary.\n */\n readonly server_retry_after: Quantity;\n /** This path goes before each probe path. The default is no prefix, because the port has only the probes. */\n readonly prefix?: string | undefined;\n /** This server answers the probes. Without it, the probe server makes a server of `node:http`. */\n readonly server?: Server | undefined;\n}\n\n/** A probe server is a health server on its own port. Its server is visible for the address of the port. */\nexport interface ProbeServer extends HealthServer {\n readonly server: Server;\n}\n\n/**\n * This function makes a health server on a port of its own (S6 decision D5). It starts first and\n * closes last, thus the probes answer through the whole drain. Each probe answers from the cache, and\n * each 503 gets the `Retry-After` of `server_retry_after`.\n */\nexport const probeServer = (options: ProbeServerOptions): ProbeServer => {\n const { prefix = \"\", server = createServer(), server_retry_after, ...listen } = options;\n const retryAfter = retryAfterOf({ server_retry_after }, \"probeServer\");\n const problem = prefixProblem(prefix);\n if (problem !== null) throw new Error(problem);\n if (server.listenerCount(\"request\") > 0) throw new Error(\"the probe server has a request listener of its own\");\n let health: Health | null = null;\n let closing: Promise<void> | null = null;\n const sockets = new Set<Socket>();\n server.on(\"connection\", (socket: Socket) => {\n sockets.add(socket);\n socket.once(\"close\", () => sockets.delete(socket));\n });\n server.on(\"request\", (req: IncomingMessage, res: ServerResponse) => {\n const response = probeResponse(health, req.method, pathOf(req.url), prefix);\n write(req, res, response ?? { status: 404, headers: { \"content-type\": \"text/plain; charset=utf-8\" }, body: \"Not Found\" }, retryAfter);\n });\n return {\n server,\n listen(runtime) {\n health = runtime;\n if (server.listening) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const failed = (error: Error): void => {\n server.off(\"listening\", listened);\n reject(error);\n };\n const listened = (): void => {\n server.off(\"error\", failed);\n resolve();\n };\n server.once(\"error\", failed);\n server.once(\"listening\", listened);\n server.listen(listen);\n });\n },\n close() {\n if (closing !== null) return closing;\n closing = new Promise<void>((resolve) => {\n if (!server.listening) {\n resolve();\n return;\n }\n server.close(() => resolve());\n server.closeAllConnections();\n for (const socket of [...sockets]) socket.destroy();\n });\n return closing;\n },\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { ownerOf, ownStream } from \"../drains.ts\";\nimport type { Refused } from \"../limiter/policy.ts\";\nimport { retryAfterSeconds } from \"../limiter/policy.ts\";\nimport type { RequestEnd, RequestEvent, RequestOptions, RequestStream, RequestWork } from \"../requests.ts\";\nimport { markAnswer } from \"./answers.ts\";\nimport type { MountContext, RequestHandler } from \"./transport.ts\";\nimport { pathOf } from \"./transport.ts\";\n\n/** An outcome response is the status, the headers and the body of a request, for any HTTP framework or none. */\nexport interface OutcomeResponse {\n readonly status: number;\n readonly headers: Readonly<Record<string, string>>;\n readonly body: string | Uint8Array;\n}\n\n/** 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. */\nexport interface ResponsePart {\n readonly status?: number | undefined;\n readonly headers?: Readonly<Record<string, string>> | undefined;\n readonly body?: string | Uint8Array | undefined;\n}\n\n/**\n * A stream format writes the items of a stream and its end in the body, as they come. An end after\n * the first item goes in the body, because the status went with the first item.\n */\nexport interface StreamFormat<T = unknown> {\n readonly contentType: string;\n /** This function gives the bytes of one item. */\n item(value: T): string | Uint8Array;\n /** This function gives the bytes of the end, the last part of the body. */\n end(end: RequestEnd): string | Uint8Array;\n}\n\n/** These options map a request to a response. */\nexport interface OutcomeOptions<T> {\n /**\n * This value tells if the response can keep its connection open, as `life.keepAlive()` does. False\n * gives `Connection: close`. A function is read when the headers go.\n */\n readonly keepAlive: boolean | (() => boolean);\n /** This time goes in the `Retry-After` of each 503, in whole seconds, rounded up. It is the knob `server_retry_after`. */\n readonly retryAfter: Quantity;\n /**\n * This function gives the response of the value of a unary request. Without it, the mapping gives\n * 200 and the value as JSON, or 204 for no value.\n */\n readonly done?: ((value: T) => ResponsePart) | undefined;\n /**\n * This function gives the response of a failure of the route before the first byte. Without it,\n * the mapping gives 500 with no detail, because an error can hold private data.\n */\n readonly failed?: ((error: unknown) => ResponsePart) | undefined;\n /**\n * This format makes the response a stream: the status and each item go as they come, and the end\n * goes in the body. Without it, the response is unary: one status and the one value.\n */\n readonly stream?: StreamFormat<T> | undefined;\n}\n\nconst JSON_TYPE = \"application/json; charset=utf-8\";\n\n/**\n * This function gives an end as a record for a body: its kind and the count of its items. A cut and\n * a refusal also give their cause, and if the caller can send the request again. A failure gives\n * no detail.\n */\nexport const endRecord = (end: RequestEnd): Readonly<Record<string, unknown>> => {\n if (end.kind === \"refused\" || end.kind === \"cut\")\n return { end: end.kind, delivered: end.delivered, retryable: end.retryable, cause: end.cause };\n return { end: end.kind, delivered: end.delivered };\n};\n\n/** This format writes each item and the end as one line of JSON (NDJSON). */\nexport const ndjson: StreamFormat = {\n contentType: \"application/x-ndjson; charset=utf-8\",\n item: (value) => `${JSON.stringify(value)}\\n`,\n end: (end) => `${JSON.stringify(endRecord(end))}\\n`,\n};\n\n/** This format writes each item as an event of server-sent events, and the end as the event `end`. */\nexport const sse: StreamFormat = {\n contentType: \"text/event-stream; charset=utf-8\",\n item: (value) => `data: ${JSON.stringify(value)}\\n\\n`,\n end: (end) => `event: end\\ndata: ${JSON.stringify(endRecord(end))}\\n\\n`,\n};\n\nconst valueResponse = (value: unknown): OutcomeResponse => {\n if (value === undefined) return { status: 204, headers: {}, body: \"\" };\n if (typeof value === \"string\") return { status: 200, headers: { \"content-type\": \"text/plain; charset=utf-8\" }, body: value };\n if (value instanceof Uint8Array) return { status: 200, headers: { \"content-type\": \"application/octet-stream\" }, body: value };\n return { status: 200, headers: { \"content-type\": JSON_TYPE }, body: JSON.stringify(value) };\n};\n\nconst merge = (base: OutcomeResponse, part: ResponsePart | undefined): OutcomeResponse =>\n part === undefined\n ? base\n : { status: part.status ?? base.status, headers: { ...base.headers, ...part.headers }, body: part.body ?? base.body };\n\nconst keeps = (keepAlive: boolean | (() => boolean)): boolean => (typeof keepAlive === \"function\" ? keepAlive() : keepAlive);\n\n/**\n * This function applies the rules of the headers to a response. Each 503 gets `Retry-After`, and a\n * 500 gets none. The response closes its connection while the service cannot keep it.\n */\nconst ruled = (response: OutcomeResponse, retryAfter: Quantity, keepAlive: boolean | (() => boolean)): OutcomeResponse => {\n const headers: Record<string, string> = { ...response.headers };\n if (response.status === 503 && headers[\"retry-after\"] === undefined)\n headers[\"retry-after\"] = String(Math.max(0, Math.ceil(valueIn(retryAfter, \"s\"))));\n if (response.status === 500) delete headers[\"retry-after\"];\n if (!keeps(keepAlive)) headers[\"connection\"] = \"close\";\n return { ...response, headers };\n};\n\n/** This function gives the status response of an end before the first byte of the body. */\nconst endStatus = <T>(end: RequestEnd, options: OutcomeOptions<T>): OutcomeResponse => {\n const body = JSON.stringify(endRecord(end));\n switch (end.kind) {\n case \"refused\":\n return { status: 503, headers: { \"content-type\": JSON_TYPE }, body };\n case \"cut\":\n // a cut after the first item has no status of its own, but no byte went: its route decides\n return { status: end.status ?? (end.retryable ? 503 : 500), headers: { \"content-type\": JSON_TYPE }, body };\n case \"failed\":\n return merge({ status: 500, headers: { \"content-type\": JSON_TYPE }, body }, options.failed?.(end.error));\n case \"done\":\n return { status: 204, headers: {}, body: \"\" };\n }\n};\n\n/**\n * This function maps a unary request to its response, from its items and its end (S6 A12 and A13).\n * - One value and the end `done`: 200 with the value, or the response of the `done` hook.\n * - No value and the end `done`: 204. More than one value: 500, because the route is not a stream.\n * - `refused`: 503. The work did not start, thus the caller can send it again. The service did not\n * start, it stopped its intake, or a controller is not ready.\n * - `cut`: 503 for a route whose callers can send it again (S6 A12), and 500 for each other route.\n * - `failed`: 500 with no detail, or the response of the `failed` hook. A failure of the route is not a 503.\n *\n * Each 503 gets `Retry-After` (RFC 9110), and a 500 gets none. Each response gets `Connection: close`\n * while the service cannot keep the connection.\n */\nexport const outcomeResponse = <T>(items: readonly T[], end: RequestEnd, options: OutcomeOptions<T>): OutcomeResponse => {\n let response: OutcomeResponse;\n if (end.kind !== \"done\") response = endStatus(end, options);\n else if (items.length === 1) response = merge(valueResponse(items[0]), options.done?.(items[0]!));\n else if (items.length === 0) response = endStatus(end, options);\n else\n response = endStatus(\n { kind: \"failed\", error: new Error(`the work gave ${items.length} values, and a unary response has one`), delivered: items.length },\n options,\n );\n return ruled(response, options.retryAfter, options.keepAlive);\n};\n\n/**\n * This function gives the response of a take that a keyed limiter refused: 429 (RFC 6585), with\n * `Retry-After` in whole seconds, rounded up (RFC 9110). The body tells the cause (`limit`, `cap` or\n * `store`) and the same whole seconds, but not the failure of a store. The work did not start, thus\n * the caller can send it again.\n */\nexport const limitedResponse = (refused: Refused): OutcomeResponse => {\n const seconds = retryAfterSeconds(refused.retryAfter);\n return {\n status: 429,\n headers: { \"content-type\": JSON_TYPE, \"retry-after\": String(seconds) },\n body: JSON.stringify({ end: \"refused\", delivered: 0, retryable: true, cause: `the limiter refused the take: ${refused.cause}`, retryAfter: seconds }),\n };\n};\n\n/** This function writes a whole response of `node:http`. A response whose headers went gets no second status: it is destroyed. */\nconst writeWhole = (res: ServerResponse, response: OutcomeResponse): void => {\n if (res.headersSent) {\n res.destroy();\n return;\n }\n if (response.headers[\"connection\"] === \"close\") res.shouldKeepAlive = false;\n // a 204 has no body, thus no length\n if (response.status === 204) {\n res.writeHead(204, response.headers);\n res.end();\n return;\n }\n const length = typeof response.body === \"string\" ? Buffer.byteLength(response.body) : response.body.byteLength;\n res.writeHead(response.status, { ...response.headers, \"content-length\": String(length) });\n res.end(response.body);\n};\n\n/**\n * This function writes the stream of a request to a response of `node:http`, and gives its end. It\n * gives null when the client closed the connection first. Then the work stops through `return()`.\n * - Unary (no format): it waits for the end, and it writes the status and the body of `outcomeResponse`.\n * - Stream: the status 200 goes with the first item, then each item goes as it comes, then the end\n * goes in the body (in-band). The next item waits until the client reads, thus a slow client\n * slows the work. An end before the first item gets a status, as a unary end does.\n * - The answer to come: until it writes the end, the response holds the drain of `httpTransport` past\n * its deadline. The cut of the drain gives that end, and the drain waits until the end goes out.\n * Only a stream of `life.request` of the lifecycle of the transport holds the drain.\n */\nexport const writeOutcome = async <T>(res: ServerResponse, stream: RequestStream<T>, options: OutcomeOptions<T>): Promise<RequestEnd | null> => {\n const iterator = stream[Symbol.asyncIterator]();\n let gone: () => void = () => {};\n const closed = new Promise<\"gone\">((resolve) => {\n gone = () => resolve(\"gone\");\n });\n // a close before the end of the response is a client that went away\n const onClose = (): void => {\n if (!res.writableFinished) gone();\n };\n res.once(\"close\", onClose);\n if (res.destroyed) gone();\n const leave = async (): Promise<null> => {\n await iterator.return?.();\n return null;\n };\n const next = async (): Promise<RequestEvent<T> | \"gone\"> => {\n const step = await Promise.race([iterator.next(), closed]);\n if (step === \"gone\") return step;\n if (step.done === true) throw new Error(\"the stream of the request ended without its end\");\n return step.value;\n };\n /** This function writes one part of the body, and waits while the client reads slower than the work. */\n const send = async (chunk: string | Uint8Array): Promise<boolean> => {\n if (res.write(chunk)) return true;\n const drained = new Promise<\"drained\">((resolve) => res.once(\"drain\", () => resolve(\"drained\")));\n return (await Promise.race([drained, closed])) === \"drained\";\n };\n const answered = markAnswer(res, ownerOf(stream));\n try {\n const format = options.stream;\n if (format === undefined) {\n const items: T[] = [];\n for (;;) {\n const event = await next();\n if (event === \"gone\") return await leave();\n if (event.kind === \"item\") {\n items.push(event.value);\n continue;\n }\n writeWhole(res, outcomeResponse(items, event, options));\n return event;\n }\n }\n const first = await next();\n if (first === \"gone\") return await leave();\n if (first.kind !== \"item\") {\n // an empty stream that ended well gives 200 and its end. Each other end before the first item gets a status\n if (first.kind === \"done\")\n writeWhole(res, ruled({ status: 200, headers: { \"content-type\": format.contentType, \"cache-control\": \"no-store\" }, body: format.end(first) }, options.retryAfter, options.keepAlive));\n else writeWhole(res, outcomeResponse([], first, options));\n return first;\n }\n if (res.headersSent) {\n res.destroy();\n return await leave();\n }\n const head = ruled({ status: 200, headers: { \"content-type\": format.contentType, \"cache-control\": \"no-store\" }, body: \"\" }, options.retryAfter, options.keepAlive);\n if (head.headers[\"connection\"] === \"close\") res.shouldKeepAlive = false;\n res.writeHead(200, head.headers);\n if (!(await send(format.item(first.value)))) return await leave();\n for (;;) {\n const event = await next();\n if (event === \"gone\") return await leave();\n if (event.kind === \"item\") {\n if (!(await send(format.item(event.value)))) return await leave();\n continue;\n }\n // the end after the first item is in-band: the status went with the first item\n res.end(format.end(event));\n return event;\n }\n } finally {\n res.off(\"close\", onClose);\n answered();\n }\n};\n\n/** A route of a handler is the name of the route that a controller declares, or a function that gives it from the request. */\nexport type HandledRoute = string | ((req: IncomingMessage) => string | null);\n\n/** A handled work does the work of one request of `node:http`. Its signal aborts when the shutdown or the client stops the request. */\nexport type HandledWork<T> = (req: IncomingMessage, signal: AbortSignal) => RequestWork<T>;\n\n/** These options bind a request of `node:http` to the request tracking of the service. */\nexport interface HandleOptions<T> extends Omit<OutcomeOptions<T>, \"keepAlive\" | \"retryAfter\"> {\n /** This function gives the detail of a request, for the function of a route pattern. */\n readonly detail?: ((req: IncomingMessage) => unknown) | undefined;\n}\n\n/**\n * This function gives a request handler that does each request of a route through `life.request`\n * (S6 A12 and A13). The transport gives the lifecycle and `server_retry_after` through the context.\n * - A route name takes a request whose method and path are that name, for example `POST /orders`.\n * A function gives the name of a route or of a pattern, or null for a request of another handler.\n * - Before the start of the transport, each request gets a retryable 503.\n * - The response is unary, or a stream with the option `stream`, as `writeOutcome` writes it.\n */\nexport const handle = <T>(controller: string, route: HandledRoute, work: HandledWork<T>, options: HandleOptions<T> = {}): RequestHandler => {\n const nameOf = (req: IncomingMessage): string | null => {\n if (typeof route === \"function\") return route(req);\n return `${req.method ?? \"GET\"} ${pathOf(req.url)}` === route ? route : null;\n };\n return async (req: IncomingMessage, res: ServerResponse, context: MountContext): Promise<boolean> => {\n const name = nameOf(req);\n if (name === null) return false;\n const life = context.life;\n const written: OutcomeOptions<T> = {\n ...options,\n keepAlive: () => life?.keepAlive() ?? false,\n retryAfter: context.retryAfter,\n };\n if (life === null) {\n const refused: RequestEnd = { kind: \"refused\", status: 503, retryable: true, cause: \"the transport has not started\", delivered: 0 };\n writeWhole(res, outcomeResponse<T>([], refused, written));\n return true;\n }\n const given: RequestOptions = options.detail === undefined ? {} : { detail: options.detail(req) };\n // the stream comes from the lifecycle of the transport, thus the drain of the transport can cut it\n await writeOutcome(res, ownStream(life.request(controller, name, (signal) => work(req, signal), given), life), written);\n return true;\n };\n};\n"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { S as retryAfterSeconds, _ as freeAt, a as LimitRule, b as limiterProble
|
|
|
5
5
|
import { $ as failSpan, A as LifecycleEvent, B as Gate, C as ProbeAnswer, Ct as Operation, D as Outcome, E as CheckFn, F as CooperativeFloor, G as Step, H as Phase, I as Coupling, J as Poll, K as Transport, L as Dependency, M as Recorder, N as Check, O as Result, P as Controller, Q as asWindow, R as ExitCodes, S as HealthVerdict, St as Idempotency, T as StartOptions, U as RepeatSignal, V as MessageTrigger, W as ServiceDescription, X as Timing, Y as Span, Z as asDebounce, _ as RequestOptions, _t as isExitSignal, a as PhaseContext, at as EXIT_SIGNALS, b as CheckVerdict, bt as nodeProcess, c as STAGES, ct as ExitTrigger, d as TransportImpl, dt as PROCESS_TRIGGERS, et as heartbeatDetection, f as cooperativeAbortAt, ft as ProcessHost, g as RequestFn, gt as isExitCode, h as RequestEvent, ht as embeddedProcess, i as LifecycleState, it as RouteRequest, j as MachineName, l as ShutdownOptions, lt as HostStream, m as RequestEnd, mt as ProcessTrigger, n as HealthServer, nt as OperationOf, o as PhaseWork, ot as EmbeddedProcess, p as stageRank, pt as ProcessPort, q as FailsBy, r as Lifecycle, rt as RoutePattern, s as RunOptions, st as ExitSignal, t as DependencyHandle, tt as recoverSpan, u as Stage, ut as MessageShape, v as RequestStream, vt as isMessageShape, w as ProbeKind, wt as mayReissue, x as Health, xt as processPortOf, y as RequestWork, yt as matchesMessage, z as FORMAT } from "./lifecycle-Chu9Pri2.js";
|
|
6
6
|
import { a as WatchdogSpec, c as Profile, d as kubernetes, f as localDev, i as Watchdog, l as daemon, n as ServiceBuilder, o as watchdog, r as service, s as PodTiming, t as ControllerBuilder, u as ephemeral } from "./builder-DyDB7zM9.js";
|
|
7
7
|
import { a as keyedLimiter, i as TakeRequest, n as LimiterStore, o as memoryStore, r as MemoryStore, t as KeyedLimiter } from "./store-DzTyosou.js";
|
|
8
|
-
import "./limiter.js";
|
|
8
|
+
import { a as limiterStateOf, i as limiterMachine, n as SLIDING_WINDOW_MACHINE, r as TOKEN_BUCKET_MACHINE, t as LimiterState } from "./limiter-DCDdzVwe.js";
|
|
9
9
|
import { Extent, Quantity } from "@mark1russell7/quantity";
|
|
10
10
|
import { replay } from "@mark1russell7/algebra/replay";
|
|
11
11
|
import { Machine } from "@mark1russell7/algebra";
|
|
@@ -431,5 +431,5 @@ export declare const drainMachine: (description: ServiceDescription) => Machine;
|
|
|
431
431
|
/** These are the static machines of the runtime, by the name that their events carry. */
|
|
432
432
|
export declare const MACHINES: Readonly<Record<Exclude<MachineName, "drain" | "client">, Machine>>;
|
|
433
433
|
//#endregion
|
|
434
|
-
export { type Allowed, type Backoff, type Binding, type Bound, type Cancel, type Check, type CheckFn, type CheckVerdict, type Clock, type Controller, type ControllerBuilder, type CooperativeFloor, type Coupling, type Decision, type DelayBand, type Dependency, type DependencyHandle, EXIT_SIGNALS, type EmbeddedProcess, type Evaluated, type ExitCodes, type ExitSignal, type ExitTrigger, FORMAT, type FailsBy, type Finding, type Gate, type Health, type HealthServer, type HealthVerdict, type Heartbeat, type HeartbeatOptions, type HostStream, type Idempotency, type Inequality, type IterationContext, type Jitter, type JobAction, type JobContext, type JobFailure, type JobSettle, type JobSource, type KeyState, type KeyedLimiter, type Lifecycle, type LifecycleEvent, type LifecycleState, type LimitPolicy, type LimitRule, type Limiter, type LimiterEviction, type LimiterSpec, type LimiterStore, type LimiterStoreSpec, type MachineName, type MemoryStore, type MessageShape, type MessageTrigger, type Operation, type OperationOf, type Outcome, PROCESS_TRIGGERS, type Phase, type PhaseContext, type PhaseWork, type PlatformClockOptions, type PodTiming, type Poll, type ProbeAnswer, type ProbeKind, type ProcessHost, type ProcessPort, type ProcessTrigger, type Profile, type QueueDrain, type QueueDrainOptions, type QueueKnobs, type Recorder, type RedeliveryPolicy, type RefusalCause, type Refused, type RepeatSignal, type RequestEnd, type RequestEvent, type RequestFn, type RequestOptions, type RequestStream, type RequestWork, type RequeueCause, type Result, type RoutePattern, type RouteRequest, type Rule, type RunOptions, STAGES, type Scenario, type ServiceBuilder, type ServiceDescription, type ShutdownOptions, type Side, type SlidingWindow, type Span, type Stage, type StartOptions, type Step, type StoreFailure, type TakeRequest, type TakeStep, type Term, type Timing, type TokenBucket, type Transport, type TransportImpl, type UnitRequest, type Verdict, type Watchdog, type WatchdogSpec, type WorkerKnobs, type WorkerLoop, type WorkerLoopOptions, asDebounce, asWindow, backoffBand, backoffDelay, backoffProblems, cooperativeAbortAt, daemon, decide, detectionTime, embeddedProcess, ephemeral, failSpan, freeAt, heartbeat, heartbeatDetection, idleSpan, isExitCode, isExitSignal, isMessageShape, keyedLimiter, kubernetes, limitRule, limiterProblems, localDev, longestRetryAfter, matchesMessage, mayReissue, memoryStore, nodeProcess, platformClock, processPortOf, realClock, recoverSpan, replay, retryAfterSeconds, service, stageRank, watchdog };
|
|
434
|
+
export { type Allowed, type Backoff, type Binding, type Bound, type Cancel, type Check, type CheckFn, type CheckVerdict, type Clock, type Controller, type ControllerBuilder, type CooperativeFloor, type Coupling, type Decision, type DelayBand, type Dependency, type DependencyHandle, EXIT_SIGNALS, type EmbeddedProcess, type Evaluated, type ExitCodes, type ExitSignal, type ExitTrigger, FORMAT, type FailsBy, type Finding, type Gate, type Health, type HealthServer, type HealthVerdict, type Heartbeat, type HeartbeatOptions, type HostStream, type Idempotency, type Inequality, type IterationContext, type Jitter, type JobAction, type JobContext, type JobFailure, type JobSettle, type JobSource, type KeyState, type KeyedLimiter, type Lifecycle, type LifecycleEvent, type LifecycleState, type LimitPolicy, type LimitRule, type Limiter, type LimiterEviction, type LimiterSpec, type LimiterState, type LimiterStore, type LimiterStoreSpec, type MachineName, type MemoryStore, type MessageShape, type MessageTrigger, type Operation, type OperationOf, type Outcome, PROCESS_TRIGGERS, type Phase, type PhaseContext, type PhaseWork, type PlatformClockOptions, type PodTiming, type Poll, type ProbeAnswer, type ProbeKind, type ProcessHost, type ProcessPort, type ProcessTrigger, type Profile, type QueueDrain, type QueueDrainOptions, type QueueKnobs, type Recorder, type RedeliveryPolicy, type RefusalCause, type Refused, type RepeatSignal, type RequestEnd, type RequestEvent, type RequestFn, type RequestOptions, type RequestStream, type RequestWork, type RequeueCause, type Result, type RoutePattern, type RouteRequest, type Rule, type RunOptions, SLIDING_WINDOW_MACHINE, STAGES, type Scenario, type ServiceBuilder, type ServiceDescription, type ShutdownOptions, type Side, type SlidingWindow, type Span, type Stage, type StartOptions, type Step, type StoreFailure, TOKEN_BUCKET_MACHINE, type TakeRequest, type TakeStep, type Term, type Timing, type TokenBucket, type Transport, type TransportImpl, type UnitRequest, type Verdict, type Watchdog, type WatchdogSpec, type WorkerKnobs, type WorkerLoop, type WorkerLoopOptions, asDebounce, asWindow, backoffBand, backoffDelay, backoffProblems, cooperativeAbortAt, daemon, decide, detectionTime, embeddedProcess, ephemeral, failSpan, freeAt, heartbeat, heartbeatDetection, idleSpan, isExitCode, isExitSignal, isMessageShape, keyedLimiter, kubernetes, limitRule, limiterMachine, limiterProblems, limiterStateOf, localDev, longestRetryAfter, matchesMessage, mayReissue, memoryStore, nodeProcess, platformClock, processPortOf, realClock, recoverSpan, replay, retryAfterSeconds, service, stageRank, watchdog };
|
|
435
435
|
//# sourceMappingURL=index.d.ts.map
|