@nifrajs/core 5.12.0 → 5.13.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 (46) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/edge-kit.d.ts +2 -1
  3. package/dist/edge-kit.d.ts.map +1 -1
  4. package/dist/edge-kit.js +2 -1
  5. package/dist/edge-kit.js.map +1 -1
  6. package/dist/idempotency.d.ts +12 -2
  7. package/dist/idempotency.d.ts.map +1 -1
  8. package/dist/idempotency.js +40 -22
  9. package/dist/idempotency.js.map +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/internal/route-execution.d.ts +3 -0
  13. package/dist/internal/route-execution.d.ts.map +1 -1
  14. package/dist/server/body-lane.d.ts +12 -0
  15. package/dist/server/body-lane.d.ts.map +1 -1
  16. package/dist/server/body-lane.js +27 -0
  17. package/dist/server/body-lane.js.map +1 -1
  18. package/dist/server/core-copies.js +1 -1
  19. package/dist/server/request-context.d.ts.map +1 -1
  20. package/dist/server/request-context.js +8 -4
  21. package/dist/server/request-context.js.map +1 -1
  22. package/dist/server/respond.d.ts.map +1 -1
  23. package/dist/server/respond.js +10 -0
  24. package/dist/server/respond.js.map +1 -1
  25. package/dist/server/server-types.d.ts +16 -5
  26. package/dist/server/server-types.d.ts.map +1 -1
  27. package/dist/server/server.d.ts +4 -2
  28. package/dist/server/server.d.ts.map +1 -1
  29. package/dist/server/server.js +46 -4
  30. package/dist/server/server.js.map +1 -1
  31. package/dist/transport-codec.d.ts +4 -6
  32. package/dist/transport-codec.d.ts.map +1 -1
  33. package/dist/transport-codec.js +4 -6
  34. package/dist/transport-codec.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/edge-kit.ts +2 -1
  37. package/src/idempotency.ts +39 -23
  38. package/src/index.ts +1 -1
  39. package/src/internal/route-execution.ts +3 -0
  40. package/src/server/body-lane.ts +35 -0
  41. package/src/server/core-copies.ts +1 -1
  42. package/src/server/request-context.ts +12 -8
  43. package/src/server/respond.ts +11 -0
  44. package/src/server/server-types.ts +15 -5
  45. package/src/server/server.ts +63 -6
  46. package/src/transport-codec.ts +4 -6
@@ -17,8 +17,8 @@ export declare class TransportCodecError extends TypeError {
17
17
  }
18
18
  /**
19
19
  * Enforce the transport byte cap on already-read text - the same limit (and the same error) the
20
- * streaming reader applies during a bounded read. Exported for callers that read a body through a
21
- * native, non-streaming path (the typed client's in-process branch) and still owe the cap contract.
20
+ * streaming reader applies during a bounded read. Exported for callers that need to decode text
21
+ * after a bounded read while preserving the text decoder's lenient UTF-8 behavior.
22
22
  */
23
23
  export declare function assertTransportTextBounded(text: string, options: TransportDecodeOptions): void;
24
24
  export declare const plainJsonCodec: TransportCodec;
