elysia 2.0.0-beta.2 → 2.0.0-beta.3

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 (145) hide show
  1. package/dist/adapter/utils.js +3 -3
  2. package/dist/adapter/utils.mjs +4 -4
  3. package/dist/adapter/web-standard/handler.js +5 -2
  4. package/dist/adapter/web-standard/handler.mjs +5 -2
  5. package/dist/base.d.ts +2 -2
  6. package/dist/base.js +42 -27
  7. package/dist/base.mjs +43 -28
  8. package/dist/compile/aot.d.ts +4 -4
  9. package/dist/compile/aot.js +14 -9
  10. package/dist/compile/aot.mjs +15 -9
  11. package/dist/compile/handler/descriptor.js +1 -1
  12. package/dist/compile/handler/descriptor.mjs +1 -1
  13. package/dist/compile/handler/index.d.ts +2 -2
  14. package/dist/compile/handler/index.js +6 -4
  15. package/dist/compile/handler/index.mjs +6 -4
  16. package/dist/compile/handler/utils.js +2 -2
  17. package/dist/compile/handler/utils.mjs +3 -3
  18. package/dist/context.js +3 -4
  19. package/dist/context.mjs +3 -4
  20. package/dist/cookie/config.d.ts +1 -0
  21. package/dist/cookie/config.js +3 -2
  22. package/dist/cookie/config.mjs +3 -2
  23. package/dist/cookie/cookie.d.ts +1 -0
  24. package/dist/cookie/cookie.js +6 -5
  25. package/dist/cookie/cookie.mjs +6 -5
  26. package/dist/cookie/crypto.d.ts +8 -7
  27. package/dist/cookie/crypto.js +38 -31
  28. package/dist/cookie/crypto.mjs +38 -32
  29. package/dist/cookie/error.js +1 -2
  30. package/dist/cookie/error.mjs +1 -2
  31. package/dist/cookie/lib.js +5 -1
  32. package/dist/cookie/lib.mjs +5 -1
  33. package/dist/cookie/serialize.js +1 -1
  34. package/dist/cookie/serialize.mjs +1 -1
  35. package/dist/cookie/types.d.ts +10 -0
  36. package/dist/cookie/utils.js +28 -14
  37. package/dist/cookie/utils.mjs +29 -15
  38. package/dist/handler/error.d.ts +23 -12
  39. package/dist/handler/error.js +24 -21
  40. package/dist/handler/error.mjs +20 -21
  41. package/dist/handler/fetch.js +6 -7
  42. package/dist/handler/fetch.mjs +7 -8
  43. package/dist/handler/utils.js +4 -2
  44. package/dist/handler/utils.mjs +5 -3
  45. package/dist/index.js +1 -8
  46. package/dist/package.js +1 -1
  47. package/dist/package.mjs +1 -1
  48. package/dist/plugin/aot/core.d.ts +22 -1
  49. package/dist/plugin/aot/core.js +35 -3
  50. package/dist/plugin/aot/core.mjs +35 -3
  51. package/dist/route-table.js +12 -10
  52. package/dist/route-table.mjs +12 -10
  53. package/dist/type/bridge-live.d.ts +1 -1
  54. package/dist/type/bridge-live.js +1 -1
  55. package/dist/type/bridge-live.mjs +1 -1
  56. package/dist/type/bridge.d.ts +1 -1
  57. package/dist/type/coerce-plan.js +3 -4
  58. package/dist/type/coerce-plan.mjs +1 -1
  59. package/dist/type/compat.d.ts +3 -0
  60. package/dist/type/compat.js +32 -14
  61. package/dist/type/compat.mjs +27 -8
  62. package/dist/type/elysia/array-buffer.js +2 -3
  63. package/dist/type/elysia/array-buffer.mjs +1 -1
  64. package/dist/type/elysia/array-string.js +7 -4
  65. package/dist/type/elysia/array-string.mjs +9 -5
  66. package/dist/type/elysia/boolean-string.js +2 -3
  67. package/dist/type/elysia/boolean-string.mjs +1 -1
  68. package/dist/type/elysia/boolean.js +4 -3
  69. package/dist/type/elysia/boolean.mjs +4 -2
  70. package/dist/type/elysia/cookie.js +2 -1
  71. package/dist/type/elysia/cookie.mjs +2 -1
  72. package/dist/type/elysia/date.js +7 -7
  73. package/dist/type/elysia/date.mjs +3 -2
  74. package/dist/type/elysia/file-type.d.ts +9 -2
  75. package/dist/type/elysia/file-type.js +16 -1
  76. package/dist/type/elysia/file-type.mjs +16 -2
  77. package/dist/type/elysia/file.js +8 -4
  78. package/dist/type/elysia/file.mjs +8 -3
  79. package/dist/type/elysia/files.js +6 -4
  80. package/dist/type/elysia/files.mjs +5 -2
  81. package/dist/type/elysia/form.js +2 -3
  82. package/dist/type/elysia/form.mjs +3 -3
  83. package/dist/type/elysia/integer-string.js +4 -5
  84. package/dist/type/elysia/integer-string.mjs +3 -3
  85. package/dist/type/elysia/integer.js +4 -3
  86. package/dist/type/elysia/integer.mjs +4 -2
  87. package/dist/type/elysia/maybe-empty.js +3 -4
  88. package/dist/type/elysia/maybe-empty.mjs +1 -1
  89. package/dist/type/elysia/nullable.js +2 -3
  90. package/dist/type/elysia/nullable.mjs +1 -1
  91. package/dist/type/elysia/number.js +4 -3
  92. package/dist/type/elysia/number.mjs +4 -2
  93. package/dist/type/elysia/numeric-enum.js +2 -3
  94. package/dist/type/elysia/numeric-enum.mjs +1 -1
  95. package/dist/type/elysia/numeric.js +5 -5
  96. package/dist/type/elysia/numeric.mjs +3 -2
  97. package/dist/type/elysia/object-string.js +7 -4
  98. package/dist/type/elysia/object-string.mjs +9 -5
  99. package/dist/type/elysia/string.js +4 -3
  100. package/dist/type/elysia/string.mjs +4 -2
  101. package/dist/type/elysia/uint8-array.js +2 -3
  102. package/dist/type/elysia/uint8-array.mjs +1 -1
  103. package/dist/type/elysia/union-enum.js +2 -3
  104. package/dist/type/elysia/union-enum.mjs +1 -1
  105. package/dist/type/elysia/utils.js +2 -3
  106. package/dist/type/elysia/utils.mjs +1 -1
  107. package/dist/type/exports.js +10 -1
  108. package/dist/type/exports.mjs +7 -0
  109. package/dist/type/index.d.ts +9 -1
  110. package/dist/type/index.js +41 -13
  111. package/dist/type/index.mjs +44 -9
  112. package/dist/type/typebox-type-live.d.ts +9 -0
  113. package/dist/type/typebox-type-live.js +31 -0
  114. package/dist/type/typebox-type-live.mjs +16 -0
  115. package/dist/type/typebox-type.d.ts +24 -0
  116. package/dist/type/typebox-type.js +130 -0
  117. package/dist/type/typebox-type.mjs +66 -0
  118. package/dist/type/typebox-value-live.d.ts +9 -0
  119. package/dist/type/typebox-value-live.js +27 -0
  120. package/dist/type/typebox-value-live.mjs +11 -0
  121. package/dist/type/typebox-value.d.ts +30 -0
  122. package/dist/type/typebox-value.js +147 -0
  123. package/dist/type/typebox-value.mjs +62 -0
  124. package/dist/type/validator/custom-error.js +4 -5
  125. package/dist/type/validator/custom-error.mjs +3 -3
  126. package/dist/type/validator/default-precompute.js +4 -5
  127. package/dist/type/validator/default-precompute.mjs +1 -1
  128. package/dist/type/validator/index.js +41 -35
  129. package/dist/type/validator/index.mjs +23 -16
  130. package/dist/type/validator/validator-cache.js +10 -5
  131. package/dist/type/validator/validator-cache.mjs +10 -4
  132. package/dist/universal/constants.d.ts +2 -1
  133. package/dist/universal/constants.js +3 -1
  134. package/dist/universal/constants.mjs +2 -1
  135. package/dist/utils.d.ts +10 -1
  136. package/dist/utils.js +18 -3
  137. package/dist/utils.mjs +14 -3
  138. package/dist/validator/index.js +2 -1
  139. package/dist/validator/index.mjs +2 -1
  140. package/dist/ws/context.d.ts +4 -0
  141. package/dist/ws/context.js +44 -26
  142. package/dist/ws/context.mjs +44 -26
  143. package/dist/ws/route.js +126 -23
  144. package/dist/ws/route.mjs +128 -25
  145. package/package.json +4 -2
