@nifrajs/middleware 2.8.2 → 2.9.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 (59) hide show
  1. package/README.md +22 -1
  2. package/dist/_utils.d.ts +6 -1
  3. package/dist/_utils.d.ts.map +1 -1
  4. package/dist/_utils.js +39 -28
  5. package/dist/_utils.js.map +1 -1
  6. package/dist/cache-control.d.ts.map +1 -1
  7. package/dist/cache-control.js +31 -15
  8. package/dist/cache-control.js.map +1 -1
  9. package/dist/cache.d.ts +1 -1
  10. package/dist/cache.d.ts.map +1 -1
  11. package/dist/cache.js +2 -3
  12. package/dist/cache.js.map +1 -1
  13. package/dist/compression.d.ts +10 -10
  14. package/dist/compression.d.ts.map +1 -1
  15. package/dist/compression.js +191 -70
  16. package/dist/compression.js.map +1 -1
  17. package/dist/cors.d.ts.map +1 -1
  18. package/dist/cors.js +27 -16
  19. package/dist/cors.js.map +1 -1
  20. package/dist/etag.d.ts +4 -2
  21. package/dist/etag.d.ts.map +1 -1
  22. package/dist/etag.js +135 -63
  23. package/dist/etag.js.map +1 -1
  24. package/dist/language.d.ts.map +1 -1
  25. package/dist/language.js +14 -15
  26. package/dist/language.js.map +1 -1
  27. package/dist/logger.d.ts.map +1 -1
  28. package/dist/logger.js +34 -16
  29. package/dist/logger.js.map +1 -1
  30. package/dist/powered-by.d.ts.map +1 -1
  31. package/dist/powered-by.js +8 -5
  32. package/dist/powered-by.js.map +1 -1
  33. package/dist/pretty-json.d.ts +5 -2
  34. package/dist/pretty-json.d.ts.map +1 -1
  35. package/dist/pretty-json.js +85 -64
  36. package/dist/pretty-json.js.map +1 -1
  37. package/dist/rate-limit.d.ts.map +1 -1
  38. package/dist/rate-limit.js +76 -19
  39. package/dist/rate-limit.js.map +1 -1
  40. package/dist/request-id.js +1 -1
  41. package/dist/request-id.js.map +1 -1
  42. package/dist/security-headers.d.ts +8 -4
  43. package/dist/security-headers.d.ts.map +1 -1
  44. package/dist/security-headers.js +18 -16
  45. package/dist/security-headers.js.map +1 -1
  46. package/package.json +3 -3
  47. package/src/_utils.ts +39 -24
  48. package/src/cache-control.ts +27 -14
  49. package/src/cache.ts +2 -4
  50. package/src/compression.ts +202 -62
  51. package/src/cors.ts +23 -14
  52. package/src/etag.ts +141 -59
  53. package/src/language.ts +12 -13
  54. package/src/logger.ts +24 -6
  55. package/src/powered-by.ts +8 -4
  56. package/src/pretty-json.ts +95 -70
  57. package/src/rate-limit.ts +86 -20
  58. package/src/request-id.ts +1 -1
  59. package/src/security-headers.ts +17 -15
@@ -1,4 +1,5 @@
1
1
  import { NIFRA_ASSURANCE, withRouteAssurance } from "@nifrajs/core/assurance";
2
+ import { withHeaders, withNodeHeaders } from "./_utils.js";
2
3
  /** Max entries scanned (oldest-first) per eviction looking for an expired victim before falling back
3
4
  * to evicting the oldest-inserted. Bounds eviction to O(1) per insertion instead of a full O(n) sweep
4
5
  * under a distinct-key flood - the abuse the key cap defends against. */
@@ -111,6 +112,24 @@ function defaultKey(req, trustedProxies, header, allowGlobalKey) {
111
112
  // from it. A shared bucket is only safe when the app deliberately asked for a global throttle.
112
113
  return allowGlobalKey ? "global" : null;
113
114
  }
