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/base.mjs CHANGED
@@ -1,10 +1,12 @@
1
+ // @bun
1
2
  import { isBun } from "./universal/constants.mjs";
2
- import { clearFlattenChainMemo, clonePlainDecorators, clonePlainDeep, coalesceSchemas, createErrorEventHandler, eventProperties, fnOrigin, fnv1a, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isEmpty, isHTMLBundle, isLocalScope, isNotEmpty, isPlainObject, isRecordNumber, joinPath, macroOrigin, mapDeriveEntry, markSingletons, mergeDeep, mergeResponse, nullObject, pushField, rootSnapshot, schemaProperties, serializeMacroSeed, throwLifecycleErrors } from "./utils.mjs";
3
+ import { clearFlattenChainMemo, clonePlainDecorators, clonePlainDeep, coalesceSchemas, createErrorEventHandler, eventProperties, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isEmpty, isHTMLBundle, isNotEmpty, isPlainObject, isRecordNumber, joinPath, macroOrigin, mapDeriveEntry, markPropertySingletons, markSingletons, mergeDeep, mergeResponse, nullObject, pluginHash, pluginId, schemaProperties, serializeMacroSeed, throwLifecycleErrors } from "./utils.mjs";
3
4
  import { Ref, warmTypebox } from "./type/bridge.mjs";
4
5
  import { isProduction } from "./universal/is-production.mjs";
5
6
  import { Capture, Compiled, beginCompilerSession, createAotFingerprint, endCompilerSession } from "./compile/aot.mjs";
6
7
  import { traceCapabilityRequired, wsCapabilityRequired } from "./generation.mjs";
7
- import { chainResolver, clearHandlerAnalysisCaches, compileHandler, composeRouteHook, localMacroRoot, resolveLocalHook } from "./compile/handler/index.mjs";
8
+ import { releaseAnalysisCaches } from "./compile/handler/descriptor.mjs";
9
+ import { chainResolver, clearHandlerAnalysisCaches, compileHandler, composeRouteHook, localMacroRoot, resolveLocalHook, runIntrospect } from "./compile/handler/index.mjs";
8
10
  import { RouteFlag, buildRouteTable, routeRow } from "./route-table.mjs";
9
11
  import { BunAdapter } from "./adapter/bun/index.mjs";
10
12
  import { clearContextCache } from "./context.mjs";
@@ -14,6 +16,9 @@ import Memoirist from "memoirist";
14
16
 
15
17
  //#region src/base.ts
16
18
  const useNodesBuffer = [];
19
+ const propagatedRegistrations = /* @__PURE__ */ new WeakMap();
20
+ const liftedRequests = /* @__PURE__ */ new WeakMap();
21
+ const hocOriginOf = (o) => typeof o === "object" ? o.origin : o;
17
22
  const emptyHistory = Object.freeze([]);
18
23
  let mutationEpoch = 0;
19
24
  const extCallbackIndexes = /* @__PURE__ */ new WeakMap();
@@ -39,6 +44,13 @@ const markStoredSingletons = (table, source) => {
39
44
  if (!table) return;
40
45
  for (const key in source) markSingletons(table[key]);
41
46
  };
47
+ const sharedTables = [
48
+ "decorator",
49
+ "store",
50
+ "headers",
51
+ "models",
52
+ "parser"
53
+ ];
42
54
  const canRegisterLoose = (path, isDynamic) => !isDynamic && (path.length === 0 || path.charCodeAt(path.length - 1) === 47);
43
55
  const isSingleSchema = (value) => "~kind" in value || "~elyAcl" in value || "~standard" in value;
44
56
  const isSchemaValue = (value) => {
@@ -78,7 +90,7 @@ function assertSchemaShape(hook, where) {
78
90
  }
79
91
  if (isStatusMap) continue;
80
92
  }
81
- throw new Error(`[Elysia] ${where}: "${key}" is not a schema — use t.Object({ ... })`);
93
+ throw new Error(`[Elysia] ${where}: "${key}" is not a schema \u2014 use t.Object({ ... })`);
82
94
  }
83
95
  }
