elysia 2.0.0-beta.11 → 2.0.0-beta.13

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 (99) hide show
  1. package/dist/adapter/bun/index.js +36 -21
  2. package/dist/adapter/bun/index.mjs +36 -21
  3. package/dist/adapter/utils.js +1 -0
  4. package/dist/adapter/utils.mjs +1 -0
  5. package/dist/adapter/web-standard/handler.js +8 -2
  6. package/dist/adapter/web-standard/handler.mjs +8 -2
  7. package/dist/base.d.ts +5 -0
  8. package/dist/base.js +109 -4
  9. package/dist/base.mjs +111 -6
  10. package/dist/compile/aot-capture.d.ts +2 -13
  11. package/dist/compile/aot-capture.js +5 -27
  12. package/dist/compile/aot-capture.mjs +6 -24
  13. package/dist/compile/aot-emit.js +1 -1
  14. package/dist/compile/aot-emit.mjs +1 -1
  15. package/dist/compile/aot.d.ts +1 -1
  16. package/dist/compile/aot.js +2 -2
  17. package/dist/compile/aot.mjs +2 -2
  18. package/dist/compile/handler/descriptor.d.ts +4 -8
  19. package/dist/compile/handler/descriptor.js +8 -21
  20. package/dist/compile/handler/descriptor.mjs +9 -21
  21. package/dist/compile/handler/index.d.ts +4 -3
  22. package/dist/compile/handler/index.js +16 -6
  23. package/dist/compile/handler/index.mjs +16 -7
  24. package/dist/compile/handler/jit.js +82 -76
  25. package/dist/compile/handler/jit.mjs +83 -77
  26. package/dist/compile/handler/params.d.ts +1 -4
  27. package/dist/compile/handler/params.js +3 -4
  28. package/dist/compile/handler/params.mjs +5 -5
  29. package/dist/compile/handler/reconstruct.d.ts +1 -1
  30. package/dist/compile/handler/static-clone-resolver.d.ts +6 -0
  31. package/dist/compile/handler/static-clone-resolver.js +8 -0
  32. package/dist/compile/handler/static-clone-resolver.mjs +7 -0
  33. package/dist/compile/handler/utils.d.ts +7 -7
  34. package/dist/compile/handler/utils.js +24 -20
  35. package/dist/compile/handler/utils.mjs +24 -20
  36. package/dist/compile/utils.js +7 -4
  37. package/dist/compile/utils.mjs +7 -4
  38. package/dist/context.js +3 -21
  39. package/dist/context.mjs +4 -22
  40. package/dist/cookie/index.d.ts +1 -2
  41. package/dist/cookie/index.js +0 -2
  42. package/dist/cookie/index.mjs +1 -2
  43. package/dist/cookie/lib.js +2 -1
  44. package/dist/cookie/lib.mjs +2 -1
  45. package/dist/cookie/utils.d.ts +3 -7
  46. package/dist/cookie/utils.js +9 -28
  47. package/dist/cookie/utils.mjs +11 -29
  48. package/dist/error.d.ts +40 -83
  49. package/dist/error.js +18 -19
  50. package/dist/error.mjs +18 -20
  51. package/dist/handler/error.js +4 -5
  52. package/dist/handler/error.mjs +4 -5
  53. package/dist/handler/fetch.js +31 -17
  54. package/dist/handler/fetch.mjs +32 -18
  55. package/dist/handler/utils.d.ts +3 -3
  56. package/dist/handler/utils.js +28 -17
  57. package/dist/handler/utils.mjs +28 -17
  58. package/dist/index.d.ts +3 -3
  59. package/dist/package.js +1 -1
  60. package/dist/package.mjs +1 -1
  61. package/dist/plugin/aot/core.d.ts +3 -7
  62. package/dist/plugin/aot/core.js +8 -18
  63. package/dist/plugin/aot/core.mjs +9 -17
  64. package/dist/plugin/aot/hooks.js +5 -1
  65. package/dist/plugin/aot/hooks.mjs +5 -1
  66. package/dist/trace.js +23 -10
  67. package/dist/trace.mjs +23 -10
  68. package/dist/type/bridge-live.d.ts +3 -2
  69. package/dist/type/bridge-live.js +4 -2
  70. package/dist/type/bridge-live.mjs +3 -2
  71. package/dist/type/bridge.d.ts +6 -4
  72. package/dist/type/bridge.js +9 -1
  73. package/dist/type/bridge.mjs +3 -1
  74. package/dist/type/coerce-plan.d.ts +2 -2
  75. package/dist/type/coerce-plan.js +0 -1
  76. package/dist/type/coerce-plan.mjs +2 -2
  77. package/dist/type/compat.js +2 -1
  78. package/dist/type/compat.mjs +3 -2
  79. package/dist/type/constants.d.ts +1 -1
  80. package/dist/type/shared.d.ts +1 -3
  81. package/dist/type/shared.js +0 -3
  82. package/dist/type/shared.mjs +1 -3
  83. package/dist/type/typebox-value-live.d.ts +4 -3
  84. package/dist/type/typebox-value-live.js +5 -3
  85. package/dist/type/typebox-value-live.mjs +4 -3
  86. package/dist/type/typebox-value.d.ts +11 -7
  87. package/dist/type/typebox-value.js +2 -1
  88. package/dist/type/typebox-value.mjs +1 -1
  89. package/dist/type/validator/index.d.ts +1 -1
  90. package/dist/type/validator/validator-cache.d.ts +1 -1
  91. package/dist/types.d.ts +10 -17
  92. package/dist/universal/file.d.ts +1 -3
  93. package/dist/universal/file.js +0 -2
  94. package/dist/universal/file.mjs +1 -2
  95. package/dist/universal/is-production.d.ts +1 -2
  96. package/dist/universal/is-production.js +1 -3
  97. package/dist/universal/is-production.mjs +2 -3
  98. package/dist/utils.d.ts +0 -1
  99. package/package.json +13 -6