115
+ /** {@link defaultKey} against the allocation-light native request view - same logic, same order. */
116
+ function nativeKey(req, trustedProxies, header, allowGlobalKey) {
117
+ if (header !== undefined) {
118
+ const ip = req.header(header);
119
+ if (ip !== null && ip.trim() !== "")
120
+ return ip.trim();
121
+ }
122
+ if (trustedProxies > 0) {
123
+ const xff = req.header("x-forwarded-for");
124
+ if (xff !== null) {
125
+ const parts = xff.split(",");
126
+ const ip = parts[parts.length - trustedProxies]?.trim();
127
+ if (ip !== undefined && ip !== "")
128
+ return ip;
129
+ }
130
+ }
131
+ return allowGlobalKey ? "global" : null;
132
+ }
114
133
  /**
115
134
  * Rate limiting as a {@link Middleware}. Runs in `onRequest` (before routing, so it
116
135
  * also covers 404s); over the limit → `429` + `Retry-After`. Every response carries
@@ -139,35 +158,73 @@ export function rateLimit(options) {
139
158
  }
140
159
  const keyOf = options.key ?? ((req) => defaultKey(req, trustedProxies, header, allowGlobalKey));
141
160
  const quota = new WeakMap();
161
+ // State for the Node twins, keyed by the NodeRequestContext identity (the same object is passed
162
+ // to the request and response twins - the core identity contract that replaces `Request` keying).
163
+ const nativeQuota = new WeakMap();
164
+ const hitStore = async (key) => {
165
+ if (typeof key !== "string" || key.trim() === "") {
166
+ return Response.json({ ok: false, error: "rate_limit_key_unavailable" }, { status: 500 });
167
+ }
168
+ const { count, resetAt } = await store.hit(key, windowMs);
169
+ const resetSeconds = Math.max(0, Math.ceil((resetAt - Date.now()) / 1000));
170
+ const info = { remaining: Math.max(0, max - count), resetSeconds };
171
+ if (count > max) {
172
+ return {
173
+ info,
174
+ reject: new Response(JSON.stringify({ ok: false, error: "rate_limited" }), {
175
+ status: 429,
176
+ headers: { "content-type": "application/json", "retry-after": String(resetSeconds) },
177
+ }),
178
+ };
179
+ }
180
+ return { info, reject: undefined };
181
+ };
142
182
  const middleware = {
143
183
  name: "rate-limit",
144
184
  async onRequest(req) {
145
- const key = keyOf(req);
146
- if (typeof key !== "string" || key.trim() === "") {
147
- return Response.json({ ok: false, error: "rate_limit_key_unavailable" }, { status: 500 });
148
- }
149
- const { count, resetAt } = await store.hit(key, windowMs);
150
- const resetSeconds = Math.max(0, Math.ceil((resetAt - Date.now()) / 1000));
151
- quota.set(req, { remaining: Math.max(0, max - count), resetSeconds });
152
- if (count > max) {
153
- return new Response(JSON.stringify({ ok: false, error: "rate_limited" }), {
154
- status: 429,
155
- headers: { "content-type": "application/json", "retry-after": String(resetSeconds) },
156
- });
157
- }
158
- return undefined;
185
+ const outcome = await hitStore(keyOf(req));
186
+ if (outcome instanceof Response)
187
+ return outcome;
188
+ quota.set(req, outcome.info);
189
+ return outcome.reject;
159
190
  },
160
191
  onResponse(res, req) {
161
192
  const info = quota.get(req);
162
193
  if (info === undefined)
163
194
  return res;
164
195
  quota.delete(req);
165
- const headers = new Headers(res.headers);
166
- headers.set("RateLimit-Limit", String(max));
167
- headers.set("RateLimit-Remaining", String(info.remaining));
168
- headers.set("RateLimit-Reset", String(info.resetSeconds));
169
- return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
196
+ return withHeaders(res, (headers) => {
197
+ headers.set("RateLimit-Limit", String(max));
198
+ headers.set("RateLimit-Remaining", String(info.remaining));
199
+ headers.set("RateLimit-Reset", String(info.resetSeconds));
200
+ });
170
201
  },
202
+ // Node twins exist only for the built-in header/XFF key derivation - a custom `key` callback
203
+ // takes a real `Request`, which is exactly the object the native lane avoids building.
204
+ ...(options.key === undefined
205
+ ? {
206
+ onNodeRequest: async (req) => {
207
+ const outcome = await hitStore(nativeKey(req, trustedProxies, header, allowGlobalKey));
208
+ if (outcome instanceof Response)
209
+ return outcome;
210
+ nativeQuota.set(req, outcome.info);
211
+ return outcome.reject;
212
+ },
213
+ onNodeResponse: (res, req) => {
214
+ const info = nativeQuota.get(req);
215
+ if (info === undefined)
216
+ return;
217
+ nativeQuota.delete(req);
218
+ withNodeHeaders(res, (headers) => {
219
+ // Lowercase on purpose: the Web path's `Headers` lowercases names on the wire, so
220
+ // this is byte-identical output.
221
+ headers["ratelimit-limit"] = String(max);
222
+ headers["ratelimit-remaining"] = String(info.remaining);
223
+ headers["ratelimit-reset"] = String(info.resetSeconds);
224
+ });
225
+ },
226
+ }
227
+ : {}),
171
228
  };
172
229
  return withRouteAssurance(middleware, {
173
230
  id: NIFRA_ASSURANCE.RATE_LIMITED,
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@ export function requestId(options = {}) {
13
13
  const header = options.header ?? "x-request-id";
14
14
  const generate = options.generate ?? (() => crypto.randomUUID());
15
15
  return definePlugin("requestId", (app) => app.derive((c) => {
16
- const id = c.req.headers.get(header) ?? generate();
16
+ const id = c.header(header) ?? generate();
17
17
  c.set.headers[header] = id; // echo on the response (route handlers)
18
18
  return { requestId: id };
19
19
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"request-id.js","sourceRoot":"","sources":["../src/request-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AASnD;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,UAA4B,EAAE;IACtD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAA;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IAChE,OAAO,YAAY,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CACvC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACf,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAA;QAClD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAA,CAAC,wCAAwC;QACnE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;IAC1B,CAAC,CAAC,CACH,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"request-id.js","sourceRoot":"","sources":["../src/request-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AASnD;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,UAA4B,EAAE;IACtD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAA;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IAChE,OAAO,YAAY,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CACvC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACf,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAA;QACzC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAA,CAAC,wCAAwC;QACnE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;IAC1B,CAAC,CAAC,CACH,CAAA;AACH,CAAC"}
@@ -14,10 +14,14 @@ export interface SecurityHeadersOptions {
14
14
  readonly referrerPolicy?: string;
15
15
  }
16
16
  /**
17
- * A safe-by-default set of response security headers (`onResponse`, so they cover
18
- * errors and 404s too): `X-Content-Type-Options: nosniff`, `X-Frame-Options`, and
19
- * `Referrer-Policy` always; `Strict-Transport-Security` and `Content-Security-Policy`
20
- * only when configured (both are environment-/app-specific).
17
+ * A safe-by-default set of response security headers, covering errors and 404s too:
18
+ * `X-Content-Type-Options: nosniff`, `X-Frame-Options`, and `Referrer-Policy` always;
19
+ * `Strict-Transport-Security` and `Content-Security-Policy` only when configured (both are
20
+ * environment-/app-specific).
21
+ *
22
+ * Every value is fixed at construction, so these are declared statically rather than written by a
23
+ * response hook: an app whose response middleware is only this keeps the fused native lanes. A route
24
+ * that sets one of these names itself keeps its own value.
21
25
  */
