@nifrajs/middleware 2.8.1 → 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 (66) 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/idempotency.d.ts.map +1 -1
  25. package/dist/idempotency.js +10 -1
  26. package/dist/idempotency.js.map +1 -1
  27. package/dist/jwt.js +1 -1
  28. package/dist/jwt.js.map +1 -1
  29. package/dist/language.d.ts.map +1 -1
  30. package/dist/language.js +14 -15
  31. package/dist/language.js.map +1 -1
  32. package/dist/logger.d.ts.map +1 -1
  33. package/dist/logger.js +34 -16
  34. package/dist/logger.js.map +1 -1
  35. package/dist/powered-by.d.ts.map +1 -1
  36. package/dist/powered-by.js +8 -5
  37. package/dist/powered-by.js.map +1 -1
  38. package/dist/pretty-json.d.ts +5 -2
  39. package/dist/pretty-json.d.ts.map +1 -1
  40. package/dist/pretty-json.js +85 -64
  41. package/dist/pretty-json.js.map +1 -1
  42. package/dist/rate-limit.d.ts.map +1 -1
  43. package/dist/rate-limit.js +76 -19
  44. package/dist/rate-limit.js.map +1 -1
  45. package/dist/request-id.js +1 -1
  46. package/dist/request-id.js.map +1 -1
  47. package/dist/security-headers.d.ts +8 -4
  48. package/dist/security-headers.d.ts.map +1 -1
  49. package/dist/security-headers.js +18 -16
  50. package/dist/security-headers.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/_utils.ts +39 -24
  53. package/src/cache-control.ts +27 -14
  54. package/src/cache.ts +2 -4
  55. package/src/compression.ts +202 -62
  56. package/src/cors.ts +23 -14
  57. package/src/etag.ts +141 -59
  58. package/src/idempotency.ts +11 -1
  59. package/src/jwt.ts +1 -1
  60. package/src/language.ts +12 -13
  61. package/src/logger.ts +24 -6
  62. package/src/powered-by.ts +8 -4
  63. package/src/pretty-json.ts +95 -70
  64. package/src/rate-limit.ts +86 -20
  65. package/src/request-id.ts +1 -1
  66. package/src/security-headers.ts +17 -15
@@ -3,14 +3,15 @@ const JSON_TYPE = /^(?:application\/json|[^/]+\/[^;]+\+json)(?:\s*;|$)/i;
3
3
  function isJson(contentType) {
4
4
  return JSON_TYPE.test(contentType);
5
5
  }