84
96
  /**
@@ -105,6 +117,7 @@ function expandPaths(path, needsEncode, registerLoose, explicit) {
105
117
  return paths;
106
118
  }
107
119
  var Elysia = class Elysia {
120
+ #pending = 0;
108
121
  get history() {
109
122
  const generation = this["~generation"];
110
123
  if (this.#isCacheable && this.cachedGeneration === generation && this.cachedEpoch === mutationEpoch && this.cachedHistory !== void 0) return this.cachedHistory;
@@ -158,8 +171,6 @@ var Elysia = class Elysia {
158
171
  return this;
159
172
  }
160
173
  constructor(config) {
161
- this._pending = 0;
162
- this.routerBuilt = false;
163
174
  /**
164
175
  * ! Don't tune
165
176
  *
@@ -205,7 +216,7 @@ var Elysia = class Elysia {
205
216
  if (config) {
206
217
  const { prefix, name, seed, adapter } = config;
207
218
  this["~Prefix"] = prefix ? prefix.charCodeAt(0) === 47 ? prefix : `/${prefix}` : void 0;
208
- if (name) this.hash = fnv1a(seed ? `${name}_${typeof seed === "object" ? JSON.stringify(seed, serializeMacroSeed) : seed}` : name);
219
+ if (name) this.hash = pluginHash(JSON.stringify(seed === void 0 ? [name] : [name, seed], serializeMacroSeed));
209
220
  if (adapter?.setup) this.#useFn(adapter.setup);
210
221
  } else this["~Prefix"] = void 0;
211
222
  }
@@ -221,6 +232,7 @@ var Elysia = class Elysia {
221
232
  if (!this.declaredRoutes?.length) return [];
222
233
  const routes = this["~routes"].map(([method, path, handler, instance, hook, appHook, inheritedChain, macroScope]) => {
223
234
  const merged = composeRouteHook(instance, hook, appHook, inheritedChain, this, macroScope);
235
+ runIntrospect(merged);
224
236
  if (merged?.schemas?.length) for (const entry of merged.schemas) {
225
237
  if (!entry?.response) continue;
226
238
  merged.response = mergeResponse(merged.response, entry.response);
@@ -243,10 +255,10 @@ var Elysia = class Elysia {
243
255
  decorate(typeOrNameOrDecorators, nameOrDecorators, value) {
244
256
  return this.#setField("decorator", arguments.length, typeOrNameOrDecorators, nameOrDecorators, value);
245
257
  }
246
- #setField(field, length, typeOrName, nameOrValue, value) {
258
+ #setField(field, argCount, typeOrName, nameOrValue, value) {
247
259
  let as = "append";
248
260
  let name = "";
249
- switch (length) {
261
+ switch (argCount) {
250
262
  case 1:
251
263
  value = typeOrName;
252
264
  break;
@@ -261,10 +273,11 @@ var Elysia = class Elysia {
261
273
  break;
262
274
  default: return this;
263
275
  }
264
- this.#assertMutable(field === "store" ? "state" : "decorate");
265
- const ext = this.ext;
266
- const fresh = !ext[field];
267
- const target = ext[field] ??= nullObject();
276
+ this.#assertTableMutable(field === "store" ? "state" : "decorate");
277
+ const ext = this.#ext;
278
+ let fresh = true;
279
+ for (let app = this; fresh && app; app = app.scopeParent) fresh = !app["~ext"]?.[field];
280
+ const target = this.#table(field);
268
281
  switch (typeof value) {
269
282
  case "object":
270
283
  if (!value) return this;
@@ -300,9 +313,8 @@ var Elysia = class Elysia {
300
313
  return this.#setField("store", arguments.length, typeOrNameOrStore, nameOrStore, value);
301
314
  }
302
315
  headers(headers) {
303
- this.#assertMutable("headers");
304
- const ext = this.ext;
305
- ext.headers = Object.assign(ext.headers ?? nullObject(), headers);
316
+ this.#assertTableMutable("headers");
317
+ Object.assign(this.#table("headers"), headers);
306
318
  return this;
307
319
  }
308
320
  #on(type, fn, scope = this["~config"]?.as) {
@@ -311,32 +323,30 @@ var Elysia = class Elysia {
311
323
  const added = nullObject();
312
324
  added[type] = fn;
313
325
  if (type === "trace") this["~hasTrace"] = true;
314
- this.#link(added, scope, this);
315
- this.#tagOrigin(fn);
326
+ this.#pushChainNode(added, scope);
316
327
  return this;
317
328
  }
318
- #link(added, scope, owner, mayRef = true, propagated) {
329
+ #pushChainNode(added, scope, registration, origin, owner) {
319
330
  const parent = this["~hookChain"];
320
- const refs = parent !== void 0 && parent.refs || mayRef && Elysia.#hookHasString(added);
321
- this["~hookChain"] = propagated ? {
331
+ const refs = parent !== void 0 && parent.refs || Elysia.#hookHasString(added);
332
+ this["~hookChain"] = registration ? {
322
333
  added,
323
334
  parent,
324
335
  refs,
325
336
  scope,
326
- propagated,
327
- owner
337
+ propagated: true,
338
+ owner,
339
+ origin,
340
+ registration
328
341
  } : {
329
342
  added,
330
343
  parent,
331
344
  refs,
332
345
  scope,
333
- owner
346
+ owner: this,
347
+ origin: this.hash
334
348
  };
335
349
  }
336
- #tagOrigin(value) {
337
- if (this.hash === void 0) return;
338
- for (const fn of Array.isArray(value) ? value : [value]) if (typeof fn === "function" && !fnOrigin.has(fn)) fnOrigin.set(fn, this.hash);
339
- }
340
350
  #onBranch(type, scopeOrFn, fn) {
341
351
  return fn ? this.#on(type, fn, scopeOrFn) : this.#on(type, scopeOrFn);
342
352
  }
@@ -351,9 +361,8 @@ var Elysia = class Elysia {
351
361
  return this.#onBranch("parse", scopeOrFnOrName, fn);
352
362
  }
353
363
  parser(name, fn) {
354
- this.#assertMutable("parser");
355
- const ext = this.ext;
356
- const parsers = ext.parser ??= nullObject();
364
+ this.#assertTableMutable("parser");
365
+ const parsers = this.#table("parser");
357
366
  parsers[name] = fn;
358
367
  return this;
359
368
  }
@@ -373,7 +382,7 @@ var Elysia = class Elysia {
373
382
  */
374
383
  setup(handler) {
375
384
  this.#assertMutable("setup");
376
- (this.ext.setup ??= []).push(...[].concat(handler));
385
+ (this.#ext.setup ??= []).push(...[].concat(handler));
377
386
  return this;
378
387
  }
379
388
  transform(scopeOrFn, fn) {
@@ -412,13 +421,13 @@ var Elysia = class Elysia {
412
421
  if (scopeOrFnOrError && typeof scopeOrFnOrError === "object") return this;
413
422
  return this.#onBranch("error", scopeOrFnOrError);
414
423
  case 2:
415
- if (typeof scopeOrFnOrError === "function" && scopeOrFnOrError.prototype instanceof Object) return this.#errorClass(scopeOrFnOrError, fnOrError);
424
+ if (typeof scopeOrFnOrError === "function" && scopeOrFnOrError.prototype instanceof Object) return this.#onErrorClass(scopeOrFnOrError, fnOrError);
416
425
  return this.#onBranch("error", scopeOrFnOrError, fnOrError);
417
- case 3: return this.#errorClass(fnOrError, fn, scopeOrFnOrError);
426
+ case 3: return this.#onErrorClass(fnOrError, fn, scopeOrFnOrError);
418
427
  }
419
428
  return this;
420
429
  }
421
- #errorClass(ErrorClass, fn, scope) {
430
+ #onErrorClass(ErrorClass, fn, scope) {
422
431
  const run = typeof fn === "function" ? fn : () => fn;
423
432
  return this.#on("error", createErrorEventHandler(run, ErrorClass), scope);
424
433
  }
@@ -437,9 +446,10 @@ var Elysia = class Elysia {
437
446
  node = node.over;
438
447
  continue;
439
448
  }
440
- if (node.scope !== "global") {
449
+ if (node.scope !== "global" && (node.scope !== scope || node.propagated)) {
441
450
  node.scope = scope;
442
451
  node.propagated = false;
452
+ invalidateMacroEpoch();
443
453
  }
444
454
  node = node.parent;
445
455
  }
