@orkestrel/middleware 0.0.19 → 0.0.20
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 +16 -11
- package/dist/src/core/index.cjs +78 -60
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +150 -81
- package/dist/src/core/index.d.ts +150 -81
- package/dist/src/core/index.js +78 -60
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +43 -41
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +62 -60
- package/dist/src/server/index.d.ts +62 -60
- package/dist/src/server/index.js +43 -41
- package/dist/src/server/index.js.map +1 -1
- package/package.json +21 -22
package/README.md
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
# @orkestrel/middleware
|
|
2
2
|
|
|
3
|
-
Batteries for the `@orkestrel/server` middleware seam
|
|
4
|
-
`MiddlewareHandler<TState>`
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
> Batteries for the `@orkestrel/server` middleware seam:
|
|
4
|
+
> `create{Noun}(options) => MiddlewareHandler<TState>` factories for error
|
|
5
|
+
> boundaries, telemetry, compression, security headers, CORS, deadlines,
|
|
6
|
+
> trusted-proxy client facts, ETag, bearer authentication, rate limiting, body
|
|
7
|
+
> parsing, sessions, and CSRF in the fetch-native core, plus in-memory assets,
|
|
8
|
+
> static files, streaming multipart uploads, and a `node:zlib` compression
|
|
9
|
+
> sibling in the node face.
|
|
10
|
+
|
|
11
|
+
Mount the batteries your threat model needs over the `compose` seam: each one
|
|
12
|
+
closes over its guarded options and returns a `MiddlewareHandler<TState>`, and
|
|
13
|
+
its position in the chain decides what it can see. Read
|
|
14
|
+
[`guides/middleware.md`](guides/middleware.md) for the ordering doctrine and
|
|
15
|
+
the security acceptance bar before you fix an order. Part of the `@orkestrel`
|
|
16
|
+
line.
|
|
10
17
|
|
|
11
18
|
## Install
|
|
12
19
|
|
|
@@ -40,10 +47,8 @@ const handle = compose<State>([boundary, security], async (_request, context) =>
|
|
|
40
47
|
```
|
|
41
48
|
|
|
42
49
|
Each battery is a typed `options => MiddlewareHandler<TState>` factory that composes with
|
|
43
|
-
the others through the frozen `@orkestrel/server` seam
|
|
44
|
-
|
|
45
|
-
`DatabaseSessionStore` over `@orkestrel/database`), CSRF, static files, and multipart uploads
|
|
46
|
-
in any combination, scoped with `only()` / `except()` where needed.
|
|
50
|
+
the others through the frozen `@orkestrel/server` seam, in any combination, scoped with
|
|
51
|
+
`only()` and `except()` where needed.
|
|
47
52
|
|
|
48
53
|
## Guides
|
|
49
54
|
|
package/dist/src/core/index.cjs
CHANGED
|
@@ -5,12 +5,12 @@ let _orkestrel_abort = require("@orkestrel/abort");
|
|
|
5
5
|
let _orkestrel_budget = require("@orkestrel/budget");
|
|
6
6
|
let _orkestrel_timeout = require("@orkestrel/timeout");
|
|
7
7
|
//#region src/core/constants.ts
|
|
8
|
-
/** Holds the default minimum buffered body size
|
|
8
|
+
/** Holds `1024`, the default minimum buffered body size in bytes `createCompression` will compress. */
|
|
9
9
|
var DEFAULT_COMPRESSION_THRESHOLD = 1024;
|
|
10
10
|
/**
|
|
11
|
-
* Lists the default content-codings `createCompression` offers
|
|
12
|
-
* intersected at construction with what the runtime's
|
|
13
|
-
* actually supports.
|
|
11
|
+
* Lists `['gzip', 'deflate']`, the default content-codings `createCompression` offers in
|
|
12
|
+
* preference order — intersected at construction with what the runtime's
|
|
13
|
+
* `CompressionStream` actually supports.
|
|
14
14
|
*
|
|
15
15
|
* @remarks
|
|
16
16
|
* The shipped `@orkestrel/server` peer's {@link Encoding} union is
|
|
@@ -20,28 +20,29 @@ var DEFAULT_COMPRESSION_THRESHOLD = 1024;
|
|
|
20
20
|
* admits; a node-face brotli variant, if one ships, extends this list there.
|
|
21
21
|
*/
|
|
22
22
|
var DEFAULT_COMPRESSION_ENCODINGS = Object.freeze(["gzip", "deflate"]);
|
|
23
|
-
/** Holds the default `X-Frame-Options` value `createSecurity` sets. */
|
|
23
|
+
/** Holds `'DENY'`, the default `X-Frame-Options` value `createSecurity` sets. */
|
|
24
24
|
var DEFAULT_FRAME_OPTIONS = "DENY";
|
|
25
25
|
/**
|
|
26
|
-
* Holds
|
|
27
|
-
* `
|
|
26
|
+
* Holds `"default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'"`,
|
|
27
|
+
* the default `Content-Security-Policy` value `createSecurity` sets — a custom
|
|
28
|
+
* `csp` option replaces this wholesale, never merges.
|
|
28
29
|
*/
|
|
29
30
|
var DEFAULT_CSP = "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'";
|
|
30
|
-
/** Holds the default `Referrer-Policy` value `createSecurity` sets. */
|
|
31
|
+
/** Holds `'strict-origin-when-cross-origin'`, the default `Referrer-Policy` value `createSecurity` sets. */
|
|
31
32
|
var DEFAULT_REFERRER_POLICY = "strict-origin-when-cross-origin";
|
|
32
|
-
/** Holds the default `Permissions-Policy` value `createSecurity` sets. */
|
|
33
|
+
/** Holds `'camera=(), microphone=(), geolocation=()'`, the default `Permissions-Policy` value `createSecurity` sets. */
|
|
33
34
|
var DEFAULT_PERMISSIONS_POLICY = "camera=(), microphone=(), geolocation=()";
|
|
34
|
-
/** Holds the default `Cross-Origin-Opener-Policy` value `createSecurity` sets. */
|
|
35
|
+
/** Holds `'same-origin'`, the default `Cross-Origin-Opener-Policy` value `createSecurity` sets. */
|
|
35
36
|
var DEFAULT_COOP = "same-origin";
|
|
36
|
-
/** Holds the default `Cross-Origin-Resource-Policy` value `createSecurity` sets. */
|
|
37
|
+
/** Holds `'same-origin'`, the default `Cross-Origin-Resource-Policy` value `createSecurity` sets. */
|
|
37
38
|
var DEFAULT_CORP = "same-origin";
|
|
38
|
-
/** Holds the default `Origin-Agent-Cluster` value `createSecurity` sets. */
|
|
39
|
+
/** Holds `'?1'`, the default `Origin-Agent-Cluster` value `createSecurity` sets. */
|
|
39
40
|
var DEFAULT_CLUSTER = "?1";
|
|
40
|
-
/** Holds the value `createSecurity` sets for `Cross-Origin-Embedder-Policy` when `coep: true`. */
|
|
41
|
+
/** Holds `'require-corp'`, the value `createSecurity` sets for `Cross-Origin-Embedder-Policy` when `coep: true`. */
|
|
41
42
|
var DEFAULT_COEP = "require-corp";
|
|
42
|
-
/** Holds the value `createSecurity` sets for `Strict-Transport-Security` when `hsts: true`. */
|
|
43
|
+
/** Holds `'max-age=31536000; includeSubDomains'`, the value `createSecurity` sets for `Strict-Transport-Security` when `hsts: true`. */
|
|
43
44
|
var DEFAULT_HSTS = "max-age=31536000; includeSubDomains";
|
|
44
|
-
/** Names the default header `createSecurity` mints
|
|
45
|
+
/** Names `'x-request-id'`, the default header `createSecurity` mints or echoes a request identifier into. */
|
|
45
46
|
var DEFAULT_IDENTIFIER_HEADER = "x-request-id";
|
|
46
47
|
/** Lists the default methods `createCors` advertises on a preflight response. */
|
|
47
48
|
var DEFAULT_CORS_METHODS = Object.freeze([
|
|
@@ -54,29 +55,29 @@ var DEFAULT_CORS_METHODS = Object.freeze([
|
|
|
54
55
|
]);
|
|
55
56
|
/** Lists the default headers `createCors` advertises on a preflight response. */
|
|
56
57
|
var DEFAULT_CORS_HEADERS = Object.freeze(["Content-Type", "Authorization"]);
|
|
57
|
-
/** Holds the default response status `createDeadline` returns when its deadline fires first. */
|
|
58
|
+
/** Holds `503`, the default response status `createDeadline` returns when its deadline fires first. */
|
|
58
59
|
var DEFAULT_DEADLINE_STATUS = 503;
|
|
59
|
-
/** Names the default header `createBearer` reads the token from. */
|
|
60
|
+
/** Names `'authorization'`, the default header `createBearer` reads the token from. */
|
|
60
61
|
var DEFAULT_BEARER_HEADER = "authorization";
|
|
61
|
-
/** Names the default scheme prefix `createBearer` strips before verification. */
|
|
62
|
+
/** Names `'Bearer'`, the default scheme prefix `createBearer` strips before verification. */
|
|
62
63
|
var DEFAULT_BEARER_SCHEME = "Bearer";
|
|
63
|
-
/** Holds the default maximum number of distinct rate-limit keys `createLimiter` tracks before LRU eviction. */
|
|
64
|
+
/** Holds `10_000`, the default maximum number of distinct rate-limit keys `createLimiter` tracks before LRU eviction. */
|
|
64
65
|
var DEFAULT_LIMITER_CAPACITY = 1e4;
|
|
65
|
-
/** Holds the default maximum number of distinct session ids `createMemorySessionStore` tracks before LRU (by last write) eviction. */
|
|
66
|
+
/** Holds `10_000`, the default maximum number of distinct session ids `createMemorySessionStore` tracks before LRU (by last write) eviction. */
|
|
66
67
|
var DEFAULT_SESSION_CAPACITY = 1e4;
|
|
67
|
-
/** Holds the default 429 body message `createLimiter` sends when a key is over budget. */
|
|
68
|
+
/** Holds `'rate limit exceeded'`, the default 429 body message `createLimiter` sends when a key is over budget. */
|
|
68
69
|
var DEFAULT_LIMITER_MESSAGE = "rate limit exceeded";
|
|
69
|
-
/** Names the default cookie `createCookieTransport` writes the signed session id under. */
|
|
70
|
+
/** Names `'session'`, the default cookie `createCookieTransport` writes the signed session id under. */
|
|
70
71
|
var DEFAULT_SESSION_COOKIE = "session";
|
|
71
|
-
/** Names the default header `createHeaderTransport` carries the session id in. */
|
|
72
|
+
/** Names `'session-id'`, the default header `createHeaderTransport` carries the session id in. */
|
|
72
73
|
var DEFAULT_SESSION_HEADER = "session-id";
|
|
73
|
-
/** Names the default signed cookie `createCSRF` writes the CSRF token under. */
|
|
74
|
+
/** Names `'csrf'`, the default signed cookie `createCSRF` writes the CSRF token under. */
|
|
74
75
|
var DEFAULT_CSRF_COOKIE = "csrf";
|
|
75
|
-
/** Names the default header `createCSRF` reads a mutating request's submitted token from. */
|
|
76
|
+
/** Names `'x-csrf-token'`, the default header `createCSRF` reads a mutating request's submitted token from. */
|
|
76
77
|
var DEFAULT_CSRF_HEADER = "x-csrf-token";
|
|
77
|
-
/** Names the default body field `createCSRF` falls back to reading a mutating request's submitted token from. */
|
|
78
|
+
/** Names `'_csrf'`, the default body field `createCSRF` falls back to reading a mutating request's submitted token from. */
|
|
78
79
|
var DEFAULT_CSRF_FIELD = "_csrf";
|
|
79
|
-
/** Lists the default methods `createCSRF` treats as safe (mint instead of verify). */
|
|
80
|
+
/** Lists `['GET', 'HEAD', 'OPTIONS']`, the default methods `createCSRF` treats as safe (mint instead of verify). */
|
|
80
81
|
var DEFAULT_CSRF_SAFE_METHODS = Object.freeze([
|
|
81
82
|
"GET",
|
|
82
83
|
"HEAD",
|
|
@@ -175,7 +176,7 @@ function buildRateLimitPolicyField(max, window) {
|
|
|
175
176
|
* never claims full CIDR generality beyond IPv4.
|
|
176
177
|
*
|
|
177
178
|
* @remarks
|
|
178
|
-
* An IPv6 `trusted` roster entry is compared as an
|
|
179
|
+
* An IPv6 `trusted` roster entry is compared as an exact string — it must be
|
|
179
180
|
* supplied in canonical form (no zero-compression normalization, no case
|
|
180
181
|
* folding) by the caller; this function performs no IPv6 normalization of
|
|
181
182
|
* its own.
|
|
@@ -209,13 +210,13 @@ function matchesTrustedEntry(address, entry) {
|
|
|
209
210
|
return (networkInt & mask) === (addressInt & mask);
|
|
210
211
|
}
|
|
211
212
|
/**
|
|
212
|
-
* Walks `X-Forwarded-For` right-to-left and resolves the first
|
|
213
|
+
* Walks `X-Forwarded-For` right-to-left and resolves the first untrusted hop
|
|
213
214
|
* address — `createForwarded`'s core algorithm.
|
|
214
215
|
*
|
|
215
216
|
* @remarks
|
|
216
217
|
* Parses `X-Forwarded-For` only. With `proxies` set, trusts exactly that
|
|
217
218
|
* many hops counted from the right (the closest to this server) and returns
|
|
218
|
-
* the next one left of them; with `trusted` set, trusts every
|
|
219
|
+
* the next one left of them; with `trusted` set, trusts every consecutive
|
|
219
220
|
* hop from the right that matches one of the roster
|
|
220
221
|
* ({@link matchesTrustedEntry}) and returns the first hop that does not. If
|
|
221
222
|
* the rightmost hop (the immediate sender) does not match the roster, the
|
|
@@ -225,7 +226,7 @@ function matchesTrustedEntry(address, entry) {
|
|
|
225
226
|
* socket peer).
|
|
226
227
|
*
|
|
227
228
|
* @param header - The raw `X-Forwarded-For` header value (comma-separated hops), if present
|
|
228
|
-
* @param trust - The {@link ForwardedOptions} form in force — a trusted hop
|
|
229
|
+
* @param trust - The {@link ForwardedOptions} form in force — a trusted hop count or a `trusted` CIDR/exact roster
|
|
229
230
|
* @returns The first untrusted hop address, or `undefined` when none qualifies
|
|
230
231
|
*
|
|
231
232
|
* @example
|
|
@@ -299,16 +300,17 @@ async function compressBytes(bytes, encoding) {
|
|
|
299
300
|
return new Uint8Array(compressed);
|
|
300
301
|
}
|
|
301
302
|
/**
|
|
302
|
-
* Checks whether a response
|
|
303
|
+
* Checks whether a response must skip the compression and ETag buffering pipeline
|
|
303
304
|
* — the shared cheap-skip predicate both batteries apply before ever touching
|
|
304
|
-
* `response.arrayBuffer()
|
|
305
|
+
* `response.arrayBuffer()`, true for a `HEAD` request, a `204`/`304` or
|
|
306
|
+
* otherwise bodyless response, an `event-stream` response, and a response
|
|
307
|
+
* already carrying the header the caller is about to set.
|
|
305
308
|
*
|
|
306
309
|
* @remarks
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* ETag battery).
|
|
310
|
+
* Buffering an `event-stream` response would hang the connection, so SSE skips
|
|
311
|
+
* whatever its size. `skipHeader` is the header whose presence already answers
|
|
312
|
+
* the question — `Content-Encoding` for compression, `ETag` for the ETag
|
|
313
|
+
* battery.
|
|
312
314
|
*
|
|
313
315
|
* @param method - The request's HTTP method
|
|
314
316
|
* @param response - The candidate response
|
|
@@ -347,7 +349,7 @@ function isCompressionNegotiated(encoding) {
|
|
|
347
349
|
}
|
|
348
350
|
/**
|
|
349
351
|
* Resolves an opt-in, value-bearing security header — `string | boolean`
|
|
350
|
-
* (default
|
|
352
|
+
* (off by default, `true` uses the secure default), the shape `createSecurity`'s
|
|
351
353
|
* `coep`/`hsts` options use, distinct from the plain value-or-`false` shape
|
|
352
354
|
* `resolveSecurityHeader` (the peer substrate) handles.
|
|
353
355
|
*
|
|
@@ -401,7 +403,7 @@ function rebuildResponse(body, response, headers) {
|
|
|
401
403
|
* even when a later skip declines to compress) → `negotiateEncoding` over
|
|
402
404
|
* `options.encodings` → {@link isCompressionNegotiated} → `isCompressibleType`
|
|
403
405
|
* on `Content-Type` → a fast skip when the response already carries a
|
|
404
|
-
* numeric `Content-Length`
|
|
406
|
+
* numeric `Content-Length` below `options.threshold` (avoids buffering a
|
|
405
407
|
* body known too small to be worth compressing) → buffer through
|
|
406
408
|
* `response.arrayBuffer()` → a threshold passthrough when the buffered size
|
|
407
409
|
* is still below `options.threshold` → `options.compress` → set
|
|
@@ -463,7 +465,7 @@ function transferSessionState(from, to) {
|
|
|
463
465
|
for (const [key, value] of from.state) to.set(key, value);
|
|
464
466
|
}
|
|
465
467
|
/**
|
|
466
|
-
* Determines whether a request is a CORS
|
|
468
|
+
* Determines whether a request is a CORS preflight — an `OPTIONS` request
|
|
467
469
|
* carrying an `Access-Control-Request-Method` header.
|
|
468
470
|
*
|
|
469
471
|
* @param method - The request's HTTP method
|
|
@@ -570,7 +572,7 @@ function validateSessionLimits(limits) {
|
|
|
570
572
|
* @remarks
|
|
571
573
|
* `state` is built on a null-prototype object (`Object.create(null)`), never
|
|
572
574
|
* a `{}` literal — a session key literally named `__proto__` must round-trip
|
|
573
|
-
* as an
|
|
575
|
+
* as an own enumerable property instead of hitting `Object.prototype`'s
|
|
574
576
|
* `__proto__` accessor (which would silently drop the entry and risk
|
|
575
577
|
* polluting the shared prototype).
|
|
576
578
|
*
|
|
@@ -593,8 +595,8 @@ function snapshotSession(session) {
|
|
|
593
595
|
* Determines whether a value implements {@link SessionInterface} — a total
|
|
594
596
|
* structural guard: an `id` string, a `state` `Map`, and the `set`, `delete`,
|
|
595
597
|
* and `clear` mutators. Prototype-agnostic — accepts a plain object, a
|
|
596
|
-
* null-prototype object,
|
|
597
|
-
* restored
|
|
598
|
+
* null-prototype object, and a class instance (a real `Session`), because a
|
|
599
|
+
* restored or stored session is routinely a class instance rather than a literal.
|
|
598
600
|
*
|
|
599
601
|
* @param value - The candidate value
|
|
600
602
|
* @returns True if `value` is shaped like a {@link SessionInterface}; false otherwise
|
|
@@ -682,7 +684,7 @@ function isMultipartBody(value) {
|
|
|
682
684
|
//#region src/core/shapers.ts
|
|
683
685
|
/**
|
|
684
686
|
* Holds the `@orkestrel/database` column shape for a
|
|
685
|
-
* {@link import('./types.js').SessionRow} table
|
|
687
|
+
* {@link import('./types.js').SessionRow} table. Pass it as-is to
|
|
686
688
|
* `createDatabase({ tables: { sessions: sessionColumns } })` so an app
|
|
687
689
|
* declaring a durable session table never hand-writes the shape.
|
|
688
690
|
*
|
|
@@ -761,7 +763,7 @@ var Session = class {
|
|
|
761
763
|
* @remarks
|
|
762
764
|
* `get` evicts a session whose idle time (`now - seen >= ttl`) or
|
|
763
765
|
* absolute lifetime (`now - created >= lifetime`) has elapsed — the
|
|
764
|
-
* lifetime check fires
|
|
766
|
+
* lifetime check fires even if the session was continuously touched, because
|
|
765
767
|
* `created` is stamped once at the first `set` and preserved across every
|
|
766
768
|
* later re-`set` of the same id. A live read touches `seen`. `delete` of
|
|
767
769
|
* an absent id is a no-op.
|
|
@@ -876,7 +878,7 @@ var MemorySessionStore = class {
|
|
|
876
878
|
* {@link MemorySessionStore}. `delete` of an absent id is a no-op (the
|
|
877
879
|
* table's `remove` contract).
|
|
878
880
|
*
|
|
879
|
-
* A malformed-snapshot or failed-guard `undefined`
|
|
881
|
+
* A malformed-snapshot or failed-guard `undefined` leaves the row in place —
|
|
880
882
|
* unlike the expired path, which removes it. This is deliberate: a
|
|
881
883
|
* caller-contextual guard may reject a session that is still perfectly
|
|
882
884
|
* valid for another flow reading the same table (a differently-shaped `S`,
|
|
@@ -946,9 +948,20 @@ var DatabaseSessionStore = class {
|
|
|
946
948
|
* @returns A `MiddlewareHandler<TState>`
|
|
947
949
|
* @throws {TypeError} When `options.expose` or `options.report` is malformed
|
|
948
950
|
*
|
|
949
|
-
* @example
|
|
951
|
+
* @example Mount a battery
|
|
950
952
|
* ```ts
|
|
953
|
+
* import { createBoundary, createSecurity } from '@orkestrel/middleware'
|
|
954
|
+
* import type { IdentifierState } from '@orkestrel/middleware'
|
|
955
|
+
* import { compose } from '@orkestrel/server'
|
|
956
|
+
*
|
|
957
|
+
* interface State extends IdentifierState {}
|
|
958
|
+
*
|
|
951
959
|
* const boundary = createBoundary({ expose: false })
|
|
960
|
+
* const security = createSecurity({ hsts: true })
|
|
961
|
+
*
|
|
962
|
+
* const handle = compose<State>([boundary, security], async (_request, context) => {
|
|
963
|
+
* return Response.json({ identifier: context.state.identifier })
|
|
964
|
+
* })
|
|
952
965
|
* ```
|
|
953
966
|
*/
|
|
954
967
|
function createBoundary(options) {
|
|
@@ -1037,7 +1050,8 @@ function createCompression(options) {
|
|
|
1037
1050
|
};
|
|
1038
1051
|
}
|
|
1039
1052
|
/**
|
|
1040
|
-
* Creates the security-headers + request-identifier battery
|
|
1053
|
+
* Creates the security-headers + request-identifier battery — sets each documented
|
|
1054
|
+
* header default, and mints or echoes a request identifier.
|
|
1041
1055
|
*
|
|
1042
1056
|
* @typeParam TState - The consumer's opaque per-request state type, must carry {@link IdentifierState}
|
|
1043
1057
|
* @param options - See {@link SecurityOptions}
|
|
@@ -1099,7 +1113,8 @@ function createSecurity(options) {
|
|
|
1099
1113
|
};
|
|
1100
1114
|
}
|
|
1101
1115
|
/**
|
|
1102
|
-
* Creates the Cross-Origin Resource Sharing battery
|
|
1116
|
+
* Creates the Cross-Origin Resource Sharing battery — answers a preflight itself, and
|
|
1117
|
+
* reflects an allow-listed origin or serves the configured wildcard.
|
|
1103
1118
|
*
|
|
1104
1119
|
* @typeParam TState - The consumer's opaque per-request state type
|
|
1105
1120
|
* @param options - See {@link CorsOptions}
|
|
@@ -1148,7 +1163,7 @@ function createCors(options) {
|
|
|
1148
1163
|
* @throws {TypeError} When `options.ms` or `options.status` is malformed
|
|
1149
1164
|
*
|
|
1150
1165
|
* @remarks
|
|
1151
|
-
*
|
|
1166
|
+
* Mount this battery outside `createBody` in the chain — it reconstructs the inbound
|
|
1152
1167
|
* `Request` (to link its `signal` to the deadline `signal`), which throws if
|
|
1153
1168
|
* the body was already consumed upstream (for example by `createBody`'s cached read).
|
|
1154
1169
|
*
|
|
@@ -1185,7 +1200,8 @@ function createDeadline(options) {
|
|
|
1185
1200
|
};
|
|
1186
1201
|
}
|
|
1187
1202
|
/**
|
|
1188
|
-
* Creates the trusted-proxy client-IP resolver battery
|
|
1203
|
+
* Creates the trusted-proxy client-IP resolver battery — walks `X-Forwarded-For` past
|
|
1204
|
+
* the hops its options declare trusted.
|
|
1189
1205
|
*
|
|
1190
1206
|
* @typeParam TState - The consumer's opaque per-request state type, must carry {@link ClientState} and {@link ConnectionState}
|
|
1191
1207
|
* @param options - See {@link ForwardedOptions}
|
|
@@ -1216,7 +1232,7 @@ function createForwarded(options) {
|
|
|
1216
1232
|
};
|
|
1217
1233
|
}
|
|
1218
1234
|
/**
|
|
1219
|
-
* Creates the dynamic response `ETag` + conditional GET battery.
|
|
1235
|
+
* Creates the dynamic response `ETag` + conditional GET battery (RFC 7232).
|
|
1220
1236
|
*
|
|
1221
1237
|
* @typeParam TState - The consumer's opaque per-request state type
|
|
1222
1238
|
* @param options - See {@link ETagOptions}
|
|
@@ -1254,7 +1270,8 @@ function createETag(options) {
|
|
|
1254
1270
|
};
|
|
1255
1271
|
}
|
|
1256
1272
|
/**
|
|
1257
|
-
* Creates the bearer-token authentication battery
|
|
1273
|
+
* Creates the bearer-token authentication battery — reads the token from its header and
|
|
1274
|
+
* verifies it with `verifyToken`.
|
|
1258
1275
|
*
|
|
1259
1276
|
* @typeParam TState - The consumer's opaque per-request state type, must carry {@link BearerState}
|
|
1260
1277
|
* @param options - See {@link BearerOptions}
|
|
@@ -1290,7 +1307,8 @@ function createBearer(options) {
|
|
|
1290
1307
|
};
|
|
1291
1308
|
}
|
|
1292
1309
|
/**
|
|
1293
|
-
* Creates the fixed-window rate-limiting battery
|
|
1310
|
+
* Creates the fixed-window rate-limiting battery — checks a key's budget before
|
|
1311
|
+
* consuming it, so one window admits exactly `max` requests.
|
|
1294
1312
|
*
|
|
1295
1313
|
* @typeParam TState - The consumer's opaque per-request state type, must carry {@link BearerState}, {@link ClientState}, and {@link ConnectionState}
|
|
1296
1314
|
* @param options - See {@link LimiterOptions}
|
|
@@ -1379,7 +1397,7 @@ function createLimiter(options) {
|
|
|
1379
1397
|
* The shipped `MiddlewareContext.body()` is a parameterless, server-owned
|
|
1380
1398
|
* cache (`ServerOptions.limit` governs its size cap) — this battery carries
|
|
1381
1399
|
* no `limit`/`decompression` options. `state.body` is
|
|
1382
|
-
* stashed from the
|
|
1400
|
+
* stashed from the same awaited call the 400 check reads — `context.body()`
|
|
1383
1401
|
* is never invoked twice.
|
|
1384
1402
|
*
|
|
1385
1403
|
* @example
|
|
@@ -1538,8 +1556,8 @@ function createCSRF(options) {
|
|
|
1538
1556
|
};
|
|
1539
1557
|
}
|
|
1540
1558
|
/**
|
|
1541
|
-
* Scopes a battery to
|
|
1542
|
-
* steps aside through `next()`.
|
|
1559
|
+
* Scopes a battery to a set of exact pathnames and nowhere else — outside that set
|
|
1560
|
+
* it steps aside through `next()`.
|
|
1543
1561
|
*
|
|
1544
1562
|
* @typeParam TState - The consumer's opaque per-request state type
|
|
1545
1563
|
* @param paths - One pathname, or a set of pathnames, matched exactly against `context.url.pathname`
|
|
@@ -1559,8 +1577,8 @@ function only(paths, handler) {
|
|
|
1559
1577
|
};
|
|
1560
1578
|
}
|
|
1561
1579
|
/**
|
|
1562
|
-
* Scopes a battery to
|
|
1563
|
-
*
|
|
1580
|
+
* Scopes a battery to every pathname outside a set of exact ones — on that set it
|
|
1581
|
+
* steps aside through `next()`.
|
|
1564
1582
|
*
|
|
1565
1583
|
* @typeParam TState - The consumer's opaque per-request state type
|
|
1566
1584
|
* @param paths - One pathname, or a set of pathnames, matched exactly against `context.url.pathname`
|