elysia 2.0.0-beta.21 → 2.0.0-beta.22

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 (215) hide show
  1. package/dist/_virtual/_rolldown/runtime.mjs +1 -0
  2. package/dist/adapter/bun/index.d.ts +6 -8
  3. package/dist/adapter/bun/index.js +6 -8
  4. package/dist/adapter/bun/index.mjs +7 -8
  5. package/dist/adapter/constants.mjs +1 -0
  6. package/dist/adapter/default-headers.mjs +1 -0
  7. package/dist/adapter/index.mjs +1 -0
  8. package/dist/adapter/origin.mjs +1 -0
  9. package/dist/adapter/response-ownership.mjs +1 -0
  10. package/dist/adapter/skip-clone.mjs +1 -0
  11. package/dist/adapter/types.mjs +1 -0
  12. package/dist/adapter/utils.d.ts +5 -8
  13. package/dist/adapter/utils.js +24 -18
  14. package/dist/adapter/utils.mjs +25 -18
  15. package/dist/adapter/web-standard/handler.mjs +2 -1
  16. package/dist/adapter/web-standard/index.mjs +1 -0
  17. package/dist/adapter/web-standard/utils.mjs +1 -0
  18. package/dist/base.d.ts +118 -69
  19. package/dist/base.js +213 -193
  20. package/dist/base.mjs +220 -199
  21. package/dist/compile/aot-capture.js +2 -2
  22. package/dist/compile/aot-capture.mjs +4 -3
  23. package/dist/compile/aot-emit.js +7 -8
  24. package/dist/compile/aot-emit.mjs +8 -8
  25. package/dist/compile/aot-reconstruct.mjs +3 -2
  26. package/dist/compile/aot.d.ts +3 -3
  27. package/dist/compile/aot.js +5 -5
  28. package/dist/compile/aot.mjs +6 -5
  29. package/dist/compile/handler/descriptor.d.ts +2 -5
  30. package/dist/compile/handler/descriptor.js +12 -17
  31. package/dist/compile/handler/descriptor.mjs +16 -21
  32. package/dist/compile/handler/frozen-validator.d.ts +2 -2
  33. package/dist/compile/handler/frozen-validator.js +3 -3
  34. package/dist/compile/handler/frozen-validator.mjs +4 -3
  35. package/dist/compile/handler/index.d.ts +3 -1
  36. package/dist/compile/handler/index.js +77 -118
  37. package/dist/compile/handler/index.mjs +83 -125
  38. package/dist/compile/handler/jit.d.ts +12 -12
  39. package/dist/compile/handler/jit.js +60 -43
  40. package/dist/compile/handler/jit.mjs +63 -47
  41. package/dist/compile/handler/params.d.ts +5 -1
  42. package/dist/compile/handler/params.js +14 -5
  43. package/dist/compile/handler/params.mjs +16 -7
  44. package/dist/compile/handler/reconstruct.d.ts +2 -2
  45. package/dist/compile/handler/reconstruct.js +2 -2
  46. package/dist/compile/handler/reconstruct.mjs +3 -2
  47. package/dist/compile/handler/static-clone-resolver.mjs +1 -0
  48. package/dist/compile/handler/utils.d.ts +13 -7
  49. package/dist/compile/handler/utils.js +75 -52
  50. package/dist/compile/handler/utils.mjs +77 -54
  51. package/dist/compile/jit-probe.mjs +1 -0
  52. package/dist/compile/lexer.js +4 -4
  53. package/dist/compile/lexer.mjs +5 -4
  54. package/dist/compile/utils.d.ts +2 -1
  55. package/dist/compile/utils.js +8 -3
  56. package/dist/compile/utils.mjs +8 -3
  57. package/dist/compiled.mjs +1 -0
  58. package/dist/constants.mjs +1 -0
  59. package/dist/context.d.ts +1 -1
  60. package/dist/context.js +1 -0
  61. package/dist/context.mjs +3 -1
  62. package/dist/cookie/config.d.ts +2 -2
  63. package/dist/cookie/config.js +21 -26
  64. package/dist/cookie/config.mjs +22 -26
  65. package/dist/cookie/cookie.mjs +1 -0
  66. package/dist/cookie/crypto.mjs +1 -0
  67. package/dist/cookie/error.mjs +1 -0
  68. package/dist/cookie/lib.mjs +1 -0
  69. package/dist/cookie/serialize.mjs +1 -0
  70. package/dist/cookie/types.mjs +1 -0
  71. package/dist/cookie/utils.d.ts +1 -1
  72. package/dist/cookie/utils.js +64 -45
  73. package/dist/cookie/utils.mjs +66 -46
  74. package/dist/error.d.ts +10 -6
  75. package/dist/error.js +22 -6
  76. package/dist/error.mjs +25 -9
  77. package/dist/generation.mjs +1 -0
  78. package/dist/handler/error.d.ts +12 -15
  79. package/dist/handler/error.js +43 -40
  80. package/dist/handler/error.mjs +45 -41
  81. package/dist/handler/fetch.js +59 -21
  82. package/dist/handler/fetch.mjs +61 -22
  83. package/dist/handler/utils.d.ts +16 -3
  84. package/dist/handler/utils.js +14 -13
  85. package/dist/handler/utils.mjs +16 -15
  86. package/dist/index.d.ts +9 -9
  87. package/dist/index.js +1 -0
  88. package/dist/index.mjs +3 -2
  89. package/dist/memory.mjs +1 -0
  90. package/dist/package.js +1 -1
  91. package/dist/package.mjs +2 -1
  92. package/dist/parse-query.js +76 -76
  93. package/dist/parse-query.mjs +77 -76
  94. package/dist/plugin/aot/bun.mjs +1 -0
  95. package/dist/plugin/aot/core.js +7 -4
  96. package/dist/plugin/aot/core.mjs +8 -5
  97. package/dist/plugin/aot/esbuild.mjs +1 -0
  98. package/dist/plugin/aot/rspack-loader.mjs +1 -0
  99. package/dist/plugin/aot/rspack.mjs +1 -0
  100. package/dist/plugin/aot/treeshake.mjs +1 -0
  101. package/dist/plugin/aot/unplugin.mjs +4 -3
  102. package/dist/plugin/aot/vite.mjs +1 -0
  103. package/dist/plugin/aot/worker.mjs +1 -0
  104. package/dist/plugin/auto-head.mjs +1 -0
  105. package/dist/plugin/trace.mjs +1 -0
  106. package/dist/plugin/websocket.mjs +1 -0
  107. package/dist/route-table.mjs +1 -0
  108. package/dist/schema-snapshot.mjs +1 -0
  109. package/dist/sucrose.d.ts +2 -2
  110. package/dist/sucrose.js +11 -11
  111. package/dist/sucrose.mjs +12 -11
  112. package/dist/trace.js +8 -10
  113. package/dist/trace.mjs +10 -11
  114. package/dist/type/bridge-live.d.ts +1 -1
  115. package/dist/type/bridge-live.mjs +1 -0
  116. package/dist/type/bridge.d.ts +1 -1
  117. package/dist/type/bridge.mjs +1 -0
  118. package/dist/type/coerce-plan.mjs +1 -0
  119. package/dist/type/coerce.d.ts +1 -1
  120. package/dist/type/coerce.mjs +1 -0
  121. package/dist/type/compat.mjs +1 -0
  122. package/dist/type/constants.d.ts +1 -1
  123. package/dist/type/constants.mjs +1 -0
  124. package/dist/type/elysia/accelerate.mjs +1 -0
  125. package/dist/type/elysia/array-buffer.d.ts +3 -2
  126. package/dist/type/elysia/array-buffer.js +5 -5
  127. package/dist/type/elysia/array-buffer.mjs +6 -5
  128. package/dist/type/elysia/array-string.mjs +1 -0
  129. package/dist/type/elysia/array.mjs +1 -0
  130. package/dist/type/elysia/boolean-string.mjs +1 -0
  131. package/dist/type/elysia/boolean.mjs +1 -0
  132. package/dist/type/elysia/cookie.mjs +1 -0
  133. package/dist/type/elysia/date.mjs +1 -0
  134. package/dist/type/elysia/file-type.mjs +1 -0
  135. package/dist/type/elysia/file.mjs +1 -0
  136. package/dist/type/elysia/files.mjs +1 -0
  137. package/dist/type/elysia/form.mjs +1 -0
  138. package/dist/type/elysia/integer-string.mjs +1 -0
  139. package/dist/type/elysia/integer.mjs +1 -0
  140. package/dist/type/elysia/intersect.mjs +1 -0
  141. package/dist/type/elysia/json-string.mjs +1 -0
  142. package/dist/type/elysia/keep-config.js +8 -8
  143. package/dist/type/elysia/keep-config.mjs +9 -8
  144. package/dist/type/elysia/maybe-empty.mjs +1 -0
  145. package/dist/type/elysia/no-validate.mjs +1 -0
  146. package/dist/type/elysia/nullable.mjs +1 -0
  147. package/dist/type/elysia/number.mjs +1 -0
  148. package/dist/type/elysia/numeric-enum.d.ts +4 -2
  149. package/dist/type/elysia/numeric-enum.mjs +1 -0
  150. package/dist/type/elysia/numeric.mjs +1 -0
  151. package/dist/type/elysia/object-string.mjs +1 -0
  152. package/dist/type/elysia/object.mjs +1 -0
  153. package/dist/type/elysia/optional.mjs +1 -0
  154. package/dist/type/elysia/problem.mjs +1 -0
  155. package/dist/type/elysia/string.mjs +1 -0
  156. package/dist/type/elysia/uint8-array.d.ts +2 -1
  157. package/dist/type/elysia/uint8-array.js +2 -2
  158. package/dist/type/elysia/uint8-array.mjs +3 -2
  159. package/dist/type/elysia/union-enum.mjs +1 -0
  160. package/dist/type/elysia/union.mjs +1 -0
  161. package/dist/type/elysia/utils.js +3 -3
  162. package/dist/type/elysia/utils.mjs +4 -3
  163. package/dist/type/exports.mjs +1 -0
  164. package/dist/type/index.mjs +2 -1
  165. package/dist/type/shared.mjs +1 -0
  166. package/dist/type/sync-require.mjs +1 -0
  167. package/dist/type/typebox-system-lite.mjs +1 -0
  168. package/dist/type/typebox-system-require.mjs +1 -0
  169. package/dist/type/typebox-type-live.mjs +1 -0
  170. package/dist/type/typebox-value-live.mjs +1 -0
  171. package/dist/type/typebox-value-ops.mjs +1 -0
  172. package/dist/type/typebox-value.mjs +1 -0
  173. package/dist/type/types.d.ts +1 -1
  174. package/dist/type/types.mjs +1 -0
  175. package/dist/type/utils.mjs +1 -0
  176. package/dist/type/validator/clean-safe.mjs +1 -0
  177. package/dist/type/validator/custom-error.js +41 -33
  178. package/dist/type/validator/custom-error.mjs +42 -33
  179. package/dist/type/validator/default-precompute.d.ts +2 -2
  180. package/dist/type/validator/default-precompute.js +10 -11
  181. package/dist/type/validator/default-precompute.mjs +13 -13
  182. package/dist/type/validator/exact-mirror.mjs +1 -0
  183. package/dist/type/validator/frozen-check.js +7 -7
  184. package/dist/type/validator/frozen-check.mjs +8 -7
  185. package/dist/type/validator/index.d.ts +2 -2
  186. package/dist/type/validator/index.js +4 -8
  187. package/dist/type/validator/index.mjs +5 -8
  188. package/dist/type/validator/string-codec-aot.d.ts +1 -1
  189. package/dist/type/validator/string-codec-aot.mjs +1 -0
  190. package/dist/type/validator/validator-cache.js +18 -18
  191. package/dist/type/validator/validator-cache.mjs +19 -18
  192. package/dist/types.d.ts +105 -69
  193. package/dist/types.mjs +1 -0
  194. package/dist/universal/constants.mjs +1 -0
  195. package/dist/universal/file.mjs +1 -0
  196. package/dist/universal/index.mjs +1 -0
  197. package/dist/universal/is-production.mjs +1 -0
  198. package/dist/universal/server.mjs +1 -0
  199. package/dist/utils.d.ts +49 -31
  200. package/dist/utils.js +221 -109
  201. package/dist/utils.mjs +215 -106
  202. package/dist/validator/index.d.ts +3 -3
  203. package/dist/validator/index.js +4 -4
  204. package/dist/validator/index.mjs +5 -4
  205. package/dist/validator/route.d.ts +2 -2
  206. package/dist/validator/route.mjs +1 -0
  207. package/dist/ws/context.js +1 -1
  208. package/dist/ws/context.mjs +2 -1
  209. package/dist/ws/index.mjs +1 -0
  210. package/dist/ws/parser.mjs +1 -0
  211. package/dist/ws/route.js +34 -25
  212. package/dist/ws/route.mjs +40 -30
  213. package/dist/ws/types.d.ts +2 -3
  214. package/dist/ws/types.mjs +1 -0
  215. package/package.json +2 -2
