@daloyjs/core 0.17.0 → 0.28.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/README.md +3 -3
- package/dist/adapters/bun.d.ts.map +1 -1
- package/dist/adapters/bun.js +38 -7
- package/dist/adapters/bun.js.map +1 -1
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +29 -7
- package/dist/adapters/node.js.map +1 -1
- package/dist/app.d.ts +322 -3
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +715 -29
- package/dist/app.js.map +1 -1
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +218 -1
- package/dist/cli.js.map +1 -1
- package/dist/combine.d.ts +97 -0
- package/dist/combine.d.ts.map +1 -0
- package/dist/combine.js +247 -0
- package/dist/combine.js.map +1 -0
- package/dist/compression.d.ts +127 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +368 -0
- package/dist/compression.js.map +1 -0
- package/dist/config.d.ts +97 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +132 -0
- package/dist/config.js.map +1 -0
- package/dist/conn-info.d.ts +121 -0
- package/dist/conn-info.d.ts.map +1 -0
- package/dist/conn-info.js +145 -0
- package/dist/conn-info.js.map +1 -0
- package/dist/cookie.d.ts +112 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cookie.js +185 -0
- package/dist/cookie.js.map +1 -0
- package/dist/dependency.d.ts +47 -0
- package/dist/dependency.d.ts.map +1 -0
- package/dist/dependency.js +68 -0
- package/dist/dependency.js.map +1 -0
- package/dist/etag.d.ts +48 -0
- package/dist/etag.d.ts.map +1 -0
- package/dist/etag.js +117 -0
- package/dist/etag.js.map +1 -0
- package/dist/index.d.ts +40 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -4
- package/dist/index.js.map +1 -1
- package/dist/ip-restriction.d.ts +75 -0
- package/dist/ip-restriction.d.ts.map +1 -0
- package/dist/ip-restriction.js +203 -0
- package/dist/ip-restriction.js.map +1 -0
- package/dist/jwk.d.ts +82 -0
- package/dist/jwk.d.ts.map +1 -0
- package/dist/jwk.js +269 -0
- package/dist/jwk.js.map +1 -0
- package/dist/jwt.d.ts +103 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +437 -0
- package/dist/jwt.js.map +1 -0
- package/dist/load-shedding.d.ts +73 -0
- package/dist/load-shedding.d.ts.map +1 -0
- package/dist/load-shedding.js +171 -0
- package/dist/load-shedding.js.map +1 -0
- package/dist/middleware.d.ts +181 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +367 -77
- package/dist/middleware.js.map +1 -1
- package/dist/multipart.d.ts +17 -0
- package/dist/multipart.d.ts.map +1 -1
- package/dist/multipart.js +117 -1
- package/dist/multipart.js.map +1 -1
- package/dist/openapi.d.ts +8 -0
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +18 -1
- package/dist/openapi.js.map +1 -1
- package/dist/security-schemes.d.ts +21 -5
- package/dist/security-schemes.d.ts.map +1 -1
- package/dist/security-schemes.js +32 -5
- package/dist/security-schemes.js.map +1 -1
- package/dist/session.d.ts +23 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +106 -86
- package/dist/session.js.map +1 -1
- package/dist/subdomains.d.ts +97 -0
- package/dist/subdomains.d.ts.map +1 -0
- package/dist/subdomains.js +157 -0
- package/dist/subdomains.js.map +1 -0
- package/dist/time-claims.d.ts +72 -0
- package/dist/time-claims.d.ts.map +1 -0
- package/dist/time-claims.js +88 -0
- package/dist/time-claims.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/websocket.d.ts +51 -34
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +162 -2
- package/dist/websocket.js.map +1 -1
- package/package.json +30 -3
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composition primitives for {@link Hooks} bundles. These let you assemble
|
|
3
|
+
* curated middleware stacks (`every`), express "any of these proofs is
|
|
4
|
+
* enough" auth (`some`), and exempt specific paths from a check (`except`).
|
|
5
|
+
*
|
|
6
|
+
* @since 0.19.0
|
|
7
|
+
*/
|
|
8
|
+
import type { Hooks, BaseContext } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Run every supplied {@link Hooks} bundle in order, pipeline-style.
|
|
11
|
+
* Equivalent to passing the bundles to `app.use(...)` one after another,
|
|
12
|
+
* but lets you package a curated stack as a single value (e.g. "the auth
|
|
13
|
+
* stack for the admin section"). All lifecycle phases compose:
|
|
14
|
+
*
|
|
15
|
+
* - `onRequest` / `onResponse` run in registration order.
|
|
16
|
+
* - `beforeHandle` / `onError` short-circuit on the first `Response`.
|
|
17
|
+
* - `afterHandle` / `onSend` thread the value through every bundle.
|
|
18
|
+
*
|
|
19
|
+
* Symbol-keyed security markers (CORS / CSRF / session / secure-headers)
|
|
20
|
+
* are forwarded onto the merged bundle so boot-time guards still see them.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const adminStack = every(
|
|
25
|
+
* requestId(),
|
|
26
|
+
* bearerAuth({ validate: (token) => token === env.ADMIN_TOKEN }),
|
|
27
|
+
* rateLimit({ windowMs: 60_000, max: 30, groupId: "admin" }),
|
|
28
|
+
* );
|
|
29
|
+
* app.use(adminStack);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @since 0.19.0
|
|
33
|
+
*/
|
|
34
|
+
export declare function every(...layers: Hooks[]): Hooks;
|
|
35
|
+
/**
|
|
36
|
+
* Run the supplied bundles until one of them passes its `beforeHandle`
|
|
37
|
+
* check without throwing. Useful for "this route accepts a bearer token
|
|
38
|
+
* OR a signed cookie OR an API key" patterns where any single proof of
|
|
39
|
+
* identity is enough.
|
|
40
|
+
*
|
|
41
|
+
* Semantics:
|
|
42
|
+
*
|
|
43
|
+
* - The bundles' `beforeHandle` hooks are awaited in order. The first one
|
|
44
|
+
* that resolves without throwing wins; its `ctx` mutations (headers,
|
|
45
|
+
* `ctx.state`, etc.) are preserved.
|
|
46
|
+
* - When a bundle returns a `Response`, that response is treated as a
|
|
47
|
+
* denial and the next bundle gets a chance to pass. If every bundle
|
|
48
|
+
* denies, the first denial wins.
|
|
49
|
+
* - When the first denial is a thrown error, that error is rethrown so the
|
|
50
|
+
* client gets a deterministic status code. Place the auth method whose
|
|
51
|
+
* `WWW-Authenticate` challenge you want clients to see first.
|
|
52
|
+
* - `afterHandle`, `onSend`, `onResponse`, and `onError` from every bundle
|
|
53
|
+
* still compose normally — `some()` only changes the `beforeHandle`
|
|
54
|
+
* evaluation strategy.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* app.use(some(
|
|
59
|
+
* bearerAuth({ validate: (token) => token === env.PUBLIC_API_TOKEN }),
|
|
60
|
+
* session(),
|
|
61
|
+
* ));
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @since 0.19.0
|
|
65
|
+
*/
|
|
66
|
+
export declare function some(...layers: Hooks[]): Hooks;
|
|
67
|
+
/**
|
|
68
|
+
* Pattern accepted by {@link except}. Strings starting with `/` are matched
|
|
69
|
+
* against the request `pathname`. `*` matches one path segment (no `/`);
|
|
70
|
+
* `**` matches any suffix (zero or more segments). Functions receive the
|
|
71
|
+
* request context and return `true` to skip the gated bundle.
|
|
72
|
+
*
|
|
73
|
+
* @since 0.19.0
|
|
74
|
+
*/
|
|
75
|
+
export type ExceptPredicate = string | string[] | ((ctx: BaseContext<any, any>) => boolean | Promise<boolean>);
|
|
76
|
+
/**
|
|
77
|
+
* Run a hook bundle on every request EXCEPT those matching `when`. The
|
|
78
|
+
* canonical use is "apply auth everywhere except the public endpoints":
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* app.use(except(
|
|
83
|
+
* ["/health", "/openapi.json", "/docs/**"],
|
|
84
|
+
* bearerAuth({ validate: (token) => token === env.API_TOKEN }),
|
|
85
|
+
* ));
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Only the `beforeHandle` phase is gated — the surrounding
|
|
89
|
+
* `onRequest`/`afterHandle`/`onSend`/`onResponse` phases still run so
|
|
90
|
+
* shared concerns like request-id propagation are not accidentally
|
|
91
|
+
* exempted. Wrap each bundle with {@link except} individually when you
|
|
92
|
+
* need to gate other phases.
|
|
93
|
+
*
|
|
94
|
+
* @since 0.19.0
|
|
95
|
+
*/
|
|
96
|
+
export declare function except(when: ExceptPredicate, hooks: Hooks): Hooks;
|
|
97
|
+
//# sourceMappingURL=combine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combine.d.ts","sourceRoot":"","sources":["../src/combine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAE/C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,IAAI,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CA8B9C;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,MAAM,EAAE,GACR,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAWjE"}
|
package/dist/combine.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composition primitives for {@link Hooks} bundles. These let you assemble
|
|
3
|
+
* curated middleware stacks (`every`), express "any of these proofs is
|
|
4
|
+
* enough" auth (`some`), and exempt specific paths from a check (`except`).
|
|
5
|
+
*
|
|
6
|
+
* @since 0.19.0
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Run every supplied {@link Hooks} bundle in order, pipeline-style.
|
|
10
|
+
* Equivalent to passing the bundles to `app.use(...)` one after another,
|
|
11
|
+
* but lets you package a curated stack as a single value (e.g. "the auth
|
|
12
|
+
* stack for the admin section"). All lifecycle phases compose:
|
|
13
|
+
*
|
|
14
|
+
* - `onRequest` / `onResponse` run in registration order.
|
|
15
|
+
* - `beforeHandle` / `onError` short-circuit on the first `Response`.
|
|
16
|
+
* - `afterHandle` / `onSend` thread the value through every bundle.
|
|
17
|
+
*
|
|
18
|
+
* Symbol-keyed security markers (CORS / CSRF / session / secure-headers)
|
|
19
|
+
* are forwarded onto the merged bundle so boot-time guards still see them.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const adminStack = every(
|
|
24
|
+
* requestId(),
|
|
25
|
+
* bearerAuth({ validate: (token) => token === env.ADMIN_TOKEN }),
|
|
26
|
+
* rateLimit({ windowMs: 60_000, max: 30, groupId: "admin" }),
|
|
27
|
+
* );
|
|
28
|
+
* app.use(adminStack);
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @since 0.19.0
|
|
32
|
+
*/
|
|
33
|
+
export function every(...layers) {
|
|
34
|
+
return mergeCombineHooks(layers);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run the supplied bundles until one of them passes its `beforeHandle`
|
|
38
|
+
* check without throwing. Useful for "this route accepts a bearer token
|
|
39
|
+
* OR a signed cookie OR an API key" patterns where any single proof of
|
|
40
|
+
* identity is enough.
|
|
41
|
+
*
|
|
42
|
+
* Semantics:
|
|
43
|
+
*
|
|
44
|
+
* - The bundles' `beforeHandle` hooks are awaited in order. The first one
|
|
45
|
+
* that resolves without throwing wins; its `ctx` mutations (headers,
|
|
46
|
+
* `ctx.state`, etc.) are preserved.
|
|
47
|
+
* - When a bundle returns a `Response`, that response is treated as a
|
|
48
|
+
* denial and the next bundle gets a chance to pass. If every bundle
|
|
49
|
+
* denies, the first denial wins.
|
|
50
|
+
* - When the first denial is a thrown error, that error is rethrown so the
|
|
51
|
+
* client gets a deterministic status code. Place the auth method whose
|
|
52
|
+
* `WWW-Authenticate` challenge you want clients to see first.
|
|
53
|
+
* - `afterHandle`, `onSend`, `onResponse`, and `onError` from every bundle
|
|
54
|
+
* still compose normally — `some()` only changes the `beforeHandle`
|
|
55
|
+
* evaluation strategy.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* app.use(some(
|
|
60
|
+
* bearerAuth({ validate: (token) => token === env.PUBLIC_API_TOKEN }),
|
|
61
|
+
* session(),
|
|
62
|
+
* ));
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @since 0.19.0
|
|
66
|
+
*/
|
|
67
|
+
export function some(...layers) {
|
|
68
|
+
if (layers.length === 0)
|
|
69
|
+
return {};
|
|
70
|
+
const stripped = layers.map(({ beforeHandle: _b, ...rest }) => rest);
|
|
71
|
+
const base = mergeCombineHooks(stripped);
|
|
72
|
+
const candidates = layers
|
|
73
|
+
.map((h) => h.beforeHandle)
|
|
74
|
+
.filter((f) => typeof f === "function");
|
|
75
|
+
if (candidates.length === 0)
|
|
76
|
+
return base;
|
|
77
|
+
return {
|
|
78
|
+
...base,
|
|
79
|
+
async beforeHandle(ctx) {
|
|
80
|
+
let firstFailure;
|
|
81
|
+
for (const fn of candidates) {
|
|
82
|
+
try {
|
|
83
|
+
const r = await fn(ctx);
|
|
84
|
+
if (r instanceof Response) {
|
|
85
|
+
// Treat as a denial — try the next layer.
|
|
86
|
+
if (!firstFailure)
|
|
87
|
+
firstFailure = { kind: "response", res: r };
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// Undefined = pass; bundle accepts the request.
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
if (!firstFailure)
|
|
95
|
+
firstFailure = { kind: "throw", err };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (firstFailure?.kind === "response")
|
|
99
|
+
return firstFailure.res;
|
|
100
|
+
throw firstFailure.err;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Run a hook bundle on every request EXCEPT those matching `when`. The
|
|
106
|
+
* canonical use is "apply auth everywhere except the public endpoints":
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* app.use(except(
|
|
111
|
+
* ["/health", "/openapi.json", "/docs/**"],
|
|
112
|
+
* bearerAuth({ validate: (token) => token === env.API_TOKEN }),
|
|
113
|
+
* ));
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* Only the `beforeHandle` phase is gated — the surrounding
|
|
117
|
+
* `onRequest`/`afterHandle`/`onSend`/`onResponse` phases still run so
|
|
118
|
+
* shared concerns like request-id propagation are not accidentally
|
|
119
|
+
* exempted. Wrap each bundle with {@link except} individually when you
|
|
120
|
+
* need to gate other phases.
|
|
121
|
+
*
|
|
122
|
+
* @since 0.19.0
|
|
123
|
+
*/
|
|
124
|
+
export function except(when, hooks) {
|
|
125
|
+
const matches = compileExceptMatcher(when);
|
|
126
|
+
const original = hooks.beforeHandle;
|
|
127
|
+
if (!original)
|
|
128
|
+
return hooks;
|
|
129
|
+
return {
|
|
130
|
+
...hooks,
|
|
131
|
+
async beforeHandle(ctx) {
|
|
132
|
+
if (await matches(ctx))
|
|
133
|
+
return undefined;
|
|
134
|
+
return original(ctx);
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function compileExceptMatcher(when) {
|
|
139
|
+
if (typeof when === "function") {
|
|
140
|
+
return async (ctx) => Boolean(await when(ctx));
|
|
141
|
+
}
|
|
142
|
+
const patterns = Array.isArray(when) ? when : [when];
|
|
143
|
+
const matchers = patterns.map(compilePathPattern);
|
|
144
|
+
return async (ctx) => {
|
|
145
|
+
const path = new URL(ctx.request.url).pathname;
|
|
146
|
+
return matchers.some((m) => m(path));
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function compilePathPattern(pattern) {
|
|
150
|
+
if (!pattern.startsWith("/")) {
|
|
151
|
+
throw new Error(`except(): path patterns must start with "/" (got ${JSON.stringify(pattern)}).`);
|
|
152
|
+
}
|
|
153
|
+
if (!pattern.includes("*")) {
|
|
154
|
+
return (path) => path === pattern;
|
|
155
|
+
}
|
|
156
|
+
const escaped = pattern
|
|
157
|
+
.split(/(\*\*|\*)/)
|
|
158
|
+
.map((part) => {
|
|
159
|
+
if (part === "**")
|
|
160
|
+
return ".*";
|
|
161
|
+
if (part === "*")
|
|
162
|
+
return "[^/]*";
|
|
163
|
+
return part.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
164
|
+
})
|
|
165
|
+
.join("");
|
|
166
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
167
|
+
return (path) => regex.test(path);
|
|
168
|
+
}
|
|
169
|
+
function mergeCombineHooks(layers) {
|
|
170
|
+
const pick = (key) => layers
|
|
171
|
+
.map((h) => h[key])
|
|
172
|
+
.filter((f) => typeof f === "function");
|
|
173
|
+
const merged = {};
|
|
174
|
+
const onRequest = pick("onRequest");
|
|
175
|
+
if (onRequest.length > 0) {
|
|
176
|
+
merged.onRequest = async (req) => {
|
|
177
|
+
for (const fn of onRequest)
|
|
178
|
+
await fn(req);
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const beforeHandle = pick("beforeHandle");
|
|
182
|
+
if (beforeHandle.length > 0) {
|
|
183
|
+
merged.beforeHandle = async (ctx) => {
|
|
184
|
+
for (const fn of beforeHandle) {
|
|
185
|
+
const r = await fn(ctx);
|
|
186
|
+
if (r instanceof Response)
|
|
187
|
+
return r;
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const afterHandle = pick("afterHandle");
|
|
193
|
+
if (afterHandle.length > 0) {
|
|
194
|
+
merged.afterHandle = async (ctx, value) => {
|
|
195
|
+
let current = value;
|
|
196
|
+
for (const fn of afterHandle) {
|
|
197
|
+
const out = await fn(ctx, current);
|
|
198
|
+
if (out !== undefined)
|
|
199
|
+
current = out;
|
|
200
|
+
}
|
|
201
|
+
return current;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const onError = pick("onError");
|
|
205
|
+
if (onError.length > 0) {
|
|
206
|
+
merged.onError = async (err, ctx) => {
|
|
207
|
+
for (const fn of onError) {
|
|
208
|
+
const r = await fn(err, ctx);
|
|
209
|
+
if (r instanceof Response)
|
|
210
|
+
return r;
|
|
211
|
+
}
|
|
212
|
+
return undefined;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const onSend = pick("onSend");
|
|
216
|
+
if (onSend.length > 0) {
|
|
217
|
+
merged.onSend = async (res, ctx) => {
|
|
218
|
+
let current = res;
|
|
219
|
+
for (const fn of onSend) {
|
|
220
|
+
const r = await fn(current, ctx);
|
|
221
|
+
if (r instanceof Response)
|
|
222
|
+
current = r;
|
|
223
|
+
}
|
|
224
|
+
return current;
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const onResponse = pick("onResponse");
|
|
228
|
+
if (onResponse.length > 0) {
|
|
229
|
+
merged.onResponse = async (res) => {
|
|
230
|
+
for (const fn of onResponse)
|
|
231
|
+
await fn(res);
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// Forward symbol-keyed security markers (CORS / CSRF / session /
|
|
235
|
+
// secure-headers) so boot-time and per-request guards still see them
|
|
236
|
+
// on the composed bundle.
|
|
237
|
+
for (const hooks of layers) {
|
|
238
|
+
const record = hooks;
|
|
239
|
+
for (const key of Object.getOwnPropertySymbols(record)) {
|
|
240
|
+
if (!(key in merged)) {
|
|
241
|
+
merged[key] = record[key];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return merged;
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=combine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combine.js","sourceRoot":"","sources":["../src/combine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 7 (first focused slice): first-party `compression()` middleware with
|
|
3
|
+
* BREACH-aware safe defaults.
|
|
4
|
+
*
|
|
5
|
+
* Built on the web-standard `CompressionStream` API so the same line works
|
|
6
|
+
* on Node, Bun, Deno, Cloudflare Workers, Vercel Edge, and Fastly Compute.
|
|
7
|
+
* The middleware deliberately refuses to expose a configurable compression
|
|
8
|
+
* level — `CompressionStream` uses the runtime's documented default (gzip
|
|
9
|
+
* level 6 on Node / V8); a `level: 9` opt-in is rejected at construction
|
|
10
|
+
* because the bytes-saved-per-CPU-cycle tradeoff turns into a small-vCPU
|
|
11
|
+
* DoS amplifier under sustained load with single-digit-percent bytes-saved
|
|
12
|
+
* gains on typical JSON payloads.
|
|
13
|
+
*
|
|
14
|
+
* BREACH guards (CRIME-style cross-domain compression-oracle defenses) are
|
|
15
|
+
* always-on and not opt-out: the middleware refuses to compress responses
|
|
16
|
+
* that carry `Set-Cookie`, responses on requests that carry an
|
|
17
|
+
* `Authorization` header, responses on requests that carry a
|
|
18
|
+
* recognized session / CSRF cookie, and responses whose content type is
|
|
19
|
+
* already entropy-coded (images, video, audio, archives, fonts, wasm, pdf).
|
|
20
|
+
*
|
|
21
|
+
* @since 0.25.0
|
|
22
|
+
*/
|
|
23
|
+
import type { Hooks } from "./types.js";
|
|
24
|
+
/** Supported response encodings, in default preference order (best → worst ratio). */
|
|
25
|
+
export type CompressionEncoding = "br" | "gzip" | "deflate";
|
|
26
|
+
/** Options for {@link compression}. */
|
|
27
|
+
export interface CompressionOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Minimum response body size (in bytes) eligible for compression.
|
|
30
|
+
*
|
|
31
|
+
* Responses smaller than this threshold are never compressed: gzip /
|
|
32
|
+
* deflate / brotli all add a header + dictionary preamble (typically
|
|
33
|
+
* 10–18 bytes for gzip, 4–8 bytes for brotli) so anything smaller than
|
|
34
|
+
* the typical TCP MSS (~1448 bytes) does not actually save wire bytes
|
|
35
|
+
* after framing overhead.
|
|
36
|
+
*
|
|
37
|
+
* Default `1024`. Must be a finite non-negative integer; values
|
|
38
|
+
* below `0` or above `2 ** 31 - 1` are refused at construction.
|
|
39
|
+
*/
|
|
40
|
+
minimumSize?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Allowed encodings, in caller-preferred order. Defaults to
|
|
43
|
+
* `["br", "gzip", "deflate"]` — the middleware will pick the
|
|
44
|
+
* highest-quality encoding the client supports AND the runtime supports
|
|
45
|
+
* (probed lazily on first use). `deflate` is included for legacy
|
|
46
|
+
* intermediaries; if a runtime lacks `CompressionStream` for an
|
|
47
|
+
* encoding, that encoding is silently dropped from the allowlist.
|
|
48
|
+
*/
|
|
49
|
+
encodings?: readonly CompressionEncoding[];
|
|
50
|
+
/**
|
|
51
|
+
* Additional MIME-type prefixes (lowercased, checked as a prefix of the
|
|
52
|
+
* response `Content-Type` token before any `;` parameter) that should
|
|
53
|
+
* NOT be compressed. Merged with the always-on built-in deny-list
|
|
54
|
+
* (image/*, video/*, audio/*, application/zip, application/x-7z-*,
|
|
55
|
+
* application/x-gzip, application/x-bzip*, application/x-xz,
|
|
56
|
+
* application/x-zstd, application/wasm, application/pdf, font/*).
|
|
57
|
+
*/
|
|
58
|
+
excludeContentTypes?: readonly string[];
|
|
59
|
+
/**
|
|
60
|
+
* Additional request cookie names that, when present, mark the response
|
|
61
|
+
* as security-state-bound and skip compression. Merged with the
|
|
62
|
+
* always-on built-in set (any cookie matching `/session/i`,
|
|
63
|
+
* `/csrf/i`, `/xsrf/i`, any `__Host-` / `__Secure-` cookie).
|
|
64
|
+
*
|
|
65
|
+
* The match is performed on the unparsed cookie name (case-insensitive
|
|
66
|
+
* substring match), so passing `"my-app-auth"` covers
|
|
67
|
+
* `my-app-auth=...`, `my-app-auth.sig=...`, etc.
|
|
68
|
+
*/
|
|
69
|
+
authCookieNames?: readonly string[];
|
|
70
|
+
/**
|
|
71
|
+
* Deliberately unsupported. Provided as a `never`-typed trap so the type
|
|
72
|
+
* system flags accidental opt-in. The actual `CompressionStream` never
|
|
73
|
+
* exposes a level knob — passing any value triggers a construction-time
|
|
74
|
+
* refusal.
|
|
75
|
+
*
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
compressLevel?: never;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Internal marker stamped on the hook so a future audit gate can confirm
|
|
82
|
+
* the BREACH-aware middleware is installed (Wave 9 audit-item parity).
|
|
83
|
+
*
|
|
84
|
+
* @since 0.25.0
|
|
85
|
+
*/
|
|
86
|
+
export declare const COMPRESSION_HOOK_MARKER: unique symbol;
|
|
87
|
+
/**
|
|
88
|
+
* @internal Reset cached runtime probe. Test-only.
|
|
89
|
+
* @since 0.25.0
|
|
90
|
+
*/
|
|
91
|
+
export declare function _resetCompressionRuntimeProbeForTests(): void;
|
|
92
|
+
/**
|
|
93
|
+
* BREACH-aware response compression middleware.
|
|
94
|
+
*
|
|
95
|
+
* Picks `br` > `gzip` > `deflate` based on the client's `Accept-Encoding`
|
|
96
|
+
* header AND the runtime's `CompressionStream` support (probed once,
|
|
97
|
+
* cached). Skips compression when ANY of the following are true:
|
|
98
|
+
*
|
|
99
|
+
* - request method is not `GET` / `HEAD` (compressing responses on
|
|
100
|
+
* state-changing requests offers no caching win and trips BREACH
|
|
101
|
+
* oracles more easily);
|
|
102
|
+
* - request carried an `Authorization` header (auth-bound bodies);
|
|
103
|
+
* - request carried a recognized session / CSRF / `__Host-` /
|
|
104
|
+
* `__Secure-` cookie (auth-bound bodies);
|
|
105
|
+
* - response status is not `2xx`;
|
|
106
|
+
* - response already declares a `Content-Encoding`;
|
|
107
|
+
* - response declares a `Set-Cookie` (response is mutating auth state);
|
|
108
|
+
* - response body byte length is below `minimumSize` (default `1024`);
|
|
109
|
+
* - response `Content-Type` is in the always-on already-compressed
|
|
110
|
+
* deny-list (image/video/audio/archives/fonts/wasm/pdf, with
|
|
111
|
+
* `image/svg+xml` carved back in as compressible XML).
|
|
112
|
+
*
|
|
113
|
+
* When skipping, the middleware still appends `Vary: Accept-Encoding`
|
|
114
|
+
* to the response so cache keys remain content-negotiation-correct.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* import { App, compression } from "@daloyjs/core";
|
|
119
|
+
*
|
|
120
|
+
* const app = new App();
|
|
121
|
+
* app.use(compression());
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @since 0.25.0
|
|
125
|
+
*/
|
|
126
|
+
export declare function compression(opts?: CompressionOptions): Hooks;
|
|
127
|
+
//# sourceMappingURL=compression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../src/compression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,sFAAsF;AACtF,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC3C;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,OAAO,MAE5C,CAAC;AA0HF;;;GAGG;AACH,wBAAgB,qCAAqC,IAAI,IAAI,CAE5D;AAoHD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,KAAK,CAgGhE"}
|