@@ -46,10 +46,8 @@ export declare function readBoundedBytes(response: Response, options: TransportD
46
46
  /**
47
47
  * Decode an ALREADY-READ transport body. The counterpart to {@link decodeTransportResponse} for a
48
48
  * caller that obtained the text through its own bounded read - notably the typed client's
49
- * in-process path, where the response body is same-process memory that was fully resident before
50
- * the read, so the native `Response.text()` is safe and the streaming byte-cap loop would protect
51
- * nothing while costing ~23x. The byte cap is still enforced here (identical error), just after
52
- * the read instead of during it.
49
+ * in-process path, which uses this to preserve lenient UTF-8 decoding after the shared byte-cap
50
+ * reader has completed.
53
51
  */
54
52
  export declare function decodeTransportText(text: string, contentType: string | null, registry?: TransportCodecRegistry, options?: TransportDecodeOptions): unknown;
55
53
  export declare function decodeTransportResponse(response: Response, registry?: TransportCodecRegistry, options?: TransportDecodeOptions): Promise<unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"transport-codec.d.ts","sourceRoot":"","sources":["../src/transport-codec.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAA;IAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAC9B;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAkCD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAU9F;AAID,eAAO,MAAM,cAAc,EAAE,cAY3B,CAAA;AAEF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAAA;IAC1D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAAA;IAChD,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAAA;CACxD;AAUD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,QAAQ,GAAE,cAA+B,GACxC,sBAAsB,CAgFxB;AAED,eAAO,MAAM,sBAAsB,EAAE,sBAEnC,CAAA;AAEF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,KAAK,GAAE,cAA+B,EACtC,IAAI,GAAE,YAAiB,GACtB,QAAQ,CAKV;AAQD;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CAkCrB;AAcD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,GAAE,sBAA+C,EACzD,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAQT;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,QAAQ,EAClB,QAAQ,GAAE,sBAA+C,EACzD,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,OAAO,CAAC,CAQlB;AAQD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,KAAK,GAAE,cAA+B,GACrC,MAAM,CAER;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,sBAA+C,EACzD,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAsBT"}
1
+ {"version":3,"file":"transport-codec.d.ts","sourceRoot":"","sources":["../src/transport-codec.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAA;IAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAC9B;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAkCD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAU9F;AAID,eAAO,MAAM,cAAc,EAAE,cAY3B,CAAA;AAEF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAAA;IAC1D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAAA;IAChD,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAAA;CACxD;AAUD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,QAAQ,GAAE,cAA+B,GACxC,sBAAsB,CAgFxB;AAED,eAAO,MAAM,sBAAsB,EAAE,sBAEnC,CAAA;AAEF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,KAAK,GAAE,cAA+B,EACtC,IAAI,GAAE,YAAiB,GACtB,QAAQ,CAKV;AAQD;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CAkCrB;AAcD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,GAAE,sBAA+C,EACzD,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAQT;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,QAAQ,EAClB,QAAQ,GAAE,sBAA+C,EACzD,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,OAAO,CAAC,CAQlB;AAQD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,KAAK,GAAE,cAA+B,GACrC,MAAM,CAER;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,sBAA+C,EACzD,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAsBT"}
@@ -43,8 +43,8 @@ function byteLength(text) {
43
43
  }
44
44
  /**
45
45
  * Enforce the transport byte cap on already-read text - the same limit (and the same error) the
46
- * streaming reader applies during a bounded read. Exported for callers that read a body through a
47
- * native, non-streaming path (the typed client's in-process branch) and still owe the cap contract.
46
+ * streaming reader applies during a bounded read. Exported for callers that need to decode text
47
+ * after a bounded read while preserving the text decoder's lenient UTF-8 behavior.
48
48
  */
49
49
  export function assertTransportTextBounded(text, options) {
50
50
  const maxBytes = maxBytesOf(options);
@@ -233,10 +233,8 @@ async function readBoundedText(response, options) {
233
233
  /**
234
234
  * Decode an ALREADY-READ transport body. The counterpart to {@link decodeTransportResponse} for a
235
235
  * caller that obtained the text through its own bounded read - notably the typed client's
236
- * in-process path, where the response body is same-process memory that was fully resident before
237
- * the read, so the native `Response.text()` is safe and the streaming byte-cap loop would protect
238
- * nothing while costing ~23x. The byte cap is still enforced here (identical error), just after
239
- * the read instead of during it.
236
+ * in-process path, which uses this to preserve lenient UTF-8 decoding after the shared byte-cap
237
+ * reader has completed.
240
238
  */
241
239
  export function decodeTransportText(text, contentType, registry = defaultTransportCodecs, options = {}) {
242
240
  assertBounded(text, options);
@@ -1 +1 @@
1
- {"version":3,"file":"transport-codec.js","sourceRoot":"","sources":["../src/transport-codec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transport-codec.js","sourceRoot":"","sources":["../src/transport-codec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/core",
3
- "version": "5.12.0",
3
+ "version": "5.13.0",
4
4
  "description": "Bun-native, contract-first HTTP framework - the router, server, and route descriptor.",
5
5
  "keywords": [
6
6
  "bun",
package/src/edge-kit.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  * exposes the one lane the fused body runner already calls, so a compact server reuses it verbatim:
9
9
  *
10
10
  * - {@link readBodyFramed} read + cap + proto-guard + content-type framing (JSON / urlencoded / 415)
11
+ * - {@link crossSiteFormRefused} the form-origin refusal for an unsafe route that reads no body
11
12
  * - {@link capBodyBytes} the same byte cap on a handler's own reads of the raw request
12
13
  * - {@link boundedJsonText} preflight JSON output against an exact UTF-8 byte budget
13
14
  * - {@link renderResult} render a {@link ResponseResult} (e.g. a 413 / 415 rejection) to a `Response`
@@ -23,7 +24,7 @@
23
24
 
24
25
  export { type ErrorLogDetail, emitRequestErrorLog } from "./server/bare-error-lane.ts"
25
26
  export { assertByteLimit, capBodyBytes, DEFAULT_MAX_BODY_BYTES } from "./server/body.ts"
26
- export { type FormOrigins, readBodyFramed } from "./server/body-lane.ts"
27
+ export { crossSiteFormRefused, type FormOrigins, readBodyFramed } from "./server/body-lane.ts"
27
28
  export { plainError } from "./server/http.ts"
28
29
  export { boundedJsonText, JsonByteLimitError, jsonResponse } from "./server/json-response.ts"
29
30
  export {
@@ -22,6 +22,9 @@ export const DEFAULT_IDEMPOTENCY_TTL_MS = 86_400_000
22
22
  /** Default lease on a key whose handler is still running, with a store that can renew it: 60 seconds. */
23
23
  export const DEFAULT_IDEMPOTENCY_PENDING_TTL_MS = 60_000
24
24
 
25
+ /** Default maximum response body retained by the standalone idempotency helpers: 1 MB. */
26
+ export const DEFAULT_IDEMPOTENCY_MAX_RESPONSE_BYTES = 1_000_000
27
+
25
28
  /** Canonical request header carrying the client-chosen idempotency key. */
26
29
  export const DEFAULT_IDEMPOTENCY_HEADER = "idempotency-key"
27
30
 
@@ -289,16 +292,24 @@ export class IdempotencyResponseTooLargeError extends Error {
289
292
  }
290
293
  }
291
294
 
295
+ /**
296
+ * Clone and buffer a response body without exceeding the storage bound. A null body represents no
297
+ * bytes, even when a HEAD/304 response carries the corresponding GET representation's
298
+ * `Content-Length`; the body check therefore precedes the header shortcut. Oversize cancellation is
299
+ * intentionally fire-and-forget because a tee branch can remain pending until its sibling is read.
300
+ */
292
301
  async function boundedResponseBytes(response: Response, maxBytes: number): Promise<Uint8Array> {
302
+ const body = response.body
303
+ if (body === null) return new Uint8Array()
293
304
  const contentLength = response.headers.get("content-length")
294
305
  if (contentLength !== null && /^\d+$/.test(contentLength) && Number(contentLength) > maxBytes) {
295
306
  // Replaced by a terminal error, never read: a streaming producer would otherwise stay open.
296
- await response.body?.cancel().catch(() => {})
307
+ void Promise.allSettled([body.cancel()])
297
308
  throw new IdempotencyResponseTooLargeError(maxBytes)
298
309
  }
299
- const body = response.clone().body
300
- if (body === null) return new Uint8Array()
301
- const reader = body.getReader()
310
+ const cloneBody = response.clone().body
311
+ if (cloneBody === null) return new Uint8Array()
312
+ const reader = cloneBody.getReader()
302
313
  const chunks: Uint8Array[] = []
303
314
  let total = 0
304
315
  try {
@@ -311,10 +322,11 @@ async function boundedResponseBytes(response: Response, maxBytes: number): Promi
311
322
  // unread, so a streaming producer can keep the tee pending forever. This path stores no
312
323
  // response and the caller will replace it with a terminal error, so cancel both branches.
313
324
  const liveBody = response.body
314
- await Promise.allSettled([
315
- reader.cancel(),
316
- liveBody === null ? Promise.resolve() : liveBody.cancel(),
317
- ])
325
+ // Do not await tee cancellation: either branch can remain pending until the other branch is
326
+ // consumed, but the size error is already definitive and must reach the caller promptly.
327
+ const cancellations = [reader.cancel()]
328
+ if (liveBody !== null) cancellations.push(liveBody.cancel())
329
+ void Promise.allSettled(cancellations)
318
330
  throw new IdempotencyResponseTooLargeError(maxBytes)
319
331
  }
320
332
  chunks.push(value)
@@ -331,19 +343,21 @@ async function boundedResponseBytes(response: Response, maxBytes: number): Promi
331
343
  return bytes
332
344
  }
333
345
 
334
- /** Buffer a response into a storable form. Clones first so the live response body stays intact. */
346
+ /**
347
+ * Buffer a response into a storable form. Clones first so the live response body stays intact on
348
+ * success. If the body exceeds the bound, both branches may be cancelled to stop the producer and
349
+ * the caller must discard or replace the response. Unless a bound is supplied, at most
350
+ * {@link DEFAULT_IDEMPOTENCY_MAX_RESPONSE_BYTES} are retained.
351
+ */
335
352
  export async function serializeResponse(
336
353
  response: Response,
337
354
  options: { readonly maxBytes?: number } = {},
338
355
  ): Promise<StoredResponse> {
339
- const maxBytes = options.maxBytes
340
- if (maxBytes !== undefined && (!Number.isInteger(maxBytes) || maxBytes < 0)) {
356
+ const maxBytes = options.maxBytes ?? DEFAULT_IDEMPOTENCY_MAX_RESPONSE_BYTES
357
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
341
358
  throw new RangeError("idempotency: max response bytes must be a non-negative integer")
342
359
  }
343
- const bytes =
344
- maxBytes === undefined
345
- ? new Uint8Array(await response.clone().arrayBuffer())
346
- : await boundedResponseBytes(response, maxBytes)
360
+ const bytes = await boundedResponseBytes(response, maxBytes)
347
361
  return {
348
362
  status: response.status,
349
363
  headers: replaySafeHeaders(response.headers),
@@ -351,7 +365,10 @@ export async function serializeResponse(
351
365
  }
352
366
  }
353
367
 
354
- /** Rebuild a live response from storage, stamping the replay marker header. */
368
+ /**
369
+ * Rebuild a live response from storage, stamping the replay marker header. Unless a bound is
370
+ * supplied, stored bodies larger than {@link DEFAULT_IDEMPOTENCY_MAX_RESPONSE_BYTES} are rejected.
371
+ */
355
372
  export function responseFromStored(
356
373
  stored: StoredResponse,
357
374
  options: { readonly maxBytes?: number } = {},
@@ -360,13 +377,12 @@ export function responseFromStored(
360
377
  throw new TypeError("idempotency: corrupt stored response status")
361
378
  }
362
379
  const decodedLength = base64DecodedLength(stored.body)
363
- if (options.maxBytes !== undefined) {
364
- if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0) {
365
- throw new RangeError("idempotency: max response bytes must be a non-negative safe integer")
366
- }
367
- if (decodedLength > options.maxBytes) {
368
- throw new IdempotencyResponseTooLargeError(options.maxBytes)
369
- }
380
+ const maxBytes = options.maxBytes ?? DEFAULT_IDEMPOTENCY_MAX_RESPONSE_BYTES
381
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
382
+ throw new RangeError("idempotency: max response bytes must be a non-negative safe integer")
383
+ }
384
+ if (decodedLength > maxBytes) {
385
+ throw new IdempotencyResponseTooLargeError(maxBytes)
370
386
  }
371
387
  const headers = new Headers(replaySafeHeaders(stored.headers) as [string, string][])
372
388
  headers.set(IDEMPOTENT_REPLAY_HEADER, "1")
package/src/index.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * read its own package.json at runtime. `scripts/version.ts` rewrites it on every release bump and
17
17
  * `check:publish` asserts it equals `@nifrajs/core`'s package version.
18
18
  */
19
- export const VERSION = "5.12.0" as const
19
+ export const VERSION = "5.13.0" as const
20
20
 
21
21
  export type Version = typeof VERSION
22
22
 
@@ -325,6 +325,9 @@ export interface RouteEntry {
325
325
  readonly onError: ReadonlyArray<RawErrorHandler>
326
326
  /** Registration-specialized hook shape: a route whose only lifecycle steps are derive, before and after hooks. */
327
327
  readonly lifecycleHookLane: "hooks" | undefined
328
+ /** An unsafe method with no body schema: no lane reads a body to refuse a cross-site form, so the
329
+ * dispatchers refuse it before the lanes run. */
330
+ readonly formOriginGuard: boolean
328
331
  /** Wraps the matched route lifecycle. Empty for the common no-around path. */
329
332
  readonly around: ReadonlyArray<RawAround>
330
333
  /** The single immutable execution decision consumed by portable, Node-direct, and Bun-native paths. */
@@ -236,6 +236,41 @@ function formOriginAllowed(source: RequestSource, formOrigins: FormOrigins | und
236
236
  : formOrigins.includes(origin)
237
237
  }
238
238
 
239
+ /** A route that needs {@link crossSiteFormRefused} ahead of its lanes: an unsafe method with no
240
+ * `schema.body`. A body schema reads its body through {@link readBodyFramed}, which refuses a
241
+ * cross-site form itself; a route without one reads nothing, so nothing else would. */
242
+ export const guardsBodylessForm = (method: string, hasBodySchema: boolean): boolean =>
243
+ !hasBodySchema && !/^(?:GET|HEAD|OPTIONS|TRACE|QUERY)$/i.test(method)
244
+
245
+ // Schemes a browser keeps in its HTTP-auth cache and re-attaches to a credentialed cross-site request
246
+ // on its own, so a page can send one with no preflight. Any other `Authorization` value has to be set
247
+ // by script, and a cross-origin script cannot set it without a preflight that CORS has judged.
248
+ const BROWSER_MANAGED_AUTH = /^(?:basic|digest|negotiate|ntlm)\b/i
249
+
250
+ /** Whether a request is a cross-site post that {@link readBodyFramed} would refuse, or one a page can
251
+ * send the same way: a form media type, or no content type at all (`fetch` with `no-cors`). A request
252
+ * with no content type that carries an `Authorization` header other than a browser-managed scheme
253
+ * (Basic, Digest, Negotiate, NTLM) passes: script-set, it forced a preflight, so the app's CORS policy
254
+ * has already judged its origin. It decides on the cheapest header first: a request without an
255
+ * `Origin` (any non-browser client) costs one lookup, and a browser's own-origin post stops at
256
+ * `Sec-Fetch-Site` before the URL is parsed. */
257
+ export const crossSiteFormRefused = (
258
+ source: RequestSource,
259
+ formOrigins: FormOrigins | undefined,
260
+ ): boolean => {
261
+ const origin = headerOf(source, "origin")
262
+ if (origin === null || headerOf(source, "sec-fetch-site") === "same-origin") return false
263
+ const type = headerOf(source, "content-type") ?? ""
264
+ const authorization = type === "" ? headerOf(source, "authorization") : null
265
+ return (
266
+ (type === ""
267
+ ? authorization === null || BROWSER_MANAGED_AUTH.test(authorization)
268
+ : isFormMediaType(type)) &&
269
+ !source.url.startsWith(`${origin}/`) &&
270
+ !formOriginAllowed(source, formOrigins)
271
+ )
272
+ }
273
+
239
274
  /**
240
275
  * The shared content-type dispatcher around the JSON and urlencoded lanes. A `bodySchema` branded
241
276
  * with {@link SCHEMA_BODY_READER} takes every other media type; JSON and urlencoded bodies keep
@@ -26,7 +26,7 @@ if (!copies.includes(here) && copies.push(here) > 1) {
26
26
 
27
27
  /** This copy's version: a literal like the package root's `VERSION` (no fs on the edge), which
28
28
  * `scripts/version.ts` rewrites on every release and `check:publish` asserts. */
29
- const COPY_VERSION = "5.12.0"
29
+ const COPY_VERSION = "5.13.0"
30
30
 
31
31
  /** Record this copy's version beside its URL. Called by each `server()` behind the development guard,
32
32
  * so a production bundle drops it with the version; a copy that built a server has recorded one. */
@@ -5,7 +5,7 @@
5
5
  * sits below the server module in the graph.
6
6
  */
7
7
  import type { RequestBudget } from "../budget.ts"
8
- import { markResidentBody } from "../mount.ts"
8
+ import { markResidentBody, residentBodiesRecorded } from "../mount.ts"
9
9
  import { applyTransportCap, readBoundedBytes } from "./body.ts"
10
10
  import { readBoundedJsonSource } from "./body-lane.ts"
11
11
  import type { Platform } from "./context.ts"
@@ -165,6 +165,8 @@ function responseTextContentType(): string {
165
165
  /** `c.text`'s own default, unchanged: the explicit one nifra has always declared when the caller
166
166
  * passes no headers of their own. */
167
167
  const TEXT_CONTENT_TYPE = "text/plain; charset=utf-8"
168
+ const TEXT_RESPONSE_HEADERS = { "content-type": TEXT_CONTENT_TYPE }
169
+ const TEXT_RESPONSE_INIT: ResponseInit = { headers: TEXT_RESPONSE_HEADERS }
168
170
 
169
171
  /** Where a serving adapter that shares one platform across requests leaves its socket-peer lookup.
170
172
  * `c.clientIp` calls it with the request when a handler reads the address, so a request whose
@@ -439,13 +441,15 @@ export class RequestContext implements RawContext {
439
441
  }
440
442
  }
441
443
  // Default to text/plain; if the caller passes their own headers, they own the content-type.
442
- if (i?.headers !== undefined) return markResidentBody(residentText(new Response(body, i), body))
443
- return markResidentBody(
444
- residentText(
445
- new Response(body, { ...i, headers: { "content-type": TEXT_CONTENT_TYPE } }),
446
- body,
447
- ),
448
- )
444
+ const response =
445
+ i?.headers !== undefined
446
+ ? new Response(body, i)
447
+ : new Response(
448
+ body,
449
+ i === undefined ? TEXT_RESPONSE_INIT : { ...i, headers: TEXT_RESPONSE_HEADERS },
450
+ )
451
+ const resident = residentText(response, body)
452
+ return residentBodiesRecorded ? markResidentBody(resident) : resident
449
453
  }
450
454
 
451
455
  get query(): unknown {
@@ -613,6 +613,17 @@ export function fusedRespond(
613
613
  unstamped = false,
614
614
  ): Response {
615
615
  const set = ctx[CONTEXT_SET]()
616
+ if (
617
+ set === undefined &&
618
+ statics === undefined &&
619
+ !tagResponseBody &&
620
+ result instanceof Response &&
621
+ !isResponseResult(result) &&
622
+ !responseBodyUsed(result)
623
+ ) {
624
+ if (!isBodylessStatus(result.status)) return result
625
+ return normalizeBodylessResponse(result, unstamped ? undefined : ctx[CONTEXT_REQUEST])
626
+ }
616
627
  const request = unstamped ? undefined : ctx[CONTEXT_REQUEST]
617
628
  if (set === undefined)
618
629
  return fusedRespondNoSet(result, tagResponseBody, statics, unstamped, request)
@@ -64,11 +64,19 @@ export interface ServerOptions {
64
64
  */
65
65
  readonly protoPoisoning?: "reject" | "strip" | "ignore"
66
66
  /**
67
- * Other sites whose HTML forms may post to a body-schema route: exact origins, or a function that
68
- * receives the `Origin`. A urlencoded, multipart or `text/plain` body is one a form on any site can
69
- * send with the visitor's cookies and no preflight, so one whose `Origin` names another site is
70
- * refused with `403` before it is read. Same-origin posts and requests without an `Origin`, such
71
- * as server-to-server calls, pass. JSON is not affected: a browser preflights it, so CORS decides.
67
+ * Other sites whose HTML forms may post to a route: exact origins, or a function that receives the
68
+ * `Origin`. A urlencoded, multipart or `text/plain` body is one a form on any site can send with the
69
+ * visitor's cookies and no preflight, so one whose `Origin` names another site is refused with `403`
70
+ * before it is read. A route without a body schema refuses the same sites' unsafe requests that carry
71
+ * no content type too, which `fetch` with `no-cors` sends; one with a script-set `Authorization` header
72
+ * passes, since a browser preflights it, but `Basic`, `Digest`, `Negotiate` and `NTLM` do not, because
73
+ * a browser re-sends those from its auth cache without one. Same-origin posts and requests without an `Origin`, such as
74
+ * server-to-server calls, pass. JSON is not affected: a browser preflights it, so CORS decides.
75
+ * The origins a `cors()` list or predicate and a `csrf({ origins })` list name are trusted as well,
76
+ * so a frontend those already allow needs no second list here; `cors()` with `"*"` adds none. That
77
+ * trust is for form posts to this server's own routes only: a mounted or grouped server keeps its own
78
+ * policy, so a parent that must accept the same sites sets them in its own `formOrigins`, and a web
79
+ * page action checks the origin against the backend's `allowedOrigins`, not this list.
72
80
  */
73
81
  readonly formOrigins?: ReadonlyArray<string> | ((origin: string) => boolean)
74
82
  /**
@@ -396,6 +404,8 @@ export interface RunningServer {
396
404
  readonly hostname: string
397
405
  readonly pendingRequests: number
398
406
  stop(closeActiveConnections?: boolean): void
407
+ /** `await using server = app.listen(3000)`: the app's graceful `stop()` with default options, not Bun's raw one. Waits for a shutdown already under way. */
408
+ [Symbol.asyncDispose](): Promise<void>
399
409
  /** Bun's native topic broadcast, present on the Bun `Server` handle. `app.publish` delegates to it
400
410
  * in native-pubsub mode; absent on other runtimes' running-server views. */
401
411
  publish?(topic: string, data: string | ArrayBufferView | ArrayBuffer): number
@@ -88,7 +88,7 @@ import {
88
88
  type RawBodyReaders,
89
89
  UNLIMITED_BODY_BYTES,
90
90
  } from "./body.ts"
91
- import type { FormOrigins } from "./body-lane.ts"
91
+ import { crossSiteFormRefused, type FormOrigins, guardsBodylessForm } from "./body-lane.ts"
92
92
  import {
93
93
  type ClientIpTrust,
94
94
  NIFRA_PLATFORM_CLIENT_IP_DERIVED,
@@ -261,6 +261,7 @@ export type MaybePromise<T> = T | Promise<T>
261
261
  // ordinary registered routes on its native table and send unmatched OPTIONS requests through the
262
262
  // normal fetch fallback where the hook still runs.
263
263
  const BUN_NATIVE_REQUEST_SAFE = Symbol.for("@nifrajs/core/bun-native-request-safe")
264
+ const TRUSTED_ORIGINS = Symbol.for("nifra.trustedOrigins")
264
265
 
265
266
  /**
266
267
  * Internal request view. A real Web `Request` already satisfies this shape, so Web/edge runtimes pass
@@ -691,6 +692,17 @@ const UNROUTED_CHAR = /[^!$%&'()*+,\-./0-9:;=?@A-Z[\]_a-z|~]/
691
692
  const unroutedParam = (value: string | undefined): boolean =>
692
693
  value !== undefined && (value === "." || value === ".." || UNROUTED_CHAR.test(value))
693
694
 
695
+ /** Native query-schema routes can pass this search substring into RequestContext directly instead of
696
+ * building both pathname and search with the portable URL helper. Keep fragment handling identical
697
+ * to `urlPartsOf`; in-process Requests can retain a fragment even though network request targets do not. */
698
+ function searchOfNativeRequest(url: string): string {
699
+ const question = url.indexOf("?")
700
+ const fragment = url.indexOf("#")
701
+ return question < 0 || (fragment >= 0 && question > fragment)
702
+ ? ""
703
+ : url.slice(question, fragment < 0 ? undefined : fragment)
704
+ }
705
+
694
706
  function hasUnroutedParam(params: Record<string, string>): boolean {
695
707
  for (const key in params) {
696
708
  if (unroutedParam(params[key])) return true
@@ -1488,7 +1500,7 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
1488
1500
  | undefined
1489
1501
  private readonly maxBodyBytes: number
1490
1502
  private readonly protoPoisoning: "reject" | "strip" | "ignore"
1491
- private readonly formOrigins: FormOrigins | undefined
1503
+ private formOrigins: FormOrigins | undefined
1492
1504
  /** `trustBodyFraming`: mark every `app.fetch` request as runtime-framed (see ServerOptions). */
1493
1505
  private readonly trustBodyFraming: boolean
1494
1506
  private readonly validateHost: boolean
@@ -1538,6 +1550,8 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
1538
1550
  /** App-wide validation-error fallback; a route's own `schema.onValidationError` takes precedence. */
1539
1551
  private readonly defaultOnValidationError?: RouteSchema["onValidationError"]
1540
1552
  private bunServer: RunningServer | undefined
1553
+ // The shutdown `stop()` last began, so a handle disposed mid-shutdown waits for it rather than for nothing.
1554
+ private stopRun: Promise<void> | undefined
1541
1555
  // Bun's `pendingRequests` leaves out upgraded sockets, so `stop()` closes these itself.
1542
1556
  private bunSockets: Set<{ close(code?: number): void }> | undefined
1543
1557
  private signalHandler: (() => void) | undefined
@@ -2322,6 +2336,15 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
2322
2336
  }
2323
2337
  this[INSTALL_RESPONSE_OBSERVER](responseObserver as ResponseObserverRuntime)
2324
2338
  }
2339
+ // `cors()` and `csrf()` name the origins they trust; a form post from one of them is the app's own
2340
+ // frontend, so it joins `formOrigins` rather than being listed twice.
2341
+ const trusted: unknown = Reflect.get(arg, TRUSTED_ORIGINS)
2342
+ if (typeof trusted === "function") {
2343
+ const listed = this.formOrigins
2344
+ this.formOrigins = (origin) =>
2345
+ (typeof listed === "function" ? listed(origin) : listed?.includes(origin)) === true ||
2346
+ trusted(origin) === true
2347
+ }
2325
2348
  if (arg.onRequest !== undefined) {
2326
2349
  this.assertConfigurable("onRequest()")
2327
2350
  this.onRequestHooks.push(arg.onRequest as RawOnRequest)
@@ -2823,6 +2846,8 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
2823
2846
  handler: (context: never) => unknown,
2824
2847
  ): CatalogRoute {
2825
2848
  const staticResponse = staticResponseDefinition(handler)
2849
+ // A handler carrying this marker checks the Origin itself (web page actions), so the default stays out.
2850
+ const ownsFormOrigin = Reflect.get(handler, Symbol.for("nifra.route.ownsFormOrigin")) === true
2826
2851
  schema = withTextSlots(schema)
2827
2852
  // Route provenance for the CLI's capability walk, which sets NIFRA_ROUTE_SITES while it loads the
2828
2853
  // app: the module that registered the route, read where a static scan cannot see the path.
@@ -2982,6 +3007,7 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
2982
3007
  afterHandle: aftersSnapshot,
2983
3008
  onError: onErrorSnapshot,
2984
3009
  lifecycleHookLane: lanes.lifecycleHookLane,
3010
+ formOriginGuard: !ownsFormOrigin && guardsBodylessForm(method, schema?.body !== undefined),
2985
3011
  around: [...this.aroundHooks],
2986
3012
  execution,
2987
3013
  program,
@@ -4807,6 +4833,9 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
4807
4833
  onTimeout: () => T,
4808
4834
  webFast: boolean,
4809
4835
  ): MaybePromise<T> {
4836
+ if (entry.formOriginGuard && crossSiteFormRefused(source, this.formOrigins)) {
4837
+ return wrapResponse(plainError(403, "forbidden_origin"))
4838
+ }
4810
4839
  // The route's transport byte cap: one property write here; the capped shadowing of direct
4811
4840
  // `c.req` body readers happens lazily at `c.req` access (`applyTransportCap`), so a route that
4812
4841
  // never direct-reads pays nothing. Framework readers bypass the shadow (`rawBodySourceOf`) and
@@ -7335,10 +7364,11 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
7335
7364
  entry.schema?.body === undefined || entry.program.authBeforeValidation
7336
7365
  ? entry.bodyLimit
7337
7366
  : undefined
7367
+ const hasQuerySchema = entry.schema?.query !== undefined
7338
7368
  // The complete plan already preserves general validation, hooks, recovery, around, and ledgers.
7339
7369
  // When native admission is safe, use it directly even if the route has no closed-shape fusion.
7340
7370
  // Idempotency still needs runMatched's request preparation and is excluded from the table.
7341
- const inner: FusedWebRunner | undefined =
7371
+ const unguarded: FusedWebRunner | undefined =
7342
7372
  fused ??
7343
7373
  (signal && budget
7344
7374
  ? (source, params, search, activeSignal, activeBudget, platform, nativeContext) =>
@@ -7356,6 +7386,13 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
7356
7386
  this.wrapWebResponse,
7357
7387
  )
7358
7388
  : undefined)
7389
+ const inner: FusedWebRunner | undefined =
7390
+ unguarded === undefined || !entry.formOriginGuard
7391
+ ? unguarded
7392
+ : (source, params, search, fusedSignal, fusedBudget, platform, nativeContext) =>
7393
+ crossSiteFormRefused(source, this.formOrigins)
7394
+ ? this.wrapWebResponse(plainError(403, "forbidden_origin"))
7395
+ : unguarded(source, params, search, fusedSignal, fusedBudget, platform, nativeContext)
7359
7396
  // The cap is never marked on a GET or HEAD, the only methods a GET or HEAD slot is handed, so those
7360
7397
  // slots skip the wrapper and its read of the method on every request.
7361
7398
  const capped: FusedWebRunner | undefined =
@@ -7377,7 +7414,15 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
7377
7414
  ): MaybePromise<Response> => {
7378
7415
  let outcome: MaybePromise<Response>
7379
7416
  try {
7380
- outcome = capped!(request, params, undefined, signal!, budget!, peer, true)
7417
+ outcome = capped!(
7418
+ request,
7419
+ params,
7420
+ hasQuerySchema ? searchOfNativeRequest(request.url) : undefined,
7421
+ signal!,
7422
+ budget!,
7423
+ peer,
7424
+ true,
7425
+ )
7381
7426
  } catch (err) {
7382
7427
  // A synchronous render throw, as in runMatchedLanes.
7383
7428
  outcome = this.contextlessBareError(
@@ -7599,7 +7644,7 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
7599
7644
  if (
7600
7645
  mayUseStaticResponse &&
7601
7646
  staticResponse !== undefined &&
7602
- (method === "GET" || method === "HEAD") &&
7647
+ (method === "GET" || method === "HEAD" || method === "POST") &&
7603
7648
  // Bun 1.3 can misframe null-body status codes in its native static table.
7604
7649
  staticResponse.status !== 204 &&
7605
7650
  staticResponse.status !== 205 &&
@@ -7882,6 +7927,8 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
7882
7927
  },
7883
7928
  })) as unknown as RunningServer
7884
7929
  this.bunServer = running
7930
+ running[Symbol.asyncDispose] = () =>
7931
+ this.bunServer === running ? this.stop() : (this.stopRun ?? Promise.resolve())
7885
7932
  // Bind `app.publish` to Bun's native broadcast now that the server handle exists. Guarded on the
7886
7933
  // method's presence so a runtime whose handle lacks it simply keeps the registry path. Cleared
7887
7934
  // first: a mounted app may have gained a WebSocket route since an earlier `listen()`.
@@ -7910,7 +7957,17 @@ export class Server<R extends Registry = EmptyRegistry, Ctx = EmptyContext, Hook
7910
7957
  * hooks after the drain. Open WebSockets get a 1001 close when the drain starts and the same
7911
7958
  * window to finish it; a socket opened after `stop()` began is closed at once.
7912
7959
  */
7913
- async stop({ drainMs = DEFAULT_DRAIN_MS, delayMs = 0 }: StopOptions = {}): Promise<void> {
7960
+ stop(options: StopOptions = {}): Promise<void> {
7961
+ const owns = this.bunServer !== undefined
7962
+ const run = this.stopListener(options)
7963
+ if (owns) this.stopRun = run
7964
+ return run
7965
+ }
7966
+
7967
+ private async stopListener({
7968
+ drainMs = DEFAULT_DRAIN_MS,
7969
+ delayMs = 0,
7970
+ }: StopOptions): Promise<void> {
7914
7971
  const onSignal = this.signalHandler
7915
7972
  if (onSignal !== undefined) {
7916
7973
  this.signalHandler = undefined
@@ -61,8 +61,8 @@ function byteLength(text: string): number {
61
61
 
62
62
  /**
63
63
  * Enforce the transport byte cap on already-read text - the same limit (and the same error) the
64
- * streaming reader applies during a bounded read. Exported for callers that read a body through a
65
- * native, non-streaming path (the typed client's in-process branch) and still owe the cap contract.
64
+ * streaming reader applies during a bounded read. Exported for callers that need to decode text
65
+ * after a bounded read while preserving the text decoder's lenient UTF-8 behavior.
66
66
  */
67
67
  export function assertTransportTextBounded(text: string, options: TransportDecodeOptions): void {
68
68
  const maxBytes = maxBytesOf(options)
@@ -278,10 +278,8 @@ async function readBoundedText(
278
278
  /**
279
279
  * Decode an ALREADY-READ transport body. The counterpart to {@link decodeTransportResponse} for a
280
280
  * caller that obtained the text through its own bounded read - notably the typed client's
281
- * in-process path, where the response body is same-process memory that was fully resident before
282
- * the read, so the native `Response.text()` is safe and the streaming byte-cap loop would protect
283
- * nothing while costing ~23x. The byte cap is still enforced here (identical error), just after
284
- * the read instead of during it.
281
+ * in-process path, which uses this to preserve lenient UTF-8 decoding after the shared byte-cap
282
+ * reader has completed.
285
283
  */
286
284
  export function decodeTransportText(
287
285
  text: string,