package/dist/error.d.ts CHANGED
@@ -20,6 +20,12 @@ declare class ElysiaError<Status extends number = number, Response extends strin
20
20
  constructor(message: string, cause?: Error);
21
21
  toResponse(headers?: Record<string, any>): globalThis.Response;
22
22
  }
23
+ /**
24
+ * `type` an error slug resolves to: the slug verbatim, or `<typeBase>/<slug>`
25
+ * once {@link HTTPError.typeBase} is set. Shared by `ElysiaError` and every
26
+ * class {@link HTTPError.id} makes, so both lanes derive `type` identically
27
+ */
28
+ declare function problemTypeOf(code: string | undefined): string | undefined;
23
29
  /**
24
30
  * Problem members an `ElysiaError` serves. `title` is left to `problemBody`,
25
31
  * which derives it from the status — so `response` is only worth serving as
@@ -56,7 +62,8 @@ declare class ValidationError extends ElysiaError {
56
62
  #private;
57
63
  type: string | undefined;
58
64
  value: unknown;
59
- status: 422;
65
+ /** Response validation is a server error; other validation is a client error. */
66
+ status: 422 | 500;
60
67
  schema?: unknown;
61
68
  message: string;
62
69
  allowUnsafeValidationDetails: boolean;
@@ -86,39 +93,43 @@ declare class ValidationError extends ElysiaError {
86
93
  errors: any[];
87
94
  };
88
95
  get payload(): {
89
- type: string;
96
+ type: string | undefined;
97
+ code: string;
90
98
  title: string;
91
99
  status: number;
92
- on?: undefined;
93
- property?: undefined;
94
- detail?: undefined;
95
- found?: undefined;
96
- expected?: undefined;
97
- errors?: undefined;
98
100
  } | {
99
101
  type: string;
100
102
  title: string;
101
103
  status: number;
102
104
  on: string | undefined;
103
105
  property: string;
104
- detail?: undefined;
105
- found?: undefined;
106
- expected?: undefined;
107
- errors?: undefined;
108
106
  } | {
109
- type: string;
107
+ detail: any;
108
+ on: string | undefined;
109
+ property: string;
110
+ found: any;
111
+ expected: unknown;
112
+ errors: any[];
113
+ type: string | undefined;
114
+ code: string;
110
115
  title: string;
111
116
  status: number;
117
+ } | {
112
118
  detail: any;
113
119
  on: string | undefined;
114
120
  property: string;
115
121
  found: any;
116
122
  expected: unknown;
117
123
  errors: any[];
124
+ type: string;
125
+ title: string;
126
+ status: 422;
118
127
  };
119
128
  toResponse(headers?: Record<string, any>): Response;
120
129
  }