@@ -6,7 +6,7 @@ function serializeCookie(cookies) {
6
6
  if (!cookies || !isNotEmpty(cookies)) return;
7
7
  let set;
8
8
  let isArray = false;
9
- for (const key in cookies) {
9
+ for (const key of Object.keys(cookies)) {
10
10
  if (!key) continue;
11
11
  const property = cookies[key];
12
12
  if (!property) continue;
@@ -108,6 +108,16 @@ interface CookieOptions {
108
108
  * `null` in the array makes Elysia accept an unsigned cookie (intended only for a one-way unsigned→signed migration)
109
109
  */
110
110
  secrets?: string | null | (string | null)[];
111
+ /**
112
+ * Only use secret for signing cookie instead of secret + seperator + name
113
+ *
114
+ * Set to `false` once every signed cookie in circulation has been re-issued
115
+ *
116
+ * Applies to the app and route level
117
+ *
118
+ * @default true
119
+ */
120
+ legacySignature?: boolean;
111
121
  }
112
122
  //#endregion
113
123
  export { BaseCookie, CookieOptions };
@@ -69,7 +69,7 @@ async function parseCookieRaw(cookieString, config) {
69
69
  if (v === void 0) continue;
70
70
  let value = (0, deuri.default)(v) ?? v;
71
71
  const signCheck = require_cookie_config.resolveSignSecrets(name, config);
72
- if (signCheck !== void 0) value = await require_cookie_crypto.unsignWithSecrets(name, value, signCheck);
72
+ if (signCheck !== void 0) value = await require_cookie_crypto.unsignWithSecrets(name, value, signCheck, config.legacySignature);
73
73
  out[name] = require_cookie_crypto.maybeJsonDecode(value);
74
74
  }
75
75
  return out;
@@ -85,7 +85,7 @@ function parseCookieRawSigned(cookieString, config) {
85
85
  if (v === void 0) continue;
86
86
  let value = (0, deuri.default)(v) ?? v;
87
87
  const signCheck = require_cookie_config.resolveSignSecrets(name, config);
88
- if (signCheck !== void 0) value = require_cookie_crypto.unsignWithSecretsSync(name, value, signCheck);
88
+ if (signCheck !== void 0) value = require_cookie_crypto.unsignWithSecretsSync(name, value, signCheck, config.legacySignature);
89
89
  out[name] = require_cookie_crypto.maybeJsonDecode(value);
90
90
  }
91
91
  return out;
@@ -101,7 +101,10 @@ function buildCookieJar(set, raw, config, lazySign, deferDecode) {
101
101
  if (entry.expires instanceof Date) entry.expires = new Date(entry.expires.getTime());
102
102
  if (lazySign && typeof entry.value === "string") {
103
103
  const secrets = require_cookie_config.resolveSignSecrets(name, config);
104
- if (secrets !== void 0) entry["~unsign"] = secrets;
104
+ if (secrets !== void 0) {
105
+ entry["~unsign"] = secrets;
106
+ if (!config.legacySignature) entry["~strict"] = 1;
107
+ }
105
108
  } else {
106
109
  const value = entry.value;
107
110
  if (value !== null && typeof value === "object") {
@@ -114,16 +117,27 @@ function buildCookieJar(set, raw, config, lazySign, deferDecode) {
114
117
  return entry;
115
118
  }
116
119
  const cache = require_utils.nullObject();
120
+ function settle(key) {
121
+ if (!(key in store)) return;
122
+ const entry = materializeEntry(key);
123
+ if ("~unsign" in entry) require_cookie_crypto.resolvePendingCookie(entry, key);
124
+ }
117
125
  return new Proxy(store, {
118
126
  get(_, key) {
119
127
  return cache[key] ??= new require_cookie_cookie.Cookie(key, set, key in store ? materializeEntry(key) : Object.assign(require_utils.nullObject(), config.defaults, config.fields[key]?.defaults));
120
128
  },
129
+ has(target, key) {
130
+ if (typeof key === "string") settle(key);
131
+ return Reflect.has(target, key);
132
+ },
133
+ ownKeys(target) {
134
+ const keys = Reflect.ownKeys(target);
135
+ for (let i = 0; i < keys.length; i++) settle(keys[i]);
136
+ return keys;
137
+ },
121
138
  getOwnPropertyDescriptor(target, key) {
122
- if (typeof key === "string" && key in target) materializeEntry(key);
123
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
124
- const entry = descriptor?.value;
125
- if (entry && typeof entry === "object" && "~unsign" in entry) require_cookie_crypto.resolvePendingCookie(entry, String(key));
126
- return descriptor;
139
+ if (typeof key === "string") settle(key);
140
+ return Reflect.getOwnPropertyDescriptor(target, key);
127
141
  }
128
142
  });
129
143
  }
@@ -142,11 +156,11 @@ function collectSignPending(cookies, config) {
142
156
  if (property["~raw"] === value) continue;
143
157
  } else if (typeof value !== "string") value = value + "";
144
158
  const secret = Array.isArray(r.secrets) ? r.secrets[0] ?? null : r.secrets;
145
- if (secret === null) throw new TypeError(`Cookie field "${key}" is signed but no \`secrets\` is provided.`);
159
+ if (!secret?.trim()) throw new TypeError(`Cookie field "${key}" is signed but no \`secrets\` is provided.`);
146
160
  (pending ??= []).push([
147
161
  property,
148
162
  value,
149
- secret
163
+ require_cookie_crypto.deriveSignKey(secret, key)
150
164
  ]);
151
165
  }
152
166
  return pending;
@@ -156,8 +170,8 @@ function signCookieValues(cookies, config) {
156
170
  if (!pending) return;
157
171
  if (require_cookie_crypto.hasSyncHmac) {
158
172
  for (let i = 0; i < pending.length; i++) {
159
- const [property, value, secret] = pending[i];
160
- property.value = require_cookie_crypto.signCookieSyncImpl(value, secret);
173
+ const [property, value, key] = pending[i];
174
+ property.value = require_cookie_crypto.signCookieSyncImpl(value, key);
161
175
  }
162
176
  return;
163
177
  }
@@ -165,8 +179,8 @@ function signCookieValues(cookies, config) {
165
179
  }
166
180
  async function signPending(pending) {
167
181
  for (let i = 0; i < pending.length; i++) {
168
- const [property, value, secret] = pending[i];
169
- property.value = await require_cookie_crypto.signCookie(value, secret);
182
+ const [property, value, key] = pending[i];
183
+ property.value = await require_cookie_crypto.signCookie(value, key);
170
184
  }
171
185
  }
172
186
 
@@ -1,7 +1,7 @@
1
1
  import { dangerousKeys } from "../constants.mjs";
2
2
  import { nullObject } from "../utils.mjs";
3
3
  import { parse } from "./lib.mjs";
4
- import { hasSyncHmac, maybeJsonDecode, rawJsonValue, resolvePendingCookie, signCookie, signCookieSyncImpl, unsignWithSecrets, unsignWithSecretsSync } from "./crypto.mjs";
4
+ import { deriveSignKey, hasSyncHmac, maybeJsonDecode, rawJsonValue, resolvePendingCookie, signCookie, signCookieSyncImpl, unsignWithSecrets, unsignWithSecretsSync } from "./crypto.mjs";
5
5
  import { Cookie } from "./cookie.mjs";
6
6
  import { compileCookieConfig, isCookieSigned, resolveSignSecrets } from "./config.mjs";
7
7
  import decodeComponent from "deuri";
@@ -66,7 +66,7 @@ async function parseCookieRaw(cookieString, config) {
66
66
  if (v === void 0) continue;
67
67
  let value = decodeComponent(v) ?? v;
68
68
  const signCheck = resolveSignSecrets(name, config);
69
- if (signCheck !== void 0) value = await unsignWithSecrets(name, value, signCheck);
69
+ if (signCheck !== void 0) value = await unsignWithSecrets(name, value, signCheck, config.legacySignature);
70
70
  out[name] = maybeJsonDecode(value);
71
71
  }
72
72
  return out;
@@ -82,7 +82,7 @@ function parseCookieRawSigned(cookieString, config) {
82
82
  if (v === void 0) continue;
83
83
  let value = decodeComponent(v) ?? v;
84
84
  const signCheck = resolveSignSecrets(name, config);
85
- if (signCheck !== void 0) value = unsignWithSecretsSync(name, value, signCheck);
85
+ if (signCheck !== void 0) value = unsignWithSecretsSync(name, value, signCheck, config.legacySignature);
86
86
  out[name] = maybeJsonDecode(value);
87
87
  }
88
88
  return out;
@@ -98,7 +98,10 @@ function buildCookieJar(set, raw, config, lazySign, deferDecode) {
98
98
  if (entry.expires instanceof Date) entry.expires = new Date(entry.expires.getTime());
99
99
  if (lazySign && typeof entry.value === "string") {
100
100
  const secrets = resolveSignSecrets(name, config);
101
- if (secrets !== void 0) entry["~unsign"] = secrets;
101
+ if (secrets !== void 0) {
102
+ entry["~unsign"] = secrets;
103
+ if (!config.legacySignature) entry["~strict"] = 1;
104
+ }
102
105
  } else {
103
106
  const value = entry.value;
104
107
  if (value !== null && typeof value === "object") {
@@ -111,16 +114,27 @@ function buildCookieJar(set, raw, config, lazySign, deferDecode) {
111
114
  return entry;
112
115
  }
113
116
  const cache = nullObject();
117
+ function settle(key) {
118
+ if (!(key in store)) return;
119
+ const entry = materializeEntry(key);
120
+ if ("~unsign" in entry) resolvePendingCookie(entry, key);
121
+ }
114
122
  return new Proxy(store, {
115
123
  get(_, key) {
116
124
  return cache[key] ??= new Cookie(key, set, key in store ? materializeEntry(key) : Object.assign(nullObject(), config.defaults, config.fields[key]?.defaults));
117
125
  },
126
+ has(target, key) {
127
+ if (typeof key === "string") settle(key);
128
+ return Reflect.has(target, key);
129
+ },
130
+ ownKeys(target) {
131
+ const keys = Reflect.ownKeys(target);
132
+ for (let i = 0; i < keys.length; i++) settle(keys[i]);
133
+ return keys;
134
+ },
118
135
  getOwnPropertyDescriptor(target, key) {
119
- if (typeof key === "string" && key in target) materializeEntry(key);
120
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
121
- const entry = descriptor?.value;
122
- if (entry && typeof entry === "object" && "~unsign" in entry) resolvePendingCookie(entry, String(key));
123
- return descriptor;
136
+ if (typeof key === "string") settle(key);
137
+ return Reflect.getOwnPropertyDescriptor(target, key);
124
138
  }
125
139
  });
126
140
  }
@@ -139,11 +153,11 @@ function collectSignPending(cookies, config) {
139
153
  if (property["~raw"] === value) continue;
140
154
  } else if (typeof value !== "string") value = value + "";
141
155
  const secret = Array.isArray(r.secrets) ? r.secrets[0] ?? null : r.secrets;
142
- if (secret === null) throw new TypeError(`Cookie field "${key}" is signed but no \`secrets\` is provided.`);
156
+ if (!secret?.trim()) throw new TypeError(`Cookie field "${key}" is signed but no \`secrets\` is provided.`);
143
157
  (pending ??= []).push([
144
158
  property,
145
159
  value,
146
- secret
160
+ deriveSignKey(secret, key)
147
161
  ]);
148
162
  }
149
163
  return pending;
@@ -153,8 +167,8 @@ function signCookieValues(cookies, config) {
153
167
  if (!pending) return;
154
168
  if (hasSyncHmac) {
155
169
  for (let i = 0; i < pending.length; i++) {
156
- const [property, value, secret] = pending[i];
157
- property.value = signCookieSyncImpl(value, secret);
170
+ const [property, value, key] = pending[i];
171
+ property.value = signCookieSyncImpl(value, key);
158
172
  }
159
173
  return;
160
174
  }
@@ -162,8 +176,8 @@ function signCookieValues(cookies, config) {
162
176
  }
163
177
  async function signPending(pending) {
164
178
  for (let i = 0; i < pending.length; i++) {
165
- const [property, value, secret] = pending[i];
166
- property.value = await signCookie(value, secret);
179
+ const [property, value, key] = pending[i];
180
+ property.value = await signCookie(value, key);
167
181
  }
168
182
  }
169
183
 
@@ -2,17 +2,7 @@ import { AppHook } from "../types.js";
2
2
  import { Context } from "../context.js";
3
3
 
4
4
  //#region src/handler/error.d.ts
5
- /**
6
- * Adopt the intercepted error's `type` into a problem the hook produced
7
- * without one.
8
- *
9
- * `'about:blank'` is RFC 9457's "unspecified" default, so it's the only value
10
- * worth replacing — an explicit type the handler set always wins.
11
- *
12
- * The result is copied rather than mutated: `.error(Class, value)` wraps a
13
- * non-function into `() => value`, so the very same `ElysiaStatus` is replayed
14
- * on every request and an in-place write would leak across requests
15
- */
5
+ declare const claimsProblemType: (error: any) => boolean;
16
6
  declare function adoptErrorType(result: any, error: any): any;
17
7
  /**
18
8
  * Response served once every error hook has declined the error.
@@ -21,6 +11,27 @@ declare function adoptErrorType(result: any, error: any): any;
21
11
  * which must land on *this* function rather than re-implementing it
22
12
  */
23
13
  declare function fallbackResponse(context: Context, error: any, mapResponse: (response: unknown, set: Context['set'], context?: Context) => unknown, defaultError?: Response): unknown;
14
+ /**
15
+ * Numeric form of an annotated status, which may be written as a name.
16
+ * A numeric string keeps coercing so the production mask below can't be
17
+ * slipped past with `'502'`
18
+ */
19
+ declare const resolveStatus: (status: unknown) => unknown;
20
+ /**
21
+ * Body served by an error that carries a status but no usable body:
22
+ * its declared `response`, otherwise its message
23
+ */
24
+ declare function statusFallbackBody(error: any, status: unknown): any;
25
+ /**
26
+ * Read one annotation knob.
27
+ *
28
+ * Both knobs are canonically methods, so they're evaluated per serve and may
29
+ * be `async`. Running a stranger's function is side-effect surface (it may
30
+ * consume a stream or do IO), so it takes the same problem claim the shaping
31
+ * does — an unclaimed duck error never invokes one. A *value* annotation stays
32
+ * inert data and keeps duck-participating as it always has
33
+ */
34
+ declare const readAnnotation: (self: any, key: "value" | "detail", claimsProblem: boolean) => any;
24
35
  declare function createErrorHandler(onErrors: AppHook['error'] | undefined, mapResponse: (response: unknown, set: Context['set'], ...any: unknown[]) => unknown, defaultError?: Response, allowUnsafe?: boolean): (context: Context, error: Error) => unknown;
25
36
  //#endregion
26
- export { adoptErrorType, createErrorHandler, fallbackResponse };
37
+ export { adoptErrorType, claimsProblemType, createErrorHandler, fallbackResponse, readAnnotation, resolveStatus, statusFallbackBody };
@@ -35,20 +35,10 @@ function parseQuery(context) {
35
35
  });
36
36
  }
37
37
  const isPristineNotFound = (context, error) => error instanceof require_error.NotFound && error.response === "Not Found" && !context.set.cookie && !require_utils.isNotEmpty(context.set.headers);
38
- /**
39
- * Adopt the intercepted error's `type` into a problem the hook produced
40
- * without one.
41
- *
42
- * `'about:blank'` is RFC 9457's "unspecified" default, so it's the only value
43
- * worth replacing — an explicit type the handler set always wins.
44
- *
45
- * The result is copied rather than mutated: `.error(Class, value)` wraps a
46
- * non-function into `() => value`, so the very same `ElysiaStatus` is replayed
47
- * on every request and an in-place write would leak across requests
48
- */
38
+ const claimsProblemType = (error) => typeof error?.type === "string" ? !(error instanceof require_error.ValidationError) && error.constructor?.name !== "ValidationError" : error instanceof require_error.HTTPError;
49
39
  function adoptErrorType(result, error) {
50
40
  const body = result?.response;
51
- if (typeof error?.type !== "string" || body?.type !== "about:blank" || result.headers?.["content-type"] !== "application/problem+json") return result;
41
+ if (typeof error?.type !== "string" || !claimsProblemType(error) || body?.type !== "about:blank" || result.headers?.["content-type"] !== "application/problem+json") return result;
52
42
  return new require_error.ElysiaStatus(result.code, {
53
43
  ...body,
54
44
  type: error.type
@@ -78,13 +68,13 @@ const resolveStatus = (status) => typeof status === "string" ? require_constants
78
68
  * Body served by an error that carries a status but no usable body:
79
69
  * its declared `response`, otherwise its message
80
70
  */
81
- const statusFallbackBody = (error, status) => error.response !== void 0 ? error.response : require_universal_is_production.isProduction() && status >= 500 ? "Internal Server Error" : error.message ?? "";
71
+ function statusFallbackBody(error, status) {
72
+ const masked = require_universal_is_production.isProduction() && status >= 500;
73
+ const declared = error.response;
74
+ return declared !== void 0 && !(masked && typeof declared === "object") ? declared : masked ? "Internal Server Error" : error.message ?? "";
75
+ }
82
76
  /**
83
77
  * RFC 9457 problem document carrying `detail` verbatim, mirroring `problem()`.
84
- *
85
- * `detail` is served exactly as annotated — objects included — it is never
86
- * spread into the envelope. `problemBody` only defaults `type` when the key is
87
- * *absent*, so an error without one has to supply `'about:blank'` itself
88
78
  */
89
79
  const problemOf = (self, detail, status) => new require_error.ElysiaStatus(status, require_error.problemBody({
90
80
  type: self.type ?? "about:blank",
@@ -109,7 +99,7 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
109
99
  const self = error;
110
100
  const status = resolveStatus(self.status);
111
101
  const owned = error instanceof require_error.HTTPError;
112
- const claimsProblem = owned || typeof self.type === "string";
102
+ const claimsProblem = claimsProblemType(error);
113
103
  const served = typeof status === "number" ? status : resolveStatus(context.set.status);
114
104
  const failed = (cause) => {
115
105
  context.set.status = 500;
@@ -124,9 +114,13 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
124
114
  if (context.set.status === void 0 || context.set.status === 200) context.set.status = 500;
125
115
  return mapResponse(require_error.internalServerErrorResponse(error), context.set, context);
126
116
  }
127
- return defaultError ? defaultError.clone() : require_error.internalServerErrorResponse(error);
117
+ return mapResponse(defaultError ? defaultError.clone() : require_error.internalServerErrorResponse(error), context.set, context);
118
+ };
119
+ const serveMessage = () => {
120
+ if (!claimsProblem) return legacy();
121
+ mergeHeaders();
122
+ return mapResponse(problemOf(self, statusFallbackBody(error, served), served), context.set, context);
128
123
  };
129
- const serveMessage = () => claimsProblem ? mapResponse(problemOf(self, statusFallbackBody(error, served), served), context.set, context) : legacy();
130
124
  const serveDetail = () => {
131
125
  let detail;
132
126
  try {
@@ -143,6 +137,11 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
143
137
  mergeHeaders();
144
138
  return mapResponse(problemOf(self, detail, served), context.set, context);
145
139
  };
140
+ if (error instanceof require_error.ValidationError) return mapResponse(new require_error.ElysiaStatus(422, require_error.problemBody({
141
+ type: "validation",
142
+ title: "Validation Error",
143
+ status: 422
144
+ }), { "content-type": require_error.PROBLEM_JSON }), context.set, context);
146
145
  if (owned || error instanceof Error && typeof status === "number" && status >= 100 && !(require_universal_is_production.isProduction() && status >= 500)) {
147
146
  let value;
148
147
  try {
@@ -213,5 +212,9 @@ function createErrorHandler(onErrors, mapResponse, defaultError, allowUnsafe = f
213
212
 
214
213
  //#endregion
215
214
  exports.adoptErrorType = adoptErrorType;
215
+ exports.claimsProblemType = claimsProblemType;
216
216
  exports.createErrorHandler = createErrorHandler;
217
- exports.fallbackResponse = fallbackResponse;
217
+ exports.fallbackResponse = fallbackResponse;
218
+ exports.readAnnotation = readAnnotation;
219
+ exports.resolveStatus = resolveStatus;
220
+ exports.statusFallbackBody = statusFallbackBody;
@@ -34,20 +34,10 @@ function parseQuery(context) {
34
34
  });
35
35
  }
36
36
  const isPristineNotFound = (context, error) => error instanceof NotFound && error.response === "Not Found" && !context.set.cookie && !isNotEmpty(context.set.headers);
37
- /**
38
- * Adopt the intercepted error's `type` into a problem the hook produced
39
- * without one.
40
- *
41
- * `'about:blank'` is RFC 9457's "unspecified" default, so it's the only value
42
- * worth replacing — an explicit type the handler set always wins.
43
- *
44
- * The result is copied rather than mutated: `.error(Class, value)` wraps a
45
- * non-function into `() => value`, so the very same `ElysiaStatus` is replayed
46
- * on every request and an in-place write would leak across requests
47
- */
37
+ const claimsProblemType = (error) => typeof error?.type === "string" ? !(error instanceof ValidationError) && error.constructor?.name !== "ValidationError" : error instanceof HTTPError;
48
38
  function adoptErrorType(result, error) {
49
39
  const body = result?.response;
50
- if (typeof error?.type !== "string" || body?.type !== "about:blank" || result.headers?.["content-type"] !== "application/problem+json") return result;
40
+ if (typeof error?.type !== "string" || !claimsProblemType(error) || body?.type !== "about:blank" || result.headers?.["content-type"] !== "application/problem+json") return result;
51
41
  return new ElysiaStatus(result.code, {
52
42
  ...body,
53
43
  type: error.type
@@ -77,13 +67,13 @@ const resolveStatus = (status) => typeof status === "string" ? StatusMap[status]
77
67
  * Body served by an error that carries a status but no usable body:
78
68
  * its declared `response`, otherwise its message
79
69
  */
80
- const statusFallbackBody = (error, status) => error.response !== void 0 ? error.response : isProduction() && status >= 500 ? "Internal Server Error" : error.message ?? "";
70
+ function statusFallbackBody(error, status) {
71
+ const masked = isProduction() && status >= 500;
72
+ const declared = error.response;
73
+ return declared !== void 0 && !(masked && typeof declared === "object") ? declared : masked ? "Internal Server Error" : error.message ?? "";
74
+ }
81
75
  /**
82
76
  * RFC 9457 problem document carrying `detail` verbatim, mirroring `problem()`.
83
- *
84
- * `detail` is served exactly as annotated — objects included — it is never
85
- * spread into the envelope. `problemBody` only defaults `type` when the key is
86
- * *absent*, so an error without one has to supply `'about:blank'` itself
87
77
  */
88
78
  const problemOf = (self, detail, status) => new ElysiaStatus(status, problemBody({
89
79
  type: self.type ?? "about:blank",
@@ -108,7 +98,7 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
108
98
  const self = error;
109
99
  const status = resolveStatus(self.status);
110
100
  const owned = error instanceof HTTPError;
111
- const claimsProblem = owned || typeof self.type === "string";
101
+ const claimsProblem = claimsProblemType(error);
112
102
  const served = typeof status === "number" ? status : resolveStatus(context.set.status);
113
103
  const failed = (cause) => {
114
104
  context.set.status = 500;
@@ -123,9 +113,13 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
123
113
  if (context.set.status === void 0 || context.set.status === 200) context.set.status = 500;
124
114
  return mapResponse(internalServerErrorResponse(error), context.set, context);
125
115
  }
126
- return defaultError ? defaultError.clone() : internalServerErrorResponse(error);
116
+ return mapResponse(defaultError ? defaultError.clone() : internalServerErrorResponse(error), context.set, context);
117
+ };
118
+ const serveMessage = () => {
119
+ if (!claimsProblem) return legacy();
120
+ mergeHeaders();
121
+ return mapResponse(problemOf(self, statusFallbackBody(error, served), served), context.set, context);
127
122
  };
128
- const serveMessage = () => claimsProblem ? mapResponse(problemOf(self, statusFallbackBody(error, served), served), context.set, context) : legacy();
129
123
  const serveDetail = () => {
130
124
  let detail;
131
125
  try {
@@ -142,6 +136,11 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
142
136
  mergeHeaders();
143
137
  return mapResponse(problemOf(self, detail, served), context.set, context);
144
138
  };
139
+ if (error instanceof ValidationError) return mapResponse(new ElysiaStatus(422, problemBody({
140
+ type: "validation",
141
+ title: "Validation Error",
142
+ status: 422
143
+ }), { "content-type": PROBLEM_JSON }), context.set, context);
145
144
  if (owned || error instanceof Error && typeof status === "number" && status >= 100 && !(isProduction() && status >= 500)) {
146
145
  let value;
147
146
  try {
@@ -211,4 +210,4 @@ function createErrorHandler(onErrors, mapResponse, defaultError, allowUnsafe = f
211
210
  }
212
211
 
213
212
  //#endregion
214
- export { adoptErrorType, createErrorHandler, fallbackResponse };
213
+ export { adoptErrorType, claimsProblemType, createErrorHandler, fallbackResponse, readAnnotation, resolveStatus, statusFallbackBody };
@@ -13,8 +13,8 @@ let deuri = require("deuri");
13
13
  deuri = require_runtime.__toESM(deuri);
14
14
 
15
15
  //#region src/handler/fetch.ts
16
- function extractPath(url, context, pathStart) {
17
- const s = url.indexOf("/", pathStart);
16
+ function extractPath(url, context) {
17
+ const s = url.indexOf("/", require_utils.authorityEnd(url));
18
18
  const q = context.qi = url.indexOf("?", s);
19
19
  return context.path = q === -1 ? url.slice(s) : url.substring(s, q);
20
20
  }
@@ -111,7 +111,6 @@ function createFetchHandler(app) {
111
111
  const hasWS = !!app["~hasWS"];
112
112
  const hasDynamicWS = hasWS && !!app["~hasDynamicWS"];
113
113
  const strictPath = !!app["~config"]?.strictPath;
114
- const pathStart = app["~config"]?.handler?.standardHostname === false ? 7 : 11;
115
114
  const hook = require_utils.flattenChain(app["~hookChain"]);
116
115
  const hasError = !!hook?.error;
117
116
  const abortSignal = app["~config"]?.abortSignal !== false;
@@ -217,7 +216,7 @@ function createFetchHandler(app) {
217
216
  const context = new Context(request);
218
217
  require_adapter_utils.materializeSetHeaders(context.set);
219
218
  if (armEager(request, context)) return require_handler_utils.emptyResponse.clone();
220
- extractPath(request.url, context, pathStart);
219
+ extractPath(request.url, context);
221
220
  context.server = server ?? null;
222
221
  context.rid = require_utils.requestId();
223
222
  const traceLength = tracerFactories.length;
@@ -269,7 +268,7 @@ function createFetchHandler(app) {
269
268
  const context = new Context(request);
270
269
  require_adapter_utils.materializeSetHeaders(context.set);
271
270
  if (armEager(request, context)) return require_handler_utils.emptyResponse.clone();
272
- extractPath(request.url, context, pathStart);
271
+ extractPath(request.url, context);
273
272
  context.server = server ?? null;
274
273
  try {
275
274
  for (let i = 0; i < onRequests.length; i++) {
@@ -291,7 +290,7 @@ function createFetchHandler(app) {
291
290
  const context = new Context(request);
292
291
  require_adapter_utils.materializeSetHeaders(context.set);
293
292
  if (armEager(request, context)) return require_handler_utils.emptyResponse.clone();
294
- extractPath(request.url, context, pathStart);
293
+ extractPath(request.url, context);
295
294
  context.server = server ?? null;
296
295
  try {
297
296
  for (let i = 0; i < onRequests.length; i++) {
@@ -315,7 +314,7 @@ function createFetchHandler(app) {
315
314
  }
316
315
  return (request, server) => {
317
316
  const context = new Context(request);
318
- const path = extractPath(request.url, context, pathStart);
317
+ const path = extractPath(request.url, context);
319
318
  context.server = server ?? null;
320
319
  const method = request.method;
321
320
  if (hasWS && method === "GET") {
@@ -1,4 +1,4 @@
1
- import { flattenChain, isNotEmpty, nullObject, requestId } from "../utils.mjs";
1
+ import { authorityEnd, flattenChain, isNotEmpty, nullObject, requestId } from "../utils.mjs";
2
2
  import { isProduction } from "../universal/is-production.mjs";
3
3
  import { NotFound, PROBLEM_JSON, internalServerErrorResponse } from "../error.mjs";
4
4
  import { handleSet, materializeSetHeaders } from "../adapter/utils.mjs";
@@ -10,8 +10,8 @@ import { createContext } from "../context.mjs";
10
10
  import decodeComponent from "deuri";
11
11
 
12
12
  //#region src/handler/fetch.ts
13
- function extractPath(url, context, pathStart) {
14
- const s = url.indexOf("/", pathStart);
13
+ function extractPath(url, context) {
14
+ const s = url.indexOf("/", authorityEnd(url));
15
15
  const q = context.qi = url.indexOf("?", s);
16
16
  return context.path = q === -1 ? url.slice(s) : url.substring(s, q);
17
17
  }
@@ -108,7 +108,6 @@ function createFetchHandler(app) {
108
108
  const hasWS = !!app["~hasWS"];
109
109
  const hasDynamicWS = hasWS && !!app["~hasDynamicWS"];
110
110
  const strictPath = !!app["~config"]?.strictPath;
111
- const pathStart = app["~config"]?.handler?.standardHostname === false ? 7 : 11;
112
111
  const hook = flattenChain(app["~hookChain"]);
113
112
  const hasError = !!hook?.error;
114
113
  const abortSignal = app["~config"]?.abortSignal !== false;
@@ -214,7 +213,7 @@ function createFetchHandler(app) {
214
213
  const context = new Context(request);
215
214
  materializeSetHeaders(context.set);
216
215
  if (armEager(request, context)) return emptyResponse.clone();
217
- extractPath(request.url, context, pathStart);
216
+ extractPath(request.url, context);
218
217
  context.server = server ?? null;
219
218
  context.rid = requestId();
220
219
  const traceLength = tracerFactories.length;
@@ -266,7 +265,7 @@ function createFetchHandler(app) {
266
265
  const context = new Context(request);
267
266
  materializeSetHeaders(context.set);
268
267
  if (armEager(request, context)) return emptyResponse.clone();
269
- extractPath(request.url, context, pathStart);
268
+ extractPath(request.url, context);
270
269
  context.server = server ?? null;
271
270
  try {
272
271
  for (let i = 0; i < onRequests.length; i++) {
@@ -288,7 +287,7 @@ function createFetchHandler(app) {
288
287
  const context = new Context(request);
289
288
  materializeSetHeaders(context.set);
290
289
  if (armEager(request, context)) return emptyResponse.clone();
291
- extractPath(request.url, context, pathStart);
290
+ extractPath(request.url, context);
292
291
  context.server = server ?? null;
293
292
  try {
294
293
  for (let i = 0; i < onRequests.length; i++) {
@@ -312,7 +311,7 @@ function createFetchHandler(app) {
312
311
  }
313
312
  return (request, server) => {
314
313
  const context = new Context(request);
315
- const path = extractPath(request.url, context, pathStart);
314
+ const path = extractPath(request.url, context);
316
315
  context.server = server ?? null;
317
316
  const method = request.method;
318
317
  if (hasWS && method === "GET") {
@@ -1,13 +1,15 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_universal_constants = require('../universal/constants.js');
3
+ const require_universal_env = require('../universal/env.js');
3
4
  const require_error = require('../error.js');
4
5
  const require_compile_utils = require('../compile/utils.js');
5
6
 
6
7
  //#region src/handler/utils.ts
7
- const emptyResponse = require_universal_constants.isCloudflareWorker ? { clone: () => new Response(null) } : new Response(null);
8
+ const isPreallocateResponseUnsafe = require_universal_constants.isCloudflareWorker || require_universal_constants.isFastly || require_universal_env.env.ELYSIA_PREALLOCATE_RESPONSE === "false";
9
+ const emptyResponse = isPreallocateResponseUnsafe ? { clone: () => new Response(null) } : new Response(null);
8
10
  function cachedResponse(body, status, headers) {
9
11
  let cached;
10
- return () => require_universal_constants.isCloudflareWorker ? new Response(body, {
12
+ return () => isPreallocateResponseUnsafe ? new Response(body, {
11
13
  status,
12
14
  headers
13
15
  }) : (cached ??= new Response(body, {
@@ -1,12 +1,14 @@
1
- import { isCloudflareWorker } from "../universal/constants.mjs";
1
+ import { isCloudflareWorker, isFastly } from "../universal/constants.mjs";
2
+ import { env } from "../universal/env.mjs";
2
3
  import { PROBLEM_JSON } from "../error.mjs";
3
4
  import { isAsyncFunction, mayReturnPromise } from "../compile/utils.mjs";
4
5
 
5
6
  //#region src/handler/utils.ts
6
- const emptyResponse = isCloudflareWorker ? { clone: () => new Response(null) } : new Response(null);
7
+ const isPreallocateResponseUnsafe = isCloudflareWorker || isFastly || env.ELYSIA_PREALLOCATE_RESPONSE === "false";
8
+ const emptyResponse = isPreallocateResponseUnsafe ? { clone: () => new Response(null) } : new Response(null);
7
9
  function cachedResponse(body, status, headers) {
8
10
  let cached;
9
- return () => isCloudflareWorker ? new Response(body, {
11
+ return () => isPreallocateResponseUnsafe ? new Response(body, {
10
12
  status,
11
13
  headers
12
14
  }) : (cached ??= new Response(body, {
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
- const require_runtime = require('./_virtual/_rolldown/runtime.js');
3
2
  const require_constants = require('./constants.js');
4
3
  const require_universal_file = require('./universal/file.js');
5
4
  const require_utils = require('./utils.js');
@@ -15,7 +14,6 @@ const require_base = require('./base.js');
15
14
  const require_type_validator_index = require('./type/validator/index.js');
16
15
  const require_type_compat = require('./type/compat.js');
17
16
  const require_type_index = require('./type/index.js');
18
- let typebox_system = require("typebox/system");
19
17
 
20
18
  //#region src/index.ts
21
19
  var src_default = require_base.Elysia;
@@ -37,12 +35,7 @@ exports.StandardValidator = require_validator_index.StandardValidator;
37
35
  exports.StatusMap = require_constants.StatusMap;
38
36
  exports.StatusMapBack = require_constants.StatusMapBack;
39
37
  exports.TypeBoxValidator = require_type_validator_index.TypeBoxValidator;
40
- Object.defineProperty(exports, 'TypeSystem', {
41
- enumerable: true,
42
- get: function () {
43
- return typebox_system.System;
44
- }
45
- });
38
+ exports.TypeSystem = require_type_index.TypeSystem;
46
39
  exports.ValidationError = require_error.ValidationError;
47
40
  exports.Validator = require_validator_index.Validator;
48
41
  exports.borrow = require_adapter_response_ownership.borrow;
package/dist/package.js CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  //#region package.json
3
- var version = "2.0.0-beta.2";
3
+ var version = "2.0.0-beta.3";
4
4
 
5
5
  //#endregion
6
6
  Object.defineProperty(exports, 'version', {
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "2.0.0-beta.2";
2
+ var version = "2.0.0-beta.3";
3
3
 
4
4
  //#endregion
5
5
  export { version };