6
- function parseLength(value) {
7
- if (value === null)
8
- return undefined;
9
- if (!/^(?:0|[1-9]\d*)$/.test(value))
10
- return undefined;
11
- return Number(value);
12
- }
13
- /** Re-emit the bytes already pulled, then the untouched remainder of the same reader. */
6
+ const concat = (chunks, total) => {
7
+ const out = new Uint8Array(total);
8
+ let offset = 0;
9
+ for (const chunk of chunks) {
10
+ out.set(chunk, offset);
11
+ offset += chunk.byteLength;
12
+ }
13
+ return out;
14
+ };
14
15
  function replayFrom(res, chunks, reader) {
15
16
  const stream = new ReadableStream({
16
17
  start(controller) {
@@ -20,15 +21,12 @@ function replayFrom(res, chunks, reader) {
20
21
  async pull(controller) {
21
22
  try {
22
23
  const { done, value } = await reader.read();
23
- // No value and not done cannot happen for a byte stream, but it means the same thing here:
24
- // there is nothing further to emit.
25
24
  if (done || value === undefined)
26
25
  controller.close();
27
26
  else
28
27
  controller.enqueue(value);
29
28
  }
30
29
  catch (error) {
31
- // The upstream body failed; surface that to the client rather than truncating silently.
32
30
  controller.error(error);
33
31
  }
34
32
  },
@@ -40,20 +38,6 @@ function replayFrom(res, chunks, reader) {
40
38
  headers: res.headers,
41
39
  });
42
40
  }
43
- /**
44
- * Read up to `maxBytes` of a response, without disturbing it when the answer is "pass through".
45
- *
46
- * `Response.clone()` is the obvious way to peek and is a trap here. Cancelling the clone's reader once
47
- * the cap is exceeded also stalls the ORIGINAL body in Bun, so an oversized streamed response hung the
48
- * client instead of passing through - a hang, from a middleware whose entire job is cosmetic. (A clone
49
- * read to completion is fine; only the cancel breaks it, which is why every buffered case passed and
50
- * only a streamed one failed.) Abandoning the clone rather than cancelling it trades the hang for
51
- * buffering the whole oversized body, which is the exact cost the cap exists to avoid.
52
- *
53
- * So the body is read directly and, when it proves too large, replayed: the bytes already pulled are
54
- * re-emitted ahead of the rest of the same reader. No tee, nothing buffered past the cap, and the
55
- * response the client receives is byte-for-byte the one the handler produced.
56
- */
57
41
  async function peekText(res, maxBytes) {
58
42
  const body = res.body;
59
43
  if (body === null)
@@ -65,12 +49,14 @@ async function peekText(res, maxBytes) {
65
49
  while (true) {
66
50
  const { done, value } = await reader.read();
67
51
  if (done) {
68
- const decoder = new TextDecoder();
52
+ const decoder = new TextDecoder("utf-8", { fatal: true });
69
53
  let text = "";
70
54
  for (const chunk of chunks)
71
55
  text += decoder.decode(chunk, { stream: true });
72
- return { text: text + decoder.decode() };
56
+ return { text: text + decoder.decode(), bytes: concat(chunks, total) };
73
57
  }
58
+ if (value === undefined)
59
+ return { response: replayFrom(res, chunks, reader) };
74
60
  chunks.push(value);
75
61
  total += value.byteLength;
76
62
  if (total > maxBytes)
@@ -81,6 +67,9 @@ async function peekText(res, maxBytes) {
81
67
  return { response: replayFrom(res, chunks, reader) };
82
68
  }
83
69
  }
70
+ function requestView(request) {
71
+ return { method: request.method, url: request.url, header: (name) => request.headers.get(name) };
72
+ }
84
73
  /**
85
74
  * Pretty-print JSON responses for debugging and developer-facing APIs. It only touches JSON content,
86
75
  * skips encoded responses, caps inspection size, and leaves invalid JSON untouched.
@@ -91,8 +80,8 @@ export function prettyJson(options = {}) {
91
80
  throw new Error("prettyJson: spaces must be an integer from 0 to 10");
92
81
  }
93
82
  const maxBytes = options.maxBytes ?? 1_000_000;
94
- if (!Number.isInteger(maxBytes) || maxBytes < 0) {
95
- throw new Error("prettyJson: maxBytes must be a non-negative integer");
83
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
84
+ throw new Error("prettyJson: maxBytes must be a non-negative safe integer");
96
85
  }
97
86
  const newline = options.newline !== false;
98
87
  const query = options.query ?? false;
@@ -100,41 +89,73 @@ export function prettyJson(options = {}) {
100
89
  throw new Error("prettyJson: query is empty");
101
90
  const enabled = options.enabled ?? true;
102
91
  const isEnabled = typeof enabled === "function" ? enabled : enabled ? () => true : () => false;
103
- return definePlugin("prettyJson", (app) => app.onResponse(async (res, req) => {
104
- if (!isEnabled(req))
105
- return res;
106
- if (query !== false && !new URL(req.url).searchParams.has(query))
107
- return res;
108
- if (res.body === null || res.headers.has("content-encoding"))
109
- return res;
110
- if (!isJson(res.headers.get("content-type") ?? ""))
111
- return res;
112
- const declared = parseLength(res.headers.get("content-length"));
113
- if (declared !== undefined && declared > maxBytes)
114
- return res;
115
- const peeked = await peekText(res, maxBytes);
116
- if ("response" in peeked)
117
- return peeked.response;
118
- let parsed;
119
- try {
120
- parsed = JSON.parse(peeked.text);
121
- }
122
- catch {
123
- // The body was read to decide that, so `res` can no longer be returned as-is. Rebuild it from
124
- // the text: re-encoding UTF-8 reproduces the original bytes, so `content-length` still holds.
125
- return new Response(peeked.text, {
126
- status: res.status,
127
- statusText: res.statusText,
128
- headers: res.headers,
129
- });
130
- }
131
- const headers = new Headers(res.headers);
132
- headers.delete("content-length");
133
- return new Response(`${JSON.stringify(parsed, null, spaces)}${newline ? "\n" : ""}`, {
134
- status: res.status,
135
- statusText: res.statusText,
136
- headers,
137
- });
92
+ const decoder = new TextDecoder();
93
+ const encoder = new TextEncoder();
94
+ return definePlugin("prettyJson", (app) => app.use({
95
+ onResponseBody(body, headers, req) {
96
+ if (!isEnabled(req))
97
+ return undefined;
98
+ if (query !== false && !new URL(req.url).searchParams.has(query))
99
+ return undefined;
100
+ if (headers.has("content-encoding"))
101
+ return undefined;
102
+ if (!isJson(headers.get("content-type") ?? ""))
103
+ return undefined;
104
+ const text = typeof body === "string" ? body : decoder.decode(body);
105
+ if ((typeof body === "string" ? encoder.encode(body).byteLength : body.byteLength) > maxBytes) {
106
+ return undefined;
107
+ }
108
+ let parsed;
109
+ try {
110
+ parsed = JSON.parse(text);
111
+ }
112
+ catch {
113
+ return undefined;
114
+ }
115
+ return `${JSON.stringify(parsed, null, spaces)}${newline ? "\n" : ""}`;
116
+ },
117
+ onResponseRaw(response, req) {
118
+ if (!isEnabled(requestView(req)))
119
+ return response;
120
+ if (query !== false && !new URL(req.url).searchParams.has(query))
121
+ return response;
122
+ if (response.body === null ||
123
+ response.status === 204 ||
124
+ response.status === 205 ||
125
+ response.status === 304 ||
126
+ response.headers.has("content-encoding"))
127
+ return response;
128
+ if (!isJson(response.headers.get("content-type") ?? ""))
129
+ return response;
130
+ const declared = response.headers.get("content-length");
131
+ if (declared !== null && /^(?:0|[1-9]\d*)$/.test(declared) && Number(declared) > maxBytes) {
132
+ return response;
133
+ }
134
+ return prettyRawResponse(response, maxBytes, spaces, newline);
135
+ },
138
136
  }));
139
137
  }
138
+ async function prettyRawResponse(response, maxBytes, spaces, newline) {
139
+ const peeked = await peekText(response, maxBytes);
140
+ if ("response" in peeked)
141
+ return peeked.response;
142
+ let parsed;
143
+ try {
144
+ parsed = JSON.parse(peeked.text);
145
+ }
146
+ catch {
147
+ return new Response(peeked.bytes, {
148
+ status: response.status,
149
+ statusText: response.statusText,
150
+ headers: response.headers,
151
+ });
152
+ }
153
+ const headers = new Headers(response.headers);
154
+ headers.delete("content-length");
155
+ return new Response(`${JSON.stringify(parsed, null, spaces)}${newline ? "\n" : ""}`, {
156
+ status: response.status,
157
+ statusText: response.statusText,
158
+ headers,
159
+ });
160
+ }
140
161
  //# sourceMappingURL=pretty-json.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pretty-json.js","sourceRoot":"","sources":["../src/pretty-json.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pretty-json.js","sourceRoot":"","sources":["../src/pretty-json.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAC7D;AAED,MAAM,WAAW,kBAAkB;IACjC,mHAAmH;IACnH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IACpC;mFAC+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAOD;;;;;;;;GAQG;AACH,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwD;IAChF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,aAAa,CAAI;IAEzB;;yGAEqG;IACrG,IAAI,iBAAiB,IAAI,MAAM,CAE9B;gBAEW,OAAO,GAAE,kBAAuB;IAiB5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAmB5D,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,cAAc;CAuBvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,iGAAiG;IACjG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAA;CACxC;AA4BD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,UAAU,CA8D/D"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAA2C,MAAM,sBAAsB,CAAA;AAG/F,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAC7D;AAED,MAAM,WAAW,kBAAkB;IACjC,mHAAmH;IACnH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IACpC;mFAC+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAOD;;;;;;;;GAQG;AACH,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwD;IAChF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,aAAa,CAAI;IAEzB;;yGAEqG;IACrG,IAAI,iBAAiB,IAAI,MAAM,CAE9B;gBAEW,OAAO,GAAE,kBAAuB;IAiB5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAmB5D,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,cAAc;CAuBvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,iGAAiG;IACjG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAA;CACxC;AAkDD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,UAAU,CAyG/D"}
@@ -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.1",
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.1"
49
+ "@nifrajs/schema": "2.9.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@nifrajs/core": "^2.8.1"
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
  }