@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.
Files changed (98) hide show
  1. package/README.md +3 -3
  2. package/dist/adapters/bun.d.ts.map +1 -1
  3. package/dist/adapters/bun.js +38 -7
  4. package/dist/adapters/bun.js.map +1 -1
  5. package/dist/adapters/node.d.ts.map +1 -1
  6. package/dist/adapters/node.js +29 -7
  7. package/dist/adapters/node.js.map +1 -1
  8. package/dist/app.d.ts +322 -3
  9. package/dist/app.d.ts.map +1 -1
  10. package/dist/app.js +715 -29
  11. package/dist/app.js.map +1 -1
  12. package/dist/cli.d.ts +4 -0
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +218 -1
  15. package/dist/cli.js.map +1 -1
  16. package/dist/combine.d.ts +97 -0
  17. package/dist/combine.d.ts.map +1 -0
  18. package/dist/combine.js +247 -0
  19. package/dist/combine.js.map +1 -0
  20. package/dist/compression.d.ts +127 -0
  21. package/dist/compression.d.ts.map +1 -0
  22. package/dist/compression.js +368 -0
  23. package/dist/compression.js.map +1 -0
  24. package/dist/config.d.ts +97 -0
  25. package/dist/config.d.ts.map +1 -0
  26. package/dist/config.js +132 -0
  27. package/dist/config.js.map +1 -0
  28. package/dist/conn-info.d.ts +121 -0
  29. package/dist/conn-info.d.ts.map +1 -0
  30. package/dist/conn-info.js +145 -0
  31. package/dist/conn-info.js.map +1 -0
  32. package/dist/cookie.d.ts +112 -0
  33. package/dist/cookie.d.ts.map +1 -0
  34. package/dist/cookie.js +185 -0
  35. package/dist/cookie.js.map +1 -0
  36. package/dist/dependency.d.ts +47 -0
  37. package/dist/dependency.d.ts.map +1 -0
  38. package/dist/dependency.js +68 -0
  39. package/dist/dependency.js.map +1 -0
  40. package/dist/etag.d.ts +48 -0
  41. package/dist/etag.d.ts.map +1 -0
  42. package/dist/etag.js +117 -0
  43. package/dist/etag.js.map +1 -0
  44. package/dist/index.d.ts +40 -11
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +19 -4
  47. package/dist/index.js.map +1 -1
  48. package/dist/ip-restriction.d.ts +75 -0
  49. package/dist/ip-restriction.d.ts.map +1 -0
  50. package/dist/ip-restriction.js +203 -0
  51. package/dist/ip-restriction.js.map +1 -0
  52. package/dist/jwk.d.ts +82 -0
  53. package/dist/jwk.d.ts.map +1 -0
  54. package/dist/jwk.js +269 -0
  55. package/dist/jwk.js.map +1 -0
  56. package/dist/jwt.d.ts +103 -0
  57. package/dist/jwt.d.ts.map +1 -0
  58. package/dist/jwt.js +437 -0
  59. package/dist/jwt.js.map +1 -0
  60. package/dist/load-shedding.d.ts +73 -0
  61. package/dist/load-shedding.d.ts.map +1 -0
  62. package/dist/load-shedding.js +171 -0
  63. package/dist/load-shedding.js.map +1 -0
  64. package/dist/middleware.d.ts +181 -2
  65. package/dist/middleware.d.ts.map +1 -1
  66. package/dist/middleware.js +367 -77
  67. package/dist/middleware.js.map +1 -1
  68. package/dist/multipart.d.ts +17 -0
  69. package/dist/multipart.d.ts.map +1 -1
  70. package/dist/multipart.js +117 -1
  71. package/dist/multipart.js.map +1 -1
  72. package/dist/openapi.d.ts +8 -0
  73. package/dist/openapi.d.ts.map +1 -1
  74. package/dist/openapi.js +18 -1
  75. package/dist/openapi.js.map +1 -1
  76. package/dist/security-schemes.d.ts +21 -5
  77. package/dist/security-schemes.d.ts.map +1 -1
  78. package/dist/security-schemes.js +32 -5
  79. package/dist/security-schemes.js.map +1 -1
  80. package/dist/session.d.ts +23 -1
  81. package/dist/session.d.ts.map +1 -1
  82. package/dist/session.js +106 -86
  83. package/dist/session.js.map +1 -1
  84. package/dist/subdomains.d.ts +97 -0
  85. package/dist/subdomains.d.ts.map +1 -0
  86. package/dist/subdomains.js +157 -0
  87. package/dist/subdomains.js.map +1 -0
  88. package/dist/time-claims.d.ts +72 -0
  89. package/dist/time-claims.d.ts.map +1 -0
  90. package/dist/time-claims.js +88 -0
  91. package/dist/time-claims.js.map +1 -0
  92. package/dist/types.d.ts +50 -0
  93. package/dist/types.d.ts.map +1 -1
  94. package/dist/websocket.d.ts +51 -34
  95. package/dist/websocket.d.ts.map +1 -1
  96. package/dist/websocket.js +162 -2
  97. package/dist/websocket.js.map +1 -1
  98. 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"}
@@ -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"}