@@ -459,13 +469,11 @@ var Elysia = class Elysia {
459
469
  if (hook) assertSchemaShape(hook, ".guard()");
460
470
  hook = snapshotHookSchemas(hook);
461
471
  hookToGuard(hook);
462
- for (const key in hook) if (eventProperties.has(key)) this.#tagOrigin(hook[key]);
463
- this.#tagOrigin(hook.derive);
464
472
  this.#pushHook(hook, scope);
465
473
  return this;
466
474
  }
467
475
  group() {
468
- this.#assertMutable("group");
476
+ this.#assertTableMutable("group");
469
477
  const prefix = arguments[0];
470
478
  const schemaOrRun = arguments[1];
471
479
  const run = arguments[2];
@@ -484,12 +492,8 @@ var Elysia = class Elysia {
484
492
  const src = this["~ext"];
485
493
  if (src) {
486
494
  const ext = child["~ext"] ??= nullObject();
487
- if (src.decorator) ext.decorator = Object.assign(nullObject(), src.decorator);
488
- if (src.store) ext.store = Object.assign(nullObject(), src.store);
489
- if (src.headers) ext.headers = Object.assign(nullObject(), src.headers);
490
- if (src.models) ext.models = Object.assign(nullObject(), src.models);
495
+ for (const field of sharedTables) if (src[field]) ext[field] = src[field];
491
496
  if (src.macro) ext.macro = Object.create(src.macro);
492
- if (src.parser) ext.parser = Object.assign(nullObject(), src.parser);
493
497
  if (src.capability) {
494
498
  const cap = ext.capability = nullObject();
495
499
  if (src.capability.trace) cap.trace = src.capability.trace;
@@ -502,9 +506,13 @@ var Elysia = class Elysia {
502
506
  this.#use(child);
503
507
  return this;
504
508
  }
505
- get ext() {
509
+ get #ext() {
506
510
  return this["~ext"] ??= nullObject();
507
511
  }
512
+ /** A group child starts or takes up its parent's table, see `group()` */
513
+ #table(field) {
514
+ return this.#ext[field] ??= this.scopeParent ? this.scopeParent.#table(field) : nullObject();
515
+ }
508
516
  ["~resolvedCapability"](kind) {
509
517
  if (kind === "ws") {
510
518
  const ws = this["~ext"]?.capability?.ws;
@@ -521,7 +529,7 @@ var Elysia = class Elysia {
521
529
  return provider;
522
530
  }
523
531
  #ensureMacroTable() {
524
- const ext = this.ext;
532
+ const ext = this.#ext;
525
533
  if (ext.macro) return ext.macro;
526
534
  const parent = this["~scopeChild"] ? this.scopeParent : void 0;
527
535
  ext.macro = parent ? Object.create(parent.#ensureMacroTable()) : nullObject();
@@ -540,13 +548,13 @@ var Elysia = class Elysia {
540
548
  }
541
549
  }
542
550
  if (hook.trace) this["~hasTrace"] = true;
543
- this.#link(hook, scope, this);
551
+ this.#pushChainNode(hook, scope);
544
552
  return this;
545
553
  }
546
554
  macro(macro, definition) {
547
555
  this.#assertMutable("macro");
548
556
  if (typeof macro === "function") throw new Error("use `.macro({ name: fn })` instead of `.macro(fn)`");
549
- if (definition != null || typeof macro === "string") throw new Error("[Elysia] .macro(name, definition) was removed in 2.0 — use .macro({ [name]: definition })");
557
+ if (definition != null || typeof macro === "string") throw new Error("[Elysia] .macro(name, definition) was removed in 2.0 \u2014 use .macro({ [name]: definition })");
550
558
  const m = this.#ensureMacroTable();
551
559
  const baseline = this.macroBaseline;
552
560
  for (const key in macro) {
@@ -577,7 +585,7 @@ var Elysia = class Elysia {
577
585
  const seedSource = hook.seed ?? value;
578
586
  const seedType = typeof seedSource;
579
587
  let seedKey;
580
- if (seedSource === null || seedType !== "object") seedKey = key + "\0" + seedType + "\0" + String(seedSource);
588
+ if (seedSource === null || seedType !== "object") seedKey = `${key}\0${seedType}\0${serializeMacroSeed("", seedSource)}`;
581
589
  else try {
582
590
  seedKey = key + "\0object\0" + JSON.stringify(seedSource, serializeMacroSeed);
583
591
  } catch {
@@ -594,7 +602,7 @@ var Elysia = class Elysia {
594
602
  const v = hook[k];
595
603
  if (k === "seed" || k === "meta" || k === "$type") continue;
596
604
  if (k === "introspect") {
597
- v?.(input);
605
+ if (v) (input["~introspect"] ??= []).push(v);
598
606
  delete input[key];
599
607
  continue;
600
608
  }
@@ -630,15 +638,9 @@ var Elysia = class Elysia {
630
638
  if (eventProperties.has(k) || k === "derive") {
631
639
  const incoming = Array.isArray(v) ? v : [v];
632
640
  const existing = k in input ? Array.isArray(input[k]) ? input[k] : [input[k]] : [];
633
- const added = [];
634
- for (const fn of incoming) if (!existing.includes(fn) && !added.includes(fn)) added.push(fn);
635
641
  const at = insertions.get(k) ?? 0;
636
- input[k] = [
637
- ...existing.slice(0, at),
638
- ...added,
639
- ...existing.slice(at)
640
- ];
641
- insertions.set(k, at + added.length);
642
+ input[k] = existing.toSpliced(at, 0, ...incoming);
643
+ insertions.set(k, at + incoming.length);
642
644
  } else if (k in input) {
643
645
  if (Array.isArray(input[k])) {
644
646
  for (const item of Array.isArray(v) ? v : [v]) if (!input[k].includes(item)) input[k].unshift(item);
@@ -651,7 +653,7 @@ var Elysia = class Elysia {
651
653
  }
652
654
  use(app) {
653
655
  if (!app) return this;
654
- this.#assertMutable("use");
656
+ this.#assertTableMutable("use");
655
657
  if (typeof app === "function") return this.#useFn(app);
656
658
  if (typeof app.then === "function") return this.#useAsync(app);
657
659
  if (Array.isArray(app)) {
@@ -664,6 +666,9 @@ var Elysia = class Elysia {
664
666
  this.#use(app);
665
667
  return this;
666
668
  }
669
+ has(plugin) {
670
+ return this.childrenHash?.has(plugin.hash ?? pluginId(plugin)) ?? false;
671
+ }
667
672
  #useFn(app) {
668
673
  const prevBaseline = this.macroBaseline;
669
674
  const baseline = /* @__PURE__ */ new Set();
@@ -705,14 +710,15 @@ var Elysia = class Elysia {
705
710
  const config = app["~config"];
706
711
  if (app["~introspect"] || config?.introspect) this["~introspect"] = true;
707
712
  const name = config?.name;
708
- if (name) {
709
- const hash = app.hash;
710
- if (this.childrenHash?.has(hash)) return;
713
+ const childKey = name ? app.hash : pluginId(app);
714
+ const exists = this.childrenHash?.has(childKey);
715
+ if (name && exists) return;
716
+ if (!exists) {
711
717
  this.childrenHash ??= /* @__PURE__ */ new Set();
712
- this.childrenHash.add(hash);
713
- (addedByThisCall ??= /* @__PURE__ */ new Set()).add(hash);
718
+ this.childrenHash.add(childKey);
719
+ (addedByThisCall ??= /* @__PURE__ */ new Set()).add(childKey);
714
720
  }
715
- if (app.childrenHash) addedByThisCall = this.#absorbChildrenHash(app, addedByThisCall);
721
+ if (app.childrenHash && !app["~scopeChild"]) addedByThisCall = this.#absorbChildrenHash(app, addedByThisCall);
716
722
  if (app["~ext"]) this.#assertMacroUnique(app, addedByThisCall);
717
723
  if (app["~hasTrace"]) this["~hasTrace"] = true;
718
724
  if (app.declaredRoutes === void 0 && app["~routeTable"]?.length) app.#materializeDeclaredRoutes();
@@ -725,20 +731,17 @@ var Elysia = class Elysia {
725
731
  for (const child of app["~scopeChildren"]) children.push(child);
726
732
  }
727
733
  const hookChain = app["~hookChain"];
728
- if (app["~ext"]) this.#absorbExt(app);
734
+ if (app["~ext"]) this.#absorbExt(app, addedByThisCall);
729
735
  if (hookChain) this.#propagateHooks(app, hookChain, addedByThisCall);
730
736
  }
731
737
  #absorbChildrenHash(app, addedByThisCall) {
732
- const incoming = app.childrenHash;
733
- if (this.childrenHash) for (const h of incoming) {
734
- if (this.childrenHash.has(h)) continue;
735
- this.childrenHash.add(h);
738
+ const childrenHash = this.childrenHash;
739
+ for (let h of app.childrenHash) {
740
+ if (typeof h === "object") h = pluginId(h);
741
+ if (childrenHash.has(h)) continue;
742
+ childrenHash.add(h);
736
743
  (addedByThisCall ??= /* @__PURE__ */ new Set()).add(h);
737
744
  }
738
- else {
739
- this.childrenHash = new Set(incoming);
740
- addedByThisCall = new Set(incoming);
741
- }
742
745
  return addedByThisCall;
743
746
  }
744
747
  /**
@@ -767,22 +770,26 @@ var Elysia = class Elysia {
767
770
  *
768
771
  * Keep the cold blocks in their own frames.
769
772
  */
770
- #absorbExt(app) {
771
- const { decorator, store, headers, models, parser, macro, hoc, setup, cleanup, capability } = app["~ext"];
773
+ #absorbExt(app, addedByThisCall) {
774
+ const source = app["~ext"];
775
+ const { decorator, store, headers, models, parser, macro, hoc, setup, cleanup, capability } = source;
772
776
  const ext = this["~ext"] ??= nullObject();
773
- if (decorator) {
777
+ if (app.scopeParent === this) {
778
+ for (const field of sharedTables) if (source[field]) ext[field] ??= source[field];
779
+ }
780
+ if (decorator && decorator !== ext.decorator) {
774
781
  if (ext.decorator) mergeDeep(ext.decorator, decorator, void 0, true, false, void 0, { map: void 0 });
775
782
  else ext.decorator = clonePlainDecorators(decorator);
776
783
  markStoredSingletons(ext.decorator, decorator);
777
784
  }
778
- if (store) {
785
+ if (store && store !== ext.store) {
779
786
  if (ext.store) mergeDeep(ext.store, store);
780
787
  else ext.store = Object.assign(nullObject(), store);
781
788
  markStoredSingletons(ext.store, store);
782
789
  }
783
- if (headers) ext.headers = Object.assign(ext.headers ?? nullObject(), headers);
784
- if (models) ext.models = Object.assign(ext.models ?? nullObject(), models);
785
- if (parser) ext.parser = Object.assign(ext.parser ?? nullObject(), parser);
790
+ if (headers && headers !== ext.headers) ext.headers = Object.assign(ext.headers ?? nullObject(), headers);
791
+ if (models && models !== ext.models) ext.models = Object.assign(ext.models ?? nullObject(), models);
792
+ if (parser && parser !== ext.parser) ext.parser = Object.assign(ext.parser ?? nullObject(), parser);
786
793
  if (macro) if (app["~scopeChild"]) {
787
794
  const pluginMacros = app.pluginMacros;
788
795
  let changed = false;
@@ -803,7 +810,20 @@ var Elysia = class Elysia {
803
810
  }
804
811
  invalidateMacroEpoch();
805
812
  }
806
- if (hoc) ext.hoc = mergeExtCallbacks(ext.hoc, hoc);
813
+ if (hoc) {
814
+ const childrenHash = this.childrenHash;
815
+ const target = ext.hoc ??= [];
816
+ const origins = ext.hocOrigin ??= [];
817
+ for (let i = 0; i < hoc.length; i++) {
818
+ const fn = hoc[i];
819
+ const registration = source.hocOrigin?.[i] ?? app.hash;
820
+ const origin = typeof registration === "object" ? registration.origin ??= app.hash : registration;
821
+ if (origin !== void 0 && childrenHash.has(origin) && !addedByThisCall?.has(origin)) continue;
822
+ if (target.some((f, j) => f === fn && hocOriginOf(origins[j]) === origin)) continue;
823
+ origins.push(registration);
824
+ target.push(fn);
825
+ }
826
+ }
807
827
  if (setup) ext.setup = mergeExtCallbacks(ext.setup, setup);
808
828
  if (cleanup) ext.cleanup = mergeExtCallbacks(ext.cleanup, cleanup);
809
829
  if (capability) {
@@ -841,10 +861,6 @@ var Elysia = class Elysia {
841
861
  * Split out from #use, see #absorbExt
842
862
  */
843
863
  #propagateHooks(app, hookChain, addedByThisCall) {
844
- let pluginEvents;
845
- let globalEvents;
846
- let pluginMayRef = false;
847
- let globalMayRef = false;
848
864
  const nodes = useNodesBuffer;
849
865
  nodes.length = 0;
850
866
  let current = hookChain;
@@ -856,96 +872,71 @@ var Elysia = class Elysia {
856
872
  nodes.push(current);
857
873
  current = current.parent;
858
874
  }
875
+ const childrenHash = this.childrenHash;
876
+ const scopeChild = app["~scopeChild"];
877
+ let held = propagatedRegistrations.get(this);
878
+ const lifted = liftedRequests.get(this);
879
+ let lifting;
859
880
  for (let i = nodes.length - 1; i >= 0; i--) {
860
881
  const node = nodes[i];
861
- const nodeScope = node.scope;
862
- if (nodeScope !== "plugin" && nodeScope !== "global") continue;
863
- if (nodeScope === "plugin" && node.propagated) continue;
864
- const isGlobal = nodeScope === "global";
865
- const added = node.added;
866
- const keys = Object.keys(added);
867
- for (let k = 0; k < keys.length; k++) {
868
- const key = keys[k];
869
- if (key === "schemas") {
870
- const schemas = added.schemas;
871
- if (!schemas) continue;
872
- const target = isGlobal ? globalEvents ??= nullObject() : pluginEvents ??= nullObject();
873
- if (isGlobal) globalMayRef = true;
874
- else pluginMayRef = true;
875
- for (const s of schemas) (target.schemas ??= []).push(s);
876
- continue;
877
- }
878
- if (key === "schema") continue;
879
- if (eventProperties.has(key)) {
880
- const raw = added[key];
881
- const many = Array.isArray(raw);
882
- const count = many ? raw.length : 1;
883
- for (let f = 0; f < count; f++) {
884
- const fn = many ? raw[f] : raw;
885
- const childrenHash = this.childrenHash;
886
- if (childrenHash !== void 0) {
887
- const origin = fnOrigin.get(fn);
888
- if (origin !== void 0 && childrenHash.has(origin) && !addedByThisCall?.has(origin)) continue;
889
- }
890
- pushField(isGlobal ? globalEvents ??= nullObject() : pluginEvents ??= nullObject(), key, fn);
891
- }
892
- continue;
893
- }
894
- if (key === "~deriveEntries") {
895
- const entries = added[key];
896
- if (!entries) continue;
897
- const target = isGlobal ? globalEvents ??= nullObject() : pluginEvents ??= nullObject();
898
- const list = target[key] ??= [];
899
- for (let j = 0; j < entries.length; j++) list.push(entries[j]);
900
- continue;
901
- }
902
- const target = isGlobal ? globalEvents ??= nullObject() : pluginEvents ??= nullObject();
903
- if (isGlobal) globalMayRef = true;
904
- else pluginMayRef = true;
905
- target[key] = added[key];
882
+ const scope = node.scope;
883
+ if (scope === "plugin" ? node.propagated : scope !== "global") continue;
884
+ let source = node.added;
885
+ if (scopeChild) {
886
+ const request = source.request;
887
+ if (!request) continue;
888
+ source = nullObject();
889
+ source.request = request;
890
+ }
891
+ const origin = node.origin ?? app.hash;
892
+ const registration = node.registration ?? node;
893
+ let added = source;
894
+ let repeated = false;
895
+ if (origin !== void 0) {
896
+ if (!held) propagatedRegistrations.set(this, held = /* @__PURE__ */ new WeakMap());
897
+ const through = held.get(registration);
898
+ if (!through) held.set(registration, [origin]);
899
+ else if (through.includes(origin)) repeated = true;
900
+ else through.push(origin);
906
901
  }
902
+ const registrant = registration.origin;
903
+ if (repeated) {
904
+ const schemas = source.schemas;
905
+ if (!schemas) continue;
906
+ added = nullObject();
907
+ added.schemas = schemas;
908
+ } else if (registrant !== void 0 && childrenHash?.has(registrant) && !addedByThisCall?.has(registrant)) {
909
+ added = nullObject();
910
+ for (const key in source) if (key !== "~deriveEntries" && !eventProperties.has(key)) added[key] = source[key];
911
+ }
912
+ let dropRequest = false;
913
+ if (origin !== void 0 && "request" in added) {
914
+ if (lifted?.has(origin)) dropRequest = true;
915
+ else if (scopeChild) (lifting ??= new Set(lifted)).add(origin);
916
+ }
917
+ if ("schema" in added || dropRequest) {
918
+ if (added === source) added = Object.assign(nullObject(), source);
919
+ delete added.schema;
920
+ if (dropRequest) delete added.request;
921
+ }
922
+ if (isNotEmpty(added)) this.#pushChainNode(added, scope, registration, origin, app);
907
923
  }
924
+ if (lifting) liftedRequests.set(this, lifting);
908
925
  nodes.length = 0;
909
- if (globalEvents) this.#link(globalEvents, "global", app, globalMayRef, true);
910
- if (pluginEvents) this.#link(pluginEvents, "plugin", app, pluginMayRef, true);
911
926
  }
912
927
  #emitChildRoutes(app, preChain, name) {
913
928
  const declared = app.declaredRoutes;
914
929
  const limit = declared.length;
915
- const tail = app["~hookChain"];
916
- let recent;
917
- let hasLocal = false;
930
+ const callback = app["~scopeChild"] ? app["~hookChain"] : void 0;
918
931
  let lastChildChain;
919
932
  let lastCombined;
920
- let lastTailChild = null;
921
- let lastTailCombined;
933
+ let lastInner = null;
934
+ let lastCovered;
922
935
  for (let i = 0; i < limit; i++) {
923
936
  const route = declared[i];
924
937
  const childChain = route[6];
925
- const absorbed = tail !== void 0 && route[3] !== app;
926
- if (absorbed && recent === void 0) {
927
- recent = /* @__PURE__ */ new Set();
928
- for (let node = tail; node && "added" in node; node = node.parent) {
929
- recent.add(node);
930
- if (isLocalScope(node.scope)) {
931
- hasLocal = true;
932
- break;
933
- }
934
- }
935
- }
936
938
  let inheritedChain;
937
- if (absorbed && hasLocal && !recent.has(rootSnapshot(childChain))) {
938
- if (childChain !== lastTailChild) {
939
- lastTailChild = childChain;
940
- lastTailCombined = {
941
- combine: childChain,
942
- over: preChain,
943
- tail,
944
- refs: !!(childChain?.refs || preChain?.refs || tail.refs)
945
- };
946
- }
947
- inheritedChain = lastTailCombined;
948
- } else if (childChain === void 0) inheritedChain = preChain;
939
+ if (childChain === void 0) inheritedChain = preChain;
949
940
  else if (preChain === void 0) inheritedChain = childChain;
950
941
  else if (childChain === lastChildChain) inheritedChain = lastCombined;
951
942
  else {
@@ -956,6 +947,19 @@ var Elysia = class Elysia {
956
947
  refs: childChain.refs || preChain.refs
957
948
  };
958
949
  }
950
+ if (callback) {
951
+ if (inheritedChain !== lastInner) {
952
+ lastInner = inheritedChain;
953
+ lastCovered = {
954
+ combine: inheritedChain,
955
+ over: void 0,
956
+ callback,
957
+ inner: childChain,
958
+ refs: !!(inheritedChain?.refs || callback.refs)
959
+ };
960
+ }
961
+ inheritedChain = lastCovered;
962
+ }
959
963
  this.#emitRoute(route, this["~scopeChild"] ? this : void 0, this["~Prefix"], inheritedChain, name);
960
964
  }
961
965
  }
@@ -985,11 +989,11 @@ var Elysia = class Elysia {
985
989
  });
986
990
  }
987
991
  get pending() {
988
- return this._pending > 0;
992
+ return this.#pending > 0;
989
993
  }
990
994
  #useAsync(promise) {
991
995
  if (!this.ready) this._error = void 0;
992
- this._pending++;
996
+ this.#pending++;
993
997
  const next = (this.ready ?? Promise.resolve()).then(() => promise).then((value) => {
994
998
  const plugin = value && typeof value === "object" && "default" in value && value.default ? value.default : value;
995
999
  const snapshots = this.macroSnapshots;
@@ -1013,19 +1017,19 @@ var Elysia = class Elysia {
1013
1017
  }
1014
1018
  }
1015
1019
  }).finally(() => {
1016
- this._pending--;
1020
+ this.#pending--;
1017
1021
  }).then(() => {}, (err) => {
1018
1022
  this._error ??= { error: err };
1019
1023
  console.error(err);
1020
1024
  }).finally(() => {
1021
- if (this._pending > 0) return;
1025
+ if (this.#pending > 0) return;
1022
1026
  if (this.ready !== next) return;
1023
1027
  const previousCompiled = this.compiled;
1024
1028
  const previousJitColdRemaining = this.jitColdRemaining;
1025
1029
  this.ready = void 0;
1026
1030
  this.compiled = void 0;
1027
1031
  this.fetchFn = void 0;
1028
- this.routerBuilt = false;
1032
+ this.#routerBuilt = false;
1029
1033
  clearContextCache(this);
1030
1034
  try {
1031
1035
  this.#buildRouter(false);
@@ -1050,7 +1054,7 @@ var Elysia = class Elysia {
1050
1054
  break;
1051
1055
  }
1052
1056
  }
1053
- if (isHook) throw new Error(`[Elysia] .${method === "*" ? "all" : method.toLowerCase()}('${path}', handler, hook) is the 1.x order; Elysia 2 takes (path, hook, handler) — see the 2.0 migration guide`);
1057
+ if (isHook) throw new Error(`[Elysia] .${method === "*" ? "all" : method.toLowerCase()}('${path}', handler, hook) is the 1.x order; Elysia 2 takes (path, hook, handler) \u2014 see the 2.0 migration guide`);
1054
1058
  }
1055
1059
  if (hasHook && hookOrFn) assertSchemaShape(hookOrFn, `${method} ${path}`);
1056
1060
  const handler = hasHook ? fn : hookOrFn;
@@ -1077,7 +1081,7 @@ var Elysia = class Elysia {
1077
1081
  handler,
1078
1082
  this
1079
1083
  ]);
1080
- if (this.routerBuilt || this.compiled !== void 0) this.#invalidate();
1084
+ if (this.#routerBuilt || this.compiled !== void 0) this.#invalidateRouter();
1081
1085
  return this;
1082
1086
  }
1083
1087
  #assertMutable(api) {
@@ -1085,8 +1089,15 @@ var Elysia = class Elysia {
1085
1089
  if (this["~generation"] === void 0) return;
1086
1090
  throw new Error(`[Elysia] .${api}() called after the app was sealed by its first request, listen or compile`);
1087
1091
  }
1092
+ /**
1093
+ * `#assertMutable` for the tables a group child shares with its parents:
1094
+ * a child kept past its callback must not write into a sealed one
1095
+ */
1096
+ #assertTableMutable(api) {
1097
+ for (let app = this; app; app = app.scopeParent) app.#assertMutable(api);
1098
+ }
1088
1099
  get #isCacheable() {
1089
- return this["~generation"] !== void 0 && this.routerBuilt && !Capture.isCapturing();
1100
+ return this["~generation"] !== void 0 && this.#routerBuilt && !Capture.isCapturing();
1090
1101
  }
1091
1102
  #cacheRoutes(generation) {
1092
1103
  if (this.cachedGeneration === generation && this.cachedEpoch === mutationEpoch) return;
@@ -1109,29 +1120,29 @@ var Elysia = class Elysia {
1109
1120
  const sequence = routes.length;
1110
1121
  routes.push(route);
1111
1122
  if (source) (this.routeSources ??= [])[sequence] = source;
1112
- if (this.routerBuilt || this.compiled !== void 0) this.#invalidate();
1123
+ if (this.#routerBuilt || this.compiled !== void 0) this.#invalidateRouter();
1113
1124
  }
1114
- #invalidate() {
1125
+ #invalidateRouter() {
1115
1126
  this.compiled = void 0;
1116
1127
  this.jitColdRemaining = void 0;
1117
1128
  this.jitTable = void 0;
1118
1129
  this.jitRoute = void 0;
1119
1130
  this.jitAliases = void 0;
1120
1131
  this.fetchFn = void 0;
1121
- this.routerBuilt = false;
1132
+ this.#routerBuilt = false;
1122
1133
  }
1123
1134
  model(name, model) {
1124
- this.#assertMutable("model");
1125
- const models = this.ext.models ??= nullObject();
1135
+ this.#assertTableMutable("model");
1136
+ const models = this.#table("models");
1126
1137
  switch (typeof name) {
1127
1138
  case "object":
1128
1139
  for (const [key, value] of Object.entries(name)) if (!(key in models)) models[key] = toModel(key, value);
1129
1140
  return this;
1130
1141
  case "function": {
1131
1142
  const remapped = name(models);
1132
- const next = nullObject();
1143
+ const next = remapped === models ? models : nullObject();
1133
1144
  for (const key in remapped) next[key] = toModel(key, remapped[key]);
1134
- this.ext.models = next;
1145
+ this.#ext.models = next;
1135
1146
  return this;
1136
1147
  }
1137
1148
  case "string":
@@ -1143,7 +1154,7 @@ var Elysia = class Elysia {
1143
1154
  * Registered reusable models (via `.model()`), keyed by name.
1144
1155
  */
1145
1156
  get models() {
1146
- return this["~ext"]?.models ?? nullObject();
1157
+ return this["~ext"]?.models ?? this.scopeParent?.models ?? nullObject();
1147
1158
  }
1148
1159
  Ref(key) {
1149
1160
  return Ref(key);
@@ -1240,13 +1251,13 @@ var Elysia = class Elysia {
1240
1251
  compile() {
1241
1252
  this["~config"] ??= nullObject();
1242
1253
  this["~config"].precompile = true;
1243
- this.#invalidate();
1254
+ this.#invalidateRouter();
1244
1255
  this.fetch;
1245
1256
  return this;
1246
1257
  }
1247
1258
  handler(index, immediate, route, aliases, table) {
1248
1259
  if (this.compiled?.[index]) return this.compiled[index];
1249
- const indexedTable = table ?? (this.routerBuilt ? this["~routeTable"] : void 0);
1260
+ const indexedTable = table ?? (this.#routerBuilt ? this["~routeTable"] : void 0);
1250
1261
  const compiled = this.compiled ??= new Array(indexedTable?.length ?? this["~routes"].length);
1251
1262
  if (immediate ?? this["~config"]?.precompile) {
1252
1263
  const row = route ?? (indexedTable ? routeRow(indexedTable, index) : this["~routes"][index]);
@@ -1265,7 +1276,7 @@ var Elysia = class Elysia {
1265
1276
  if (indexedTable !== void 0) this.jitTable = indexedTable;
1266
1277
  if (jitRoute !== void 0) (this.jitRoute ??= [])[index] = jitRoute;
1267
1278
  if (aliases !== void 0) (this.jitAliases ??= [])[index] = aliases;
1268
- return (context) => this.#jitDispatch(index, context);
1279
+ return (context) => this["~dispatch"](index, context);
1269
1280
  }
1270
1281
  #staticAliases(table, index) {
1271
1282
  const flags = table.flags[index];
@@ -1291,6 +1302,7 @@ var Elysia = class Elysia {
1291
1302
  };
1292
1303
  }
1293
1304
  #releaseJit() {
1305
+ releaseAnalysisCaches();
1294
1306
  Compiled.release(this["~programId"]);
1295
1307
  this.jitColdRemaining = void 0;
1296
1308
  this.jitRoute = void 0;
@@ -1309,7 +1321,7 @@ var Elysia = class Elysia {
1309
1321
  this.#releaseJit();
1310
1322
  return true;
1311
1323
  }
1312
- #jitDispatch(index, context) {
1324
+ "~dispatch"(index, context) {
1313
1325
  if (this.compiled[index]) return this.compiled[index](context);
1314
1326
  const route = this.jitRoute?.[index];
1315
1327
  const table = this.jitTable ?? this["~routeTable"];
@@ -1321,7 +1333,7 @@ var Elysia = class Elysia {
1321
1333
  } catch (error) {
1322
1334
  const routeError = new Error(`[Elysia] Failed to compile route ${materialized[0]} ${materialized[1]}: ${error?.message ?? error}`, { cause: error });
1323
1335
  const finalize = this["~finalizeError"];
1324
- if (finalize) return finalize(context, routeError);
1336
+ if (finalize) return finalize(context, routeError, void 0, true);
1325
1337
  throw routeError;
1326
1338
  }
1327
1339
  this.compiled[index] = handler;
@@ -1366,11 +1378,11 @@ var Elysia = class Elysia {
1366
1378
  }
1367
1379
  return false;
1368
1380
  }
1369
- static #slotHasTypeBox(value, models, record) {
1381
+ static #slotHasTypeBox(value, models, allowStatusMap) {
1370
1382
  if (typeof value === "string") value = models?.[value];
1371
1383
  if (!value || typeof value !== "object") return false;
1372
1384
  if ("~kind" in value || "~elyAcl" in value) return true;
1373
- if (!record || "~standard" in value) return false;
1385
+ if (!allowStatusMap || "~standard" in value) return false;
1374
1386
  for (const status in value) if (Elysia.#slotHasTypeBox(value[status], models)) return true;
1375
1387
  return false;
1376
1388
  }
@@ -1396,7 +1408,7 @@ var Elysia = class Elysia {
1396
1408
  if (Elysia.#hookHasTypeBox(resolve ? resolve(node) : node.added, models)) return true;
1397
1409
  node = node.parent;
1398
1410
  } else {
1399
- if (Elysia.#chainHasTypeBox(node.combine, models, seen, resolve) || Elysia.#chainHasTypeBox(node.tail, models, seen, resolve)) return true;
1411
+ if (Elysia.#chainHasTypeBox(node.combine, models, seen, resolve)) return true;
1400
1412
  node = node.over;
1401
1413
  }
1402
1414
  }
@@ -1427,8 +1439,9 @@ var Elysia = class Elysia {
1427
1439
  const schemas = hook?.schemas;
1428
1440
  if (Array.isArray(schemas)) for (let s = 0; s < schemas.length; s++) checkSlots(schemas[s]);
1429
1441
  }
1442
+ #routerBuilt = false;
1430
1443
  #buildRouter(seal = false) {
1431
- if (this.routerBuilt) {
1444
+ if (this.#routerBuilt) {
1432
1445
  if (seal && this["~generation"] === void 0) this.#publishGeneration();
1433
1446
  return;
1434
1447
  }
@@ -1463,8 +1476,8 @@ var Elysia = class Elysia {
1463
1476
  Object.assign(previousRouter, nextRouter);
1464
1477
  this["~router"] = previousRouter;
1465
1478
  }
1466
- this.routerBuilt = true;
1467
1479
  if (seal) this.#publishGeneration();
1480
+ this.#routerBuilt = true;
1468
1481
  buildSucceeded = true;
1469
1482
  } catch (error) {
1470
1483
  this["~map"] = previousMap;
@@ -1516,9 +1529,10 @@ var Elysia = class Elysia {
1516
1529
  if (!this["~ext"]?.macro && !this["~scopeChildren"]) this.declaredRoutes = void 0;
1517
1530
  }
1518
1531
  const ext = this["~ext"];
1519
- if (ext?.hoc) extCallbackIndexes.delete(ext.hoc);
1520
1532
  if (ext?.setup) extCallbackIndexes.delete(ext.setup);
1521
1533
  if (ext?.cleanup) extCallbackIndexes.delete(ext.cleanup);
1534
+ if (ext?.decorator) markPropertySingletons(ext.decorator);
1535
+ if (ext?.store) markPropertySingletons(ext.store);
1522
1536
  }
1523
1537
  #buildRouterUnsafe() {
1524
1538
  const precompile = this["~config"]?.precompile;
@@ -1621,8 +1635,14 @@ var Elysia = class Elysia {
1621
1635
  if (isDynamic) {
1622
1636
  if (isHTMLBundle(table.handler[i])) continue;
1623
1637
  const router = this["~router"] ??= new Memoirist({ loosePath: isLoose });
1624
- const handler = this.handler(i, precompile, void 0, void 0, table);
1625
- for (let p = 0; p < paths.length; p++) router.add(routeMethod, paths[p], handler);
1638
+ let store;
1639
+ if (precompile || this.compiled?.[i]) store = this.handler(i, precompile, void 0, void 0, table);
1640
+ else {
1641
+ this.compiled ??= new Array(length);
1642
+ this.jitTable = table;
1643
+ store = i;
1644
+ }
1645
+ for (let p = 0; p < paths.length; p++) router.add(routeMethod, paths[p], store);
1626
1646
  } else {
1627
1647
  const map = methods[routeMethod] ??= nullObject();
1628
1648
  const handler = this.handler(i, precompile, void 0, {
@@ -1640,21 +1660,21 @@ var Elysia = class Elysia {
1640
1660
  const run = () => this.fetch(request, ...rest);
1641
1661
  return this.ready ? this.modules.then(run, run) : run();
1642
1662
  };
1643
- return this.#dispatch();
1663
+ return this.#buildFetch();
1644
1664
  }
1645
- #dispatch() {
1646
- this.#buildRouter(!this._pending);
1665
+ #buildFetch() {
1666
+ this.#buildRouter(!this.#pending);
1647
1667
  if (this.ready) return applyHoc(this, createFetchHandler(this));
1648
1668
  return this.fetchFn ??= applyHoc(this, createFetchHandler(this));
1649
1669
  }
1650
1670
  get handle() {
1651
- return this._handle ??= async (requestOrUrl, options) => (this.fetchFn ?? this.#dispatch())(typeof requestOrUrl === "string" ? new Request(requestOrUrl.charCodeAt(0) === 47 ? `http://e.ly${requestOrUrl}` : requestOrUrl, options) : requestOrUrl);
1671
+ return this._handle ??= async (requestOrUrl, options) => (this.fetchFn ?? this.#buildFetch())(typeof requestOrUrl === "string" ? new Request(requestOrUrl.charCodeAt(0) === 47 ? `http://e.ly${requestOrUrl}` : requestOrUrl, options) : requestOrUrl);
1652
1672
  }
1653
1673
  listen(options, callback) {
1654
1674
  const listen = (this["~config"]?.adapter ?? (isBun ? BunAdapter : void 0))?.listen;
1655
1675
  if (!listen) {
1656
1676
  const deno = typeof Deno !== "undefined";
1657
- throw new Error(`[Elysia] listen() requires an adapter on ${deno ? "Deno" : typeof process === "undefined" ? "this runtime" : "Node.js"} — https://elysiajs.com/integrations/${deno ? "deno" : "node"}`);
1677
+ throw new Error(`[Elysia] listen() requires an adapter on ${deno ? "Deno" : typeof process === "undefined" ? "this runtime" : "Node.js"} \u2014 https://elysiajs.com/integrations/${deno ? "deno" : "node"}`);
1658
1678
  }
1659
1679
  if (!Capture.isAotBuildEnv()) listen(this, options, callback);
1660
1680
  return this;
@@ -1668,17 +1688,18 @@ var Elysia = class Elysia {
1668
1688
  *
1669
1689
  * new Elysia()
1670
1690
  * .wrap(
1671
- * (fetch) => (request) =>
1672
- * ctx.run({ counter: 0 }, () => fetch(request))
1691
+ * (fetch) => (request, ...rest) =>
1692
+ * ctx.run({ counter: 0 }, () => fetch(request, ...rest))
1673
1693
  * )
1674
1694
  * .get('/', () => ctx.getStore())
1675
1695
  * ```
1676
1696
  */
1677
1697
  wrap(callback) {
1678
1698
  this.#assertMutable("wrap");
1679
- if (this.fetchFn && !this._pending) console.warn("[Elysia] .wrap() was called after the fetch handler was built");
1680
- const ext = this.ext;
1699
+ if (this.fetchFn && !this.#pending) console.warn("[Elysia] .wrap() was called after the fetch handler was built");
1700
+ const ext = this.#ext;
1681
1701
  (ext.hoc ??= []).push(callback);
1702
+ (ext.hocOrigin ??= []).push(this.hash ?? {});
1682
1703
  return this;
1683
1704
  }
1684
1705
  /**
@@ -1697,7 +1718,7 @@ var Elysia = class Elysia {
1697
1718
  cleanup(handler) {
1698
1719
  if (this["~ext"]?.cleanupEpoch?.(handler)) return this;
1699
1720
  this.#assertMutable("cleanup");
1700
- (this.ext.cleanup ??= []).push(...[].concat(handler));
1721
+ (this.#ext.cleanup ??= []).push(...[].concat(handler));
1701
1722
  return this;
1702
1723
  }
1703
1724
  /**