package/dist/utils.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  import { isBun } from "./universal/constants.mjs";
2
3
  import { dangerousKeys } from "./constants.mjs";
3
4
  import { ElysiaFile } from "./universal/file.mjs";
@@ -7,6 +8,27 @@ const nullObject = () => Object.create(null);
7
8
  const mapDeriveEntry = (fn) => [fn, "mapDerive"];
8
9
  const deriveEntryFn = (entry) => Array.isArray(entry) ? entry[0] : entry;
9
10
  const isMapDeriveEntry = (entry) => Array.isArray(entry) && entry[1] === "mapDerive";
11
+ const isPlainDeriveEntry = (entry) => Array.isArray(entry) && entry[1] === "beforeHandle";
12
+ /**
13
+ * `~deriveEntries` for `hooks` whose role per occurrence is `at` (an entry
14
+ * for a derive, undefined for a plain hook). `deriveModes` gives each
15
+ * function's entries to its occurrences in order, so a plain occurrence
16
+ * before a derive of the same function gets a placeholder
17
+ */
18
+ function occurrenceDeriveEntries(hooks, at) {
19
+ const derived = /* @__PURE__ */ new Set();
20
+ const entries = [];
21
+ for (let i = hooks.length; i--;) {
22
+ const entry = at[i];
23
+ if (entry !== void 0) {
24
+ entries.push(entry);
25
+ derived.add(hooks[i]);
26
+ } else if (derived.has(hooks[i])) entries.push([hooks[i], "beforeHandle"]);
27
+ }
28
+ return entries.reverse();
29
+ }
30
+ /** Each function's derive entries, in occurrence order */
31
+ const deriveQueues = (entries) => entries && Map.groupBy(entries, deriveEntryFn);
10
32
  function isEmpty(obj) {
11
33
  for (const _ in obj) return false;
12
34
  return true;
@@ -36,6 +58,20 @@ function fnv1a(str) {
36
58
  }
37
59
  return hash >>> 0;
38
60
  }