121
- declare class ElysiaStatus<const in out Code extends number | keyof StatusMap, T = (Code extends keyof StatusMapBack ? StatusMapBack[Code] : Code), const in out Status extends (Code extends keyof StatusMap ? StatusMap[Code] : Code) = (Code extends keyof StatusMap ? StatusMap[Code] : Code)> {
130
+ /** Default body for a status with no response. Named to keep emitted types small. */
131
+ type StatusResponse<Code extends number | keyof StatusMap> = Code extends keyof StatusMapBack ? StatusMapBack[Code] : Code;
132
+ declare class ElysiaStatus<const in out Code extends number | keyof StatusMap, T = StatusResponse<Code>, const in out Status extends NumericStatus<Code> = NumericStatus<Code>> {
122
133
  /**
123
134
  * Type-only brand, erased at runtime.
124
135
  *
@@ -132,69 +143,7 @@ declare class ElysiaStatus<const in out Code extends number | keyof StatusMap, T
132
143
  headers?: Record<string, string>;
133
144
  constructor(code: Code, res: T, headers?: Record<string, string>);
134
145
  }
135
- declare const status: <const Code extends number | keyof StatusMap, const T = (Code extends keyof StatusMapBack ? StatusMapBack[Code] : Code)>(code: Code, response?: T) => ElysiaStatus<Code, T, Code extends "Continue" | "Switching Protocols" | "Processing" | "Early Hints" | "OK" | "Created" | "Accepted" | "Non-Authoritative Information" | "No Content" | "Reset Content" | "Partial Content" | "Multi-Status" | "Already Reported" | "Multiple Choices" | "Moved Permanently" | "Found" | "See Other" | "Not Modified" | "Temporary Redirect" | "Permanent Redirect" | "Bad Request" | "Unauthorized" | "Payment Required" | "Forbidden" | "Not Found" | "Method Not Allowed" | "Not Acceptable" | "Proxy Authentication Required" | "Request Timeout" | "Conflict" | "Gone" | "Length Required" | "Precondition Failed" | "Payload Too Large" | "URI Too Long" | "Unsupported Media Type" | "Range Not Satisfiable" | "Expectation Failed" | "I'm a teapot" | "Enhance Your Calm" | "Misdirected Request" | "Unprocessable Content" | "Locked" | "Failed Dependency" | "Too Early" | "Upgrade Required" | "Precondition Required" | "Too Many Requests" | "Request Header Fields Too Large" | "Unavailable For Legal Reasons" | "Internal Server Error" | "Not Implemented" | "Bad Gateway" | "Service Unavailable" | "Gateway Timeout" | "HTTP Version Not Supported" | "Variant Also Negotiates" | "Insufficient Storage" | "Loop Detected" | "Not Extended" | "Network Authentication Required" ? {
136
- readonly Continue: 100;
137
- readonly 'Switching Protocols': 101;
138
- readonly Processing: 102;
139
- readonly 'Early Hints': 103;
140
- readonly OK: 200;
141
- readonly Created: 201;
142
- readonly Accepted: 202;
143
- readonly 'Non-Authoritative Information': 203;
144
- readonly 'No Content': 204;
145
- readonly 'Reset Content': 205;
146
- readonly 'Partial Content': 206;
147
- readonly 'Multi-Status': 207;
148
- readonly 'Already Reported': 208;
149
- readonly 'Multiple Choices': 300;
150
- readonly 'Moved Permanently': 301;
151
- readonly Found: 302;
152
- readonly 'See Other': 303;
153
- readonly 'Not Modified': 304;
154
- readonly 'Temporary Redirect': 307;
155
- readonly 'Permanent Redirect': 308;
156
- readonly 'Bad Request': 400;
157
- readonly Unauthorized: 401;
158
- readonly 'Payment Required': 402;
159
- readonly Forbidden: 403;
160
- readonly 'Not Found': 404;
161
- readonly 'Method Not Allowed': 405;
162
- readonly 'Not Acceptable': 406;
163
- readonly 'Proxy Authentication Required': 407;
164
- readonly 'Request Timeout': 408;
165
- readonly Conflict: 409;
166
- readonly Gone: 410;
167
- readonly 'Length Required': 411;
168
- readonly 'Precondition Failed': 412;
169
- readonly 'Payload Too Large': 413;
170
- readonly 'URI Too Long': 414;
171
- readonly 'Unsupported Media Type': 415;
172
- readonly 'Range Not Satisfiable': 416;
173
- readonly 'Expectation Failed': 417;
174
- readonly "I'm a teapot": 418;
175
- readonly 'Enhance Your Calm': 420;
176
- readonly 'Misdirected Request': 421;
177
- readonly 'Unprocessable Content': 422;
178
- readonly Locked: 423;
179
- readonly 'Failed Dependency': 424;
180
- readonly 'Too Early': 425;
181
- readonly 'Upgrade Required': 426;
182
- readonly 'Precondition Required': 428;
183
- readonly 'Too Many Requests': 429;
184
- readonly 'Request Header Fields Too Large': 431;
185
- readonly 'Unavailable For Legal Reasons': 451;
186
- readonly 'Internal Server Error': 500;
187
- readonly 'Not Implemented': 501;
188
- readonly 'Bad Gateway': 502;
189
- readonly 'Service Unavailable': 503;
190
- readonly 'Gateway Timeout': 504;
191
- readonly 'HTTP Version Not Supported': 505;
192
- readonly 'Variant Also Negotiates': 506;
193
- readonly 'Insufficient Storage': 507;
194
- readonly 'Loop Detected': 508;
195
- readonly 'Not Extended': 510;
196
- readonly 'Network Authentication Required': 511;
197
- }[Code] : Code>;
146
+ declare const status: <const Code extends number | keyof StatusMap, const T = StatusResponse<Code>>(code: Code, response?: T) => ElysiaStatus<Code, T, NumericStatus<Code>>;
198
147
  declare const PROBLEM_JSON = "application/problem+json";
199
148
  /**
200
149
  * RFC 9457 Problem Details shape.
@@ -215,10 +164,22 @@ type Problem<Code extends number | keyof StatusMap = number | keyof StatusMap, E
215
164
  detail?: string; /** URI identifying the specific occurrence of the problem */
216
165
  instance?: string;
217
166
  } & Extension;
167
+ /** Numeric form of a status name or code. */
218
168
  type NumericStatus<Code extends number | keyof StatusMap> = Code extends keyof StatusMap ? StatusMap[Code] : Code;
219
169
  type ProblemStatus<P> = P extends {
220
170
  status: infer S extends number | keyof StatusMap;
221
171
  } ? NumericStatus<S> : 500;
172
+ /** Request validation problem. Diagnostic fields are development-only. */
173
+ type ValidationErrorResponse = {
174
+ type: 'validation';
175
+ title: 'Validation Error';
176
+ status: 422;
177
+ detail?: string;
178
+ on: string;
179
+ found?: unknown;
180
+ property?: string;
181
+ expected?: string;
182
+ };
222
183
  type ProblemResponseBody<Status extends number, P> = Omit<{
223
184
  type: string;
224
185
  title: string;
@@ -347,11 +308,7 @@ declare abstract class HTTPError<const in out T extends string = string> extends
347
308
  /**
348
309
  * Absolute base for the RFC 9457 `type` URI, applied to every class made
349
310
  * by {@link HTTPError.id} and to every built-in `ElysiaError`
350
- * (`NotFound`, `ParseError`, …) — both derive `type` from the same slug
351
- *
352
- * Unset, `type` mirrors `code` verbatim. Set, `type` resolves to
353
- * `<typeBase>/<code>` with the code carried through as written — no
354
- * casing or separator rewrite — while `code` keeps serving the bare token
311
+ * (`NotFound`, `ParseError`, …) both derive `type` from the same slug
355
312
  *
356
313
  * @example
357
314
  * ```ts
@@ -422,4 +379,4 @@ declare abstract class HTTPError<const in out T extends string = string> extends
422
379
  }>;
423
380
  }
424
381
  //#endregion
425
- export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, PROBLEM_JSON, ParseError, Problem, ProblemResponseBody, SelectiveStatus, TaggedHTTPError, ValidationError, elysiaErrorProblem, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, status, subValueAt, validationDetail };
382
+ export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, NumericStatus, PROBLEM_JSON, ParseError, Problem, ProblemResponseBody, SelectiveStatus, StatusResponse, TaggedHTTPError, ValidationError, ValidationErrorResponse, elysiaErrorProblem, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, problemTypeOf, status, subValueAt, validationDetail };
package/dist/error.js CHANGED
@@ -278,6 +278,7 @@ var ValidationError = class extends ElysiaError {
278
278
  this.value = value;
279
279
  this.status = 422;
280
280
  this.allowUnsafeValidationDetails = false;
281
+ if (type === "response") this.status = 500;
281
282
  this.schema = schema;
282
283
  this.#thunk = typeof errors === "function" ? errors : () => errors;
283
284
  this.#findCustomError = findCustomError;
@@ -437,12 +438,9 @@ var ValidationError = class extends ElysiaError {
437
438
  };
438
439
  }
439
440
  get payload() {
441
+ const server = this.type === "response";
440
442
  if (this.#productionDetail) {
441
- if (this.type === "response") return {
442
- type: "internal-server-error",
443
- title: "Internal Server Error",
444
- status: 500
445
- };
443
+ if (server) return internalServerErrorProblem();
446
444
  const first = (this.errors ?? []).find(Boolean);
447
445
  return {
448
446
  type: "validation",
@@ -471,9 +469,11 @@ var ValidationError = class extends ElysiaError {
471
469
  else expected = created;
472
470
  } catch {}
473
471
  return {
474
- type: "validation",
475
- title: "Validation Error",
476
- status: 422,
472
+ ...server ? internalServerErrorProblem() : {
473
+ type: "validation",
474
+ title: "Validation Error",
475
+ status: 422
476
+ },
477
477
  detail,
478
478
  on: this.type,
479
479
  property,
@@ -487,7 +487,7 @@ var ValidationError = class extends ElysiaError {
487
487
  if (this.customError !== void 0) {
488
488
  const isString = typeof this.customError === "string";
489
489
  const response = new Response(isString ? this.customError : JSON.stringify(this.customError), {
490
- status: this.status ?? 422,
490
+ status: this.type === "response" ? 500 : this.status ?? 422,
491
491
  headers: {
492
492
  ...headers,
493
493
  "content-type": isString ? "text/plain" : "application/json"
@@ -555,12 +555,14 @@ function problemResponse(p, headers) {
555
555
  require_adapter_skip_clone.skipClone.add(response);
556
556
  return response;
557
557
  }
558
+ const internalServerErrorProblem = () => ({
559
+ type: problemTypeOf("internal-server-error"),
560
+ code: "internal-server-error",
561
+ title: "Internal Server Error",
562
+ status: 500
563
+ });
558
564
  function internalServerErrorBody(error) {
559
- const body = {
560
- type: "internal-server-error",
561
- title: "Internal Server Error",
562
- status: 500
563
- };
565
+ const body = internalServerErrorProblem();
564
566
  if (!require_universal_is_production.isProduction()) {
565
567
  if (error?.message != null) body.detail = error.message;
566
568
  if (error?.name) body.name = error.name;
@@ -577,11 +579,7 @@ function internalServerErrorBodyString(error) {
577
579
  delete safe.cause;
578
580
  return JSON.stringify(safe);
579
581
  } catch {
580
- return JSON.stringify({
581
- type: "internal-server-error",
582
- title: "Internal Server Error",
583
- status: 500
584
- });
582
+ return JSON.stringify(internalServerErrorProblem());
585
583
  }
586
584
  }
587
585
  }
@@ -696,6 +694,7 @@ exports.isProduction = require_universal_is_production.isProduction;
696
694
  exports.problem = problem;
697
695
  exports.problemBody = problemBody;
698
696
  exports.problemResponse = problemResponse;
697
+ exports.problemTypeOf = problemTypeOf;
699
698
  exports.status = status;
700
699
  exports.subValueAt = subValueAt;
701
700
  exports.validationDetail = validationDetail;
package/dist/error.mjs CHANGED
@@ -277,6 +277,7 @@ var ValidationError = class extends ElysiaError {
277
277
  this.value = value;
278
278
  this.status = 422;
279
279
  this.allowUnsafeValidationDetails = false;
280
+ if (type === "response") this.status = 500;
280
281
  this.schema = schema;
281
282
  this.#thunk = typeof errors === "function" ? errors : () => errors;
282
283
  this.#findCustomError = findCustomError;
@@ -436,12 +437,9 @@ var ValidationError = class extends ElysiaError {
436
437
  };
437
438
  }
438
439
  get payload() {
440
+ const server = this.type === "response";
439
441
  if (this.#productionDetail) {
440
- if (this.type === "response") return {
441
- type: "internal-server-error",
442
- title: "Internal Server Error",
443
- status: 500
444
- };
442
+ if (server) return internalServerErrorProblem();
445
443
  const first = (this.errors ?? []).find(Boolean);
446
444
  return {
447
445
  type: "validation",
@@ -470,9 +468,11 @@ var ValidationError = class extends ElysiaError {
470
468
  else expected = created;
471
469
  } catch {}
472
470
  return {
473
- type: "validation",
474
- title: "Validation Error",
475
- status: 422,
471
+ ...server ? internalServerErrorProblem() : {
472
+ type: "validation",
473
+ title: "Validation Error",
474
+ status: 422
475
+ },
476
476
  detail,
477
477
  on: this.type,
478
478
  property,
@@ -486,7 +486,7 @@ var ValidationError = class extends ElysiaError {
486
486
  if (this.customError !== void 0) {
487
487
  const isString = typeof this.customError === "string";
488
488
  const response = new Response(isString ? this.customError : JSON.stringify(this.customError), {
489
- status: this.status ?? 422,
489
+ status: this.type === "response" ? 500 : this.status ?? 422,
490
490
  headers: {
491
491
  ...headers,
492
492
  "content-type": isString ? "text/plain" : "application/json"
@@ -554,12 +554,14 @@ function problemResponse(p, headers) {
554
554
  skipClone.add(response);
555
555
  return response;
556
556
  }
557
+ const internalServerErrorProblem = () => ({
558
+ type: problemTypeOf("internal-server-error"),
559
+ code: "internal-server-error",
560
+ title: "Internal Server Error",
561
+ status: 500
562
+ });
557
563
  function internalServerErrorBody(error) {
558
- const body = {
559
- type: "internal-server-error",
560
- title: "Internal Server Error",
561
- status: 500
562
- };
564
+ const body = internalServerErrorProblem();
563
565
  if (!isProduction()) {
564
566
  if (error?.message != null) body.detail = error.message;
565
567
  if (error?.name) body.name = error.name;
@@ -576,11 +578,7 @@ function internalServerErrorBodyString(error) {
576
578
  delete safe.cause;
577
579
  return JSON.stringify(safe);
578
580
  } catch {
579
- return JSON.stringify({
580
- type: "internal-server-error",
581
- title: "Internal Server Error",
582
- status: 500
583
- });
581
+ return JSON.stringify(internalServerErrorProblem());
584
582
  }
585
583
  }
586
584
  }
@@ -678,4 +676,4 @@ var HTTPError = class HTTPError extends Error {
678
676
  };
679
677
 
680
678
  //#endregion
681
- export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, PROBLEM_JSON, ParseError, ValidationError, elysiaErrorProblem, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, status, subValueAt, validationDetail };
679
+ export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, PROBLEM_JSON, ParseError, ValidationError, elysiaErrorProblem, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, problemTypeOf, status, subValueAt, validationDetail };
@@ -98,7 +98,7 @@ const readAnnotation = (self, key, claimsProblem) => {
98
98
  };
99
99
  function fallbackErrorResponse(context, error, mapResponse, defaultError) {
100
100
  if (error instanceof require_error.ElysiaStatus) return mapResponse(error, context.set, context);
101
- const self = error;
101
+ const self = error ?? {};
102
102
  const status = resolveStatus(self.status);
103
103
  const owned = error instanceof require_error.HTTPError;
104
104
  const claimsProblem = claimsProblemType(error);
@@ -174,16 +174,15 @@ function createErrorHandler(onErrors, mapResponse, defaultError, allowUnsafe = f
174
174
  parseQuery(context);
175
175
  return fallbackResponse(context, error, mapResponse, defaultError);
176
176
  };
177
- const asyncIndexes = require_handler_utils.getAsyncIndexes(onErrors);
178
- if (asyncIndexes) return async (context, error) => {
177
+ if (require_handler_utils.getAsyncIndexes(onErrors)) return async (context, error) => {
179
178
  require_adapter_utils.materializeSetHeaders(context.set);
180
179
  context.error = error;
181
180
  if (allowUnsafe && error instanceof require_error.ValidationError) error.allowUnsafeValidationDetails = true;
182
181
  applyErrorStatus(context, error);
183
182
  parseQuery(context);
184
183
  for (let i = 0; i < onErrors.length; i++) {
185
- let result = asyncIndexes?.[i] ? await onErrors[i](context) : onErrors[i](context);
186
- if (result instanceof Promise) result = await result;
184
+ let result = onErrors[i](context);
185
+ if (typeof result?.then === "function") result = await result;
187
186
  if (result !== void 0) {
188
187
  if (result instanceof require_error.ElysiaStatus || result instanceof Response) context.set.status = result.status;
189
188
  else if (context.set.status === void 0 || context.set.status === 200) context.set.status = 500;
@@ -97,7 +97,7 @@ const readAnnotation = (self, key, claimsProblem) => {
97
97
  };
98
98
  function fallbackErrorResponse(context, error, mapResponse, defaultError) {
99
99
  if (error instanceof ElysiaStatus) return mapResponse(error, context.set, context);
100
- const self = error;
100
+ const self = error ?? {};
101
101
  const status = resolveStatus(self.status);
102
102
  const owned = error instanceof HTTPError;
103
103
  const claimsProblem = claimsProblemType(error);
@@ -173,16 +173,15 @@ function createErrorHandler(onErrors, mapResponse, defaultError, allowUnsafe = f
173
173
  parseQuery(context);
174
174
  return fallbackResponse(context, error, mapResponse, defaultError);
175
175
  };
176
- const asyncIndexes = getAsyncIndexes(onErrors);
177
- if (asyncIndexes) return async (context, error) => {
176
+ if (getAsyncIndexes(onErrors)) return async (context, error) => {
178
177
  materializeSetHeaders(context.set);
179
178
  context.error = error;
180
179
  if (allowUnsafe && error instanceof ValidationError) error.allowUnsafeValidationDetails = true;
181
180
  applyErrorStatus(context, error);
182
181
  parseQuery(context);
183
182
  for (let i = 0; i < onErrors.length; i++) {
184
- let result = asyncIndexes?.[i] ? await onErrors[i](context) : onErrors[i](context);
185
- if (result instanceof Promise) result = await result;
183
+ let result = onErrors[i](context);
184
+ if (typeof result?.then === "function") result = await result;
186
185
  if (result !== void 0) {
187
186
  if (result instanceof ElysiaStatus || result instanceof Response) context.set.status = result.status;
188
187
  else if (context.set.status === void 0 || context.set.status === 200) context.set.status = 500;
@@ -23,7 +23,7 @@ function notFound(context) {
23
23
  if (set.cookie || require_utils.isNotEmpty(set.headers)) {
24
24
  require_adapter_utils.handleSet(set);
25
25
  if (!set.headers["content-type"]) require_adapter_utils.materializeSetHeaders(set)["content-type"] = require_error.PROBLEM_JSON;
26
- return new Response(require_handler_utils.NOT_FOUND_BODY, {
26
+ return new Response(require_handler_utils.getNotFoundBody(), {
27
27
  status: 404,
28
28
  headers: set.headers
29
29
  });
@@ -38,6 +38,12 @@ function decodeParams(params) {
38
38
  return params;
39
39
  }
40
40
  const notFoundBody = Object.freeze({ code: "NOT_FOUND" });
41
+ const warnedPathMutation = /* @__PURE__ */ new WeakSet();
42
+ function warnPathMutation(app) {
43
+ if (warnedPathMutation.has(app)) return;
44
+ warnedPathMutation.add(app);
45
+ console.warn("[elysia] context.path is readonly; request-hook rerouting will stop working in a future release.");
46
+ }
41
47
  function finalizeError(context, handleError, afterResponse, error) {
42
48
  if (error === notFoundBody) error = new require_error.NotFound();
43
49
  let resp;
@@ -114,6 +120,7 @@ function createFetchHandler(app) {
114
120
  const hook = require_utils.flattenChain(app["~hookChain"]);
115
121
  const hasError = !!hook?.error;
116
122
  const abortSignal = app["~config"]?.abortSignal !== false;
123
+ const watchPath = !require_universal_is_production.isProduction();
117
124
  /**
118
125
  * Arm `context['~sig']` unless this request is provably the untouched
119
126
  * `Request` the Bun adapter received, in which case materializing
@@ -129,11 +136,10 @@ function createFetchHandler(app) {
129
136
  return (context["~sig"] = request.signal).aborted;
130
137
  };
131
138
  /**
132
- * Post-suspension check: arm if nobody has yet, then read. Only emitted on
133
- * lanes that actually awaited, so a deferred request that never suspends
134
- * never reaches it.
139
+ * Peek after each callback. The actual await branch arms deferred requests;
140
+ * immediate callbacks must not materialize a signal just for this check.
135
141
  */
136
- const armedAbort = (request, context) => abortSignal && (context["~sig"] ??= request.signal).aborted === true;
142
+ const armedAbort = (context) => abortSignal && context["~sig"]?.aborted === true;
137
143
  const baseMapResponse = (app["~config"]?.adapter ?? require_adapter_constants.defaultAdapter).response.map;
138
144
  const mapResponseHooks = hook?.mapResponse;
139
145
  const mapResponse = mapResponseHooks?.length ? (response, set, context) => {
@@ -143,7 +149,7 @@ function createFetchHandler(app) {
143
149
  const run = (i) => {
144
150
  for (; i < mapResponseHooks.length; i++) {
145
151
  const result = mapResponseHooks[i](context);
146
- if (result instanceof Promise) return result.then((resolved) => {
152
+ if (typeof result?.then === "function") return Promise.resolve(result).then((resolved) => {
147
153
  if (resolved !== void 0) return baseMapResponse(resolved, set, request, true);
148
154
  return run(i + 1);
149
155
  });
@@ -211,12 +217,11 @@ function createFetchHandler(app) {
211
217
  app["~finalizeError"] = (context, error) => finalizeError(context, handleError, afterResponse, error);
212
218
  if (traceRequestPhase) {
213
219
  const onRequests = hook?.request ?? [];
214
- const asyncIndexes = onRequests.length ? require_handler_utils.getAsyncIndexes(onRequests) : void 0;
215
220
  return async (request, server) => {
216
221
  const context = new Context(request);
217
222
  require_adapter_utils.materializeSetHeaders(context.set);
218
223
  if (armEager(request, context)) return require_handler_utils.emptyResponse.clone();
219
- extractPath(request.url, context);
224
+ const path = extractPath(request.url, context);
220
225
  context.server = server ?? null;
221
226
  context.rid = require_utils.requestId();
222
227
  const traceLength = tracerFactories.length;
@@ -240,9 +245,14 @@ function createFetchHandler(app) {
240
245
  name: onRequests[i].name || "anonymous",
241
246
  begin: performance.now()
242
247
  });
243
- const result = asyncIndexes?.[i] ? await onRequests[i](context) : onRequests[i](context);
248
+ let result = onRequests[i](context);
249
+ if (typeof result?.then === "function") {
250
+ if (abortSignal) context["~sig"] ??= request.signal;
251
+ result = await result;
252
+ }
253
+ if (watchPath && context.path !== path) warnPathMutation(app);
244
254
  for (let i = 0; i < traceLength; i++) endReports[i]?.();
245
- if (armedAbort(request, context)) {
255
+ if (armedAbort(context)) {
246
256
  for (let j = 0; j < traceLength; j++) trace[j].r(requestReports[j]);
247
257
  return require_handler_utils.emptyResponse.clone();
248
258
  }
@@ -263,18 +273,21 @@ function createFetchHandler(app) {
263
273
  }
264
274
  if (hook?.request) {
265
275
  const onRequests = hook.request;
266
- const asyncIndexes = require_handler_utils.getAsyncIndexes(onRequests);
267
- if (asyncIndexes) return async (request, server) => {
276
+ if (require_handler_utils.getAsyncIndexes(onRequests)) return async (request, server) => {
268
277
  const context = new Context(request);
269
278
  require_adapter_utils.materializeSetHeaders(context.set);
270
279
  if (armEager(request, context)) return require_handler_utils.emptyResponse.clone();
271
- extractPath(request.url, context);
280
+ const path = extractPath(request.url, context);
272
281
  context.server = server ?? null;
273
282
  try {
274
283
  for (let i = 0; i < onRequests.length; i++) {
275
- let result = asyncIndexes?.[i] ? await onRequests[i](context) : onRequests[i](context);
276
- if (result instanceof Promise) result = await result;
277
- if (armedAbort(request, context)) return require_handler_utils.emptyResponse.clone();
284
+ let result = onRequests[i](context);
285
+ if (typeof result?.then === "function") {
286
+ if (abortSignal) context["~sig"] ??= request.signal;
287
+ result = await result;
288
+ }
289
+ if (watchPath && context.path !== path) warnPathMutation(app);
290
+ if (armedAbort(context)) return require_handler_utils.emptyResponse.clone();
278
291
  if (result !== void 0) {
279
292
  const response = await mapResponse(result, context.set, context);
280
293
  afterResponse?.(context);
@@ -290,11 +303,12 @@ function createFetchHandler(app) {
290
303
  const context = new Context(request);
291
304
  require_adapter_utils.materializeSetHeaders(context.set);
292
305
  if (armEager(request, context)) return require_handler_utils.emptyResponse.clone();
293
- extractPath(request.url, context);
306
+ const path = extractPath(request.url, context);
294
307
  context.server = server ?? null;
295
308
  try {
296
309
  for (let i = 0; i < onRequests.length; i++) {
297
310
  const result = onRequests[i](context);
311
+ if (watchPath && context.path !== path) warnPathMutation(app);
298
312
  if (abortSignal && context["~sig"]?.aborted) return require_handler_utils.emptyResponse.clone();
299
313
  if (result !== void 0) {
300
314
  const response = mapResponse(result, context.set, context);