@nifrajs/core 2.11.0 → 2.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assurance.d.ts +61 -3
- package/dist/assurance.d.ts.map +1 -1
- package/dist/assurance.js +152 -5
- package/dist/assurance.js.map +1 -1
- package/dist/capabilities.d.ts +9 -1
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +5 -1
- package/dist/capabilities.js.map +1 -1
- package/dist/diff.d.ts +2 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +3 -1
- package/dist/diff.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/evidence.d.ts +89 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +170 -0
- package/dist/evidence.js.map +1 -0
- package/dist/execution-policy.d.ts +23 -0
- package/dist/execution-policy.d.ts.map +1 -0
- package/dist/execution-policy.js +28 -0
- package/dist/execution-policy.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/route-assurance.d.ts +19 -0
- package/dist/internal/route-assurance.d.ts.map +1 -1
- package/dist/internal/route-assurance.js +38 -5
- package/dist/internal/route-assurance.js.map +1 -1
- package/dist/internal/route-execution.d.ts +18 -0
- package/dist/internal/route-execution.d.ts.map +1 -1
- package/dist/internal/route-execution.js +67 -1
- package/dist/internal/route-execution.js.map +1 -1
- package/dist/manifest.d.ts +5 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +22 -4
- package/dist/manifest.js.map +1 -1
- package/dist/reflection.d.ts +5 -0
- package/dist/reflection.d.ts.map +1 -1
- package/dist/reflection.js +13 -4
- package/dist/reflection.js.map +1 -1
- package/dist/replay.d.ts +26 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +63 -0
- package/dist/replay.js.map +1 -0
- package/dist/schema/standard.d.ts +2 -0
- package/dist/schema/standard.d.ts.map +1 -1
- package/dist/schema/standard.js +13 -0
- package/dist/schema/standard.js.map +1 -1
- package/dist/server/body.d.ts +49 -6
- package/dist/server/body.d.ts.map +1 -1
- package/dist/server/body.js +226 -8
- package/dist/server/body.js.map +1 -1
- package/dist/server/context.d.ts +20 -3
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/contract.d.ts +15 -3
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +22 -0
- package/dist/server/contract.js.map +1 -1
- package/dist/server/cookies.d.ts +26 -5
- package/dist/server/cookies.d.ts.map +1 -1
- package/dist/server/cookies.js +76 -9
- package/dist/server/cookies.js.map +1 -1
- package/dist/server/edge.d.ts.map +1 -1
- package/dist/server/edge.js +11 -0
- package/dist/server/edge.js.map +1 -1
- package/dist/server/header-case.d.ts +9 -0
- package/dist/server/header-case.d.ts.map +1 -0
- package/dist/server/header-case.js +54 -0
- package/dist/server/header-case.js.map +1 -0
- package/dist/server/headers.d.ts +8 -0
- package/dist/server/headers.d.ts.map +1 -0
- package/dist/server/headers.js +13 -0
- package/dist/server/headers.js.map +1 -0
- package/dist/server/node-outcome-hook.d.ts.map +1 -1
- package/dist/server/node-outcome-hook.js +7 -11
- package/dist/server/node-outcome-hook.js.map +1 -1
- package/dist/server/node-outcome.d.ts +8 -1
- package/dist/server/node-outcome.d.ts.map +1 -1
- package/dist/server/node-outcome.js +10 -3
- package/dist/server/node-outcome.js.map +1 -1
- package/dist/server/plugin.d.ts +66 -7
- package/dist/server/plugin.d.ts.map +1 -1
- package/dist/server/plugin.js +14 -10
- package/dist/server/plugin.js.map +1 -1
- package/dist/server/proto-guard.d.ts +30 -0
- package/dist/server/proto-guard.d.ts.map +1 -0
- package/dist/server/proto-guard.js +116 -0
- package/dist/server/proto-guard.js.map +1 -0
- package/dist/server/registry.d.ts +6 -2
- package/dist/server/registry.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +24 -6
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +157 -30
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/respond.d.ts +2 -2
- package/dist/server/respond.d.ts.map +1 -1
- package/dist/server/respond.js +34 -8
- package/dist/server/respond.js.map +1 -1
- package/dist/server/response-contract-lane.d.ts +1 -1
- package/dist/server/response-contract-lane.d.ts.map +1 -1
- package/dist/server/response-contract-lane.js +45 -13
- package/dist/server/response-contract-lane.js.map +1 -1
- package/dist/server/response-hooks.d.ts +10 -0
- package/dist/server/response-hooks.d.ts.map +1 -0
- package/dist/server/response-hooks.js +52 -0
- package/dist/server/response-hooks.js.map +1 -0
- package/dist/server/runtime-core.d.ts +12 -0
- package/dist/server/runtime-core.d.ts.map +1 -1
- package/dist/server/runtime-core.js +8 -0
- package/dist/server/runtime-core.js.map +1 -1
- package/dist/server/server-types.d.ts +60 -6
- package/dist/server/server-types.d.ts.map +1 -1
- package/dist/server/server.d.ts +80 -23
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +429 -213
- package/dist/server/server.js.map +1 -1
- package/dist/server/static-headers.d.ts +14 -3
- package/dist/server/static-headers.d.ts.map +1 -1
- package/dist/server/static-headers.js +28 -6
- package/dist/server/static-headers.js.map +1 -1
- package/dist/server/transport-lane.d.ts +8 -0
- package/dist/server/transport-lane.d.ts.map +1 -1
- package/dist/server/transport-lane.js +12 -8
- package/dist/server/transport-lane.js.map +1 -1
- package/dist/server/websocket.d.ts +3 -0
- package/dist/server/websocket.d.ts.map +1 -1
- package/dist/server/websocket.js +14 -0
- package/dist/server/websocket.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/tool-catalog.d.ts +21 -0
- package/dist/tool-catalog.d.ts.map +1 -0
- package/dist/tool-catalog.js +16 -0
- package/dist/tool-catalog.js.map +1 -0
- package/dist/tool-contract.d.ts +238 -0
- package/dist/tool-contract.d.ts.map +1 -0
- package/dist/tool-contract.js +569 -0
- package/dist/tool-contract.js.map +1 -0
- package/package.json +26 -1
- package/src/assurance.ts +236 -6
- package/src/capabilities.ts +14 -1
- package/src/diff.ts +5 -2
- package/src/errors.ts +1 -0
- package/src/evidence.ts +319 -0
- package/src/execution-policy.ts +57 -0
- package/src/index.ts +1 -1
- package/src/internal/route-assurance.ts +67 -4
- package/src/internal/route-execution.ts +276 -1
- package/src/manifest.ts +27 -5
- package/src/reflection.ts +24 -4
- package/src/replay.ts +96 -0
- package/src/schema/standard.ts +16 -0
- package/src/server/body.ts +275 -7
- package/src/server/context.ts +23 -3
- package/src/server/contract.ts +43 -3
- package/src/server/cookies.ts +80 -10
- package/src/server/edge.ts +10 -0
- package/src/server/header-case.ts +54 -0
- package/src/server/headers.ts +11 -0
- package/src/server/node-outcome-hook.ts +6 -11
- package/src/server/node-outcome.ts +10 -2
- package/src/server/plugin.ts +87 -8
- package/src/server/proto-guard.ts +118 -0
- package/src/server/registry.ts +6 -2
- package/src/server/request-context.ts +242 -26
- package/src/server/respond.ts +35 -4
- package/src/server/response-contract-lane.ts +6 -17
- package/src/server/response-hooks.ts +51 -0
- package/src/server/runtime-core.ts +14 -0
- package/src/server/server-types.ts +66 -6
- package/src/server/server.ts +672 -287
- package/src/server/static-headers.ts +27 -3
- package/src/server/transport-lane.ts +19 -8
- package/src/server/websocket.ts +18 -2
- package/src/server.ts +7 -0
- package/src/tool-catalog.ts +40 -0
- package/src/tool-contract.ts +896 -0
package/dist/server/body.d.ts
CHANGED
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bounded request-body reading - the single source of truth for nifra's body-size cap. A lying or
|
|
3
3
|
* absent `Content-Length` can't force us to buffer an oversized payload: a declared length over the
|
|
4
|
-
* cap is rejected *before* buffering,
|
|
5
|
-
* running byte count exceeds the cap
|
|
6
|
-
*
|
|
4
|
+
* cap is rejected *before* buffering, a chunked / length-less body is aborted mid-stream once the
|
|
5
|
+
* running byte count exceeds the cap, and the fast path re-checks the real byte count after the
|
|
6
|
+
* read - the declared length is a hint, never the enforcement. Shared by the server's schema path,
|
|
7
|
+
* `c.boundedBody`, and `verifyWebhook` so they all enforce the same guarantee.
|
|
7
8
|
*/
|
|
8
9
|
interface BodySource {
|
|
9
10
|
readonly headers: Pick<Headers, "get">;
|
|
10
11
|
readonly body: ReadableStream<Uint8Array> | null;
|
|
11
12
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
13
|
+
bytes?(): Promise<Uint8Array>;
|
|
12
14
|
}
|
|
15
|
+
/** The pre-cap reader surface a transport-capped request stashes under {@link RAW_BODY_READERS}. */
|
|
16
|
+
export interface RawBodyReaders {
|
|
17
|
+
readonly headers: Pick<Headers, "get">;
|
|
18
|
+
readonly body: ReadableStream<Uint8Array> | null;
|
|
19
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
20
|
+
/** Runtime-native exact-byte reader, when the request implementation exposes one. */
|
|
21
|
+
bytes?(): Promise<Uint8Array>;
|
|
22
|
+
json(): Promise<unknown>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The raw readers of a transport-capped request (see {@link capTransportBodyReads}), or the source
|
|
26
|
+
* itself when it was never capped. Framework readers (the schema lane, `c.boundedBody`,
|
|
27
|
+
* `verifyWebhook`, the idempotency lane) read through this so the transport cap on *direct* user
|
|
28
|
+
* reads never narrows their own caller-supplied cap - `c.boundedBody(explicitBytes)` must keep
|
|
29
|
+
* overriding the route cap upward, exactly as before the transport cap existed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function rawBodySourceOf<T extends BodySource>(req: T): T;
|
|
13
32
|
/** Security/resource limits must be finite byte counts. Invalid values otherwise make `> maxBytes`
|
|
14
33
|
* comparisons fail open (notably for `NaN`) and can re-enable unbounded buffering. */
|
|
15
34
|
export declare function assertByteLimit(value: number, name?: string): void;
|
|
@@ -28,15 +47,39 @@ export declare function drainCapped(body: ReadableStream<Uint8Array>, maxBytes:
|
|
|
28
47
|
* Read a request body as **bytes**, capped at `maxBytes`. Rejects a `Content-Length` over the cap
|
|
29
48
|
* before buffering (`413`) and a malformed `Content-Length` (`400`); a chunked / length-less body
|
|
30
49
|
* falls through to the streaming byte-cap guard. Fast path: a non-chunked request with a
|
|
31
|
-
* `Content-Length` within the cap is read via native `arrayBuffer()
|
|
32
|
-
*
|
|
50
|
+
* `Content-Length` within the cap is read via native `arrayBuffer()`, then the **real** byte count
|
|
51
|
+
* is checked against the declared length - a source that delivers more than it declared (a lying
|
|
52
|
+
* or upstream-decoding adapter) is rejected with `413` even though its header passed the hint.
|
|
33
53
|
*/
|
|
34
|
-
export declare function readBoundedBytes(
|
|
54
|
+
export declare function readBoundedBytes(source: BodySource, maxBytes: number): Promise<{
|
|
35
55
|
ok: true;
|
|
36
56
|
bytes: Uint8Array;
|
|
37
57
|
} | {
|
|
38
58
|
ok: false;
|
|
39
59
|
status: 400 | 413;
|
|
40
60
|
}>;
|
|
61
|
+
/** Key under which a transport-capped request stashes its {@link RawBodyReaders}. */
|
|
62
|
+
export declare const RAW_BODY_READERS: unique symbol;
|
|
63
|
+
/** Mark a request delivered straight from a runtime HTTP parser. */
|
|
64
|
+
export declare function markTrustedBodyFraming(source: object): void;
|
|
65
|
+
/** True only for the runtime-framed ingress mark. */
|
|
66
|
+
export declare function hasTrustedBodyFraming(source: object): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Mark a request source with its route's transport byte cap. Dispatch-time cost is one property
|
|
69
|
+
* write; nothing is shadowed, wrapped, or read here - the hot path stays untouched. The cap is
|
|
70
|
+
* applied by {@link applyTransportCap} only when user code actually reaches for the request
|
|
71
|
+
* (`c.req` / `c.request`), so a route that never direct-reads the body pays nothing.
|
|
72
|
+
*/
|
|
73
|
+
export declare function markTransportCap(source: object, maxBytes: number): void;
|
|
74
|
+
/**
|
|
75
|
+
* Enforce a marked source's transport byte cap on **direct user reads** of its request
|
|
76
|
+
* (`c.req.json()`, `c.req.arrayBuffer()`, the raw `body` stream, `clone()`, ...), without swapping
|
|
77
|
+
* the request's identity and without reading anything eagerly: the direct readers are shadowed with
|
|
78
|
+
* byte-capped versions on the same object, so `WeakMap` keys, symbol stashes (the transport-codec
|
|
79
|
+
* lane's pre-decoded body), and hook-observed references all survive. Framework readers bypass the
|
|
80
|
+
* shadow through {@link rawBodySourceOf} and enforce their own caps - `c.boundedBody(explicit)`
|
|
81
|
+
* still overrides in either direction. An unmarked source (`bodyLimit: "unlimited"`) is untouched.
|
|
82
|
+
*/
|
|
83
|
+
export declare function applyTransportCap(source: object, request: Request): void;
|
|
41
84
|
export {};
|
|
42
85
|
//# sourceMappingURL=body.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../../src/server/body.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../../src/server/body.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,UAAU,UAAU;IAClB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACtC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAA;IAChD,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;IACnC,KAAK,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;CAC9B;AAED,oGAAoG;AACpG,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACtC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAA;IAChD,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;IACnC,qFAAqF;IACrF,KAAK,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;IAC7B,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,UAAU,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAE/D;AAED;sFACsF;AACtF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAa,GAAG,IAAI,CAItE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUpE;AAED;;qDAEqD;AACrD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CA8BvE;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAA;CAAE,CAAC,CA4B7E;AAED,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,eAAkC,CAAA;AAsB/D,oEAAoE;AACpE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE3D;AAED,qDAAqD;AACrD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE7D;AAqKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAaxE"}
|
package/dist/server/body.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bounded request-body reading - the single source of truth for nifra's body-size cap. A lying or
|
|
3
3
|
* absent `Content-Length` can't force us to buffer an oversized payload: a declared length over the
|
|
4
|
-
* cap is rejected *before* buffering,
|
|
5
|
-
* running byte count exceeds the cap
|
|
6
|
-
*
|
|
4
|
+
* cap is rejected *before* buffering, a chunked / length-less body is aborted mid-stream once the
|
|
5
|
+
* running byte count exceeds the cap, and the fast path re-checks the real byte count after the
|
|
6
|
+
* read - the declared length is a hint, never the enforcement. Shared by the server's schema path,
|
|
7
|
+
* `c.boundedBody`, and `verifyWebhook` so they all enforce the same guarantee.
|
|
7
8
|
*/
|
|
9
|
+
import { jsonError } from "./http.js";
|
|
10
|
+
/**
|
|
11
|
+
* The raw readers of a transport-capped request (see {@link capTransportBodyReads}), or the source
|
|
12
|
+
* itself when it was never capped. Framework readers (the schema lane, `c.boundedBody`,
|
|
13
|
+
* `verifyWebhook`, the idempotency lane) read through this so the transport cap on *direct* user
|
|
14
|
+
* reads never narrows their own caller-supplied cap - `c.boundedBody(explicitBytes)` must keep
|
|
15
|
+
* overriding the route cap upward, exactly as before the transport cap existed.
|
|
16
|
+
*/
|
|
17
|
+
export function rawBodySourceOf(req) {
|
|
18
|
+
return req[RAW_BODY_READERS] ?? req;
|
|
19
|
+
}
|
|
8
20
|
/** Security/resource limits must be finite byte counts. Invalid values otherwise make `> maxBytes`
|
|
9
21
|
* comparisons fail open (notably for `NaN`) and can re-enable unbounded buffering. */
|
|
10
22
|
export function assertByteLimit(value, name = "maxBytes") {
|
|
@@ -67,11 +79,13 @@ export async function drainCapped(body, maxBytes) {
|
|
|
67
79
|
* Read a request body as **bytes**, capped at `maxBytes`. Rejects a `Content-Length` over the cap
|
|
68
80
|
* before buffering (`413`) and a malformed `Content-Length` (`400`); a chunked / length-less body
|
|
69
81
|
* falls through to the streaming byte-cap guard. Fast path: a non-chunked request with a
|
|
70
|
-
* `Content-Length` within the cap is read via native `arrayBuffer()
|
|
71
|
-
*
|
|
82
|
+
* `Content-Length` within the cap is read via native `arrayBuffer()`, then the **real** byte count
|
|
83
|
+
* is checked against the declared length - a source that delivers more than it declared (a lying
|
|
84
|
+
* or upstream-decoding adapter) is rejected with `413` even though its header passed the hint.
|
|
72
85
|
*/
|
|
73
|
-
export async function readBoundedBytes(
|
|
86
|
+
export async function readBoundedBytes(source, maxBytes) {
|
|
74
87
|
assertByteLimit(maxBytes);
|
|
88
|
+
const req = rawBodySourceOf(source);
|
|
75
89
|
const declared = req.headers.get("content-length");
|
|
76
90
|
if (declared !== null) {
|
|
77
91
|
// A present Content-Length must be a non-negative integer (HTTP grammar: `1*DIGIT`). A non-numeric
|
|
@@ -83,12 +97,216 @@ export async function readBoundedBytes(req, maxBytes) {
|
|
|
83
97
|
if (length > maxBytes)
|
|
84
98
|
return { ok: false, status: 413 };
|
|
85
99
|
const chunked = req.headers.get("transfer-encoding") !== null;
|
|
86
|
-
if (!chunked)
|
|
87
|
-
|
|
100
|
+
if (!chunked) {
|
|
101
|
+
const bytesReader = req.bytes;
|
|
102
|
+
const bytes = bytesReader === undefined
|
|
103
|
+
? new Uint8Array(await req.arrayBuffer())
|
|
104
|
+
: await bytesReader.call(req);
|
|
105
|
+
// Content-Length was the fast-reject hint, never the enforcement: a lying or buffering
|
|
106
|
+
// source (an adapter that decoded/expanded the body upstream) can hand over more bytes
|
|
107
|
+
// than it declared. `length <= maxBytes` held above, so one comparison seals the cap.
|
|
108
|
+
if (bytes.byteLength > length)
|
|
109
|
+
return { ok: false, status: 413 };
|
|
110
|
+
return { ok: true, bytes };
|
|
111
|
+
}
|
|
88
112
|
}
|
|
89
113
|
const body = req.body;
|
|
90
114
|
if (body === null)
|
|
91
115
|
return { ok: true, bytes: new Uint8Array(0) };
|
|
92
116
|
return drainCapped(body, maxBytes);
|
|
93
117
|
}
|
|
118
|
+
/** Key under which a transport-capped request stashes its {@link RawBodyReaders}. */
|
|
119
|
+
export const RAW_BODY_READERS = Symbol("nifra.body.rawReaders");
|
|
120
|
+
/**
|
|
121
|
+
* Ingress mark for a request whose bytes a runtime HTTP parser already delimited - Bun's compiled
|
|
122
|
+
* native route table, `Deno.serve`, or an edge runtime that hands its own `Request` through
|
|
123
|
+
* untouched. For those the declared Content-Length IS the transport frame, so the JSON lane keeps
|
|
124
|
+
* the runtime's fused decode+parse instead of copying the body out to count it.
|
|
125
|
+
*
|
|
126
|
+
* Everything unmarked - a hand-built `Request`, the in-process client, and above all an adapter
|
|
127
|
+
* that REBUILDS a body from parts an attacker supplied (an event envelope carrying both a header
|
|
128
|
+
* map and a payload) - stays on the delivered-byte check. That is the shape the cap actually
|
|
129
|
+
* defends against: a `content-length` copied from the caller while the body was decoded to a
|
|
130
|
+
* different size.
|
|
131
|
+
*
|
|
132
|
+
* Registered (`Symbol.for`) on purpose, matching `nifra.response.body`: `@nifrajs/deno` mirrors
|
|
133
|
+
* core's types rather than importing them, so an adapter must be able to set this without taking a
|
|
134
|
+
* dependency on core. Forging it needs in-process code execution, which could equally just call
|
|
135
|
+
* `app.fetch` with any `Request` it likes - so the registry costs no ground the process didn't
|
|
136
|
+
* already own.
|
|
137
|
+
*/
|
|
138
|
+
const TRUSTED_FRAMING = Symbol.for("nifra.body.trustedFraming");
|
|
139
|
+
/** Mark a request delivered straight from a runtime HTTP parser. */
|
|
140
|
+
export function markTrustedBodyFraming(source) {
|
|
141
|
+
;
|
|
142
|
+
source[TRUSTED_FRAMING] = true;
|
|
143
|
+
}
|
|
144
|
+
/** True only for the runtime-framed ingress mark. */
|
|
145
|
+
export function hasTrustedBodyFraming(source) {
|
|
146
|
+
return source[TRUSTED_FRAMING] === true;
|
|
147
|
+
}
|
|
148
|
+
/** A capped request/source is capped exactly once; re-entry is a no-op. */
|
|
149
|
+
const TRANSPORT_CAPPED = new WeakSet();
|
|
150
|
+
const CAP_DECODER = new TextDecoder();
|
|
151
|
+
/** Key under which the dispatcher marks a source with its route's transport byte cap. */
|
|
152
|
+
const TRANSPORT_CAP = Symbol("nifra.body.transportCap");
|
|
153
|
+
/** The transport cap's over-cap rejection: the flat error a shadowed direct reader throws (and the
|
|
154
|
+
* capped `body` stream errors with). The lifecycle already treats a thrown `Response` as control
|
|
155
|
+
* flow (the `c.boundedBody` contract), so an over-cap direct read answers 413 like every other cap. */
|
|
156
|
+
function transportCapError(status) {
|
|
157
|
+
return jsonError(status, status === 413 ? "payload_too_large" : "invalid_content_length");
|
|
158
|
+
}
|
|
159
|
+
/** First matching own-or-inherited descriptor, so instance shadows and prototype accessors both resolve. */
|
|
160
|
+
function descriptorOf(target, key) {
|
|
161
|
+
let current = target;
|
|
162
|
+
while (current !== null) {
|
|
163
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
164
|
+
if (descriptor !== undefined)
|
|
165
|
+
return descriptor;
|
|
166
|
+
current = Object.getPrototypeOf(current);
|
|
167
|
+
}
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
/** An `ArrayBuffer` spanning exactly `bytes`, without a copy when the view already owns its buffer. */
|
|
171
|
+
function exactArrayBuffer(bytes) {
|
|
172
|
+
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength
|
|
173
|
+
? bytes.buffer
|
|
174
|
+
: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
175
|
+
}
|
|
176
|
+
/** A pass-through stream that errors (with `reject(413)`) once more than `maxBytes` flow through. */
|
|
177
|
+
function cappedStreamOf(body, maxBytes, reject) {
|
|
178
|
+
let reader;
|
|
179
|
+
let total = 0;
|
|
180
|
+
return new ReadableStream({
|
|
181
|
+
async pull(controller) {
|
|
182
|
+
reader ??= body.getReader();
|
|
183
|
+
const active = reader;
|
|
184
|
+
const { done, value } = await active.read();
|
|
185
|
+
if (done) {
|
|
186
|
+
controller.close();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
total += value.byteLength;
|
|
190
|
+
if (total > maxBytes) {
|
|
191
|
+
await active.cancel().catch(() => { });
|
|
192
|
+
controller.error(reject(413));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
controller.enqueue(value);
|
|
196
|
+
},
|
|
197
|
+
cancel(reason) {
|
|
198
|
+
return reader === undefined ? body.cancel(reason) : reader.cancel(reason);
|
|
199
|
+
},
|
|
200
|
+
}, { highWaterMark: 0 });
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Shadow a request's direct body readers (`arrayBuffer`/`json`/`text`/`blob`/`bytes`/`formData`,
|
|
204
|
+
* the `body` stream, and `clone`) with byte-capped versions, **on the same object** - the request's
|
|
205
|
+
* identity is never swapped, so `WeakMap` keys, symbol stashes (the transport-codec lane's
|
|
206
|
+
* pre-decoded body), and hook-observed references all survive. Nothing is read eagerly: the cap
|
|
207
|
+
* only pays when user code actually reads, and framework readers bypass it through
|
|
208
|
+
* {@link rawBodySourceOf} (stashed under {@link RAW_BODY_READERS}) to enforce their own caps.
|
|
209
|
+
* An over-cap read rejects with `reject(413)` (a lying `Content-Length` with `reject(400)`), which
|
|
210
|
+
* the lifecycle already treats as control flow - the same contract as `c.boundedBody`.
|
|
211
|
+
*/
|
|
212
|
+
function capRequestBodyReads(request, maxBytes) {
|
|
213
|
+
if (TRANSPORT_CAPPED.has(request))
|
|
214
|
+
return;
|
|
215
|
+
TRANSPORT_CAPPED.add(request);
|
|
216
|
+
const reject = transportCapError;
|
|
217
|
+
const bodyDescriptor = descriptorOf(request, "body");
|
|
218
|
+
const raw = {
|
|
219
|
+
get headers() {
|
|
220
|
+
return request.headers;
|
|
221
|
+
},
|
|
222
|
+
get body() {
|
|
223
|
+
return (bodyDescriptor?.get !== undefined
|
|
224
|
+
? bodyDescriptor.get.call(request)
|
|
225
|
+
: (bodyDescriptor?.value ?? null));
|
|
226
|
+
},
|
|
227
|
+
arrayBuffer: request.arrayBuffer.bind(request),
|
|
228
|
+
json: request.json.bind(request),
|
|
229
|
+
};
|
|
230
|
+
if (typeof request.bytes === "function")
|
|
231
|
+
raw.bytes = request.bytes.bind(request);
|
|
232
|
+
// One bounded buffer backs every shadowed reader; a second read replays it instead of failing
|
|
233
|
+
// with "body already used" (a strict superset of the native one-shot contract).
|
|
234
|
+
let buffered;
|
|
235
|
+
const cappedBytes = () => (buffered ??= readBoundedBytes(raw, maxBytes).then((read) => {
|
|
236
|
+
if (!read.ok)
|
|
237
|
+
throw reject(read.status);
|
|
238
|
+
return read.bytes;
|
|
239
|
+
}));
|
|
240
|
+
// The reader methods live on the prototype as writable data properties, so a plain instance
|
|
241
|
+
// assignment shadows them - materially cheaper than defineProperty on this per-read path. Only
|
|
242
|
+
// `body` (a prototype accessor) needs defineProperty below.
|
|
243
|
+
const shadowed = request;
|
|
244
|
+
shadowed[RAW_BODY_READERS] = raw;
|
|
245
|
+
shadowed.arrayBuffer = () => cappedBytes().then(exactArrayBuffer);
|
|
246
|
+
shadowed.bytes = () => cappedBytes().then((bytes) => bytes.slice());
|
|
247
|
+
shadowed.json = () => cappedBytes().then((bytes) => JSON.parse(CAP_DECODER.decode(bytes)));
|
|
248
|
+
shadowed.text = () => cappedBytes().then((bytes) => CAP_DECODER.decode(bytes));
|
|
249
|
+
shadowed.blob = () => cappedBytes().then((bytes) => new Blob([exactArrayBuffer(bytes)], {
|
|
250
|
+
type: request.headers.get("content-type") ?? "",
|
|
251
|
+
}));
|
|
252
|
+
shadowed.formData = () => cappedBytes().then((bytes) => new Response(exactArrayBuffer(bytes), {
|
|
253
|
+
headers: { "content-type": request.headers.get("content-type") ?? "" },
|
|
254
|
+
}).formData());
|
|
255
|
+
// `clone()` must not mint an uncapped escape hatch: the clone gets the same cap.
|
|
256
|
+
if (typeof request.clone === "function") {
|
|
257
|
+
// Node's lib typing returns undici's `Request` here; at runtime it's the same object shape.
|
|
258
|
+
const rawClone = request.clone.bind(request);
|
|
259
|
+
shadowed.clone = () => {
|
|
260
|
+
const clone = rawClone();
|
|
261
|
+
capRequestBodyReads(clone, maxBytes);
|
|
262
|
+
return clone;
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
let cappedBody;
|
|
266
|
+
Object.defineProperty(request, "body", {
|
|
267
|
+
configurable: true,
|
|
268
|
+
get() {
|
|
269
|
+
if (cappedBody === undefined) {
|
|
270
|
+
const rawBody = raw.body;
|
|
271
|
+
cappedBody = rawBody === null ? null : cappedStreamOf(rawBody, maxBytes, reject);
|
|
272
|
+
}
|
|
273
|
+
return cappedBody;
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Mark a request source with its route's transport byte cap. Dispatch-time cost is one property
|
|
279
|
+
* write; nothing is shadowed, wrapped, or read here - the hot path stays untouched. The cap is
|
|
280
|
+
* applied by {@link applyTransportCap} only when user code actually reaches for the request
|
|
281
|
+
* (`c.req` / `c.request`), so a route that never direct-reads the body pays nothing.
|
|
282
|
+
*/
|
|
283
|
+
export function markTransportCap(source, maxBytes) {
|
|
284
|
+
;
|
|
285
|
+
source[TRANSPORT_CAP] = maxBytes;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Enforce a marked source's transport byte cap on **direct user reads** of its request
|
|
289
|
+
* (`c.req.json()`, `c.req.arrayBuffer()`, the raw `body` stream, `clone()`, ...), without swapping
|
|
290
|
+
* the request's identity and without reading anything eagerly: the direct readers are shadowed with
|
|
291
|
+
* byte-capped versions on the same object, so `WeakMap` keys, symbol stashes (the transport-codec
|
|
292
|
+
* lane's pre-decoded body), and hook-observed references all survive. Framework readers bypass the
|
|
293
|
+
* shadow through {@link rawBodySourceOf} and enforce their own caps - `c.boundedBody(explicit)`
|
|
294
|
+
* still overrides in either direction. An unmarked source (`bodyLimit: "unlimited"`) is untouched.
|
|
295
|
+
*/
|
|
296
|
+
export function applyTransportCap(source, request) {
|
|
297
|
+
const maxBytes = source[TRANSPORT_CAP];
|
|
298
|
+
// The capped check comes BEFORE the body-null check: post-cap, `request.body` is the shadowed
|
|
299
|
+
// getter, and re-reading it on every `c.req` access would rebuild capped state for nothing.
|
|
300
|
+
if (maxBytes === undefined || TRANSPORT_CAPPED.has(request) || request.body === null)
|
|
301
|
+
return;
|
|
302
|
+
capRequestBodyReads(request, maxBytes);
|
|
303
|
+
// A wrapper source (Node's lazy source) delegates its own body readers to the request once it
|
|
304
|
+
// materializes - which are now the shadowed ones. Mirror the raw-reader stash onto the source so
|
|
305
|
+
// framework readers (`rawBodySourceOf(source)`) keep reading pre-shadow there too.
|
|
306
|
+
const raw = request[RAW_BODY_READERS];
|
|
307
|
+
if (source !== request && raw !== undefined) {
|
|
308
|
+
;
|
|
309
|
+
source[RAW_BODY_READERS] = raw;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
94
312
|
//# sourceMappingURL=body.js.map
|
package/dist/server/body.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../src/server/body.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../src/server/body.ts"],"names":[],"mappings":"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"}
|
package/dist/server/context.d.ts
CHANGED
|
@@ -48,6 +48,13 @@ type RawParams<Path extends string> = string extends Path ? Record<string, strin
|
|
|
48
48
|
export type Params<Path extends string> = Prettify<RawParams<Path>>;
|
|
49
49
|
/** Per-route input schemas. Each is any Standard Schema (zod/valibot/arktype/…). */
|
|
50
50
|
export interface RouteSchema {
|
|
51
|
+
/** Transport body cap for this route. Defaults to the server's maxBodyBytes. Use a finite
|
|
52
|
+
* smaller/larger limit for a specific endpoint. The 'unlimited' value is an explicit
|
|
53
|
+
* streaming/upload exemption and requires a non-empty bodyLimitReason for auditability. */
|
|
54
|
+
readonly bodyLimit?: number | "unlimited";
|
|
55
|
+
/** Required explanation when bodyLimit is 'unlimited' (for example, a streaming upload is
|
|
56
|
+
* bounded by an upstream object-store protocol). */
|
|
57
|
+
readonly bodyLimitReason?: string;
|
|
51
58
|
/** Declared effect tokens. Reflected for assurance; never read by validation or serialization. */
|
|
52
59
|
readonly capabilities?: readonly string[];
|
|
53
60
|
/**
|
|
@@ -92,6 +99,10 @@ export interface RouteSchema {
|
|
|
92
99
|
* The schema must cover every path param; with the strict default an undeclared path param is rejected.
|
|
93
100
|
* When omitted, `c.params` stays the path-inferred `Record<name, string>`. */
|
|
94
101
|
readonly params?: StandardSchemaV1;
|
|
102
|
+
/** Optional request-header schema. Header names are normalized to lower-case and the validated
|
|
103
|
+
* output is exposed as `c.headers`; use lower-case keys because HTTP header names are
|
|
104
|
+
* case-insensitive. */
|
|
105
|
+
readonly headers?: StandardSchemaV1;
|
|
95
106
|
readonly body?: StandardSchemaV1;
|
|
96
107
|
readonly query?: StandardSchemaV1;
|
|
97
108
|
/** Optional **response contract**. When declared: the handler's return is type-checked against it
|
|
@@ -116,8 +127,8 @@ export interface RouteSchema {
|
|
|
116
127
|
*/
|
|
117
128
|
readonly sse?: StandardSchemaV1;
|
|
118
129
|
/**
|
|
119
|
-
* Hook fired when the request fails `body`/`query` validation, before the handler
|
|
120
|
-
* which input failed
|
|
130
|
+
* Hook fired when the request fails `headers`/`params`/`body`/`query` validation, before the handler
|
|
131
|
+
* runs. `kind` says which input failed. Its return value selects one of three outcomes (may be async):
|
|
121
132
|
* - a **`Response`** → returned as-is, short-circuiting the route (custom error envelope, redirect, …).
|
|
122
133
|
* - **any other value** → treated as a repaired payload and **re-validated once** against the same
|
|
123
134
|
* schema. If it now passes, the handler runs with it; if it still fails, the original `422` stands.
|
|
@@ -128,7 +139,7 @@ export interface RouteSchema {
|
|
|
128
139
|
* error envelope, coercing the input, or prompting an LLM to repair the payload (re-validated, so it can't
|
|
129
140
|
* bypass the schema).
|
|
130
141
|
*/
|
|
131
|
-
readonly onValidationError?: (issues: ReadonlyArray<StandardIssue>, ctx: Context, kind: "body" | "query" | "params") => Response | unknown | Promise<Response | unknown>;
|
|
142
|
+
readonly onValidationError?: (issues: ReadonlyArray<StandardIssue>, ctx: Context, kind: "body" | "query" | "params" | "headers") => Response | unknown | Promise<Response | unknown>;
|
|
132
143
|
}
|
|
133
144
|
/** The validated body type, or `undefined` when no body schema is declared. */
|
|
134
145
|
type BodyOf<S extends RouteSchema> = S extends {
|
|
@@ -138,6 +149,10 @@ type BodyOf<S extends RouteSchema> = S extends {
|
|
|
138
149
|
type QueryOf<S extends RouteSchema> = S extends {
|
|
139
150
|
query: infer Q extends StandardSchemaV1;
|
|
140
151
|
} ? InferOutput<Q> : URLSearchParams;
|
|
152
|
+
/** The validated request-header type when a header schema is declared, else a lower-case record. */
|
|
153
|
+
type HeadersOf<S extends RouteSchema> = S extends {
|
|
154
|
+
headers: infer H extends StandardSchemaV1;
|
|
155
|
+
} ? InferOutput<H> : Readonly<Record<string, string>>;
|
|
141
156
|
/** The validated params type when a params schema is declared, else the path-inferred `Params<Path>`. */
|
|
142
157
|
type ParamsOf<S extends RouteSchema, Path extends string> = S extends {
|
|
143
158
|
params: infer P extends StandardSchemaV1;
|
|
@@ -190,6 +205,8 @@ export interface Context<Path extends string = string, S extends RouteSchema = R
|
|
|
190
205
|
/** Read one request header without materializing a Web `Request` on the Node adapter hot path. */
|
|
191
206
|
readonly header: (name: string) => string | null;
|
|
192
207
|
readonly params: ParamsOf<S, Path>;
|
|
208
|
+
/** Lower-case request headers, validated/coerced when `schema.headers` is declared. */
|
|
209
|
+
readonly headers: HeadersOf<S>;
|
|
193
210
|
readonly query: QueryOf<S>;
|
|
194
211
|
readonly body: BodyOf<S>;
|
|
195
212
|
/** The request's cookies, parsed from the `Cookie` header (values URL-decoded). Parsed lazily on
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACzF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAA;IAChC,sDAAsD;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,oGAAoG;IACpG,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EACd,MAAM,GACN,CAAC,CACC,OAAO,EAAE;QACP,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;QACpB,QAAQ,CAAC,OAAO,EAAE;YAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;SAAE,CAAA;KACvD,EACD,QAAQ,EAAE,QAAQ,GAAG,SAAS,KAC3B,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;CACnC;AAED,8EAA8E;AAC9E,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAA;AAEvD;;;;;GAKG;AACH,KAAK,SAAS,CAAC,IAAI,SAAS,MAAM,IAAI,MAAM,SAAS,IAAI,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,IAAI,SAAS,GAAG,MAAM,MAAM,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GACzD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,GAC7C,IAAI,SAAS,GAAG,MAAM,MAAM,IAAI,MAAM,KAAK,EAAE,GAC3C,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GACrB,IAAI,SAAS,GAAG,MAAM,MAAM,IAAI,MAAM,IAAI,EAAE,GAC1C,MAAM,CAAC,IAAI,SAAS,EAAE,GAAG,GAAG,GAAG,IAAI,EAAE,MAAM,CAAC,GAC5C,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;AAE/B,MAAM,MAAM,MAAM,CAAC,IAAI,SAAS,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;AAEnE,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC1B,kGAAkG;IAClG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAA;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAA;IAC5C;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;;;;kFAI8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAA;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAA;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACjC;;;;0EAIsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAA;IACpC;;;;;oGAKgG;IAChG,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAA;IAC5D;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAA;IAC/B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAC3B,MAAM,EAAE,aAAa,CAAC,aAAa,CAAC,EACpC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACzF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAA;IAChC,sDAAsD;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,oGAAoG;IACpG,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EACd,MAAM,GACN,CAAC,CACC,OAAO,EAAE;QACP,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;QACpB,QAAQ,CAAC,OAAO,EAAE;YAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;SAAE,CAAA;KACvD,EACD,QAAQ,EAAE,QAAQ,GAAG,SAAS,KAC3B,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;CACnC;AAED,8EAA8E;AAC9E,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAA;AAEvD;;;;;GAKG;AACH,KAAK,SAAS,CAAC,IAAI,SAAS,MAAM,IAAI,MAAM,SAAS,IAAI,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,IAAI,SAAS,GAAG,MAAM,MAAM,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GACzD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,GAC7C,IAAI,SAAS,GAAG,MAAM,MAAM,IAAI,MAAM,KAAK,EAAE,GAC3C,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GACrB,IAAI,SAAS,GAAG,MAAM,MAAM,IAAI,MAAM,IAAI,EAAE,GAC1C,MAAM,CAAC,IAAI,SAAS,EAAE,GAAG,GAAG,GAAG,IAAI,EAAE,MAAM,CAAC,GAC5C,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;AAE/B,MAAM,MAAM,MAAM,CAAC,IAAI,SAAS,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;AAEnE,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC1B;;+FAE2F;IAC3F,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IACzC;wDACoD;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,kGAAkG;IAClG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAA;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAA;IAC5C;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;;;;kFAI8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAA;IAClC;;2BAEuB;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAA;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACjC;;;;0EAIsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAA;IACpC;;;;;oGAKgG;IAChG,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAA;IAC5D;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAA;IAC/B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAC3B,MAAM,EAAE,aAAa,CAAC,aAAa,CAAC,EACpC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,KAC1C,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAA;CACtD;AAED,+EAA+E;AAC/E,KAAK,MAAM,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAAE,GACrF,WAAW,CAAC,CAAC,CAAC,GACd,SAAS,CAAA;AAEb,2FAA2F;AAC3F,KAAK,OAAO,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAAE,GACvF,WAAW,CAAC,CAAC,CAAC,GACd,eAAe,CAAA;AAEnB,oGAAoG;AACpG,KAAK,SAAS,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IAChD,OAAO,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAC1C,GACG,WAAW,CAAC,CAAC,CAAC,GACd,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAEpC,yGAAyG;AACzG,KAAK,QAAQ,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,SAAS,MAAM,IAAI,CAAC,SAAS;IACpE,MAAM,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CACzC,GACG,WAAW,CAAC,CAAC,CAAC,GACd,MAAM,CAAC,IAAI,CAAC,CAAA;AAEhB,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAClE;gDAC4C;IAC5C,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAA;CACnF;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,GAAG,GAAG,OAAO;IACrC;qEACiE;IACjE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAA;IAClB,oFAAoF;IACpF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IACxD;;yEAEqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,OAAO,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,SAAS,WAAW,GAAG,WAAW;IACxF,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;IACrB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,kGAAkG;IAClG,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IAChD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;IAClC,uFAAuF;IACvF,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACxB;wGACoG;IACpG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAClD,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAA;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;IACrB;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IACvD;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IAChE;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IACpE;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,MAAM,KAAK,QAAQ,CAAA;IACxE,0GAA0G;IAC1G,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,MAAM,KAAK,QAAQ,CAAA;CACxE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DataClassification } from "../classification.js";
|
|
2
2
|
import { type Method } from "../router/router.js";
|
|
3
|
-
import type { InferOutput, StandardSchemaV1 } from "../schema/standard.js";
|
|
3
|
+
import type { InferInput, InferOutput, StandardSchemaV1 } from "../schema/standard.js";
|
|
4
4
|
import type { Context, IdempotencyConfig, Params, RouteSchema } from "./context.js";
|
|
5
5
|
import type { EmptyRegistry, OutputOf, Registry } from "./registry.js";
|
|
6
6
|
import { Server } from "./server.js";
|
|
@@ -24,6 +24,8 @@ export interface OperationDef {
|
|
|
24
24
|
readonly path: string;
|
|
25
25
|
/** Optional path-params schema (validated + coercible at the boundary; see {@link RouteSchema.params}). */
|
|
26
26
|
readonly params?: StandardSchemaV1;
|
|
27
|
+
/** Optional request-header schema (header names are normalized to lower-case). */
|
|
28
|
+
readonly headers?: StandardSchemaV1;
|
|
27
29
|
readonly body?: StandardSchemaV1;
|
|
28
30
|
readonly query?: StandardSchemaV1;
|
|
29
31
|
readonly response?: StandardSchemaV1;
|
|
@@ -31,6 +33,9 @@ export interface OperationDef {
|
|
|
31
33
|
readonly capabilities?: readonly string[];
|
|
32
34
|
/** Dedupe retries of this operation on an `Idempotency-Key` header (see {@link RouteSchema.idempotency}). */
|
|
33
35
|
readonly idempotency?: IdempotencyConfig;
|
|
36
|
+
/** Per-route transport body cap; see RouteSchema.bodyLimit. */
|
|
37
|
+
readonly bodyLimit?: number | "unlimited";
|
|
38
|
+
readonly bodyLimitReason?: string;
|
|
34
39
|
/** Highest data-sensitivity the response carries (see {@link RouteSchema.classification}). */
|
|
35
40
|
readonly classification?: DataClassification;
|
|
36
41
|
/** Inline route-assurance evidence this operation carries (see {@link RouteSchema.assurance}). */
|
|
@@ -56,12 +61,15 @@ export interface OperationDef {
|
|
|
56
61
|
}
|
|
57
62
|
/** A contract: named operations. Names are the handler keys and OpenAPI operationIds. */
|
|
58
63
|
export type ContractShape = Record<string, OperationDef>;
|
|
64
|
+
/** Client-visible request inputs use the schema's INPUT side (what a caller sends pre-validation,
|
|
65
|
+
* so a defaulted field stays optional on the wire) - mirroring the inline registry's
|
|
66
|
+
* `RegistryBody`/`RegistryQuery`. Handler-facing types (ContextForOp) still use the output side. */
|
|
59
67
|
type OpBody<O extends OperationDef> = O extends {
|
|
60
68
|
body: infer B extends StandardSchemaV1;
|
|
61
|
-
} ?
|
|
69
|
+
} ? InferInput<B> : never;
|
|
62
70
|
type OpQuery<O extends OperationDef> = O extends {
|
|
63
71
|
query: infer Q extends StandardSchemaV1;
|
|
64
|
-
} ?
|
|
72
|
+
} ? InferInput<Q> : never;
|
|
65
73
|
type OpResponse<O extends OperationDef> = O extends {
|
|
66
74
|
response: infer R extends StandardSchemaV1;
|
|
67
75
|
} ? InferOutput<R> : unknown;
|
|
@@ -106,6 +114,10 @@ type SchemaForOp<O extends OperationDef> = (O extends {
|
|
|
106
114
|
query: infer Q extends StandardSchemaV1;
|
|
107
115
|
} ? {
|
|
108
116
|
query: Q;
|
|
117
|
+
} : Record<never, never>) & (O extends {
|
|
118
|
+
headers: infer H extends StandardSchemaV1;
|
|
119
|
+
} ? {
|
|
120
|
+
headers: H;
|
|
109
121
|
} : Record<never, never>) & (O extends {
|
|
110
122
|
params: infer P extends StandardSchemaV1;
|
|
111
123
|
} ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/server/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAG9D,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/server/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/server/contract.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RouteConfigError } from "../errors.js";
|
|
2
2
|
import { normalizeRouteCapabilities } from "../internal/capability-runtime.js";
|
|
3
3
|
import { METHODS } from "../router/router.js";
|
|
4
|
+
import { assertByteLimit } from "./body.js";
|
|
4
5
|
import { Server } from "./server.js";
|
|
5
6
|
const METHOD_SET = new Set(METHODS);
|
|
6
7
|
/**
|
|
@@ -21,6 +22,22 @@ export function defineContract(contract) {
|
|
|
21
22
|
if (op.path.length === 0 || op.path.charCodeAt(0) !== 47 /* "/" */) {
|
|
22
23
|
throw new RouteConfigError("INVALID_PATH", `operation "${name}": path must start with "/": "${op.path}"`);
|
|
23
24
|
}
|
|
25
|
+
if (op.bodyLimitReason !== undefined && op.bodyLimit !== "unlimited") {
|
|
26
|
+
throw new RouteConfigError("INVALID_BODY_LIMIT", `operation "${name}": bodyLimitReason is only valid with bodyLimit: "unlimited"`);
|
|
27
|
+
}
|
|
28
|
+
if (op.bodyLimit === "unlimited") {
|
|
29
|
+
if (typeof op.bodyLimitReason !== "string" || op.bodyLimitReason.trim().length === 0) {
|
|
30
|
+
throw new RouteConfigError("INVALID_BODY_LIMIT", `operation "${name}": bodyLimit: "unlimited" requires a non-empty bodyLimitReason`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (op.bodyLimit !== undefined) {
|
|
34
|
+
try {
|
|
35
|
+
assertByteLimit(op.bodyLimit, "operation bodyLimit");
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new RouteConfigError("INVALID_BODY_LIMIT", `operation "${name}": bodyLimit must be a non-negative safe integer or "unlimited"`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
24
41
|
const key = `${method} ${op.path}`;
|
|
25
42
|
if (seen.has(key)) {
|
|
26
43
|
throw new RouteConfigError("DUPLICATE_ROUTE", `duplicate operation route: ${key}`);
|
|
@@ -59,21 +76,26 @@ export function implement(contract, handlers, app) {
|
|
|
59
76
|
// once here at bind time, not per request - it just carries a reference to the op's existing schema.
|
|
60
77
|
// A response-less op still yields `undefined`, byte-identical to before.
|
|
61
78
|
const schema = op.params !== undefined ||
|
|
79
|
+
op.headers !== undefined ||
|
|
62
80
|
op.body !== undefined ||
|
|
63
81
|
op.query !== undefined ||
|
|
64
82
|
op.response !== undefined ||
|
|
65
83
|
op.capabilities !== undefined ||
|
|
66
84
|
op.idempotency !== undefined ||
|
|
85
|
+
op.bodyLimit !== undefined ||
|
|
67
86
|
op.classification !== undefined ||
|
|
68
87
|
op.assurance !== undefined ||
|
|
69
88
|
op.family !== undefined
|
|
70
89
|
? {
|
|
71
90
|
...(op.params !== undefined ? { params: op.params } : {}),
|
|
91
|
+
...(op.headers !== undefined ? { headers: op.headers } : {}),
|
|
72
92
|
...(op.body !== undefined ? { body: op.body } : {}),
|
|
73
93
|
...(op.query !== undefined ? { query: op.query } : {}),
|
|
74
94
|
...(op.response !== undefined ? { response: op.response } : {}),
|
|
75
95
|
...(op.capabilities !== undefined ? { capabilities: op.capabilities } : {}),
|
|
76
96
|
...(op.idempotency !== undefined ? { idempotency: op.idempotency } : {}),
|
|
97
|
+
...(op.bodyLimit !== undefined ? { bodyLimit: op.bodyLimit } : {}),
|
|
98
|
+
...(op.bodyLimitReason !== undefined ? { bodyLimitReason: op.bodyLimitReason } : {}),
|
|
77
99
|
...(op.classification !== undefined ? { classification: op.classification } : {}),
|
|
78
100
|
...(op.assurance !== undefined ? { assurance: op.assurance } : {}),
|
|
79
101
|
...(op.family !== undefined ? { family: op.family } : {}),
|