61
+ /**
62
+ * 48-bit hash of a named plugin's key (fnv1a + 16 bits of a second hash): at
63
+ * 32 bits two plugins collide by ~77k and the second is dropped. 48 keeps
64
+ * `origin * 16 + slot` exact
65
+ */
66
+ function pluginHash(str) {
67
+ let mix = 2654435769;
68
+ const len = str.length;
69
+ for (let i = 0; i < len; i++) {
70
+ mix = Math.imul(mix ^ str.charCodeAt(i), 1540483477);
71
+ mix ^= mix >>> 15;
72
+ }
73
+ return (mix >>> 16) * 4294967296 + fnv1a(str);
74
+ }
39
75
  const compositionKeys = Object.freeze([
40
76
  "allOf",
41
77
  "anyOf",
@@ -57,24 +93,27 @@ function refTargets(roots, models = {}) {
57
93
  roots.concat(Object.values(models)).forEach(index);
58
94
  return (name) => targets[name] ?? [];
59
95
  }
96
+ const macroOrigin = /* @__PURE__ */ new WeakMap();
60
97
  /**
61
- * Maps each lifecycle/derive function to the hash of the named plugin it was
62
- * first registered on. Used by `.use()` to dedup absorbed hooks: if a fn's
63
- * origin is already in `parent.#apps`, the parent has already absorbed that
64
- * named plugin via another path and should skip re-adding the fn.
65
- *
66
- * Anonymous plugins (no name) don't tag their fns - there's no hash to dedup
67
- * by, so their fns always propagate.
98
+ * Id of `ref` in the registry at `globalThis[key]`, `next` being the last id
99
+ * handed out. Weak so it doesn't retain `ref`, realm-wide so every installed
100
+ * Elysia copy agrees
68
101
  */
69
- const fnOrigin = /* @__PURE__ */ new WeakMap();
70
- const macroOrigin = /* @__PURE__ */ new WeakMap();
71
- const isLocalScope = (s) => s === "local" || s === void 0;
102
+ function realmId(key, ref) {
103
+ const realm = globalThis;
104
+ const registry = realm[key] ??= {
105
+ ids: /* @__PURE__ */ new WeakMap(),
106
+ next: 0
107
+ };
108
+ let id = registry.ids.get(ref);
109
+ if (id === void 0) registry.ids.set(ref, id = ++registry.next);
110
+ return id;
111
+ }
72
112
  /**
73
- * The absorbing instance's chain as it stood at `.use()`: the `over` of a
74
- * combine node, or the node itself. A combine node is never on an instance's
75
- * own chain, so it can't serve as a `stopAt` directly
113
+ * Id of an unnamed plugin in `childrenHash`, negative so it never collides
114
+ * with a named plugin's unsigned hash
76
115
  */
77
- const rootSnapshot = (node) => node && "combine" in node ? node.over : node;
116
+ const pluginId = (plugin) => -realmId("~elysiaPluginIds", plugin);
78
117
  const COMPACT_CHUNK_SIZE = 32;
79
118
  const emptyCompactBeforeHandlePrefix = {
80
119
  length: 0,
@@ -157,14 +196,21 @@ function compactBeforeHandlePrefix(start) {
157
196
  }
158
197
  const flattenNodeStack = [];
159
198
  const flattenPhaseStack = [];
199
+ const deriveSlot = 15;
200
+ const withLayer = (outer, layer) => new Set([...outer ?? [], ...layer]);
160
201
  /**
161
202
  * Walk the chain tail-first into a fresh `Partial<AppHook>`
162
203
  *
204
+ * Each instance chain a combine node links is one layer, outermost first.
205
+ * A named plugin's hook is skipped when an outer layer, or `skip`, already
206
+ * installed that plugin's same lifecycle event: it runs once, at its
207
+ * outermost position. Within a layer every registration stays
208
+ *
163
209
  * Explicit-stack walk (no recursion) so it works uniformly for linear
164
210
  * chains and combine nodes without risking stack overflow on deep chains
165
211
  */
166
- function flattenChain(start, keep, stopAt, resolveAdded) {
167
- if (!start || start === stopAt) return;
212
+ function flattenChain(start, resolveAdded, skip, info) {
213
+ if (!start) return;
168
214
  const result = nullObject();
169
215
  const nodes = flattenNodeStack;
170
216
  const phases = flattenPhaseStack;
@@ -172,47 +218,79 @@ function flattenChain(start, keep, stopAt, resolveAdded) {
172
218
  phases.length = 0;
173
219
  nodes.push(start);
174
220
  phases.push(0);
221
+ let outer = skip;
222
+ let layer;
223
+ let roles;
175
224
  while (nodes.length) {
176
225
  const node = nodes.pop();
177
- if (phases.pop() === 1) {
226
+ const phase = phases.pop();
227
+ if (phase === 2) {
228
+ if (layer?.length) {
229
+ outer = withLayer(outer, layer);
230
+ layer.length = 0;
231
+ }
232
+ continue;
233
+ }
234
+ if (phase === 1) {
235
+ const { origin, propagated, scope } = node;
236
+ if (info && origin !== void 0) info.named = true;
178
237
  const added = resolveAdded ? resolveAdded(node) : node.added;
179
- if (added) {
180
- const nodeScope = node.scope;
181
- if (!(keep && !keep(nodeScope))) for (const key in added) {
182
- const v = added[key];
183
- if (v === void 0 || v === null) continue;
184
- if (eventProperties.has(key) || key === "schemas" || key === "~deriveEntries") {
185
- const existing = result[key];
186
- if (Array.isArray(v)) if (existing) {
187
- const arr = existing;
188
- for (let i = 0; i < v.length; i++) arr.push(v[i]);
189
- } else result[key] = v.slice();
190
- else if (existing) existing.push(v);
191
- else result[key] = [v];
192
- } else result[key] = v;
238
+ const base = origin !== void 0 && (propagated || scope === "plugin" || scope === "global") ? origin * 16 : void 0;
239
+ const derives = deriveQueues(added?.["~deriveEntries"]);
240
+ if (added) for (const key in added) {
241
+ const v = added[key];
242
+ if (v == null || key === "~deriveEntries") continue;
243
+ const slot = lifecycleEvents.indexOf(key);
244
+ if (key === "beforeHandle") {
245
+ for (const fn of Array.isArray(v) ? v : [v]) {
246
+ const entry = derives?.get(fn)?.shift();
247
+ if (base !== void 0) {
248
+ const pair = base + (entry === void 0 ? slot : deriveSlot);
249
+ if (outer?.has(pair)) continue;
250
+ (layer ??= []).push(pair);
251
+ }
252
+ const list = result.beforeHandle ??= [];
253
+ if (entry !== void 0) (roles ??= [])[list.length] = entry;
254
+ list.push(fn);
255
+ }
256
+ continue;
193
257
  }
258
+ if (base !== void 0 && slot !== -1) {
259
+ if (outer?.has(base + slot)) continue;
260
+ if (!Array.isArray(v) || v.length) (layer ??= []).push(base + slot);
261
+ }
262
+ if (slot !== -1 || key === "schemas") {
263
+ const existing = result[key];
264
+ if (Array.isArray(v)) if (existing) for (let i = 0; i < v.length; i++) existing.push(v[i]);
265
+ else result[key] = v.slice();
266
+ else if (existing) existing.push(v);
267
+ else result[key] = [v];
268
+ } else result[key] = v;
194
269
  }
195
270
  continue;
196
271
  }
197
- if (stopAt && node === stopAt) continue;
198
272
  if ("combine" in node) {
199
273
  if (node.combine) {
200
274
  nodes.push(node.combine);
201
275
  phases.push(0);
202
276
  }
203
277
  if (node.over) {
278
+ nodes.push(node);
279
+ phases.push(2);
204
280
  nodes.push(node.over);
205
281
  phases.push(0);
206
282
  }
207
283
  } else {
208
284
  nodes.push(node);
209
285
  phases.push(1);
210
- if (node.parent && node.parent !== stopAt) {
286
+ if (node.parent) {
211
287
  nodes.push(node.parent);
212
288
  phases.push(0);
213
289
  }
214
290
  }
215
291
  }
292
+ if (info) info.installs = layer?.length ? withLayer(outer, layer) : outer;
293
+ if (roles) result["~deriveEntries"] = occurrenceDeriveEntries(result.beforeHandle, roles);
216
294
  if (isNotEmpty(result)) return result;
217
295
  }
218
296
  /**
@@ -233,27 +311,45 @@ function clearFlattenChainMemo(root) {
233
311
  flattenChainMemos.delete(root);
234
312
  }
235
313
  const emptyFlatten = Object.freeze(nullObject());
236
- function flattenChainMemoReadonly(root, start, resolveAdded) {
237
- if (!start) return;
314
+ function flattenMemoOf(root) {
238
315
  let bucket = flattenChainMemos.get(root);
239
316
  if (!bucket || bucket.e !== macroTableEpoch) {
240
317
  bucket = {
241
318
  e: macroTableEpoch,
242
- per: /* @__PURE__ */ new WeakMap()
319
+ per: /* @__PURE__ */ new WeakMap(),
320
+ info: /* @__PURE__ */ new WeakMap()
243
321
  };
244
322
  flattenChainMemos.set(root, bucket);
245
323
  }
246
- const perRoot = bucket.per;
247
- let cached = perRoot.get(start);
324
+ return bucket;
325
+ }
326
+ /** What the chain installs, see {@link flattenChain} */
327
+ function flattenChainInfo(root, start, resolveAdded) {
328
+ if (!start) return;
329
+ const bucket = flattenMemoOf(root);
330
+ flattenChainMemoReadonly(root, start, resolveAdded);
331
+ return bucket.info.get(start);
332
+ }
333
+ function flattenChainMemoReadonly(root, start, resolveAdded, skip) {
334
+ if (!start) return;
335
+ const bucket = flattenMemoOf(root);
336
+ let memo = bucket.per;
337
+ if (skip) {
338
+ const skipped = bucket.skipped ??= /* @__PURE__ */ new WeakMap();
339
+ memo = skipped.get(start);
340
+ if (!memo) skipped.set(start, memo = /* @__PURE__ */ new WeakMap());
341
+ }
342
+ let cached = memo.get(skip ?? start);
248
343
  if (cached === void 0) {
249
- cached = flattenChain(start, void 0, void 0, resolveAdded) ?? emptyFlatten;
250
- perRoot.set(start, cached);
344
+ const info = skip ? void 0 : {};
345
+ cached = flattenChain(start, resolveAdded, skip, info) ?? emptyFlatten;
346
+ memo.set(skip ?? start, cached);
347
+ if (info?.named) bucket.info.set(start, info);
251
348
  }
252
- if (cached === emptyFlatten) return;
253
- return cached;
349
+ if (cached !== emptyFlatten) return cached;
254
350
  }
255
- function flattenChainMemo(root, start, resolveAdded) {
256
- const cached = flattenChainMemoReadonly(root, start, resolveAdded);
351
+ function flattenChainMemo(root, start, resolveAdded, skip) {
352
+ const cached = flattenChainMemoReadonly(root, start, resolveAdded, skip);
257
353
  if (cached) return cloneHook(cached);
258
354
  }
259
355
  const notLatin = /[^\x00-\xFF]/;
@@ -302,6 +398,7 @@ function formToFormData(value) {
302
398
  * ```
303
399
  */
304
400
  const form = (value) => Object.setPrototypeOf({ ...value }, ELYSIA_FORM_PROTOTYPE);
401
+ const macroType = () => void 0;
305
402
  const assignOwn = (target, source) => source != null && Object.hasOwn(source, "__proto__") ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : Object.assign(target, source);
306
403
  const getLoosePath = (path) => path.charCodeAt(path.length - 1) === 47 ? path.slice(0, -1) : path + "/";
307
404
  const byteStreams = /* @__PURE__ */ new WeakSet();
@@ -426,26 +523,6 @@ function mergeArray(a, b, reverse = false) {
426
523
  }
427
524
  return [a, b];
428
525
  }
429
- /**
430
- * Like {@link mergeArray} but drops entries from `a` that already appear in
431
- * `b` by reference. Always allocates fresh arrays - never mutates inputs.
432
- *
433
- * Used at the compile-time merge of a route's snapshotted `appHook` with the
434
- * root's current `rootHook`: the same fn can sit on both sides because
435
- * `.use()` propagates global/plugin-scoped hooks into the parent, while the
436
- * route's `appHook` was captured on the child and still holds the original.
437
- * The fn must run once, in `b`'s position.
438
- */
439
- function dedupedMergeArray(a, b, reverse = false) {
440
- if (!a) return Array.isArray(b) ? b.slice() : b;
441
- if (!b) return Array.isArray(a) ? a.slice() : a;
442
- const aArr = Array.isArray(a) ? a : [a];
443
- const bArr = Array.isArray(b) ? b : [b];
444
- const seen = new Set(bArr);
445
- const filtered = [];
446
- for (let i = 0; i < aArr.length; i++) if (!seen.has(aArr[i])) filtered.push(aArr[i]);
447
- return reverse ? bArr.concat(filtered) : filtered.concat(bArr);
448
- }
449
526
  const hookSchemaKeys = [
450
527
  "body",
451
528
  "headers",
@@ -466,7 +543,7 @@ const hookEventKeys = [
466
543
  "error",
467
544
  "trace"
468
545
  ];
469
- const eventProperties = new Set([
546
+ const lifecycleEvents = [
470
547
  "start",
471
548
  "stop",
472
549
  "trace",
@@ -478,7 +555,8 @@ const eventProperties = new Set([
478
555
  "mapResponse",
479
556
  "afterResponse",
480
557
  "error"
481
- ]);
558
+ ];
559
+ const eventProperties = new Set(lifecycleEvents);
482
560
  function hookToGuard(a) {
483
561
  if (a.schema !== "merge") {
484
562
  if (a.schema !== void 0 && a.schema !== "override") throw new Error(`[Elysia] Invalid guard schema ${JSON.stringify(a.schema)}, expected 'merge' or 'override' (1.x 'standalone' is 'merge')`);
@@ -515,25 +593,24 @@ function coalesceSchemas(existing, incoming) {
515
593
  if (!merged) existing.push(entry);
516
594
  }
517
595
  }
518
- function mergeHook(a, b, reverse = false, dedup = false) {
596
+ function mergeHook(a, b) {
519
597
  if (!b) return a;
520
598
  if (!a) return b;
521
- const merge = dedup ? dedupedMergeArray : mergeArray;
522
599
  for (const key of hookSchemaKeys) if (!a[key] && b[key]) a[key] = b[key];
523
600
  else if (key === "response" && a.response && b.response) a.response = mergeResponse(b.response, a.response);
524
- for (const key of hookEventKeys) if (a[key] || b[key]) a[key] = merge(a[key], b[key], reverse);
525
- if (a.schemas || b.schemas) a.schemas = mergeArray(a.schemas, b.schemas, reverse);
601
+ for (const key of hookEventKeys) if (a[key] || b[key]) a[key] = mergeArray(a[key], b[key], true);
602
+ if (a.schemas || b.schemas) a.schemas = mergeArray(a.schemas, b.schemas, true);
526
603
  const aDerive = a["~deriveEntries"];
527
604
  const bDerive = b["~deriveEntries"];
528
- if (aDerive || bDerive) a["~deriveEntries"] = mergeArray(aDerive, bDerive, reverse);
605
+ if (aDerive || bDerive) a["~deriveEntries"] = mergeArray(aDerive, bDerive, true);
529
606
  return a;
530
607
  }
531
608
  const createErrorEventHandler = (fn, error) => {
532
609
  const handler = (context) => {
533
610
  if (context.error instanceof error) return fn(context);
534
611
  };
535
- const p = error?.prototype;
536
- if (typeof p === "object" && p !== null && p !== Error.prototype && !(p instanceof Error)) handler["~errorClass"] = error;
612
+ const prototype = error?.prototype;
613
+ if (typeof prototype === "object" && prototype !== null && prototype !== Error.prototype && !(prototype instanceof Error)) handler["~errorClass"] = error;
537
614
  return handler;
538
615
  };
539
616
  const isObject = (item) => item && typeof item === "object" && !Array.isArray(item);
@@ -623,12 +700,6 @@ function joinPath(base, path) {
623
700
  if (!baseEndsWithSlash && !pathStartsWithSlash) return base + "/" + path;
624
701
  return base + path;
625
702
  }
626
- function pushField(target, key, item) {
627
- const v = target[key];
628
- if (v) if (Array.isArray(v)) target[key].push(item);
629
- else target[key] = [v, item];
630
- else target[key] = item;
631
- }
632
703
  const requestId = isBun ? Bun.randomUUIDv7 : crypto.randomUUID.bind(crypto);
633
704
  /**
634
705
  * Offset of the first byte after `://`, so the authority is never scanned as
@@ -646,22 +717,34 @@ function isPlainObject(v) {
646
717
  const proto = Object.getPrototypeOf(v);
647
718
  return proto === Object.prototype || proto === null;
648
719
  }
649
- function clonePlainDecorators(source, seen) {
650
- if (seen !== void 0) {
651
- const existing = seen.get(source);
652
- if (existing) return existing;
653
- }
720
+ function clonePlainDecorators(source, seen = /* @__PURE__ */ new WeakMap()) {
721
+ const existing = seen.get(source);
722
+ if (existing) return existing;
654
723
  const out = nullObject();
655
- if (seen !== void 0) seen.set(source, out);
656
- for (const key in source) {
657
- const value = source[key];
658
- if (isPlainObject(value)) {
659
- if (seen === void 0) (seen = /* @__PURE__ */ new WeakMap()).set(source, out);
660
- out[key] = clonePlainDecorators(value, seen);
661
- } else out[key] = value;
724
+ seen.set(source, out);
725
+ for (const key in source) copyKey(source, out, key, seen);
726
+ const own = Reflect.ownKeys(source);
727
+ if (own.length !== Object.keys(source).length) for (const key of own) {
728
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
729
+ if (!descriptor || descriptor.enumerable && typeof key === "string") continue;
730
+ try {
731
+ copyKey(source, out, key, seen, descriptor);
732
+ } catch {}
662
733
  }
734
+ if (!Object.isExtensible(source)) Object.preventExtensions(out);
663
735
  return out;
664
736
  }
737
+ const copyKey = (source, out, key, seen, descriptor = Object.getOwnPropertyDescriptor(source, key)) => {
738
+ let value = source[key];
739
+ if (isPlainObject(value)) value = clonePlainDecorators(value, seen);
740
+ if (!descriptor || descriptor.writable !== false && descriptor.enumerable && descriptor.configurable) out[key] = value;
741
+ else Object.defineProperty(out, key, {
742
+ value,
743
+ writable: descriptor.writable ?? true,
744
+ enumerable: descriptor.enumerable,
745
+ configurable: descriptor.configurable
746
+ });
747
+ };
665
748
  function prefix(prefix, models) {
666
749
  const prefixed = nullObject();
667
750
  for (const key in models) prefixed[`${prefix}.${key}`] = models[key];
@@ -672,18 +755,14 @@ prefix.capitalize = function prefixModelsCapitalize(prefix, models) {
672
755
  for (const key in models) prefixed[`${prefix}.${key.charAt(0).toUpperCase() + key.slice(1)}`] = models[key];
673
756
  return prefixed;
674
757
  };
675
- const macroSeedRefIds = /* @__PURE__ */ new WeakMap();
676
- let macroSeedRefCounter = 0;
677
- function macroSeedRefId(ref) {
678
- let id = macroSeedRefIds.get(ref);
679
- if (id === void 0) macroSeedRefIds.set(ref, id = ++macroSeedRefCounter);
680
- return id;
681
- }
682
758
  function serializeMacroSeed(_key, value) {
683
759
  switch (typeof value) {
684
- case "function": return "\0fn:" + macroSeedRefId(value);
760
+ case "function": return "\0ref:" + realmId("~elysiaSeedIds", value);
685
761
  case "bigint": return "\0bigint:" + value.toString();
686
- case "symbol": return "\0sym:" + String(value);
762
+ case "symbol": {
763
+ const key = Symbol.keyFor(value);
764
+ return key === void 0 ? "\0ref:" + realmId("~elysiaSeedIds", value) : "\0symfor:" + key;
765
+ }
687
766
  case "undefined": return "\0undefined";
688
767
  default: return value;
689
768
  }
@@ -704,7 +783,21 @@ const isDisposable = (value) => {
704
783
  };
705
784
  const singletons = /* @__PURE__ */ new WeakSet();
706
785
  const isSingleton = (value) => singletons.has(value);
707
- const markSingletons = (value, depth = 4, seen = /* @__PURE__ */ new Set()) => {
786
+ function pushHeld(object, into, seen) {
787
+ if (ArrayBuffer.isView(object)) return;
788
+ let all = false;
789
+ try {
790
+ all = typeof object !== "function" && object !== globalThis && !(object instanceof Error);
791
+ } catch {}
792
+ try {
793
+ const keys = all ? Reflect.ownKeys(object) : [...Object.keys(object), ...Object.getOwnPropertySymbols(object)];
794
+ for (const key of keys) {
795
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
796
+ if (descriptor && "value" in descriptor) enqueue(descriptor.value, into, seen);
797
+ }
798
+ } catch {}
799
+ }
800
+ function enqueue(value, into, seen) {
708
801
  if (value == null) return;
709
802
  const kind = typeof value;
710
803
  if (kind !== "object" && kind !== "function") return;
@@ -712,14 +805,30 @@ const markSingletons = (value, depth = 4, seen = /* @__PURE__ */ new Set()) => {
712
805
  if (seen.has(object)) return;
713
806
  seen.add(object);
714
807
  singletons.add(object);
715
- if (depth <= 0) return;
716
- for (const key of Object.keys(object)) {
717
- const descriptor = Object.getOwnPropertyDescriptor(object, key);
718
- if (descriptor && "value" in descriptor) markSingletons(descriptor.value, depth - 1, seen);
808
+ into.push(object);
809
+ }
810
+ function markLevels(level, depth, seen) {
811
+ while (level.length && depth > 0) {
812
+ const next = [];
813
+ for (const object of level) pushHeld(object, next, seen);
814
+ level = next;
815
+ depth--;
719
816
  }
817
+ }
818
+ const markSingletons = (value, depth = 4) => {
819
+ const seen = /* @__PURE__ */ new Set();
820
+ const level = [];
821
+ enqueue(value, level, seen);
822
+ markLevels(level, depth, seen);
823
+ };
824
+ const markPropertySingletons = (object) => {
825
+ const seen = /* @__PURE__ */ new Set();
826
+ const level = [];
827
+ pushHeld(object, level, seen);
828
+ markLevels(level, 4, seen);
720
829
  };
721
830
  const isSocketQuiet = (socket) => socket.readyState === 3 && !socket.data.inflight && !socket.data.opening && !socket.data.settling;
722
831
  const isHTMLBundle = (value) => Object.prototype.toString.call(value) === "[object HTMLBundle]" || typeof value?.index === "string" && Array.isArray(value.files);
723
832
 
724
833
  //#endregion
725
- export { ELYSIA_FORM_PROTOTYPE, assignOwn, authorityEnd, bytes, clearFlattenChainMemo, cloneHook, clonePlainDecorators, clonePlainDeep, coalesceSchemas, compactBeforeHandleConflicts, compactBeforeHandlePrefix, compositionKeys, constantTimeEqual, createErrorEventHandler, deriveEntryFn, eventProperties, evictOldestHalf, flattenChain, flattenChainMemo, flattenChainMemoReadonly, fnOrigin, fnv1a, form, formToFormData, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isBlob, isByteStream, isCompactBeforeHandleOnly, isDisposable, isElysiaForm, isEmpty, isHTMLBundle, isLocalScope, isMapDeriveEntry, isNotEmpty, isPlainObject, isRecordNumber, isSingleton, isSocketQuiet, joinPath, macroEpoch, macroOrigin, mapDeriveEntry, markSingletons, mergeArray, mergeDeep, mergeHook, mergeResponse, nullObject, prefix, pushField, redirect, refTargets, replaceUrlPath, requestId, rootSnapshot, schemaProperties, serializeMacroSeed, sse, sseData, throwLifecycleErrors };
834
+ export { ELYSIA_FORM_PROTOTYPE, assignOwn, authorityEnd, bytes, clearFlattenChainMemo, cloneHook, clonePlainDecorators, clonePlainDeep, coalesceSchemas, compactBeforeHandleConflicts, compactBeforeHandlePrefix, compositionKeys, constantTimeEqual, createErrorEventHandler, deriveEntryFn, deriveQueues, eventProperties, evictOldestHalf, flattenChain, flattenChainInfo, flattenChainMemo, flattenChainMemoReadonly, fnv1a, form, formToFormData, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isBlob, isByteStream, isCompactBeforeHandleOnly, isDisposable, isElysiaForm, isEmpty, isHTMLBundle, isMapDeriveEntry, isNotEmpty, isPlainDeriveEntry, isPlainObject, isRecordNumber, isSingleton, isSocketQuiet, joinPath, macroEpoch, macroOrigin, macroType, mapDeriveEntry, markPropertySingletons, markSingletons, mergeArray, mergeDeep, mergeHook, mergeResponse, nullObject, occurrenceDeriveEntries, pluginHash, pluginId, prefix, redirect, refTargets, replaceUrlPath, requestId, schemaProperties, serializeMacroSeed, sse, sseData, throwLifecycleErrors };
@@ -1,8 +1,8 @@
1
1
  import { AnySchema, StandardSchemaV1Like } from "../type/types.js";
2
- import { ElysiaConfig, MaybePromise } from "../types.js";
3
- import { TypeBoxValidator } from "../type/bridge.js";
4
2
  import { CoerceOption } from "../type/coerce.js";
5
3
  import { ProgramId, ValidatorSlot } from "../compile/aot.js";
4
+ import { TypeBoxValidator } from "../type/bridge.js";
5
+ import { ElysiaConfig, MaybePromise } from "../types.js";
6
6
  import { TSchema } from "typebox/type";
7
7
  import { TLocalizedValidationError } from "typebox/error";
8
8
 
@@ -38,7 +38,7 @@ declare abstract class Validator {
38
38
  static create<const Schema extends TSchema>(schema: Schema, options?: ValidatorOptions): TypeBoxValidator<Schema>;
39
39
  static create<const Schema extends StandardSchemaV1Like>(schema: Schema, options?: ValidatorOptions): StandardValidator;
40
40
  static create(schema: undefined | null, options?: ValidatorOptions): Validator | undefined;
41
- static response(schema: TSchema | StandardSchemaV1Like | Record<number, TSchema | StandardSchemaV1Like> | undefined | null, options?: ResponseValidatorOptions): Record<number, Validator> | undefined;
41
+ static response(schema: TSchema | StandardSchemaV1Like | Record<number, TSchema | StandardSchemaV1Like> | undefined | null, options?: ResponseValidatorOptions): Map<number, Validator> | undefined;
42
42
  static clear(): void;
43
43
  }
44
44
  declare class StandardValidator extends Validator {
@@ -76,13 +76,13 @@ var Validator = class Validator {
76
76
  }
77
77
  schema = Validator.reference(schema, options?.models);
78
78
  const responseSlot = (status) => options?.aot ? `response:${status}` : void 0;
79
- if (isSingleSchema(schema)) return { 200: Validator.create(schema, {
79
+ if (isSingleSchema(schema)) return new Map([[200, Validator.create(schema, {
80
80
  ...options,
81
81
  slot: responseSlot(200),
82
82
  schemas: options?.schemas?.map((s) => toStatusBased(s)[200])?.filter(Boolean)
83
- }) };
83
+ })]]);
84
84
  const entries = Object.entries(schema);
85
- return Object.fromEntries(entries.map(([k, v]) => [k, v instanceof Validator ? v : Validator.create(v, {
85
+ return new Map(entries.map(([k, v]) => [+k, v instanceof Validator ? v : Validator.create(v, {
86
86
  ...options,
87
87
  slot: responseSlot(k),
88
88
  schemas: options?.schemas?.map((s) => toStatusBased(s)[k])?.filter(Boolean)
@@ -154,7 +154,7 @@ var MultiValidator = class MultiValidator extends Validator {
154
154
  const compiled = require_type_bridge.Compile(coercedSchema);
155
155
  codecs.push(require_type_bridge.HasCodec(coercedSchema));
156
156
  hasDefaults.push(hd);
157
- if (require_type_bridge.TypeBoxValidator.member(coercedSchema, compiled)) {
157
+ if (require_type_bridge.TypeBoxValidator.isAsyncMember(coercedSchema, compiled)) {
158
158
  this.isAsync = true;
159
159
  asyncMembers.push(Validator.create(rawSchema, {
160
160
  coerces: options?.coerces,
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  import { assignOwn } from "../utils.mjs";
2
3
  import { Clone, Compile, HasCodec, Intersect, TypeBoxValidator, TypeBoxValidatorCache, applyCoercions } from "../type/bridge.mjs";
3
4
  import { ValidationError } from "../error.mjs";
@@ -75,13 +76,13 @@ var Validator = class Validator {
75
76
  }
76
77
  schema = Validator.reference(schema, options?.models);
77
78
  const responseSlot = (status) => options?.aot ? `response:${status}` : void 0;
78
- if (isSingleSchema(schema)) return { 200: Validator.create(schema, {
79
+ if (isSingleSchema(schema)) return new Map([[200, Validator.create(schema, {
79
80
  ...options,
80
81
  slot: responseSlot(200),
81
82
  schemas: options?.schemas?.map((s) => toStatusBased(s)[200])?.filter(Boolean)
82
- }) };
83
+ })]]);
83
84
  const entries = Object.entries(schema);
84
- return Object.fromEntries(entries.map(([k, v]) => [k, v instanceof Validator ? v : Validator.create(v, {
85
+ return new Map(entries.map(([k, v]) => [+k, v instanceof Validator ? v : Validator.create(v, {
85
86
  ...options,
86
87
  slot: responseSlot(k),
87
88
  schemas: options?.schemas?.map((s) => toStatusBased(s)[k])?.filter(Boolean)
@@ -153,7 +154,7 @@ var MultiValidator = class MultiValidator extends Validator {
153
154
  const compiled = Compile(coercedSchema);
154
155
  codecs.push(HasCodec(coercedSchema));
155
156
  hasDefaults.push(hd);
156
- if (TypeBoxValidator.member(coercedSchema, compiled)) {
157
+ if (TypeBoxValidator.isAsyncMember(coercedSchema, compiled)) {
157
158
  this.isAsync = true;
158
159
  asyncMembers.push(Validator.create(rawSchema, {
159
160
  coerces: options?.coerces,
@@ -1,5 +1,5 @@
1
1
  import { AnySchema } from "../type/types.js";
2
- import { ToSubTypeValidator, ValidatorOptions } from "./index.js";
2
+ import { ToSubTypeValidator, Validator, ValidatorOptions } from "./index.js";
3
3
  //#region src/validator/route.d.ts
4
4
  interface RouteSchema {
5
5
  body?: AnySchema;
@@ -25,7 +25,7 @@ declare class RouteValidator<const in out T extends RouteSchema> {
25
25
  query: ToSubTypeValidator<T['query']> | undefined;
26
26
  params: ToSubTypeValidator<T['params']> | undefined;
27
27
  cookie: ToSubTypeValidator<T['cookie']> | undefined;
28
- response: { [Status in keyof T['response']]: ToSubTypeValidator<T['response'][Status]> } | undefined;
28
+ response: Map<number, Validator> | undefined;
29
29
  constructor(route: T, options?: RouteValidatorOptions);
30
30
  }
31
31
  //#endregion
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  import { coerceBody, coerceFormData, coerceQuery, coerceRoot, coerceStringToStructure, hasTypes, mayHaveFileType } from "../type/bridge.mjs";
2
3
  import { ELYSIA_TYPES } from "../type/constants.mjs";
3
4
  import { Validator } from "./index.mjs";
@@ -84,7 +84,7 @@ var ElysiaWS = class {
84
84
  const validators = connectionData?.validator;
85
85
  const defaultValidator = connectionData?.defaultValidator;
86
86
  const status = data instanceof require_error.ElysiaStatus ? data : void 0;
87
- const v = validators && (status ? validators[status.status] : defaultValidator);
87
+ const v = validators && (status ? validators.get(+status.status) : defaultValidator);
88
88
  let value = status ? status.response : data;
89
89
  if (v) {
90
90
  const encode = v.EncodeFrom;
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  import { requestId } from "../utils.mjs";
2
3
  import { ElysiaStatus, ValidationError } from "../error.mjs";
3
4
 
@@ -83,7 +84,7 @@ var ElysiaWS = class {
83
84
  const validators = connectionData?.validator;
84
85
  const defaultValidator = connectionData?.defaultValidator;
85
86
  const status = data instanceof ElysiaStatus ? data : void 0;
86
- const v = validators && (status ? validators[status.status] : defaultValidator);
87
+ const v = validators && (status ? validators.get(+status.status) : defaultValidator);
87
88
  let value = status ? status.response : data;
88
89
  if (v) {
89
90
  const encode = v.EncodeFrom;
package/dist/ws/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  import { ElysiaWS, isGeneratorObject } from "./context.mjs";
2
3
  import { createMessageParser, defaultWSParse } from "./parser.mjs";
3
4
  import { buildGlobalWSHandler, buildWSRoute } from "./route.mjs";
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  //#region src/ws/parser.ts
2
3
  function defaultWSParse(message) {
3
4
  if (typeof message !== "string") return message;