@nifrajs/core 2.10.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +424 -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 +667 -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/plugin.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* `use`/`derive`/`decorate` and/or registering routes) and returns it; these helpers attach a name
|
|
4
4
|
* for idempotent dedupe and pin the type-threading so `.use()` preserves the caller's typed server.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { Registry } from "./registry.js";
|
|
7
|
+
import type { AnyServer, Server } from "./server.js";
|
|
7
8
|
/**
|
|
8
9
|
* A nifra **plugin**: a function that augments an app - calling `use`/`derive`/`decorate` and/or
|
|
9
10
|
* registering routes - and returns it. Because `derive`/`decorate` are type-threaded, an **inline**
|
|
@@ -23,22 +24,79 @@ export type NifraPlugin<In extends AnyServer = AnyServer, Out extends AnyServer
|
|
|
23
24
|
export type IdentityPlugin = (<S extends AnyServer>(app: S) => S) & {
|
|
24
25
|
readonly pluginName?: string;
|
|
25
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* A named plugin built with {@link defineContextPlugin}: it adds the context `D` to every handler
|
|
29
|
+
* downstream while threading the caller's route registry and existing context UNCHANGED. This is the
|
|
30
|
+
* type {@link definePlugin} cannot express - `definePlugin` infers its parameter from an untyped
|
|
31
|
+
* `(app) => ...` arrow, which lands on `AnyServer`, so `use` returns `Server<any, any>`.
|
|
32
|
+
*/
|
|
33
|
+
export type ContextPlugin<D extends object> = (<R extends Registry, Ctx>(app: Server<R, Ctx>) => Server<R, Ctx & D>) & {
|
|
34
|
+
readonly pluginName?: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Define a named plugin that **adds typed context and nothing else** - the `derive` case - without
|
|
38
|
+
* collapsing the caller's types. `use` instantiates the generic signature against the concrete
|
|
39
|
+
* receiver, so `R` and `Ctx` come back unchanged with `D` intersected onto the context.
|
|
40
|
+
*
|
|
41
|
+
* Use this instead of {@link definePlugin} for every `(app) => app.derive(...)` plugin. `definePlugin`
|
|
42
|
+
* is only safe when the caller supplies `In`/`Out` explicitly; with the usual untyped arrow both
|
|
43
|
+
* default to `AnyServer` and the typed client silently collapses to `any`.
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* export const requestId = defineContextPlugin<{ requestId: string }>("requestId", (app) =>
|
|
47
|
+
* app.derive(() => ({ requestId: crypto.randomUUID() })),
|
|
48
|
+
* )
|
|
49
|
+
* const api = server().get("/a", h).use(requestId).get("/b", h) // /a AND /b stay typed; c.requestId typed
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function defineContextPlugin<D extends object>(name: string, apply: <R extends Registry, Ctx>(app: Server<R, Ctx>) => Server<R, Ctx & D>): ContextPlugin<D>;
|
|
53
|
+
declare const COLLAPSED: unique symbol;
|
|
54
|
+
/**
|
|
55
|
+
* What {@link definePlugin} returns when its `apply` argument never pinned the input server type -
|
|
56
|
+
* the `definePlugin("x", (app) => ...)` arrow, where `app` falls back to `AnyServer`.
|
|
57
|
+
*
|
|
58
|
+
* It is deliberately **not callable**, so `app.use(thatPlugin)` is a type error at the call site
|
|
59
|
+
* instead of a silent widening to `Server<any, any>` that surfaces hundreds of lines away as
|
|
60
|
+
* `Property 'x' does not exist on type 'Promise<Result<unknown, unknown>>'` in the typed client.
|
|
61
|
+
*
|
|
62
|
+
* The plugin still works at runtime. To fix the type, pick the definer that matches what it does:
|
|
63
|
+
*
|
|
64
|
+
* - adds context via `derive`/`decorate` -> {@link defineContextPlugin}
|
|
65
|
+
* - mounts routes/hooks and adds NO context -> {@link defineRouterPlugin} ({@link defineIdentityPlugin})
|
|
66
|
+
* - genuinely needs `definePlugin`: annotate the parameter (`(app: typeof api) => ...`) or pass
|
|
67
|
+
* explicit type arguments (`definePlugin<typeof api, typeof api>(...)`).
|
|
68
|
+
*/
|
|
69
|
+
export interface PluginTypeCollapsed {
|
|
70
|
+
readonly [COLLAPSED]: "definePlugin erased the caller's server type: use defineContextPlugin (adds context) or defineRouterPlugin (routes/hooks only), or pin the input type";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* True when `S`'s route registry is `any` - i.e. nothing pinned the plugin's input server type, so
|
|
74
|
+
* every route type flowing through it is about to be widened away.
|
|
75
|
+
*/
|
|
76
|
+
type ServerTypeUnpinned<S> = [S] extends [Server<infer R, any>] ? 0 extends 1 & R ? true : false : false;
|
|
77
|
+
export type DefinePluginResult<In extends AnyServer, Out extends AnyServer> = ServerTypeUnpinned<In> extends true ? PluginTypeCollapsed : NifraPlugin<In, Out>;
|
|
26
78
|
/**
|
|
27
79
|
* Name + ergonomics for a plugin that **adds typed context** (`derive`/`decorate`). `app.use(myPlugin)`
|
|
28
80
|
* applies it once; a second `use` of the same name is skipped (idempotent), so plugins can depend on each
|
|
29
81
|
* other without double-registering hooks.
|
|
30
82
|
*
|
|
31
83
|
* ```ts
|
|
32
|
-
* export const requestId = definePlugin("requestId", (app
|
|
84
|
+
* export const requestId = definePlugin("requestId", (app: typeof api) =>
|
|
85
|
+
* app.derive(() => ({ requestId: uuid() })),
|
|
86
|
+
* )
|
|
33
87
|
* app.use(requestId) // downstream handlers see c.requestId
|
|
34
88
|
* ```
|
|
35
89
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
90
|
+
* This helper only threads types when the caller pins the input server type - by annotating the
|
|
91
|
+
* parameter as above, or with explicit type arguments. With a bare `(app) => ...` arrow `app` infers as
|
|
92
|
+
* `AnyServer`, which would widen the caller's whole route registry to `any`; that case now returns
|
|
93
|
+
* {@link PluginTypeCollapsed}, so `.use()` fails to compile with one error at the call site rather than
|
|
94
|
+
* producing a typed client full of `any`. Prefer a definer that cannot collapse at all:
|
|
95
|
+
*
|
|
96
|
+
* - adds context via `derive`/`decorate` -> {@link defineContextPlugin}
|
|
97
|
+
* - mounts routes/hooks and adds NO context -> {@link defineRouterPlugin} ({@link defineIdentityPlugin})
|
|
40
98
|
*/
|
|
41
|
-
export declare function definePlugin<In extends AnyServer, Out extends AnyServer>(name: string, apply: (app: In) => Out):
|
|
99
|
+
export declare function definePlugin<In extends AnyServer, Out extends AnyServer>(name: string, apply: (app: In) => Out): DefinePluginResult<In, Out>;
|
|
42
100
|
/**
|
|
43
101
|
* Define a type-**identity** plugin: it registers routes/hooks as a side effect but returns the app with
|
|
44
102
|
* its `Registry` + `Context` UNCHANGED. Use this (not {@link definePlugin}) for any plugin that doesn't
|
|
@@ -75,4 +133,5 @@ export declare function defineIdentityPlugin(name: string, apply: <S extends Any
|
|
|
75
133
|
* ```
|
|
76
134
|
*/
|
|
77
135
|
export declare const defineRouterPlugin: typeof defineIdentityPlugin;
|
|
136
|
+
export {};
|
|
78
137
|
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/server/plugin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/server/plugin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CAAC,EAAE,SAAS,SAAS,GAAG,SAAS,EAAE,GAAG,SAAS,SAAS,GAAG,EAAE,IAAI,CAAC,CACvF,GAAG,EAAE,EAAE,KACJ,GAAG,CAAC,GAAG;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5C;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG;IAClE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS,QAAQ,EAAE,GAAG,EACrE,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,KAChB,MAAM,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAClD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,CAAC,SAAS,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAC1E,aAAa,CAAC,CAAC,CAAC,CAElB;AAED,OAAO,CAAC,MAAM,SAAS,EAAE,OAAO,MAAM,CAAA;AAEtC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,CAAC,SAAS,CAAC,EAAE,uJAAuJ,CAAA;CAC9K;AAED;;;GAGG;AAEH,KAAK,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,GAC3D,CAAC,SAAS,CAAC,GAAG,CAAC,GACb,IAAI,GACJ,KAAK,GACP,KAAK,CAAA;AAET,MAAM,MAAM,kBAAkB,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,SAAS,IACxE,kBAAkB,CAAC,EAAE,CAAC,SAAS,IAAI,GAAG,mBAAmB,GAAG,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;AAElF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,SAAS,EACtE,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,KAAK,GAAG,GACtB,kBAAkB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;AAQ9B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GACxC,cAAc,CAEhB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,EAAE,OAAO,oBAA2C,CAAA"}
|
package/dist/server/plugin.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Define a named plugin that **adds typed context and nothing else** - the `derive` case - without
|
|
3
|
+
* collapsing the caller's types. `use` instantiates the generic signature against the concrete
|
|
4
|
+
* receiver, so `R` and `Ctx` come back unchanged with `D` intersected onto the context.
|
|
5
|
+
*
|
|
6
|
+
* Use this instead of {@link definePlugin} for every `(app) => app.derive(...)` plugin. `definePlugin`
|
|
7
|
+
* is only safe when the caller supplies `In`/`Out` explicitly; with the usual untyped arrow both
|
|
8
|
+
* default to `AnyServer` and the typed client silently collapses to `any`.
|
|
5
9
|
*
|
|
6
10
|
* ```ts
|
|
7
|
-
* export const requestId =
|
|
8
|
-
*
|
|
11
|
+
* export const requestId = defineContextPlugin<{ requestId: string }>("requestId", (app) =>
|
|
12
|
+
* app.derive(() => ({ requestId: crypto.randomUUID() })),
|
|
13
|
+
* )
|
|
14
|
+
* const api = server().get("/a", h).use(requestId).get("/b", h) // /a AND /b stay typed; c.requestId typed
|
|
9
15
|
* ```
|
|
10
|
-
*
|
|
11
|
-
* FOOTGUN: only use this for a plugin that adds context. For a plugin that **mounts routes/hooks but adds
|
|
12
|
-
* NO context** (an auth router, an audit logger), use {@link defineRouterPlugin} ({@link defineIdentityPlugin}).
|
|
13
|
-
* `definePlugin((app) => app.get(...))` infers `app: Server<any, any>`, so `.use()` returns `Server<any, any>`
|
|
14
|
-
* and your whole typed client silently collapses to `any` - no type error, no runtime error.
|
|
15
16
|
*/
|
|
17
|
+
export function defineContextPlugin(name, apply) {
|
|
18
|
+
return Object.assign(apply, { pluginName: name });
|
|
19
|
+
}
|
|
16
20
|
export function definePlugin(name, apply) {
|
|
17
21
|
return Object.assign(apply, { pluginName: name });
|
|
18
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/server/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/server/plugin.ts"],"names":[],"mappings":"AAuCA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,KAA2E;IAE3E,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAqB,CAAA;AACvE,CAAC;AA8DD,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,KAAoC;IAEpC,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;AACnD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,KAAyC;IAEzC,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAmB,CAAA;AACrE,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAgC,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prototype-poisoning guard for the JSON body lane - the check behind `c.boundedJson` and the
|
|
3
|
+
* schema path. A single walk of the parsed value, never a reviver (a reviver taxes every key of
|
|
4
|
+
* every parse, including the parses that carry no object at all).
|
|
5
|
+
*
|
|
6
|
+
* No raw-text pre-scan: the Fastify-style `text.includes('"__proto__"')` filter is a pessimization
|
|
7
|
+
* on 2026 engines. A quoted-name search runs a general substring algorithm over the whole body,
|
|
8
|
+
* while the walk only touches the nodes the parse already built. Measured on Node 26 and Bun 1.3,
|
|
9
|
+
* three `includes()` calls cost 2-4x the walk on every body shape an API actually receives
|
|
10
|
+
* (records, strings, nested objects); the scan only wins on a body that is mostly a flat array of
|
|
11
|
+
* numbers, where it saves ~1.5us on a 9KB payload that spends 20us in `JSON.parse` regardless.
|
|
12
|
+
* Dropping the tier also removes an escape-analysis obligation - the scan had to route every `\u`
|
|
13
|
+
* to the walk, because `_` spells `_`.
|
|
14
|
+
*/
|
|
15
|
+
export type ProtoPoisoning = "reject" | "strip" | "ignore";
|
|
16
|
+
/**
|
|
17
|
+
* `JSON.parse` + the poisoning policy. Returns the parsed value (stripped in place under
|
|
18
|
+
* `"strip"`); throws on invalid JSON or - via the same catch path - on a rejected poisoning, so
|
|
19
|
+
* a poisoned payload is indistinguishable from malformed JSON to the caller.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseJsonGuarded(text: string, policy: ProtoPoisoning): unknown;
|
|
22
|
+
/**
|
|
23
|
+
* The policy applied to an already-parsed value - the native-`json()` lane, which never holds the
|
|
24
|
+
* raw text, and the transport codecs, which parse with their own decoder. Escapes are resolved by
|
|
25
|
+
* the time a key is an own property, so a `\u`-spelled `__proto__` and a literal one look identical
|
|
26
|
+
* here. Cost is one iterative pass over the value's object nodes; `"ignore"` skips even that.
|
|
27
|
+
* Throws the reject singleton; callers map it to their lane's flat error.
|
|
28
|
+
*/
|
|
29
|
+
export declare function guardParsedValue(value: unknown, policy: ProtoPoisoning): unknown;
|
|
30
|
+
//# sourceMappingURL=proto-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proto-guard.d.ts","sourceRoot":"","sources":["../../src/server/proto-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;AAK1D;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAGhF"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prototype-poisoning guard for the JSON body lane - the check behind `c.boundedJson` and the
|
|
3
|
+
* schema path. A single walk of the parsed value, never a reviver (a reviver taxes every key of
|
|
4
|
+
* every parse, including the parses that carry no object at all).
|
|
5
|
+
*
|
|
6
|
+
* No raw-text pre-scan: the Fastify-style `text.includes('"__proto__"')` filter is a pessimization
|
|
7
|
+
* on 2026 engines. A quoted-name search runs a general substring algorithm over the whole body,
|
|
8
|
+
* while the walk only touches the nodes the parse already built. Measured on Node 26 and Bun 1.3,
|
|
9
|
+
* three `includes()` calls cost 2-4x the walk on every body shape an API actually receives
|
|
10
|
+
* (records, strings, nested objects); the scan only wins on a body that is mostly a flat array of
|
|
11
|
+
* numbers, where it saves ~1.5us on a 9KB payload that spends 20us in `JSON.parse` regardless.
|
|
12
|
+
* Dropping the tier also removes an escape-analysis obligation - the scan had to route every `\u`
|
|
13
|
+
* to the walk, because `_` spells `_`.
|
|
14
|
+
*/
|
|
15
|
+
/** Thrown (as a reused singleton - never surfaced, always mapped to a flat 400) on `"reject"`. */
|
|
16
|
+
const POISONED = new Error("json_proto_poisoning");
|
|
17
|
+
/**
|
|
18
|
+
* `JSON.parse` + the poisoning policy. Returns the parsed value (stripped in place under
|
|
19
|
+
* `"strip"`); throws on invalid JSON or - via the same catch path - on a rejected poisoning, so
|
|
20
|
+
* a poisoned payload is indistinguishable from malformed JSON to the caller.
|
|
21
|
+
*/
|
|
22
|
+
export function parseJsonGuarded(text, policy) {
|
|
23
|
+
return guardParsedValue(JSON.parse(text), policy);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The policy applied to an already-parsed value - the native-`json()` lane, which never holds the
|
|
27
|
+
* raw text, and the transport codecs, which parse with their own decoder. Escapes are resolved by
|
|
28
|
+
* the time a key is an own property, so a `\u`-spelled `__proto__` and a literal one look identical
|
|
29
|
+
* here. Cost is one iterative pass over the value's object nodes; `"ignore"` skips even that.
|
|
30
|
+
* Throws the reject singleton; callers map it to their lane's flat error.
|
|
31
|
+
*/
|
|
32
|
+
export function guardParsedValue(value, policy) {
|
|
33
|
+
if (policy === "ignore")
|
|
34
|
+
return value;
|
|
35
|
+
return sweep(value, policy);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Iterative deep-walk (adversarial nesting must not blow the call stack). An own key
|
|
39
|
+
* `__proto__`, or an own `constructor` whose value carries an own `prototype`, is the poisoning
|
|
40
|
+
* shape - `JSON.parse` creates these as plain data properties, and the blast radius is whatever
|
|
41
|
+
* downstream merge/assign later copies them onto a real prototype. A string *value* of
|
|
42
|
+
* `"__proto__"` is legal data and never triggers.
|
|
43
|
+
*/
|
|
44
|
+
/** Reused walk stack - `sweep` is synchronous and single-threaded, and a thrown rejection can
|
|
45
|
+
* leave residue, so each entry clears it. Saves one array allocation per swept request. */
|
|
46
|
+
const STACK = [];
|
|
47
|
+
function sweep(root, policy) {
|
|
48
|
+
if (root === null || typeof root !== "object")
|
|
49
|
+
return root;
|
|
50
|
+
const stack = STACK;
|
|
51
|
+
stack.length = 0;
|
|
52
|
+
stack.push(root);
|
|
53
|
+
while (stack.length > 0) {
|
|
54
|
+
const node = stack.pop();
|
|
55
|
+
if (Array.isArray(node)) {
|
|
56
|
+
// Only objects are stacked, here and below: a scalar carries no keys, so pushing it just to
|
|
57
|
+
// pop and type-test it is pure stack traffic. Filtering at the push site measured 1.5-3x
|
|
58
|
+
// faster on every body shape, both engines - the win grows with how scalar-heavy the body is.
|
|
59
|
+
//
|
|
60
|
+
// Indexed, not `for...of`: JSC allocates an array iterator per loop and calls `next()` per
|
|
61
|
+
// element, and it does not escape either. Measured on Bun 1.3 the iterator form costs 4x on
|
|
62
|
+
// an array of records, 8x on strings, and 12x on numbers - a 9KB numeric body walked in 27us
|
|
63
|
+
// instead of 2.2us, which at the 1MB default cap is milliseconds of CPU an attacker picks.
|
|
64
|
+
// V8 optimizes the iterator away, so on Node the two forms measure identical; the indexed
|
|
65
|
+
// loop is simply the form that is fast on both.
|
|
66
|
+
for (let i = 0; i < node.length; i++) {
|
|
67
|
+
const item = node[i];
|
|
68
|
+
if (item !== null && typeof item === "object")
|
|
69
|
+
stack.push(item);
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const record = node;
|
|
74
|
+
// One pass, not two `Object.hasOwn` probes plus a walk: the suspect names are checked against
|
|
75
|
+
// the keys the walk already enumerates, so a clean node pays two pointer-comparisons per key
|
|
76
|
+
// instead of two hash lookups per node (measured ~30ns/request cheaper on a typical API body).
|
|
77
|
+
// `for-in`, not `Object.keys()`: the keys array would be an allocation per node (~2x the whole
|
|
78
|
+
// walk on measured bodies, both engines, at realistic widths; V8 only prefers `Object.keys` on
|
|
79
|
+
// 200+-property dictionary-mode objects). On JSON.parse output the two enumerate identically -
|
|
80
|
+
// own enumerable string keys, nothing enumerable on the prototype chain. A custom transport
|
|
81
|
+
// codec could hand `guardParsedValue` an object with enumerable INHERITED properties; `for-in`
|
|
82
|
+
// sweeps those too, which only over-sweeps - a poisoned inherited subtree rejects rather than
|
|
83
|
+
// slips through. A *non-enumerable* own `__proto__` is the one shape this misses, and it is
|
|
84
|
+
// not the poisoning shape: `JSON.parse` never produces one, and the merges that carry the
|
|
85
|
+
// payload onward (spread, `Object.assign`, key loops) copy enumerable own keys only.
|
|
86
|
+
for (const key in record) {
|
|
87
|
+
const value = record[key];
|
|
88
|
+
const nested = value !== null && typeof value === "object";
|
|
89
|
+
// Keep the hot clean-key check branch-local and cheap. JSON keys are strings, and the length
|
|
90
|
+
// + first-code-unit guards avoid a full string comparison for the overwhelming majority of
|
|
91
|
+
// fields while preserving exact equality for the two security-sensitive names.
|
|
92
|
+
if (key.length === 9 && key.charCodeAt(0) === 95 /* _ */ && key === "__proto__") {
|
|
93
|
+
if (policy === "reject")
|
|
94
|
+
throw POISONED;
|
|
95
|
+
// biome-ignore lint/complexity/useLiteralKeys: bracket access keeps the guarded key an explicit string, never a prototype walk
|
|
96
|
+
delete record["__proto__"];
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (key.length === 11 &&
|
|
100
|
+
key.charCodeAt(0) === 99 /* c */ &&
|
|
101
|
+
key === "constructor" &&
|
|
102
|
+
nested &&
|
|
103
|
+
Object.hasOwn(value, "prototype")) {
|
|
104
|
+
if (policy === "reject")
|
|
105
|
+
throw POISONED;
|
|
106
|
+
// biome-ignore lint/complexity/useLiteralKeys: bracket access keeps the guarded key an explicit string, never a prototype walk
|
|
107
|
+
delete record["constructor"];
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (nested)
|
|
111
|
+
stack.push(value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return root;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=proto-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proto-guard.js","sourceRoot":"","sources":["../../src/server/proto-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,12 +27,16 @@ export interface RouteInfo {
|
|
|
27
27
|
export type Registry = Record<string, Record<string, RouteInfo>>;
|
|
28
28
|
/** The empty registry (no routes). `NonNullable<unknown>` is `{}` without tripping noBannedTypes. */
|
|
29
29
|
export type EmptyRegistry = NonNullable<unknown>;
|
|
30
|
+
/** The client-visible request inputs use the schema's INPUT side: the registry describes what a
|
|
31
|
+
* caller must SEND over the wire (pre-validation), not what the handler receives after parsing. With
|
|
32
|
+
* a Zod `.default()`/transform the two differ - `InferOutput` would force the client to supply
|
|
33
|
+
* fields the schema fills in. (Same rule as the WS `in` channel below.) */
|
|
30
34
|
type RegistryBody<S extends RouteSchema> = S extends {
|
|
31
35
|
body: infer B extends StandardSchemaV1;
|
|
32
|
-
} ?
|
|
36
|
+
} ? InferInput<B> : never;
|
|
33
37
|
type RegistryQuery<S extends RouteSchema> = S extends {
|
|
34
38
|
query: infer Q extends StandardSchemaV1;
|
|
35
|
-
} ?
|
|
39
|
+
} ? InferInput<Q> : never;
|
|
36
40
|
/** The client-visible output: the declared `response` contract when present (so the client sees the
|
|
37
41
|
* contract, not the handler's incidental return), otherwise the inferred handler output. */
|
|
38
42
|
type RegistryOutput<S extends RouteSchema, Output> = S extends {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/server/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACtF,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IAKxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB;;+CAE2C;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;gGAC4F;IAC5F,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;IACtB;;kFAE8E;IAC9E,QAAQ,CAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,6FAA6F;AAC7F,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;AAEhE,qGAAqG;AACrG,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAEhD,KAAK,YAAY,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAAE,GAC3F,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/server/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACtF,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IAKxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB;;+CAE2C;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;gGAC4F;IAC5F,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;IACtB;;kFAE8E;IAC9E,QAAQ,CAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,6FAA6F;AAC7F,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;AAEhE,qGAAqG;AACrG,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAEhD;;;2EAG2E;AAC3E,KAAK,YAAY,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAAE,GAC3F,UAAU,CAAC,CAAC,CAAC,GACb,KAAK,CAAA;AAET,KAAK,aAAa,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAAE,GAC7F,UAAU,CAAC,CAAC,CAAC,GACb,KAAK,CAAA;AAET;4FAC4F;AAC5F,KAAK,cAAc,CAAC,CAAC,SAAS,WAAW,EAAE,MAAM,IAAI,CAAC,SAAS;IAC7D,QAAQ,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAC3C,GACG,WAAW,CAAC,CAAC,CAAC,GACd,MAAM,CAAA;AAEV;;wFAEwF;AACxF,KAAK,cAAc,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IACrD,MAAM,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CACzD,GACG;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,gBAAgB,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;CAAE,GAC7E,OAAO,CAAA;AAEX,2FAA2F;AAC3F,KAAK,WAAW,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,CAAC,SAAS,gBAAgB,CAAA;CAAE,GACzF,WAAW,CAAC,CAAC,CAAC,GACd,KAAK,CAAA;AAET,sFAAsF;AACtF,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,MAAM,EAAE,CAAC,SAAS,WAAW,EAAE,MAAM,IAAI;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IAChC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAA;IAC9B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IAC1C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAA;IAClC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;CAC7B,CAAA;AAED,wFAAwF;AACxF,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,IAAI,OAAO,CACrE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EACtB,QAAQ,CACT,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,CACxB,IAAI,SAAS,MAAM,EACnB,EAAE,SAAS,gBAAgB,GAAG,SAAS,EACvC,GAAG,SAAS,gBAAgB,GAAG,SAAS,IACtC;IACF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAA;IACnB,QAAQ,CAAC,EAAE,EAAE;QACX,QAAQ,CAAC,EAAE,EAAE,EAAE,SAAS,gBAAgB,GAAG,UAAU,CAAC,EAAE,CAAC,GAAG,OAAO,CAAA;QACnE,QAAQ,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,OAAO,CAAA;KACxE,CAAA;CACF,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,QAAQ,CAClB,CAAC,SAAS,QAAQ,EAClB,MAAM,SAAS,MAAM,EACrB,IAAI,SAAS,MAAM,EACnB,IAAI,SAAS,SAAS,IACpB,CAAC,GAAG;KAAG,CAAC,IAAI,IAAI,GAAG;SAAG,CAAC,IAAI,MAAM,GAAG,IAAI;KAAE;CAAE,CAAA"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { RequestBudget } from "../budget.js";
|
|
8
8
|
import type { Platform } from "./context.js";
|
|
9
|
+
import { type ProtoPoisoning } from "./proto-guard.js";
|
|
9
10
|
import { CONTEXT_SEARCH, CONTEXT_SET } from "./runtime-core.js";
|
|
10
11
|
import type { CtxSet, RawContext, RequestSource } from "./server.js";
|
|
11
12
|
export declare class RequestContext implements RawContext {
|
|
@@ -18,18 +19,20 @@ export declare class RequestContext implements RawContext {
|
|
|
18
19
|
private setValue;
|
|
19
20
|
private queryValue;
|
|
20
21
|
private queryReady;
|
|
22
|
+
private headersValue;
|
|
21
23
|
private cookiesValue;
|
|
22
24
|
private readonly source;
|
|
23
25
|
private readonly maxBodyBytes;
|
|
24
|
-
|
|
25
|
-
constructor(source: RequestSource, params: Record<string, string>,
|
|
26
|
+
private readonly protoPoisoning;
|
|
27
|
+
constructor(source: RequestSource, params: Record<string, string>, maxBodyBytes: number, protoPoisoning?: ProtoPoisoning);
|
|
28
|
+
constructor(source: RequestSource, params: Record<string, string>, search: string | undefined, signal: AbortSignal, budget: RequestBudget, platform: Platform | undefined, maxBodyBytes: number, protoPoisoning?: ProtoPoisoning);
|
|
26
29
|
/**
|
|
27
30
|
* The no-timeout context: `c.signal`/`c.budget` fall back to the never-abort signal and the
|
|
28
31
|
* unbounded budget, which is exactly what the request would have been handed, so the two slots
|
|
29
32
|
* are left unwritten instead of initialized. `search` carries the router's already-split query
|
|
30
33
|
* string when it has one; `undefined` leaves `c.query` to re-scan the URL lazily.
|
|
31
34
|
*/
|
|
32
|
-
static native(source: RequestSource, params: Record<string, string>, search: string | undefined, maxBodyBytes: number, platform?: Platform): RequestContext;
|
|
35
|
+
static native(source: RequestSource, params: Record<string, string>, search: string | undefined, maxBodyBytes: number, platform?: Platform, protoPoisoning?: ProtoPoisoning): RequestContext;
|
|
33
36
|
[CONTEXT_SET](): CtxSet | undefined;
|
|
34
37
|
get [CONTEXT_SEARCH](): string;
|
|
35
38
|
get set(): CtxSet;
|
|
@@ -41,6 +44,8 @@ export declare class RequestContext implements RawContext {
|
|
|
41
44
|
get req(): Request;
|
|
42
45
|
get request(): Request;
|
|
43
46
|
header(name: string): string | null;
|
|
47
|
+
get headers(): Record<string, string>;
|
|
48
|
+
set headers(value: Record<string, string>);
|
|
44
49
|
json(body: unknown, init?: ResponseInit | number): Response;
|
|
45
50
|
text(body: string, init?: ResponseInit | number): Response;
|
|
46
51
|
get query(): unknown;
|
|
@@ -50,9 +55,22 @@ export declare class RequestContext implements RawContext {
|
|
|
50
55
|
boundedJson<T = unknown>(maxBytes?: number): Promise<T>;
|
|
51
56
|
}
|
|
52
57
|
/**
|
|
53
|
-
* Read a JSON body with the same byte cap used by schema validation and `c.boundedJson
|
|
54
|
-
*
|
|
58
|
+
* Read a JSON body with the same byte cap used by schema validation and `c.boundedJson`, then
|
|
59
|
+
* parse it through the prototype-poisoning guard (`protoPoisoning`, default `"reject"` - a
|
|
60
|
+
* poisoned payload answers the same flat 400 as malformed JSON). Non-chunked, framed requests
|
|
61
|
+
* with an in-cap `Content-Length` take the runtime's fused `json()` and walk the parsed value;
|
|
55
62
|
* chunked or length-less requests fall back to the streaming byte-cap guard.
|
|
56
63
|
*/
|
|
57
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Deliberately **not** `async`: the framed lane is the hot path for every JSON POST, and an async
|
|
66
|
+
* wrapper costs a coroutine frame, a wrapper promise, and an extra microtask hop on top of the one
|
|
67
|
+
* the body read already pays (~90ns/request on Node, measured). The lane returns the body read's
|
|
68
|
+
* own promise chain instead, and only the streaming fallback - which is already doing IO in a loop
|
|
69
|
+
* - runs as an async function. Callers that `await` the result are unaffected.
|
|
70
|
+
*/
|
|
71
|
+
type JsonResultContinuation<T> = (result: unknown | Response) => T | Promise<T>;
|
|
72
|
+
type JsonErrorContinuation<T> = (error: unknown) => T | Promise<T>;
|
|
73
|
+
export declare function readBoundedJsonSource(req: RequestSource, maxBytes: number, protoPoisoning?: ProtoPoisoning): Promise<unknown | Response>;
|
|
74
|
+
export declare function readBoundedJsonSource<T>(req: RequestSource, maxBytes: number, protoPoisoning: ProtoPoisoning, onResult: JsonResultContinuation<T>, onError: JsonErrorContinuation<T>): Promise<T>;
|
|
75
|
+
export {};
|
|
58
76
|
//# sourceMappingURL=request-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"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"}
|