22
26
  export declare function securityHeaders(options?: SecurityHeadersOptions): Middleware;
23
27
  //# sourceMappingURL=security-headers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../src/security-headers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAGtD,MAAM,WAAW,sBAAsB;IACrC,+FAA+F;IAC/F,QAAQ,CAAC,IAAI,CAAC,EAAE;QACd,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;QACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAC3B,CAAA;IACD,sEAAsE;IACtE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IACvC,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,CAAA;IAC7C,kDAAkD;IAClD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,UAAU,CAiChF"}
1
+ {"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../src/security-headers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,WAAW,sBAAsB;IACrC,+FAA+F;IAC/F,QAAQ,CAAC,IAAI,CAAC,EAAE;QACd,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;QACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAC3B,CAAA;IACD,sEAAsE;IACtE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IACvC,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,CAAA;IAC7C,kDAAkD;IAClD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,UAAU,CAgChF"}
@@ -1,10 +1,13 @@
1
1
  import { NIFRA_ASSURANCE, withRouteAssurance } from "@nifrajs/core/assurance";
2
- import { withHeaders } from "./_utils.js";
3
2
  /**
4
- * A safe-by-default set of response security headers (`onResponse`, so they cover
5
- * errors and 404s too): `X-Content-Type-Options: nosniff`, `X-Frame-Options`, and
6
- * `Referrer-Policy` always; `Strict-Transport-Security` and `Content-Security-Policy`
7
- * only when configured (both are environment-/app-specific).
3
+ * A safe-by-default set of response security headers, covering errors and 404s too:
4
+ * `X-Content-Type-Options: nosniff`, `X-Frame-Options`, and `Referrer-Policy` always;
5
+ * `Strict-Transport-Security` and `Content-Security-Policy` only when configured (both are
6
+ * environment-/app-specific).
7
+ *
8
+ * Every value is fixed at construction, so these are declared statically rather than written by a
9
+ * response hook: an app whose response middleware is only this keeps the fused native lanes. A route
10
+ * that sets one of these names itself keeps its own value.
8
11
  */
9
12
  export function securityHeaders(options = {}) {
10
13
  const frameOptions = options.frameOptions ?? "DENY";
@@ -19,19 +22,18 @@ export function securityHeaders(options = {}) {
19
22
  parts.push("preload");
20
23
  hstsValue = parts.join("; ");
21
24
  }
25
+ const declared = {
26
+ "x-content-type-options": "nosniff",
27
+ "x-frame-options": frameOptions,
28
+ "referrer-policy": referrerPolicy,
29
+ };
30
+ if (hstsValue !== undefined)
31
+ declared["strict-transport-security"] = hstsValue;
32
+ if (csp !== undefined)
33
+ declared["content-security-policy"] = csp;
22
34
  return withRouteAssurance({
23
35
  name: "security-headers",
24
- // Mutate in place on the common (mutable-headers) response; clone only for an immutable one
25
- // (see withHeaders - the old always-clone was ~3% of a realistic request).
26
- onResponse: (res) => withHeaders(res, (headers) => {
27
- headers.set("X-Content-Type-Options", "nosniff");
28
- headers.set("X-Frame-Options", frameOptions);
29
- headers.set("Referrer-Policy", referrerPolicy);
30
- if (hstsValue !== undefined)
31
- headers.set("Strict-Transport-Security", hstsValue);
32
- if (csp !== undefined)
33
- headers.set("Content-Security-Policy", csp);
34
- }),
36
+ responseHeaders: declared,
35
37
  }, {
36
38
  id: NIFRA_ASSURANCE.SECURITY_HEADERS,
37
39
  source: "security-headers",
@@ -1 +1 @@
1
- {"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../src/security-headers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE7E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAiBzC;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkC,EAAE;IAClE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAA;IACnD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,aAAa,CAAA;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,qBAAqB,CAAA;IAEzC,IAAI,SAA6B,CAAA;IACjC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;QAChD,IAAI,OAAO,CAAC,IAAI,CAAC,iBAAiB;YAAE,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;QACnE,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC/C,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAED,OAAO,kBAAkB,CACvB;QACE,IAAI,EAAE,kBAAkB;QACxB,4FAA4F;QAC5F,2EAA2E;QAC3E,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAClB,WAAW,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE;YAC3B,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAA;YAChD,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAA;YAC5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAA;YAC9C,IAAI,SAAS,KAAK,SAAS;gBAAE,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,SAAS,CAAC,CAAA;YAChF,IAAI,GAAG,KAAK,SAAS;gBAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAA;QACpE,CAAC,CAAC;KACL,EACD;QACE,EAAE,EAAE,eAAe,CAAC,gBAAgB;QACpC,MAAM,EAAE,kBAAkB;QAC1B,KAAK,EAAE,QAAQ;KAChB,CACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../src/security-headers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAkB7E;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkC,EAAE;IAClE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAA;IACnD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,aAAa,CAAA;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,qBAAqB,CAAA;IAEzC,IAAI,SAA6B,CAAA;IACjC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;QAChD,IAAI,OAAO,CAAC,IAAI,CAAC,iBAAiB;YAAE,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;QACnE,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC/C,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,QAAQ,GAA2B;QACvC,wBAAwB,EAAE,SAAS;QACnC,iBAAiB,EAAE,YAAY;QAC/B,iBAAiB,EAAE,cAAc;KAClC,CAAA;IACD,IAAI,SAAS,KAAK,SAAS;QAAE,QAAQ,CAAC,2BAA2B,CAAC,GAAG,SAAS,CAAA;IAC9E,IAAI,GAAG,KAAK,SAAS;QAAE,QAAQ,CAAC,yBAAyB,CAAC,GAAG,GAAG,CAAA;IAEhE,OAAO,kBAAkB,CACvB;QACE,IAAI,EAAE,kBAAkB;QACxB,eAAe,EAAE,QAAQ;KAC1B,EACD;QACE,EAAE,EAAE,eAAe,CAAC,gBAAgB;QACpC,MAAM,EAAE,kBAAkB;QAC1B,KAAK,EAAE,QAAQ;KAChB,CACF,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/middleware",
3
- "version": "2.8.2",
3
+ "version": "2.9.0",
4
4
  "description": "Composable middleware for nifra - auth, CSRF, JWT/JWKS, IP restriction, CORS, body limits, response cache, timing, and operational helpers.",
5
5
  "keywords": [
6
6
  "bun",
@@ -46,10 +46,10 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@nifrajs/schema": "2.8.2"
49
+ "@nifrajs/schema": "2.9.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@nifrajs/core": "^2.8.2"
52
+ "@nifrajs/core": "^2.9.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@nifrajs/core": "workspace:*",
package/src/_utils.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export type MaybePromise<T> = T | Promise<T>
2
2
 
3
+ import type { NodeResponseContext } from "@nifrajs/core/server"
4
+ import { parseCookies as parseCoreCookies } from "@nifrajs/core/server"
5
+
3
6
  export const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE"])
4
7
 
5
8
  const TEXT = new TextEncoder()
@@ -15,28 +18,8 @@ export function jsonError(
15
18
  )
16
19
  }
17
20
 
18
- export function parseCookies(header: string | null | undefined): Record<string, string> {
19
- // Null-prototype: an untrusted cookie name (`constructor`/`__proto__`/…) is an inert own key,
20
- // never a prototype-member shadow. Mirrors core's parseCookies + the query/form parsers.
21
- const out: Record<string, string> = Object.create(null)
22
- if (!header) return out
23
- for (const part of header.split(";")) {
24
- const eq = part.indexOf("=")
25
- if (eq < 1) continue
26
- const name = part.slice(0, eq).trim()
27
- if (name === "") continue
28
- let value = part.slice(eq + 1).trim()
29
- if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
30
- value = value.slice(1, -1)
31
- }
32
- try {
33
- out[name] = decodeURIComponent(value)
34
- } catch {
35
- out[name] = value
36
- }
37
- }
38
- return out
39
- }
21
+ /** Shared with core so auth/CSRF middleware gets the allocation-light cookie scanner too. */
22
+ export const parseCookies = parseCoreCookies
40
23
 
41
24
  export function quotedHeaderValue(value: string): string {
42
25
  let out = ""
@@ -152,12 +135,44 @@ export async function verifyHmacSha256(
152
135
  * - the first mutation throws before any partial change), so a mutation chain is safe to pass.
153
136
  */
154
137
  export function withHeaders(res: Response, apply: (headers: Headers) => void): Response {
138
+ const responseHeaders = res.headers
139
+ const probe = "x-nifra-header-probe"
140
+ let previous: string | null = null
141
+ let mutable = true
155
142
  try {
156
- apply(res.headers)
157
- return res
143
+ previous = responseHeaders.get(probe)
144
+ responseHeaders.set(probe, "1")
145
+ if (previous === null) responseHeaders.delete(probe)
146
+ else responseHeaders.set(probe, previous)
158
147
  } catch {
148
+ mutable = false
149
+ try {
150
+ if (previous === null) responseHeaders.delete(probe)
151
+ else responseHeaders.set(probe, previous)
152
+ } catch {
153
+ // The guarded response rejected cleanup; the clone below is authoritative.
154
+ }
155
+ }
156
+ if (!mutable) {
159
157
  const headers = new Headers(res.headers)
160
158
  apply(headers)
161
159
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers })
162
160
  }
161
+ apply(responseHeaders)
162
+ return res
163
+ }
164
+
165
+ /** Mutate Node-direct response headers without constructing a Web Response. */
166
+ export function withNodeHeaders(
167
+ res: NodeResponseContext,
168
+ apply: (headers: Record<string, string | readonly string[]>) => void,
169
+ ): void {
170
+ if (res.headers === undefined) {
171
+ res.headers = Object.create(null) as Record<string, string | readonly string[]>
172
+ } else if (Object.getPrototypeOf(res.headers) !== null) {
173
+ const safe = Object.create(null) as Record<string, string | readonly string[]>
174
+ Object.assign(safe, res.headers)
175
+ res.headers = safe
176
+ }
177
+ apply(res.headers)
163
178
  }
@@ -1,4 +1,5 @@
1
- import { definePlugin } from "@nifrajs/core/server"
1
+ import { definePlugin, type Middleware } from "@nifrajs/core/server"
2
+ import { withHeaders } from "./_utils.ts"
2
3
 
3
4
  export interface CacheControlOptions {
4
5
  /** Methods whose responses get the header. Default `["GET", "HEAD"]`. */
@@ -29,17 +30,29 @@ export function cacheControl(
29
30
  const methods = new Set((options.methods ?? ["GET", "HEAD"]).map((m) => m.toUpperCase()))
30
31
  const statusOk = options.status ?? ((status: number) => status >= 200 && status < 300)
31
32
  const respectExisting = options.respectExisting !== false
32
- const resolve = typeof value === "function" ? value : () => value
33
- return definePlugin("cacheControl", (app) =>
34
- app.onResponse((res, req) => {
35
- if (!methods.has(req.method)) return res
36
- if (!statusOk(res.status)) return res
37
- if (respectExisting && res.headers.has("cache-control")) return res
38
- const directive = resolve(req)
39
- if (directive === undefined) return res
40
- const headers = new Headers(res.headers)
41
- headers.set("cache-control", directive)
42
- return new Response(res.body, { status: res.status, statusText: res.statusText, headers })
43
- }),
44
- )
33
+ return definePlugin("cacheControl", (app) => {
34
+ // A fixed directive needs nothing from the request beyond the method, so it ships as one
35
+ // portable header hook. A dynamic directive's resolver takes a real `Request`, which only the
36
+ // Web response walk carries - it keeps the full `onResponse` contract (and its cost).
37
+ const middleware: Middleware =
38
+ typeof value === "string"
39
+ ? {
40
+ onResponseHeaders(headers, req, status) {
41
+ if (!methods.has(req.method) || !statusOk(status)) return
42
+ if (respectExisting && headers.has("cache-control")) return
43
+ headers.set("cache-control", value)
44
+ },
45
+ }
46
+ : {
47
+ onResponse(res, req) {
48
+ if (!methods.has(req.method)) return res
49
+ if (!statusOk(res.status)) return res
50
+ if (respectExisting && res.headers.has("cache-control")) return res
51
+ const directive = value(req)
52
+ if (directive === undefined) return res
53
+ return withHeaders(res, (headers) => headers.set("cache-control", directive))
54
+ },
55
+ }
56
+ return app.use(middleware)
57
+ })
45
58
  }
package/src/cache.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Middleware } from "@nifrajs/core/server"
2
- import type { MaybePromise } from "./_utils.ts"
2
+ import { type MaybePromise, withHeaders } from "./_utils.ts"
3
3
 
4
4
  export interface CachedResponse {
5
5
  readonly status: number
@@ -174,9 +174,7 @@ function withStatusHeader(
174
174
  status: "HIT" | "MISS" | "BYPASS",
175
175
  ): Response {
176
176
  if (header === false) return res
177
- const headers = new Headers(res.headers)
178
- headers.set(header, status)
179
- return new Response(res.body, { status: res.status, statusText: res.statusText, headers })
177
+ return withHeaders(res, (headers) => headers.set(header, status))
180
178
  }
181
179
 
182
180
  /**