elysia 2.0.0-beta.2 → 2.0.0-beta.21

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 (339) hide show
  1. package/dist/adapter/bun/index.d.ts +18 -2
  2. package/dist/adapter/bun/index.js +467 -110
  3. package/dist/adapter/bun/index.mjs +467 -112
  4. package/dist/adapter/types.d.ts +2 -21
  5. package/dist/adapter/utils.d.ts +14 -3
  6. package/dist/adapter/utils.js +146 -87
  7. package/dist/adapter/utils.mjs +148 -90
  8. package/dist/adapter/web-standard/handler.d.ts +1 -1
  9. package/dist/adapter/web-standard/handler.js +47 -36
  10. package/dist/adapter/web-standard/handler.mjs +48 -37
  11. package/dist/adapter/web-standard/index.js +2 -30
  12. package/dist/adapter/web-standard/index.mjs +2 -30
  13. package/dist/adapter/web-standard/utils.js +5 -18
  14. package/dist/adapter/web-standard/utils.mjs +5 -18
  15. package/dist/base.d.ts +99 -67
  16. package/dist/base.js +678 -510
  17. package/dist/base.mjs +682 -514
  18. package/dist/compile/aot-capture.d.ts +2 -13
  19. package/dist/compile/aot-capture.js +26 -69
  20. package/dist/compile/aot-capture.mjs +28 -67
  21. package/dist/compile/aot-emit.d.ts +9 -7
  22. package/dist/compile/aot-emit.js +42 -19
  23. package/dist/compile/aot-emit.mjs +42 -20
  24. package/dist/compile/aot.d.ts +17 -19
  25. package/dist/compile/aot.js +41 -51
  26. package/dist/compile/aot.mjs +39 -49
  27. package/dist/compile/handler/descriptor.d.ts +9 -12
  28. package/dist/compile/handler/descriptor.js +19 -44
  29. package/dist/compile/handler/descriptor.mjs +20 -42
  30. package/dist/compile/handler/frozen-validator.d.ts +5 -10
  31. package/dist/compile/handler/frozen-validator.js +55 -105
  32. package/dist/compile/handler/frozen-validator.mjs +53 -105
  33. package/dist/compile/handler/index.d.ts +7 -5
  34. package/dist/compile/handler/index.js +270 -111
  35. package/dist/compile/handler/index.mjs +270 -113
  36. package/dist/compile/handler/jit.d.ts +19 -17
  37. package/dist/compile/handler/jit.js +222 -149
  38. package/dist/compile/handler/jit.mjs +224 -152
  39. package/dist/compile/handler/params.d.ts +1 -4
  40. package/dist/compile/handler/params.js +10 -5
  41. package/dist/compile/handler/params.mjs +14 -8
  42. package/dist/compile/handler/reconstruct.d.ts +2 -2
  43. package/dist/compile/handler/reconstruct.js +6 -4
  44. package/dist/compile/handler/reconstruct.mjs +6 -4
  45. package/dist/compile/handler/static-clone-resolver.d.ts +6 -0
  46. package/dist/compile/handler/static-clone-resolver.js +8 -0
  47. package/dist/compile/handler/static-clone-resolver.mjs +7 -0
  48. package/dist/compile/handler/utils.d.ts +22 -11
  49. package/dist/compile/handler/utils.js +159 -253
  50. package/dist/compile/handler/utils.mjs +155 -252
  51. package/dist/compile/jit-probe.d.ts +2 -2
  52. package/dist/compile/jit-probe.js +2 -3
  53. package/dist/compile/jit-probe.mjs +2 -3
  54. package/dist/compile/lexer.d.ts +7 -4
  55. package/dist/compile/lexer.js +268 -6
  56. package/dist/compile/lexer.mjs +268 -4
  57. package/dist/compile/utils.js +7 -4
  58. package/dist/compile/utils.mjs +7 -4
  59. package/dist/compiled.d.ts +0 -1
  60. package/dist/compiled.js +0 -1
  61. package/dist/compiled.mjs +0 -1
  62. package/dist/context.d.ts +14 -4
  63. package/dist/context.js +17 -32
  64. package/dist/context.mjs +19 -34
  65. package/dist/cookie/config.d.ts +7 -2
  66. package/dist/cookie/config.js +76 -16
  67. package/dist/cookie/config.mjs +77 -18
  68. package/dist/cookie/cookie.d.ts +1 -1
  69. package/dist/cookie/cookie.js +8 -10
  70. package/dist/cookie/cookie.mjs +8 -10
  71. package/dist/cookie/crypto.d.ts +8 -7
  72. package/dist/cookie/crypto.js +51 -40
  73. package/dist/cookie/crypto.mjs +51 -41
  74. package/dist/cookie/error.d.ts +1 -2
  75. package/dist/cookie/error.js +6 -4
  76. package/dist/cookie/error.mjs +6 -4
  77. package/dist/cookie/lib.js +7 -2
  78. package/dist/cookie/lib.mjs +7 -2
  79. package/dist/cookie/serialize.js +1 -1
  80. package/dist/cookie/serialize.mjs +1 -1
  81. package/dist/cookie/types.d.ts +10 -0
  82. package/dist/cookie/utils.d.ts +4 -8
  83. package/dist/cookie/utils.js +86 -52
  84. package/dist/cookie/utils.mjs +89 -54
  85. package/dist/error.d.ts +113 -103
  86. package/dist/error.js +181 -117
  87. package/dist/error.mjs +179 -118
  88. package/dist/generation.d.ts +2 -2
  89. package/dist/handler/error.d.ts +26 -15
  90. package/dist/handler/error.js +77 -96
  91. package/dist/handler/error.mjs +74 -97
  92. package/dist/handler/fetch.js +87 -108
  93. package/dist/handler/fetch.mjs +89 -110
  94. package/dist/handler/utils.d.ts +12 -6
  95. package/dist/handler/utils.js +84 -28
  96. package/dist/handler/utils.mjs +80 -28
  97. package/dist/index.d.ts +12 -12
  98. package/dist/index.js +2 -9
  99. package/dist/index.mjs +1 -1
  100. package/dist/memory.js +1 -1
  101. package/dist/memory.mjs +1 -1
  102. package/dist/package.js +1 -1
  103. package/dist/package.mjs +1 -1
  104. package/dist/plugin/aot/bun.d.ts +8 -1
  105. package/dist/plugin/aot/bun.js +16 -9
  106. package/dist/plugin/aot/bun.mjs +17 -10
  107. package/dist/plugin/aot/core.d.ts +52 -60
  108. package/dist/plugin/aot/core.js +208 -109
  109. package/dist/plugin/aot/core.mjs +205 -111
  110. package/dist/plugin/aot/esbuild.js +8 -5
  111. package/dist/plugin/aot/esbuild.mjs +9 -6
  112. package/dist/plugin/aot/hooks.d.ts +6 -17
  113. package/dist/plugin/aot/hooks.js +36 -14
  114. package/dist/plugin/aot/hooks.mjs +37 -16
  115. package/dist/plugin/aot/rspack-loader.d.ts +1 -5
  116. package/dist/plugin/aot/rspack-loader.js +1 -10
  117. package/dist/plugin/aot/rspack-loader.mjs +1 -10
  118. package/dist/plugin/aot/rspack.js +2 -5
  119. package/dist/plugin/aot/rspack.mjs +3 -6
  120. package/dist/plugin/aot/source.d.ts +6 -42
  121. package/dist/plugin/aot/source.js +71 -71
  122. package/dist/plugin/aot/source.mjs +72 -72
  123. package/dist/plugin/aot/treeshake.js +2 -6
  124. package/dist/plugin/aot/treeshake.mjs +2 -6
  125. package/dist/plugin/aot/unplugin.d.ts +1 -4
  126. package/dist/plugin/aot/unplugin.js +4 -13
  127. package/dist/plugin/aot/unplugin.mjs +4 -13
  128. package/dist/plugin/aot/vite.d.ts +2 -6
  129. package/dist/plugin/aot/worker.js +2 -2
  130. package/dist/plugin/aot/worker.mjs +2 -2
  131. package/dist/plugin/auto-head.js +1 -10
  132. package/dist/plugin/auto-head.mjs +1 -10
  133. package/dist/plugin/trace.d.ts +2 -1
  134. package/dist/route-table.d.ts +3 -0
  135. package/dist/route-table.js +16 -11
  136. package/dist/route-table.mjs +16 -11
  137. package/dist/schema-snapshot.d.ts +0 -4
  138. package/dist/schema-snapshot.js +32 -49
  139. package/dist/schema-snapshot.mjs +32 -49
  140. package/dist/sucrose.d.ts +6 -55
  141. package/dist/sucrose.js +103 -731
  142. package/dist/sucrose.mjs +102 -724
  143. package/dist/trace.d.ts +5 -8
  144. package/dist/trace.js +23 -134
  145. package/dist/trace.mjs +24 -135
  146. package/dist/type/bridge-live.d.ts +4 -2
  147. package/dist/type/bridge-live.js +6 -3
  148. package/dist/type/bridge-live.mjs +4 -2
  149. package/dist/type/bridge.d.ts +6 -4
  150. package/dist/type/bridge.js +17 -9
  151. package/dist/type/bridge.mjs +5 -3
  152. package/dist/type/coerce-plan.d.ts +5 -5
  153. package/dist/type/coerce-plan.js +41 -39
  154. package/dist/type/coerce-plan.mjs +43 -39
  155. package/dist/type/coerce.d.ts +3 -7
  156. package/dist/type/coerce.js +18 -75
  157. package/dist/type/coerce.mjs +19 -76
  158. package/dist/type/compat.d.ts +3 -0
  159. package/dist/type/compat.js +23 -14
  160. package/dist/type/compat.mjs +20 -10
  161. package/dist/type/elysia/array-buffer.d.ts +4 -1
  162. package/dist/type/elysia/array-buffer.js +17 -17
  163. package/dist/type/elysia/array-buffer.mjs +17 -17
  164. package/dist/type/elysia/array-string.js +2 -40
  165. package/dist/type/elysia/array-string.mjs +2 -39
  166. package/dist/type/elysia/array.js +5 -9
  167. package/dist/type/elysia/array.mjs +5 -9
  168. package/dist/type/elysia/boolean-string.js +2 -3
  169. package/dist/type/elysia/boolean-string.mjs +1 -1
  170. package/dist/type/elysia/boolean.js +3 -12
  171. package/dist/type/elysia/boolean.mjs +3 -11
  172. package/dist/type/elysia/cookie.d.ts +3 -4
  173. package/dist/type/elysia/cookie.js +5 -15
  174. package/dist/type/elysia/cookie.mjs +5 -15
  175. package/dist/type/elysia/date.js +13 -20
  176. package/dist/type/elysia/date.mjs +10 -16
  177. package/dist/type/elysia/file-type.d.ts +11 -4
  178. package/dist/type/elysia/file-type.js +17 -2
  179. package/dist/type/elysia/file-type.mjs +17 -3
  180. package/dist/type/elysia/file.d.ts +1 -3
  181. package/dist/type/elysia/file.js +11 -27
  182. package/dist/type/elysia/file.mjs +11 -12
  183. package/dist/type/elysia/files.js +7 -11
  184. package/dist/type/elysia/files.mjs +7 -10
  185. package/dist/type/elysia/form.js +2 -3
  186. package/dist/type/elysia/form.mjs +3 -3
  187. package/dist/type/elysia/integer-string.d.ts +1 -1
  188. package/dist/type/elysia/integer-string.js +4 -30
  189. package/dist/type/elysia/integer-string.mjs +5 -30
  190. package/dist/type/elysia/integer.js +3 -12
  191. package/dist/type/elysia/integer.mjs +3 -11
  192. package/dist/type/elysia/intersect.js +0 -5
  193. package/dist/type/elysia/intersect.mjs +0 -5
  194. package/dist/type/elysia/json-string.d.ts +7 -0
  195. package/dist/type/elysia/json-string.js +47 -0
  196. package/dist/type/elysia/json-string.mjs +46 -0
  197. package/dist/type/elysia/keep-config.d.ts +14 -0
  198. package/dist/type/elysia/keep-config.js +62 -0
  199. package/dist/type/elysia/keep-config.mjs +53 -0
  200. package/dist/type/elysia/maybe-empty.js +3 -4
  201. package/dist/type/elysia/maybe-empty.mjs +1 -1
  202. package/dist/type/elysia/nullable.js +2 -3
  203. package/dist/type/elysia/nullable.mjs +1 -1
  204. package/dist/type/elysia/number.d.ts +2 -1
  205. package/dist/type/elysia/number.js +19 -11
  206. package/dist/type/elysia/number.mjs +18 -10
  207. package/dist/type/elysia/numeric-enum.d.ts +1 -1
  208. package/dist/type/elysia/numeric-enum.js +4 -5
  209. package/dist/type/elysia/numeric-enum.mjs +3 -3
  210. package/dist/type/elysia/numeric.d.ts +4 -2
  211. package/dist/type/elysia/numeric.js +20 -17
  212. package/dist/type/elysia/numeric.mjs +19 -16
  213. package/dist/type/elysia/object-string.js +2 -37
  214. package/dist/type/elysia/object-string.mjs +2 -36
  215. package/dist/type/elysia/object.js +5 -9
  216. package/dist/type/elysia/object.mjs +5 -9
  217. package/dist/type/elysia/optional.js +8 -8
  218. package/dist/type/elysia/optional.mjs +7 -7
  219. package/dist/type/elysia/problem.d.ts +20 -0
  220. package/dist/type/elysia/problem.js +25 -0
  221. package/dist/type/elysia/problem.mjs +24 -0
  222. package/dist/type/elysia/string.js +4 -3
  223. package/dist/type/elysia/string.mjs +4 -2
  224. package/dist/type/elysia/uint8-array.d.ts +1 -1
  225. package/dist/type/elysia/uint8-array.js +5 -20
  226. package/dist/type/elysia/uint8-array.mjs +5 -19
  227. package/dist/type/elysia/union-enum.js +7 -8
  228. package/dist/type/elysia/union-enum.mjs +6 -6
  229. package/dist/type/elysia/union.js +0 -5
  230. package/dist/type/elysia/union.mjs +0 -5
  231. package/dist/type/elysia/utils.d.ts +2 -4
  232. package/dist/type/elysia/utils.js +10 -21
  233. package/dist/type/elysia/utils.mjs +7 -15
  234. package/dist/type/exports.d.ts +4 -2
  235. package/dist/type/exports.js +23 -2
  236. package/dist/type/exports.mjs +11 -2
  237. package/dist/type/index.d.ts +10 -2
  238. package/dist/type/index.js +62 -15
  239. package/dist/type/index.mjs +65 -11
  240. package/dist/type/shared.d.ts +3 -9
  241. package/dist/type/shared.js +44 -97
  242. package/dist/type/shared.mjs +43 -97
  243. package/dist/type/sync-require.d.ts +11 -0
  244. package/dist/type/sync-require.js +14 -0
  245. package/dist/type/sync-require.mjs +12 -0
  246. package/dist/type/typebox-system-lite.d.ts +2 -0
  247. package/dist/type/typebox-system-lite.js +34 -0
  248. package/dist/type/typebox-system-lite.mjs +3 -0
  249. package/dist/type/typebox-system-require.d.ts +7 -0
  250. package/dist/type/typebox-system-require.js +24 -0
  251. package/dist/type/typebox-system-require.mjs +23 -0
  252. package/dist/type/typebox-type-live.d.ts +11 -0
  253. package/dist/type/typebox-type-live.js +45 -0
  254. package/dist/type/typebox-type-live.mjs +20 -0
  255. package/dist/type/typebox-type.d.ts +34 -0
  256. package/dist/type/typebox-type.js +199 -0
  257. package/dist/type/typebox-type.mjs +86 -0
  258. package/dist/type/typebox-value-live.d.ts +11 -0
  259. package/dist/type/typebox-value-live.js +31 -0
  260. package/dist/type/typebox-value-live.mjs +13 -0
  261. package/dist/type/typebox-value-ops.d.ts +4 -0
  262. package/dist/type/typebox-value-ops.js +90 -0
  263. package/dist/type/typebox-value-ops.mjs +5 -0
  264. package/dist/type/typebox-value-require.d.ts +8 -0
  265. package/dist/type/typebox-value-require.js +28 -0
  266. package/dist/type/typebox-value-require.mjs +25 -0
  267. package/dist/type/typebox-value.d.ts +35 -0
  268. package/dist/type/typebox-value.js +161 -0
  269. package/dist/type/typebox-value.mjs +74 -0
  270. package/dist/type/utils.js +1 -10
  271. package/dist/type/utils.mjs +1 -10
  272. package/dist/type/validator/clean-safe.d.ts +2 -4
  273. package/dist/type/validator/clean-safe.js +15 -11
  274. package/dist/type/validator/clean-safe.mjs +16 -10
  275. package/dist/type/validator/custom-error.js +12 -16
  276. package/dist/type/validator/custom-error.mjs +11 -14
  277. package/dist/type/validator/default-precompute.d.ts +9 -1
  278. package/dist/type/validator/default-precompute.js +40 -77
  279. package/dist/type/validator/default-precompute.mjs +37 -74
  280. package/dist/type/validator/exact-mirror-live.d.ts +8 -0
  281. package/dist/type/validator/exact-mirror-live.js +22 -0
  282. package/dist/type/validator/exact-mirror-live.mjs +17 -0
  283. package/dist/type/validator/exact-mirror-require.d.ts +4 -0
  284. package/dist/type/validator/exact-mirror-require.js +11 -0
  285. package/dist/type/validator/exact-mirror-require.mjs +9 -0
  286. package/dist/type/validator/exact-mirror.js +8 -8
  287. package/dist/type/validator/exact-mirror.mjs +9 -8
  288. package/dist/type/validator/frozen-check.js +5 -6
  289. package/dist/type/validator/frozen-check.mjs +6 -7
  290. package/dist/type/validator/index.d.ts +1 -0
  291. package/dist/type/validator/index.js +169 -166
  292. package/dist/type/validator/index.mjs +162 -158
  293. package/dist/type/validator/string-codec-aot.d.ts +2 -1
  294. package/dist/type/validator/string-codec-aot.js +8 -2
  295. package/dist/type/validator/string-codec-aot.mjs +7 -2
  296. package/dist/type/validator/validator-cache.d.ts +5 -4
  297. package/dist/type/validator/validator-cache.js +41 -24
  298. package/dist/type/validator/validator-cache.mjs +40 -22
  299. package/dist/types.d.ts +263 -95
  300. package/dist/universal/constants.d.ts +2 -1
  301. package/dist/universal/constants.js +3 -1
  302. package/dist/universal/constants.mjs +2 -1
  303. package/dist/universal/env.js +2 -3
  304. package/dist/universal/env.mjs +2 -3
  305. package/dist/universal/file.d.ts +6 -4
  306. package/dist/universal/file.js +11 -16
  307. package/dist/universal/file.mjs +12 -16
  308. package/dist/universal/index.d.ts +2 -2
  309. package/dist/universal/is-production.d.ts +1 -2
  310. package/dist/universal/is-production.js +1 -3
  311. package/dist/universal/is-production.mjs +2 -3
  312. package/dist/universal/server.d.ts +0 -272
  313. package/dist/utils.d.ts +43 -7
  314. package/dist/utils.js +181 -126
  315. package/dist/utils.mjs +168 -126
  316. package/dist/validator/index.d.ts +1 -1
  317. package/dist/validator/index.js +17 -30
  318. package/dist/validator/index.mjs +18 -31
  319. package/dist/validator/route.js +2 -1
  320. package/dist/validator/route.mjs +2 -1
  321. package/dist/ws/context.d.ts +8 -1
  322. package/dist/ws/context.js +68 -44
  323. package/dist/ws/context.mjs +67 -44
  324. package/dist/ws/parser.js +10 -20
  325. package/dist/ws/parser.mjs +10 -20
  326. package/dist/ws/route.d.ts +2 -2
  327. package/dist/ws/route.js +249 -141
  328. package/dist/ws/route.mjs +254 -146
  329. package/dist/ws/types.d.ts +2 -2
  330. package/package.json +23 -49
  331. package/dist/compile/analysis-cache.d.ts +0 -6
  332. package/dist/compile/analysis-cache.js +0 -12
  333. package/dist/compile/analysis-cache.mjs +0 -11
  334. package/dist/cookie/index.d.ts +0 -7
  335. package/dist/cookie/index.js +0 -13
  336. package/dist/cookie/index.mjs +0 -7
  337. package/dist/handler/index.d.ts +0 -2
  338. package/dist/handler/index.js +0 -5
  339. package/dist/handler/index.mjs +0 -3
package/dist/base.js CHANGED
@@ -9,8 +9,8 @@ const require_generation = require('./generation.js');
9
9
  const require_compile_handler_index = require('./compile/handler/index.js');
10
10
  const require_route_table = require('./route-table.js');
11
11
  const require_adapter_bun_index = require('./adapter/bun/index.js');
12
+ const require_context = require('./context.js');
12
13
  const require_handler_fetch = require('./handler/fetch.js');
13
- const require_compile_analysis_cache = require('./compile/analysis-cache.js');
14
14
  const require_schema_snapshot = require('./schema-snapshot.js');
15
15
  let memoirist = require("memoirist");
16
16
  memoirist = require_runtime.__toESM(memoirist);
@@ -18,27 +18,105 @@ memoirist = require_runtime.__toESM(memoirist);
18
18
  //#region src/base.ts
19
19
  const useNodesBuffer = [];
20
20
  const emptyHistory = Object.freeze([]);
21
+ let mutationEpoch = 0;
22
+ const extCallbackIndexes = /* @__PURE__ */ new WeakMap();
23
+ const mergeExtCallbacks = (target, incoming) => {
24
+ if (!target) return incoming.slice();
25
+ let index = extCallbackIndexes.get(target);
26
+ if (!index) {
27
+ index = {
28
+ indexedLength: target.length,
29
+ seen: new Set(target)
30
+ };
31
+ extCallbackIndexes.set(target, index);
32
+ } else for (let i = index.indexedLength; i < target.length; i++) index.seen.add(target[i]);
33
+ for (const fn of incoming) {
34
+ if (index.seen.has(fn)) continue;
35
+ target.push(fn);
36
+ index.seen.add(fn);
37
+ }
38
+ index.indexedLength = target.length;
39
+ return target;
40
+ };
41
+ const markStoredSingletons = (table, source) => {
42
+ if (!table) return;
43
+ for (const key in source) require_utils.markSingletons(table[key]);
44
+ };
21
45
  const canRegisterLoose = (path, isDynamic) => !isDynamic && (path.length === 0 || path.charCodeAt(path.length - 1) === 47);
46
+ const isSingleSchema = (value) => "~kind" in value || "~elyAcl" in value || "~standard" in value;
47
+ const isSchemaValue = (value) => {
48
+ if (value == null || typeof value === "string") return true;
49
+ if (typeof value !== "object" && typeof value !== "function") return false;
50
+ return isSingleSchema(value) || typeof value.Check === "function";
51
+ };
52
+ const toModel = (key, value) => {
53
+ if ("~standard" in value) return value;
54
+ value = require_schema_snapshot.snapshotSchema(value);
55
+ value.$id ??= key;
56
+ return value;
57
+ };
58
+ const hookKeys = new Set([
59
+ ...require_utils.schemaProperties,
60
+ ...require_utils.eventProperties,
61
+ "detail",
62
+ "tags"
63
+ ]);
64
+ const assertScope = (scope) => {
65
+ if (scope !== void 0 && scope !== "local" && scope !== "plugin" && scope !== "global") throw new Error(`[Elysia] Invalid hook scope ${JSON.stringify(scope)}, expected 'local', 'plugin' or 'global' (1.x 'scoped' is 'plugin')`);
66
+ };
67
+ const hasHookKeys = (value) => {
68
+ for (const key in value) if (hookKeys.has(key)) return true;
69
+ return false;
70
+ };
71
+ function assertSchemaShape(hook, where) {
72
+ for (const key in hook) {
73
+ if (!require_utils.schemaProperties.has(key)) continue;
74
+ const value = hook[key];
75
+ if (isSchemaValue(value)) continue;
76
+ if (key === "response" && require_utils.isPlainObject(value)) {
77
+ let isStatusMap = true;
78
+ for (const status in value) if (status !== "default" && isNaN(+status) || !isSchemaValue(value[status])) {
79
+ isStatusMap = false;
80
+ break;
81
+ }
82
+ if (isStatusMap) continue;
83
+ }
84
+ throw new Error(`[Elysia] ${where}: "${key}" is not a schema — use t.Object({ ... })`);
85
+ }
86
+ }
87
+ /**
88
+ * Every key a single route must answer to, in registration order.
89
+ *
90
+ * `registerLoose` is the caller's call, not this helper's: HTTP gates it on
91
+ * `canRegisterLoose` because its lookup retries the trailing slash, while WS
92
+ * has no such retry and must own both directions outright
93
+ */
94
+ function expandPaths(path, needsEncode, registerLoose, explicit) {
95
+ const variants = [path];
96
+ if (needsEncode) {
97
+ const encoded = encodeURI(path);
98
+ if (encoded !== path) variants.push(encoded);
99
+ }
100
+ if (!registerLoose) return variants;
101
+ const paths = [];
102
+ for (let v = 0; v < variants.length; v++) {
103
+ const p = variants[v];
104
+ paths.push(p);
105
+ const loose = require_utils.getLoosePath(p);
106
+ if (loose !== p && !explicit?.has(loose)) paths.push(loose);
107
+ }
108
+ return paths;
109
+ }
22
110
  var Elysia = class Elysia {
23
- #hasPlugin;
24
- #hasGlobal;
25
- #ready;
26
- #pending = 0;
27
- #error;
28
- #hash;
29
- #childrenHash;
30
- #scopeParent;
31
- #pluginMacros;
32
- #macroBaseline;
33
- #declaredRoutes;
34
- #routeSources;
35
111
  get history() {
36
- if (this.#declaredRoutes === void 0 && this["~routeTable"]?.length) this.#materializeDeclaredRoutes();
37
- if (!this.#declaredRoutes?.length) return emptyHistory;
38
- const history = new Array(this.#declaredRoutes.length);
39
- for (let sequence = 0; sequence < this.#declaredRoutes.length; sequence++) {
40
- const route = this.#declaredRoutes[sequence];
41
- const source = this.#routeSources?.[sequence];
112
+ const generation = this["~generation"];
113
+ if (this.#isCacheable && this.cachedGeneration === generation && this.cachedEpoch === mutationEpoch && this.cachedHistory !== void 0) return this.cachedHistory;
114
+ if (this.declaredRoutes === void 0 && this["~routeTable"]?.length) this.#materializeDeclaredRoutes();
115
+ if (!this.declaredRoutes?.length) return emptyHistory;
116
+ const history = new Array(this.declaredRoutes.length);
117
+ for (let sequence = 0; sequence < this.declaredRoutes.length; sequence++) {
118
+ const route = this.declaredRoutes[sequence];
119
+ const source = this.routeSources?.[sequence];
42
120
  history[sequence] = Object.freeze({
43
121
  sequence,
44
122
  method: route[0],
@@ -46,12 +124,17 @@ var Elysia = class Elysia {
46
124
  ...source === void 0 ? {} : { source }
47
125
  });
48
126
  }
49
- return Object.freeze(history);
127
+ Object.freeze(history);
128
+ if (this.#isCacheable) {
129
+ this.#cacheRoutes(generation);
130
+ this.cachedHistory = history;
131
+ }
132
+ return history;
50
133
  }
51
134
  get ["~routes"]() {
52
- if (this.#declaredRoutes === void 0 && this["~routeTable"]?.length) this.#materializeDeclaredRoutes();
53
- if (!this.#declaredRoutes?.length) return [];
54
- const routes = this.#declaredRoutes;
135
+ if (this.declaredRoutes === void 0 && this["~routeTable"]?.length) this.#materializeDeclaredRoutes();
136
+ if (!this.declaredRoutes?.length) return [];
137
+ const routes = this.declaredRoutes;
55
138
  if (!this["~ext"]?.macro && !this["~scopeChildren"]) return routes;
56
139
  return routes.map((r) => {
57
140
  const localHook = r[4];
@@ -74,17 +157,16 @@ var Elysia = class Elysia {
74
157
  this.#registerRoute(route);
75
158
  return this;
76
159
  }
77
- #compiled;
78
- #jitColdRemaining;
79
- #jitTable;
80
- #jitRoute;
81
- #jitStatic;
82
- #jitAliases;
160
+ get ["~programId"]() {
161
+ return this;
162
+ }
83
163
  constructor(config) {
164
+ this._pending = 0;
165
+ this.routerBuilt = false;
84
166
  /**
85
167
  * ! Don't tune
86
168
  *
87
- * Interesting JSC internal: These `= undefined` are load-bearing
169
+ * Interesting JSC internal: These `= undefined` shouldn't be removed
88
170
  *
89
171
  * Inline slots live inside the object cell, so JSC must pick their
90
172
  * count at allocation before any initializer runs
@@ -102,7 +184,8 @@ var Elysia = class Elysia {
102
184
  * Don't omitted field either for monomorphic access
103
185
  *
104
186
  * @see test/memory/instance-footprint.test.ts
105
- * Est. Overflow 37 -> 21 = butterfly 64 -> 32 slots = 738B -> 450B/instance
187
+ * Est. overflow 37 -> 21 = butterfly 64 -> 32 slots = 738B -> 450B;
188
+ * reusing the app as its program identity brings the current cell to ~386B.
106
189
  **/
107
190
  this["~config"] = config;
108
191
  this["~ext"] = void 0;
@@ -122,18 +205,24 @@ var Elysia = class Elysia {
122
205
  this["~introspect"] = void 0;
123
206
  this["~scopeChild"] = void 0;
124
207
  this["~scopeChildren"] = void 0;
125
- this["~programId"] = require_compile_aot.createProgramId();
126
208
  if (config) {
127
209
  const { prefix, name, seed, adapter } = config;
128
210
  this["~Prefix"] = prefix ? prefix.charCodeAt(0) === 47 ? prefix : `/${prefix}` : void 0;
129
- if (name) this.#hash = require_utils.fnv1a(seed ? `${name}_${typeof seed === "object" ? JSON.stringify(seed, require_utils.serializeMacroSeed) : seed}` : name);
211
+ if (name) this.hash = require_utils.fnv1a(seed ? `${name}_${typeof seed === "object" ? JSON.stringify(seed, require_utils.serializeMacroSeed) : seed}` : name);
130
212
  if (adapter?.setup) this.#useFn(adapter.setup);
131
213
  } else this["~Prefix"] = void 0;
132
214
  }
215
+ /**
216
+ * Every registered route with its merged hooks.
217
+ * Development shares a frozen cache. Production rebuilds the list so its
218
+ * metadata can be released after startup.
219
+ */
133
220
  get routes() {
134
- if (this.#declaredRoutes === void 0 && this["~routeTable"]?.length) this.#materializeDeclaredRoutes();
135
- if (!this.#declaredRoutes?.length) return [];
136
- return this["~routes"].map(([method, path, handler, instance, hook, appHook, inheritedChain, macroScope]) => {
221
+ const generation = this["~generation"];
222
+ if (this.#isCacheable && this.cachedGeneration === generation && this.cachedEpoch === mutationEpoch && this.cachedRoutes !== void 0) return this.cachedRoutes;
223
+ if (this.declaredRoutes === void 0 && this["~routeTable"]?.length) this.#materializeDeclaredRoutes();
224
+ if (!this.declaredRoutes?.length) return [];
225
+ const routes = this["~routes"].map(([method, path, handler, instance, hook, appHook, inheritedChain, macroScope]) => {
137
226
  const merged = require_compile_handler_index.composeRouteHook(instance, hook, appHook, inheritedChain, this, macroScope);
138
227
  if (merged?.schemas?.length) for (const entry of merged.schemas) {
139
228
  if (!entry?.response) continue;
@@ -147,20 +236,36 @@ var Elysia = class Elysia {
147
236
  hooks: merged
148
237
  };
149
238
  });
239
+ if (this.#isCacheable && !require_universal_is_production.isProduction()) {
240
+ Object.freeze(routes);
241
+ this.#cacheRoutes(generation);
242
+ this.cachedRoutes = routes;
243
+ }
244
+ return routes;
150
245
  }
151
246
  decorate(typeOrNameOrDecorators, nameOrDecorators, value) {
152
- switch (arguments.length) {
153
- case 1: return this.#decorate("append", "", typeOrNameOrDecorators);
247
+ return this.#setField("decorator", arguments.length, typeOrNameOrDecorators, nameOrDecorators, value);
248
+ }
249
+ #setField(field, length, typeOrName, nameOrValue, value) {
250
+ let as = "append";
251
+ let name = "";
252
+ switch (length) {
253
+ case 1:
254
+ value = typeOrName;
255
+ break;
154
256
  case 2:
155
- if (typeOrNameOrDecorators === "append" || typeOrNameOrDecorators === "override") return this.#decorate(typeOrNameOrDecorators, "", nameOrDecorators);
156
- return this.#decorate("append", typeOrNameOrDecorators, nameOrDecorators);
157
- case 3: return this.#decorate(typeOrNameOrDecorators, nameOrDecorators, value);
257
+ if (typeOrName === "append" || typeOrName === "override") as = typeOrName;
258
+ else name = typeOrName;
259
+ value = nameOrValue;
260
+ break;
261
+ case 3:
262
+ as = typeOrName;
263
+ name = nameOrValue;
264
+ break;
265
+ default: return this;
158
266
  }
159
- return this;
160
- }
161
- #setField(field, as, name, value) {
162
- if (this["~generation"] !== void 0) this.#assertMutable(field === "store" ? "state" : "decorate");
163
- const ext = this.#ext;
267
+ this.#assertMutable(field === "store" ? "state" : "decorate");
268
+ const ext = this.ext;
164
269
  const fresh = !ext[field];
165
270
  const target = ext[field] ??= require_utils.nullObject();
166
271
  switch (typeof value) {
@@ -169,69 +274,71 @@ var Elysia = class Elysia {
169
274
  if (!name && require_utils.isEmpty(value)) return this;
170
275
  if (name) {
171
276
  const existing = target[name];
172
- if (!fresh && name in target && !!existing && typeof existing === "object" && !Array.isArray(existing) && !Array.isArray(value)) target[name] = require_utils.mergeDeep(target[name], value, void 0, as === "override");
277
+ if (existing && typeof existing === "object" && !Array.isArray(existing) && !Array.isArray(value)) target[name] = require_utils.mergeDeep(target[name], value, void 0, as === "override");
173
278
  else target[name] = value;
279
+ require_utils.markSingletons(target[name]);
174
280
  return this;
175
281
  }
176
282
  if (fresh) Object.assign(target, value);
177
283
  else ext[field] = require_utils.mergeDeep(target, value, void 0, as === "override");
284
+ markStoredSingletons(ext[field], value);
178
285
  return this;
179
286
  case "function":
180
287
  if (name) {
181
- if (as === "override" || !(name in target)) target[name] = value;
182
- } else ext[field] = value(target);
288
+ if (as === "override" || !(name in target)) {
289
+ target[name] = value;
290
+ require_utils.markSingletons(value);
291
+ }
292
+ } else {
293
+ ext[field] = value(target);
294
+ markStoredSingletons(ext[field], ext[field]);
295
+ }
183
296
  return this;
184
297
  default:
185
298
  if (as === "override" || !(name in target)) target[name] = value;
186
299
  return this;
187
300
  }
188
301
  }
189
- #decorate(as, name, value) {
190
- return this.#setField("decorator", as, name, value);
191
- }
192
302
  state(typeOrNameOrStore, nameOrStore, value) {
193
- switch (arguments.length) {
194
- case 1: return this.#state("append", "", typeOrNameOrStore);
195
- case 2:
196
- if (typeOrNameOrStore === "append" || typeOrNameOrStore === "override") return this.#state(typeOrNameOrStore, "", nameOrStore);
197
- return this.#state("append", typeOrNameOrStore, nameOrStore);
198
- case 3: return this.#state(typeOrNameOrStore, nameOrStore, value);
199
- }
200
- return this;
201
- }
202
- #state(as, name, value) {
203
- return this.#setField("store", as, name, value);
303
+ return this.#setField("store", arguments.length, typeOrNameOrStore, nameOrStore, value);
204
304
  }
205
305
  headers(headers) {
206
306
  this.#assertMutable("headers");
207
- const ext = this.#ext;
208
- if (ext.headers) Object.assign(ext.headers, headers);
209
- else ext.headers = Object.assign(require_utils.nullObject(), headers);
307
+ const ext = this.ext;
308
+ ext.headers = Object.assign(ext.headers ?? require_utils.nullObject(), headers);
210
309
  return this;
211
310
  }
212
311
  #on(type, fn, scope = this["~config"]?.as) {
213
- if (this["~generation"] !== void 0) this.#assertMutable("on" + (type[0].toUpperCase() + type.slice(1)));
312
+ this.#assertMutable(type);
313
+ assertScope(scope);
214
314
  const added = require_utils.nullObject();
215
315
  added[type] = fn;
216
316
  if (type === "trace") this["~hasTrace"] = true;
317
+ this.#link(added, scope, this);
318
+ this.#tagOrigin(fn);
319
+ return this;
320
+ }
321
+ #link(added, scope, owner, mayRef = true, propagated) {
217
322
  const parent = this["~hookChain"];
218
- this["~hookChain"] = {
323
+ const refs = parent !== void 0 && parent.refs || mayRef && Elysia.#hookHasString(added);
324
+ this["~hookChain"] = propagated ? {
325
+ added,
326
+ parent,
327
+ refs,
328
+ scope,
329
+ propagated,
330
+ owner
331
+ } : {
219
332
  added,
220
333
  parent,
221
- refs: parent !== void 0 && parent.refs || Elysia.#hookHasString(added),
334
+ refs,
222
335
  scope,
223
- owner: this
336
+ owner
224
337
  };
225
- if (scope === "plugin") this.#hasPlugin = true;
226
- else if (scope === "global") this.#hasGlobal = true;
227
- if (this.#hash !== void 0) {
228
- const tag = (f) => {
229
- if (typeof f === "function" && !require_utils.fnOrigin.has(f)) require_utils.fnOrigin.set(f, this.#hash);
230
- };
231
- if (Array.isArray(fn)) for (const f of fn) tag(f);
232
- else tag(fn);
233
- }
234
- return this;
338
+ }
339
+ #tagOrigin(value) {
340
+ if (this.hash === void 0) return;
341
+ for (const fn of Array.isArray(value) ? value : [value]) if (typeof fn === "function" && !require_utils.fnOrigin.has(fn)) require_utils.fnOrigin.set(fn, this.hash);
235
342
  }
236
343
  #onBranch(type, scopeOrFn, fn) {
237
344
  return fn ? this.#on(type, fn, scopeOrFn) : this.#on(type, scopeOrFn);
@@ -248,7 +355,7 @@ var Elysia = class Elysia {
248
355
  }
249
356
  parser(name, fn) {
250
357
  this.#assertMutable("parser");
251
- const ext = this.#ext;
358
+ const ext = this.ext;
252
359
  const parsers = ext.parser ??= require_utils.nullObject();
253
360
  parsers[name] = fn;
254
361
  return this;
@@ -269,9 +376,7 @@ var Elysia = class Elysia {
269
376
  */
270
377
  setup(handler) {
271
378
  this.#assertMutable("setup");
272
- const arr = this.#ext.setup ??= [];
273
- if (Array.isArray(handler)) arr.push(...handler);
274
- else arr.push(handler);
379
+ (this.ext.setup ??= []).push(...[].concat(handler));
275
380
  return this;
276
381
  }
277
382
  transform(scopeOrFn, fn) {
@@ -281,26 +386,18 @@ var Elysia = class Elysia {
281
386
  return this.#onBranch("beforeHandle", scopeOrFn, fn);
282
387
  }
283
388
  derive(scopeOrFn, fn) {
284
- const result = this.#onBranch("beforeHandle", scopeOrFn, fn);
285
- const node = this["~hookChain"];
286
- if (node?.added) {
287
- const d = fn ?? scopeOrFn;
288
- const entries = node.added["~deriveEntries"] ??= [];
289
- if (Array.isArray(d)) for (const f of d) entries.push(f);
290
- else entries.push(d);
291
- }
292
- return result;
389
+ return this.#derive(scopeOrFn, fn);
390
+ }
391
+ #derive(scopeOrFn, fn, map) {
392
+ this.#onBranch("beforeHandle", scopeOrFn, fn);
393
+ const d = fn ?? scopeOrFn;
394
+ const entries = this["~hookChain"].added["~deriveEntries"] ??= [];
395
+ if (Array.isArray(d)) for (const f of d) entries.push(map ? map(f) : f);
396
+ else entries.push(map ? map(d) : d);
397
+ return this;
293
398
  }
294
399
  mapDerive(scopeOrFn, fn) {
295
- const result = this.#onBranch("beforeHandle", scopeOrFn, fn);
296
- const node = this["~hookChain"];
297
- if (node?.added) {
298
- const d = fn ?? scopeOrFn;
299
- const entries = node.added["~deriveEntries"] ??= [];
300
- if (Array.isArray(d)) for (const f of d) entries.push(require_utils.mapDeriveEntry(f));
301
- else entries.push(require_utils.mapDeriveEntry(d));
302
- }
303
- return result;
400
+ return this.#derive(scopeOrFn, fn, require_utils.mapDeriveEntry);
304
401
  }
305
402
  afterHandle(scopeOrFn, fn) {
306
403
  return this.#onBranch("afterHandle", scopeOrFn, fn);
@@ -315,26 +412,19 @@ var Elysia = class Elysia {
315
412
  this.#assertMutable("error");
316
413
  switch (arguments.length) {
317
414
  case 1:
318
- if (scopeOrFnOrError && typeof scopeOrFnOrError === "object") {
319
- for (const [code, ErrorClass] of Object.entries(scopeOrFnOrError)) if (typeof ErrorClass === "function") (this.#ext.error ??= /* @__PURE__ */ new Map()).set(ErrorClass, code);
320
- return this;
321
- }
415
+ if (scopeOrFnOrError && typeof scopeOrFnOrError === "object") return this;
322
416
  return this.#onBranch("error", scopeOrFnOrError);
323
417
  case 2:
324
- if (typeof scopeOrFnOrError === "function" && (scopeOrFnOrError === Error || scopeOrFnOrError.prototype instanceof Error)) {
325
- const run = typeof fnOrError === "function" ? fnOrError : () => fnOrError;
326
- (this.#ext.error ??= /* @__PURE__ */ new Map()).set(scopeOrFnOrError, scopeOrFnOrError.name);
327
- return this.#onBranch("error", require_utils.createErrorEventHandler(run, scopeOrFnOrError));
328
- }
418
+ if (typeof scopeOrFnOrError === "function" && scopeOrFnOrError.prototype instanceof Object) return this.#errorClass(scopeOrFnOrError, fnOrError);
329
419
  return this.#onBranch("error", scopeOrFnOrError, fnOrError);
330
- case 3: {
331
- const run = typeof fn === "function" ? fn : () => fn;
332
- (this.#ext.error ??= /* @__PURE__ */ new Map()).set(fnOrError, fnOrError.name);
333
- return this.#onBranch("error", scopeOrFnOrError, require_utils.createErrorEventHandler(run, fnOrError));
334
- }
420
+ case 3: return this.#errorClass(fnOrError, fn, scopeOrFnOrError);
335
421
  }
336
422
  return this;
337
423
  }
424
+ #errorClass(ErrorClass, fn, scope) {
425
+ const run = typeof fn === "function" ? fn : () => fn;
426
+ return this.#on("error", require_utils.createErrorEventHandler(run, ErrorClass), scope);
427
+ }
338
428
  trace(scopeOrFn, fn) {
339
429
  return this.#onBranch("trace", scopeOrFn, fn);
340
430
  }
@@ -353,21 +443,13 @@ var Elysia = class Elysia {
353
443
  if (node.scope !== "global") {
354
444
  node.scope = scope;
355
445
  node.propagated = false;
356
- for (const key in node.added) {
357
- if (!require_utils.eventProperties.has(key)) continue;
358
- const v = node.added[key];
359
- const fns = Array.isArray(v) ? v : [v];
360
- for (const fn of fns) {
361
- if (typeof fn !== "function") continue;
362
- if (scope === "plugin") this.#hasPlugin = true;
363
- else this.#hasGlobal = true;
364
- }
365
- }
366
446
  }
367
447
  node = node.parent;
368
448
  }
369
449
  }
370
450
  guard() {
451
+ const maybeHook = typeof arguments[0] === "string" ? arguments[1] : arguments[0];
452
+ if (maybeHook && typeof maybeHook === "object" && "as" in maybeHook && maybeHook.as !== void 0) throw new Error(`[elysia] guard({ as: '${maybeHook.as}' }) was removed in 2.0. Pass the scope as the first argument instead: guard('${maybeHook.as === "scoped" ? "plugin" : maybeHook.as}', { ... }). To combine a guard schema with a route's own schema for the same slot, add schema: 'merge'.`);
371
453
  if (arguments.length === 1) return this.#guard("local", arguments[0]);
372
454
  if (arguments.length === 2) {
373
455
  if (typeof arguments[1] === "function") return this.group("", arguments[0], arguments[1]);
@@ -376,21 +458,12 @@ var Elysia = class Elysia {
376
458
  return this;
377
459
  }
378
460
  #guard(scope, hook) {
461
+ assertScope(scope);
462
+ if (hook) assertSchemaShape(hook, ".guard()");
379
463
  hook = require_schema_snapshot.snapshotHookSchemas(hook);
380
464
  require_utils.hookToGuard(hook);
381
- const trackFn = (fn) => {
382
- if (typeof fn !== "function") return;
383
- if (this.#hash !== void 0 && !require_utils.fnOrigin.has(fn)) require_utils.fnOrigin.set(fn, this.#hash);
384
- };
385
- for (const key in hook) {
386
- if (!require_utils.eventProperties.has(key)) continue;
387
- const raw = hook[key];
388
- if (raw === null) continue;
389
- if (Array.isArray(raw)) for (const fn of raw) trackFn(fn);
390
- else trackFn(raw);
391
- }
392
- if (hook.derive) if (Array.isArray(hook.derive)) for (const fn of hook.derive) trackFn(fn);
393
- else trackFn(hook.derive);
465
+ for (const key in hook) if (require_utils.eventProperties.has(key)) this.#tagOrigin(hook[key]);
466
+ this.#tagOrigin(hook.derive);
394
467
  this.#pushHook(hook, scope);
395
468
  return this;
396
469
  }
@@ -409,7 +482,7 @@ var Elysia = class Elysia {
409
482
  prefix
410
483
  } : void 0);
411
484
  child["~scopeChild"] = true;
412
- child.#scopeParent = this;
485
+ child.scopeParent = this;
413
486
  (this["~scopeChildren"] ??= []).push(child);
414
487
  const src = this["~ext"];
415
488
  if (src) {
@@ -432,7 +505,7 @@ var Elysia = class Elysia {
432
505
  this.#use(child);
433
506
  return this;
434
507
  }
435
- get #ext() {
508
+ get ext() {
436
509
  return this["~ext"] ??= require_utils.nullObject();
437
510
  }
438
511
  ["~resolvedCapability"](kind) {
@@ -451,9 +524,9 @@ var Elysia = class Elysia {
451
524
  return provider;
452
525
  }
453
526
  #ensureMacroTable() {
454
- const ext = this.#ext;
527
+ const ext = this.ext;
455
528
  if (ext.macro) return ext.macro;
456
- const parent = this["~scopeChild"] ? this.#scopeParent : void 0;
529
+ const parent = this["~scopeChild"] ? this.scopeParent : void 0;
457
530
  ext.macro = parent ? Object.create(parent.#ensureMacroTable()) : require_utils.nullObject();
458
531
  return ext.macro;
459
532
  }
@@ -461,41 +534,30 @@ var Elysia = class Elysia {
461
534
  this.#assertMutable("guard");
462
535
  let hook = _hook;
463
536
  if (hook.derive) {
464
- const promoted = require_utils.nullObject();
465
- for (const key of Object.keys(hook)) {
466
- if (key === "derive") continue;
467
- promoted[key] = hook[key];
468
- }
469
- const extras = [];
470
- if (hook.derive) if (Array.isArray(hook.derive)) extras.push(...hook.derive);
471
- else extras.push(hook.derive);
537
+ const { derive, ...rest } = hook;
538
+ const extras = [].concat(derive);
539
+ hook = Object.assign(require_utils.nullObject(), rest);
472
540
  if (extras.length) {
473
- const existing = promoted.beforeHandle;
474
- if (existing) promoted.beforeHandle = Array.isArray(existing) ? [...extras, ...existing] : [...extras, existing];
475
- else promoted.beforeHandle = extras;
476
- (promoted["~deriveEntries"] ??= []).push(...extras);
541
+ hook.beforeHandle = extras.concat(hook.beforeHandle || []);
542
+ (hook["~deriveEntries"] ??= []).push(...extras);
477
543
  }
478
- hook = promoted;
479
544
  }
480
545
  if (hook.trace) this["~hasTrace"] = true;
481
- const parent = this["~hookChain"];
482
- this["~hookChain"] = {
483
- added: hook,
484
- parent,
485
- refs: parent !== void 0 && parent.refs || Elysia.#hookHasString(hook),
486
- scope,
487
- owner: this
488
- };
546
+ this.#link(hook, scope, this);
489
547
  return this;
490
548
  }
491
- macro(macro) {
549
+ macro(macro, definition) {
492
550
  this.#assertMutable("macro");
493
551
  if (typeof macro === "function") throw new Error("use `.macro({ name: fn })` instead of `.macro(fn)`");
552
+ if (definition != null || typeof macro === "string") throw new Error("[Elysia] .macro(name, definition) was removed in 2.0 — use .macro({ [name]: definition })");
494
553
  const m = this.#ensureMacroTable();
495
- const baseline = this.#macroBaseline;
554
+ const baseline = this.macroBaseline;
496
555
  for (const key in macro) {
497
- if (typeof macro[key] === "object") macro[key] = require_utils.hookToGuard(macro[key]);
498
- if (this.#hash !== void 0 && !require_utils.macroOrigin.has(macro[key])) require_utils.macroOrigin.set(macro[key], this.#hash);
556
+ if (typeof macro[key] === "object") {
557
+ if (macro[key]) assertSchemaShape(macro[key], `.macro(${key})`);
558
+ macro[key] = require_utils.hookToGuard(macro[key]);
559
+ }
560
+ if (this.hash !== void 0 && !require_utils.macroOrigin.has(macro[key])) require_utils.macroOrigin.set(macro[key], this.hash);
499
561
  if (baseline?.has(key) && key in m && m[key] !== macro[key]) throw new Error(`[Elysia] Macro "${key}" can be only define once`);
500
562
  }
501
563
  Object.assign(m, macro);
@@ -518,7 +580,7 @@ var Elysia = class Elysia {
518
580
  const seedSource = hook.seed ?? value;
519
581
  const seedType = typeof seedSource;
520
582
  let seedKey;
521
- if (seedSource === null || seedSource === void 0 || seedType !== "object") seedKey = key + "\0" + seedType + "\0" + String(seedSource);
583
+ if (seedSource === null || seedType !== "object") seedKey = key + "\0" + seedType + "\0" + String(seedSource);
522
584
  else try {
523
585
  seedKey = key + "\0object\0" + JSON.stringify(seedSource, require_utils.serializeMacroSeed);
524
586
  } catch {
@@ -533,7 +595,7 @@ var Elysia = class Elysia {
533
595
  }
534
596
  for (const k in hook) {
535
597
  const v = hook[k];
536
- if (k === "seed") continue;
598
+ if (k === "seed" || k === "meta" || k === "$type") continue;
537
599
  if (k === "introspect") {
538
600
  v?.(input);
539
601
  delete input[key];
@@ -582,11 +644,9 @@ var Elysia = class Elysia {
582
644
  insertions.set(k, at + added.length);
583
645
  } else if (k in input) {
584
646
  if (Array.isArray(input[k])) {
585
- if (Array.isArray(v)) {
586
- for (const item of v) if (!input[k].some((e) => e === item)) input[k].unshift(item);
587
- } else if (!input[k].some((item) => item === v)) input[k].unshift(v);
647
+ for (const item of Array.isArray(v) ? v : [v]) if (!input[k].includes(item)) input[k].unshift(item);
588
648
  } else if (input[k] !== v) input[k] = [v, input[k]];
589
- } else input[k] = require_utils.eventProperties.has(k) ? Array.isArray(v) ? v.slice() : [v] : Array.isArray(v) ? v.slice() : v;
649
+ } else input[k] = Array.isArray(v) ? v.slice() : v;
590
650
  delete input[key];
591
651
  }
592
652
  }
@@ -594,7 +654,7 @@ var Elysia = class Elysia {
594
654
  }
595
655
  use(app) {
596
656
  if (!app) return this;
597
- if (this["~generation"] !== void 0) this.#assertMutable("use");
657
+ this.#assertMutable("use");
598
658
  if (typeof app === "function") return this.#useFn(app);
599
659
  if (typeof app.then === "function") return this.#useAsync(app);
600
660
  if (Array.isArray(app)) {
@@ -608,21 +668,37 @@ var Elysia = class Elysia {
608
668
  return this;
609
669
  }
610
670
  #useFn(app) {
611
- const prevBaseline = this.#macroBaseline;
671
+ const prevBaseline = this.macroBaseline;
612
672
  const baseline = /* @__PURE__ */ new Set();
613
673
  const existingMacro = this["~ext"]?.macro;
614
674
  if (existingMacro) for (const k in existingMacro) baseline.add(k);
615
- this.#macroBaseline = baseline;
675
+ this.macroBaseline = baseline;
676
+ let wantSnapshot = false;
677
+ let settled = false;
678
+ let beforeMacro;
679
+ queueMicrotask(() => {
680
+ if (wantSnapshot && !settled) {
681
+ beforeMacro = new Map(Object.entries(this["~ext"]?.macro ?? require_utils.nullObject()));
682
+ (this.macroSnapshots ??= /* @__PURE__ */ new Set()).add(beforeMacro);
683
+ }
684
+ });
616
685
  const result = app(this);
617
- this.#macroBaseline = prevBaseline;
686
+ this.macroBaseline = prevBaseline;
618
687
  if (result && typeof result.then === "function") {
619
- const beforeMacro = new Map(Object.entries(this["~ext"]?.macro ?? require_utils.nullObject()));
620
- return this.#useAsync(result.then((value) => {
688
+ wantSnapshot = true;
689
+ return this.#useAsync(Promise.resolve(result).then((value) => {
690
+ settled = true;
691
+ if (!beforeMacro) return value;
692
+ this.macroSnapshots?.delete(beforeMacro);
621
693
  const after = this["~ext"]?.macro;
622
694
  if (after) {
623
695
  for (const [k, def] of Object.entries(after)) if (beforeMacro.get(k) !== def) throw new Error(`Macro ${k} can only run in sync plugin`);
624
696
  }
625
697
  return value;
698
+ }, (err) => {
699
+ settled = true;
700
+ if (beforeMacro) this.macroSnapshots?.delete(beforeMacro);
701
+ throw err;
626
702
  }));
627
703
  }
628
704
  return this;
@@ -633,17 +709,17 @@ var Elysia = class Elysia {
633
709
  if (app["~introspect"] || config?.introspect) this["~introspect"] = true;
634
710
  const name = config?.name;
635
711
  if (name) {
636
- const hash = app.#hash;
637
- if (this.#childrenHash?.has(hash)) return;
638
- this.#childrenHash ??= /* @__PURE__ */ new Set();
639
- this.#childrenHash.add(hash);
712
+ const hash = app.hash;
713
+ if (this.childrenHash?.has(hash)) return;
714
+ this.childrenHash ??= /* @__PURE__ */ new Set();
715
+ this.childrenHash.add(hash);
640
716
  (addedByThisCall ??= /* @__PURE__ */ new Set()).add(hash);
641
717
  }
642
- if (app.#childrenHash) addedByThisCall = this.#absorbChildrenHash(app, addedByThisCall);
718
+ if (app.childrenHash) addedByThisCall = this.#absorbChildrenHash(app, addedByThisCall);
643
719
  if (app["~ext"]) this.#assertMacroUnique(app, addedByThisCall);
644
720
  if (app["~hasTrace"]) this["~hasTrace"] = true;
645
- if (app.#declaredRoutes === void 0 && app["~routeTable"]?.length) app.#materializeDeclaredRoutes();
646
- if (app.#declaredRoutes?.length) {
721
+ if (app.declaredRoutes === void 0 && app["~routeTable"]?.length) app.#materializeDeclaredRoutes();
722
+ if (app.declaredRoutes?.length) {
647
723
  if (app["~hasWS"]) this["~hasWS"] = true;
648
724
  this.#emitChildRoutes(app, this["~hookChain"], name);
649
725
  }
@@ -653,17 +729,17 @@ var Elysia = class Elysia {
653
729
  }
654
730
  const hookChain = app["~hookChain"];
655
731
  if (app["~ext"]) this.#absorbExt(app);
656
- if (app.#hasPlugin || app.#hasGlobal || hookChain) this.#propagateHooks(app, hookChain, addedByThisCall);
732
+ if (hookChain) this.#propagateHooks(app, hookChain, addedByThisCall);
657
733
  }
658
734
  #absorbChildrenHash(app, addedByThisCall) {
659
- const incoming = app.#childrenHash;
660
- if (this.#childrenHash) for (const h of incoming) {
661
- if (this.#childrenHash.has(h)) continue;
662
- this.#childrenHash.add(h);
735
+ const incoming = app.childrenHash;
736
+ if (this.childrenHash) for (const h of incoming) {
737
+ if (this.childrenHash.has(h)) continue;
738
+ this.childrenHash.add(h);
663
739
  (addedByThisCall ??= /* @__PURE__ */ new Set()).add(h);
664
740
  }
665
741
  else {
666
- this.#childrenHash = new Set(incoming);
742
+ this.childrenHash = new Set(incoming);
667
743
  addedByThisCall = new Set(incoming);
668
744
  }
669
745
  return addedByThisCall;
@@ -682,7 +758,7 @@ var Elysia = class Elysia {
682
758
  if (existing === incoming) continue;
683
759
  const origin = require_utils.macroOrigin.get(existing);
684
760
  if (origin !== void 0 && origin === require_utils.macroOrigin.get(incoming)) continue;
685
- if (addedByThisCall) for (const h of addedByThisCall) this.#childrenHash.delete(h);
761
+ if (addedByThisCall) for (const h of addedByThisCall) this.childrenHash.delete(h);
686
762
  throw new Error(`[Elysia] Macro "${macroName}" can be only define once`);
687
763
  }
688
764
  }
@@ -695,80 +771,54 @@ var Elysia = class Elysia {
695
771
  * Keep the cold blocks in their own frames.
696
772
  */
697
773
  #absorbExt(app) {
698
- const { decorator, store, headers, models, parser, macro, error, hoc, setup, cleanup, capability } = app["~ext"];
774
+ const { decorator, store, headers, models, parser, macro, hoc, setup, cleanup, capability } = app["~ext"];
699
775
  const ext = this["~ext"] ??= require_utils.nullObject();
700
- if (decorator) if (ext.decorator) require_utils.mergeDeep(ext.decorator, decorator, void 0, true, false, void 0, { map: void 0 });
701
- else ext.decorator = require_utils.clonePlainDecorators(decorator);
702
- if (store) if (ext.store) require_utils.mergeDeep(ext.store, store);
703
- else ext.store = Object.assign(require_utils.nullObject(), store);
704
- if (headers) if (ext.headers) Object.assign(ext.headers, headers);
705
- else ext.headers = Object.assign(require_utils.nullObject(), headers);
706
- if (models) if (ext.models) Object.assign(ext.models, models);
707
- else ext.models = Object.assign(require_utils.nullObject(), models);
708
- if (parser) if (ext.parser) Object.assign(ext.parser, parser);
709
- else ext.parser = Object.assign(require_utils.nullObject(), parser);
776
+ if (decorator) {
777
+ if (ext.decorator) require_utils.mergeDeep(ext.decorator, decorator, void 0, true, false, void 0, { map: void 0 });
778
+ else ext.decorator = require_utils.clonePlainDecorators(decorator);
779
+ markStoredSingletons(ext.decorator, decorator);
780
+ }
781
+ if (store) {
782
+ if (ext.store) require_utils.mergeDeep(ext.store, store);
783
+ else ext.store = Object.assign(require_utils.nullObject(), store);
784
+ markStoredSingletons(ext.store, store);
785
+ }
786
+ if (headers) ext.headers = Object.assign(ext.headers ?? require_utils.nullObject(), headers);
787
+ if (models) ext.models = Object.assign(ext.models ?? require_utils.nullObject(), models);
788
+ if (parser) ext.parser = Object.assign(ext.parser ?? require_utils.nullObject(), parser);
710
789
  if (macro) if (app["~scopeChild"]) {
711
- const pluginMacros = app.#pluginMacros;
790
+ const pluginMacros = app.pluginMacros;
712
791
  let changed = false;
713
792
  if (pluginMacros?.size) {
714
793
  const target = this.#ensureMacroTable();
715
794
  for (const [name, def] of pluginMacros) if (!(name in target)) {
716
795
  target[name] = def;
717
796
  changed = true;
718
- if (this["~scopeChild"]) (this.#pluginMacros ??= /* @__PURE__ */ new Map()).set(name, def);
797
+ if (this["~scopeChild"]) (this.pluginMacros ??= /* @__PURE__ */ new Map()).set(name, def);
719
798
  }
720
799
  }
721
800
  if (changed) require_utils.invalidateMacroEpoch();
722
801
  } else {
723
802
  Object.assign(this.#ensureMacroTable(), macro);
724
803
  if (this["~scopeChild"]) {
725
- const pluginMacros = this.#pluginMacros ??= /* @__PURE__ */ new Map();
804
+ const pluginMacros = this.pluginMacros ??= /* @__PURE__ */ new Map();
726
805
  for (const name in macro) pluginMacros.set(name, macro[name]);
727
806
  }
728
807
  require_utils.invalidateMacroEpoch();
729
808
  }
730
- if (error) if (ext.error) for (const [code, handler] of error) ext.error.set(code, handler);
731
- else ext.error = new Map(error);
732
- if (hoc) if (ext.hoc) {
733
- const seen = new Set(ext.hoc);
734
- for (const fn of hoc) if (!seen.has(fn)) {
735
- seen.add(fn);
736
- ext.hoc.push(fn);
737
- }
738
- } else ext.hoc = hoc.slice();
739
- if (setup) if (ext.setup) {
740
- const seen = new Set(ext.setup);
741
- for (const fn of setup) if (!seen.has(fn)) {
742
- seen.add(fn);
743
- ext.setup.push(fn);
744
- }
745
- } else ext.setup = setup.slice();
746
- if (cleanup) if (ext.cleanup) {
747
- const seen = new Set(ext.cleanup);
748
- for (const fn of cleanup) if (!seen.has(fn)) {
749
- seen.add(fn);
750
- ext.cleanup.push(fn);
751
- }
752
- } else ext.cleanup = cleanup.slice();
809
+ if (hoc) ext.hoc = mergeExtCallbacks(ext.hoc, hoc);
810
+ if (setup) ext.setup = mergeExtCallbacks(ext.setup, setup);
811
+ if (cleanup) ext.cleanup = mergeExtCallbacks(ext.cleanup, cleanup);
753
812
  if (capability) {
754
813
  const target = ext.capability ??= require_utils.nullObject();
755
- if (capability.trace) {
756
- const incoming = capability.trace.provider;
757
- if (!target.trace) target.trace = { provider: incoming };
758
- }
814
+ if (capability.trace) target.trace ??= { provider: capability.trace.provider };
759
815
  if (capability.ws) {
760
- const incoming = capability.ws.provider;
761
- const existing = target.ws;
762
816
  const incomingOptions = capability.ws.options;
763
- if (!existing) target.ws = {
764
- provider: incoming,
765
- options: incomingOptions?.length ? incomingOptions.map((entry) => ({
766
- depth: entry.depth + 1,
767
- value: entry.value,
768
- origin: entry.origin
769
- })) : void 0
817
+ const existing = target.ws ??= {
818
+ provider: capability.ws.provider,
819
+ options: void 0
770
820
  };
771
- else if (incomingOptions?.length) {
821
+ if (incomingOptions?.length) {
772
822
  const base = existing.options ?? [];
773
823
  const seen = new Set(base.map((e) => e.origin));
774
824
  let next;
@@ -798,7 +848,6 @@ var Elysia = class Elysia {
798
848
  let globalEvents;
799
849
  let pluginMayRef = false;
800
850
  let globalMayRef = false;
801
- if (app.#hasGlobal) this.#hasGlobal = true;
802
851
  const nodes = useNodesBuffer;
803
852
  nodes.length = 0;
804
853
  let current = hookChain;
@@ -826,10 +875,7 @@ var Elysia = class Elysia {
826
875
  const target = isGlobal ? globalEvents ??= require_utils.nullObject() : pluginEvents ??= require_utils.nullObject();
827
876
  if (isGlobal) globalMayRef = true;
828
877
  else pluginMayRef = true;
829
- for (const s of schemas) {
830
- (target.schemas ??= []).push(s);
831
- if (isGlobal) this.#hasGlobal = true;
832
- }
878
+ for (const s of schemas) (target.schemas ??= []).push(s);
833
879
  continue;
834
880
  }
835
881
  if (key === "schema") continue;
@@ -839,13 +885,12 @@ var Elysia = class Elysia {
839
885
  const count = many ? raw.length : 1;
840
886
  for (let f = 0; f < count; f++) {
841
887
  const fn = many ? raw[f] : raw;
842
- const childrenHash = this.#childrenHash;
888
+ const childrenHash = this.childrenHash;
843
889
  if (childrenHash !== void 0) {
844
890
  const origin = require_utils.fnOrigin.get(fn);
845
891
  if (origin !== void 0 && childrenHash.has(origin) && !addedByThisCall?.has(origin)) continue;
846
892
  }
847
893
  require_utils.pushField(isGlobal ? globalEvents ??= require_utils.nullObject() : pluginEvents ??= require_utils.nullObject(), key, fn);
848
- if (isGlobal) this.#hasGlobal = true;
849
894
  }
850
895
  continue;
851
896
  }
@@ -864,40 +909,46 @@ var Elysia = class Elysia {
864
909
  }
865
910
  }
866
911
  nodes.length = 0;
867
- if (globalEvents) {
868
- const parent = this["~hookChain"];
869
- this["~hookChain"] = {
870
- added: globalEvents,
871
- parent,
872
- refs: parent !== void 0 && parent.refs || globalMayRef && Elysia.#hookHasString(globalEvents),
873
- scope: "global",
874
- propagated: true,
875
- owner: app
876
- };
877
- }
878
- if (pluginEvents) {
879
- const parent = this["~hookChain"];
880
- this["~hookChain"] = {
881
- added: pluginEvents,
882
- parent,
883
- refs: parent !== void 0 && parent.refs || pluginMayRef && Elysia.#hookHasString(pluginEvents),
884
- scope: "plugin",
885
- propagated: true,
886
- owner: app
887
- };
888
- }
912
+ if (globalEvents) this.#link(globalEvents, "global", app, globalMayRef, true);
913
+ if (pluginEvents) this.#link(pluginEvents, "plugin", app, pluginMayRef, true);
889
914
  }
890
915
  #emitChildRoutes(app, preChain, name) {
891
- const declared = app.#declaredRoutes;
892
- if (!declared?.length) return;
916
+ const declared = app.declaredRoutes;
893
917
  const limit = declared.length;
918
+ const tail = app["~hookChain"];
919
+ let recent;
920
+ let hasLocal = false;
894
921
  let lastChildChain;
895
922
  let lastCombined;
923
+ let lastTailChild = null;
924
+ let lastTailCombined;
896
925
  for (let i = 0; i < limit; i++) {
897
926
  const route = declared[i];
898
927
  const childChain = route[6];
928
+ const absorbed = tail !== void 0 && route[3] !== app;
929
+ if (absorbed && recent === void 0) {
930
+ recent = /* @__PURE__ */ new Set();
931
+ for (let node = tail; node && "added" in node; node = node.parent) {
932
+ recent.add(node);
933
+ if (require_utils.isLocalScope(node.scope)) {
934
+ hasLocal = true;
935
+ break;
936
+ }
937
+ }
938
+ }
899
939
  let inheritedChain;
900
- if (childChain === void 0) inheritedChain = preChain;
940
+ if (absorbed && hasLocal && !recent.has(require_utils.rootSnapshot(childChain))) {
941
+ if (childChain !== lastTailChild) {
942
+ lastTailChild = childChain;
943
+ lastTailCombined = {
944
+ combine: childChain,
945
+ over: preChain,
946
+ tail,
947
+ refs: !!(childChain?.refs || preChain?.refs || tail.refs)
948
+ };
949
+ }
950
+ inheritedChain = lastTailCombined;
951
+ } else if (childChain === void 0) inheritedChain = preChain;
901
952
  else if (preChain === void 0) inheritedChain = childChain;
902
953
  else if (childChain === lastChildChain) inheritedChain = lastCombined;
903
954
  else {
@@ -926,55 +977,91 @@ var Elysia = class Elysia {
926
977
  ], source);
927
978
  }
928
979
  get modules() {
929
- const ready = this.#ready;
980
+ const ready = this.ready;
930
981
  if (!ready) {
931
- if (this.#error !== void 0) return Promise.reject(this.#error);
982
+ if (this._error !== void 0) return Promise.reject(this._error.error);
932
983
  return Promise.resolve();
933
984
  }
934
985
  return ready.then(() => {
935
- if (this.#error !== void 0) throw this.#error;
936
- if (this.#ready && this.#ready !== ready) return this.modules;
986
+ if (this._error !== void 0) throw this._error.error;
987
+ if (this.ready && this.ready !== ready) return this.modules;
937
988
  });
938
989
  }
939
990
  get pending() {
940
- return this.#pending > 0;
991
+ return this._pending > 0;
941
992
  }
942
993
  #useAsync(promise) {
943
- if (!this.#ready) this.#error = void 0;
944
- this.#pending++;
945
- const next = (this.#ready ?? Promise.resolve()).then(() => promise).then((value) => {
994
+ if (!this.ready) this._error = void 0;
995
+ this._pending++;
996
+ const next = (this.ready ?? Promise.resolve()).then(() => promise).then((value) => {
946
997
  const plugin = value && typeof value === "object" && "default" in value && value.default ? value.default : value;
998
+ const snapshots = this.macroSnapshots;
999
+ let before;
1000
+ if (snapshots?.size) {
1001
+ const macro = this["~ext"]?.macro;
1002
+ before = macro ? new Map(Object.entries(macro)) : /* @__PURE__ */ new Map();
1003
+ }
947
1004
  if (plugin) try {
948
1005
  this.use(plugin);
949
1006
  } catch (err) {
950
- this.#error ??= err;
1007
+ this._error ??= { error: err };
951
1008
  console.error(err);
952
1009
  }
1010
+ if (before) {
1011
+ const after = this["~ext"]?.macro;
1012
+ if (after) {
1013
+ for (const [k, def] of Object.entries(after)) if (before.get(k) !== def) {
1014
+ for (const snap of snapshots) if (snap.has(k) === before.has(k) && snap.get(k) === before.get(k)) snap.set(k, def);
1015
+ }
1016
+ }
1017
+ }
953
1018
  }).finally(() => {
954
- this.#pending--;
1019
+ this._pending--;
955
1020
  }).then(() => {}, (err) => {
956
- this.#error ??= err;
1021
+ this._error ??= { error: err };
957
1022
  console.error(err);
958
- }).finally(() => this.#tryDrain(next));
959
- this.#ready = next;
1023
+ }).finally(() => {
1024
+ if (this._pending > 0) return;
1025
+ if (this.ready !== next) return;
1026
+ const previousCompiled = this.compiled;
1027
+ const previousJitColdRemaining = this.jitColdRemaining;
1028
+ this.ready = void 0;
1029
+ this.compiled = void 0;
1030
+ this.fetchFn = void 0;
1031
+ this.routerBuilt = false;
1032
+ require_context.clearContextCache(this);
1033
+ try {
1034
+ this.#buildRouter(false);
1035
+ } catch (error) {
1036
+ this.compiled = previousCompiled;
1037
+ this.jitColdRemaining = previousJitColdRemaining;
1038
+ this._error ??= { error };
1039
+ }
1040
+ });
1041
+ this.ready = next;
960
1042
  return this;
961
1043
  }
962
- #tryDrain(sentinel) {
963
- if (this.#pending > 0) return;
964
- if (this.#ready !== sentinel) return;
965
- this.#ready = void 0;
966
- this.#fetchFn = void 0;
967
- this.#routerBuilt = false;
968
- this.#buildRouter(false);
969
- }
970
1044
  #add(method, path, hookOrFn, fn, hasHook = fn !== void 0) {
971
1045
  if (this["~Prefix"]) path = require_utils.joinPath(this["~Prefix"], path);
972
1046
  else if (path && path.charCodeAt(0) !== 47) path = "/" + path;
1047
+ if (typeof hookOrFn === "function" && require_utils.isPlainObject(fn) && !hasHookKeys(hookOrFn)) {
1048
+ let isHook = hasHookKeys(fn);
1049
+ const macro = this["~ext"]?.macro;
1050
+ if (!isHook && macro) {
1051
+ for (const key in fn) if (key in macro) {
1052
+ isHook = true;
1053
+ break;
1054
+ }
1055
+ }
1056
+ 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
+ }
1058
+ if (hasHook && hookOrFn) assertSchemaShape(hookOrFn, `${method} ${path}`);
973
1059
  const handler = hasHook ? fn : hookOrFn;
974
1060
  const hook = hasHook ? require_schema_snapshot.snapshotHookSchemas(hookOrFn) : void 0;
975
1061
  const appHook = this["~hookChain"];
976
- if (this["~generation"] !== void 0) this.#assertMutable("route");
977
- (this.#declaredRoutes ?? this.#materializeDeclaredRoutes()).push(appHook ? [
1062
+ const verb = this["~generation"] === void 0 ? "method" : method === "*" ? "all" : method.toLowerCase();
1063
+ this.#assertMutable(verb in this ? verb : "method");
1064
+ (this.declaredRoutes ?? this.#materializeDeclaredRoutes()).push(appHook ? [
978
1065
  method,
979
1066
  path,
980
1067
  handler,
@@ -993,76 +1080,61 @@ var Elysia = class Elysia {
993
1080
  handler,
994
1081
  this
995
1082
  ]);
996
- if (this.#routerBuilt || this.#compiled !== void 0) {
997
- this.#compiled = void 0;
998
- this.#jitColdRemaining = void 0;
999
- this.#jitTable = void 0;
1000
- this.#jitRoute = void 0;
1001
- this.#jitStatic = void 0;
1002
- this.#jitAliases = void 0;
1003
- this.#fetchFn = void 0;
1004
- this.#routerBuilt = false;
1005
- }
1083
+ if (this.routerBuilt || this.compiled !== void 0) this.#invalidate();
1006
1084
  return this;
1007
1085
  }
1008
1086
  #assertMutable(api) {
1087
+ mutationEpoch++;
1009
1088
  if (this["~generation"] === void 0) return;
1010
- throw new Error(`[Elysia] .${api}() called after the app was sealed`);
1089
+ throw new Error(`[Elysia] .${api}() called after the app was sealed by its first request, listen or compile`);
1090
+ }
1091
+ get #isCacheable() {
1092
+ return this["~generation"] !== void 0 && this.routerBuilt && !require_compile_aot.Capture.isCapturing();
1093
+ }
1094
+ #cacheRoutes(generation) {
1095
+ if (this.cachedGeneration === generation && this.cachedEpoch === mutationEpoch) return;
1096
+ this.cachedGeneration = generation;
1097
+ this.cachedEpoch = mutationEpoch;
1098
+ this.cachedRoutes = void 0;
1099
+ this.cachedHistory = void 0;
1011
1100
  }
1012
1101
  #materializeDeclaredRoutes() {
1013
- if (this.#declaredRoutes !== void 0) return this.#declaredRoutes;
1102
+ if (this.declaredRoutes !== void 0) return this.declaredRoutes;
1014
1103
  const table = this["~routeTable"];
1015
- if (!table) return this.#declaredRoutes = [];
1104
+ if (!table) return this.declaredRoutes = [];
1016
1105
  const routes = new Array(table.length);
1017
1106
  for (let i = 0; i < table.length; i++) routes[i] = require_route_table.routeRow(table, i);
1018
- return this.#declaredRoutes = routes;
1107
+ return this.declaredRoutes = routes;
1019
1108
  }
1020
1109
  #registerRoute(route, source) {
1021
- if (this["~generation"] !== void 0) this.#assertMutable("route");
1022
- const routes = this.#declaredRoutes ?? this.#materializeDeclaredRoutes();
1110
+ this.#assertMutable("route");
1111
+ const routes = this.declaredRoutes ?? this.#materializeDeclaredRoutes();
1023
1112
  const sequence = routes.length;
1024
1113
  routes.push(route);
1025
- if (source) (this.#routeSources ??= [])[sequence] = source;
1026
- if (this.#routerBuilt || this.#compiled !== void 0) {
1027
- this.#compiled = void 0;
1028
- this.#jitColdRemaining = void 0;
1029
- this.#jitTable = void 0;
1030
- this.#jitRoute = void 0;
1031
- this.#jitStatic = void 0;
1032
- this.#jitAliases = void 0;
1033
- this.#fetchFn = void 0;
1034
- this.#routerBuilt = false;
1035
- }
1114
+ if (source) (this.routeSources ??= [])[sequence] = source;
1115
+ if (this.routerBuilt || this.compiled !== void 0) this.#invalidate();
1116
+ }
1117
+ #invalidate() {
1118
+ this.compiled = void 0;
1119
+ this.jitColdRemaining = void 0;
1120
+ this.jitTable = void 0;
1121
+ this.jitRoute = void 0;
1122
+ this.jitAliases = void 0;
1123
+ this.fetchFn = void 0;
1124
+ this.routerBuilt = false;
1036
1125
  }
1037
1126
  model(name, model) {
1038
1127
  this.#assertMutable("model");
1039
- const models = this.#ext.models ??= require_utils.nullObject();
1128
+ const models = this.ext.models ??= require_utils.nullObject();
1040
1129
  switch (typeof name) {
1041
1130
  case "object":
1042
- const entries = Object.entries(name);
1043
- if (entries.length) for (let [key, value] of entries) {
1044
- if (key in models) continue;
1045
- if ("~standard" in value) models[key] = value;
1046
- else {
1047
- value = require_schema_snapshot.snapshotSchema(value);
1048
- value.$id ??= key;
1049
- models[key] = value;
1050
- }
1051
- }
1131
+ for (const [key, value] of Object.entries(name)) if (!(key in models)) models[key] = toModel(key, value);
1052
1132
  return this;
1053
1133
  case "function": {
1054
- const remapped = name(models ?? require_utils.nullObject());
1134
+ const remapped = name(models);
1055
1135
  const next = require_utils.nullObject();
1056
- for (const key in remapped) {
1057
- let value = remapped[key];
1058
- if ("~standard" in value) next[key] = value;
1059
- else {
1060
- value = require_schema_snapshot.snapshotSchema(value);
1061
- value.$id ??= key;
1062
- next[key] = value;
1063
- }
1064
- }
1065
- this.#ext.models = next;
1136
+ for (const key in remapped) next[key] = toModel(key, remapped[key]);
1137
+ this.ext.models = next;
1066
1138
  return this;
1067
1139
  }
1068
1140
  case "string":
@@ -1097,6 +1169,9 @@ var Elysia = class Elysia {
1097
1169
  options(path, hookOrFn, fn) {
1098
1170
  return this.#add("OPTIONS", path, hookOrFn, fn);
1099
1171
  }
1172
+ query(path, hookOrFn, fn) {
1173
+ return this.#add("QUERY", path, hookOrFn, fn);
1174
+ }
1100
1175
  head(path, hookOrFn, fn) {
1101
1176
  return this.#add("HEAD", path, hookOrFn, fn);
1102
1177
  }
@@ -1121,22 +1196,18 @@ var Elysia = class Elysia {
1121
1196
  this.#add("WS", path, opts, void 0, true);
1122
1197
  return this;
1123
1198
  }
1124
- mount(path, handleOrConfig, config) {
1199
+ mount(path, handler) {
1125
1200
  const options = {
1126
- ...config,
1127
1201
  parse: "none",
1128
- detail: {
1129
- ...config?.detail,
1130
- hide: true
1131
- }
1202
+ detail: { hide: true }
1132
1203
  };
1133
1204
  if (typeof path === "function" || path === "" || path === "/") {
1134
- const run = typeof path === "function" ? path : typeof handleOrConfig === "function" ? handleOrConfig : null;
1205
+ const run = typeof path === "function" ? path : typeof handler === "function" ? handler : null;
1135
1206
  if (!run) throw new Error("Invalid handler");
1136
1207
  this.all("/*", options, Elysia.#mountHandler(run, 2));
1137
1208
  return this;
1138
1209
  }
1139
- const handle = typeof handleOrConfig === "function" ? handleOrConfig : null;
1210
+ const handle = typeof handler === "function" ? handler : null;
1140
1211
  if (!handle) throw new Error("Invalid handler");
1141
1212
  const endsStar = path.endsWith("*") ? 1 : 0;
1142
1213
  const wildcardSuffix = path.endsWith("/") ? "*" : "/*";
@@ -1172,75 +1243,102 @@ var Elysia = class Elysia {
1172
1243
  compile() {
1173
1244
  this["~config"] ??= require_utils.nullObject();
1174
1245
  this["~config"].precompile = true;
1175
- this.#routerBuilt = false;
1176
- this.#compiled = void 0;
1177
- this.#jitColdRemaining = void 0;
1178
- this.#jitTable = void 0;
1179
- this.#jitRoute = void 0;
1180
- this.#jitStatic = void 0;
1181
- this.#jitAliases = void 0;
1182
- this.#fetchFn = void 0;
1246
+ this.#invalidate();
1183
1247
  this.fetch;
1184
1248
  return this;
1185
1249
  }
1186
- handler(index, immediate = this["~config"]?.precompile, route, precomputedStatic, aliases, table) {
1187
- if (this.#compiled?.[index]) return this.#compiled[index];
1188
- const compiled = this.#compiled ??= new Array(table?.length ?? this["~routes"].length);
1189
- if (immediate) {
1190
- const row = route ?? (table ? require_route_table.routeRow(table, index) : this["~routes"][index]);
1250
+ handler(index, immediate, route, aliases, table) {
1251
+ if (this.compiled?.[index]) return this.compiled[index];
1252
+ const indexedTable = table ?? (this.routerBuilt ? this["~routeTable"] : void 0);
1253
+ const compiled = this.compiled ??= new Array(indexedTable?.length ?? this["~routes"].length);
1254
+ if (immediate ?? this["~config"]?.precompile) {
1255
+ const row = route ?? (indexedTable ? require_route_table.routeRow(indexedTable, index) : this["~routes"][index]);
1256
+ const exactDuplicate = ((indexedTable?.flags[index] ?? 0) & require_route_table.RouteFlag.ExactDuplicate) !== 0;
1191
1257
  let handler;
1192
1258
  try {
1193
- handler = require_compile_handler_index.compileHandler(row, this, precomputedStatic);
1259
+ handler = require_compile_handler_index.compileHandler(row, this, exactDuplicate && require_compile_aot.Compiled.hasProgram(this["~programId"]));
1194
1260
  } catch (error) {
1195
1261
  throw new Error(`[Elysia] Failed to compile route ${row[0]} ${row[1]}: ${error?.message ?? error}`, { cause: error });
1196
1262
  }
1197
1263
  compiled[index] = handler;
1198
- this.#saveHandler(row[0], row[1], handler);
1264
+ if (indexedTable) this.#satisfyJit(indexedTable, index);
1199
1265
  return handler;
1200
1266
  }
1201
- return this.#jitHandler(index, route ?? (table ? void 0 : this["~routes"][index]), precomputedStatic, aliases, table);
1202
- }
1203
- #jitHandler(index, route, precomputedStatic, aliases, table) {
1204
- if (table !== void 0) this.#jitTable = table;
1205
- if (route !== void 0) (this.#jitRoute ??= [])[index] = route;
1206
- if (precomputedStatic !== void 0) (this.#jitStatic ??= [])[index] = precomputedStatic;
1207
- if (aliases !== void 0) (this.#jitAliases ??= [])[index] = aliases;
1267
+ const jitRoute = route ?? (indexedTable ? void 0 : this["~routes"][index]);
1268
+ if (indexedTable !== void 0) this.jitTable = indexedTable;
1269
+ if (jitRoute !== void 0) (this.jitRoute ??= [])[index] = jitRoute;
1270
+ if (aliases !== void 0) (this.jitAliases ??= [])[index] = aliases;
1208
1271
  return (context) => this.#jitDispatch(index, context);
1209
1272
  }
1273
+ #staticAliases(table, index) {
1274
+ const flags = table.flags[index];
1275
+ if ((flags & (require_route_table.RouteFlag.WS | require_route_table.RouteFlag.Dynamic)) !== 0) return;
1276
+ const path = table.path[index];
1277
+ const method = table.method[index];
1278
+ const needsEncode = (flags & require_route_table.RouteFlag.Encode) !== 0;
1279
+ const registerLoose = this["~config"]?.strictPath !== true && canRegisterLoose(path, false);
1280
+ if (!needsEncode && !registerLoose) return;
1281
+ let explicit;
1282
+ if (registerLoose) {
1283
+ explicit = /* @__PURE__ */ new Set();
1284
+ for (let i = 0; i < table.length; i++) {
1285
+ if (table.method[i] !== method) continue;
1286
+ const declared = table.path[i];
1287
+ explicit.add(declared);
1288
+ if ((table.flags[i] & require_route_table.RouteFlag.Encode) !== 0) explicit.add(encodeURI(declared));
1289
+ }
1290
+ }
1291
+ return {
1292
+ method,
1293
+ paths: expandPaths(path, needsEncode, registerLoose, explicit)
1294
+ };
1295
+ }
1296
+ #releaseJit() {
1297
+ require_compile_aot.Compiled.release(this["~programId"]);
1298
+ this.jitColdRemaining = void 0;
1299
+ this.jitRoute = void 0;
1300
+ this.jitAliases = void 0;
1301
+ this.jitTable = void 0;
1302
+ }
1303
+ #satisfyJit(table, index) {
1304
+ if ((table.flags[index] & require_route_table.RouteFlag.ExactDuplicate) !== 0) return false;
1305
+ if (this.jitColdRemaining === void 0) {
1306
+ if (this["~generation"] === void 0 && this["~config"]?.precompile !== true && require_universal_is_production.isProduction() && !require_compile_aot.Capture.isAotBuildEnv()) table.flags[index] |= require_route_table.RouteFlag.JITSatisfied;
1307
+ return false;
1308
+ }
1309
+ if ((table.flags[index] & require_route_table.RouteFlag.JITCold) === 0) return false;
1310
+ table.flags[index] &= ~require_route_table.RouteFlag.JITCold;
1311
+ if (--this.jitColdRemaining > 0) return false;
1312
+ this.#releaseJit();
1313
+ return true;
1314
+ }
1210
1315
  #jitDispatch(index, context) {
1211
- if (this.#compiled[index]) return this.#compiled[index](context);
1212
- const materialized = this.#jitRoute?.[index] ?? require_route_table.routeRow(this.#jitTable, index);
1316
+ if (this.compiled[index]) return this.compiled[index](context);
1317
+ const route = this.jitRoute?.[index];
1318
+ const table = this.jitTable ?? this["~routeTable"];
1319
+ const materialized = route ?? require_route_table.routeRow(table, index);
1320
+ const exactDuplicate = ((table?.flags[index] ?? 0) & require_route_table.RouteFlag.ExactDuplicate) !== 0;
1213
1321
  let handler;
1214
1322
  try {
1215
- handler = require_compile_handler_index.compileHandler(materialized, this, this.#jitStatic?.[index]);
1323
+ handler = require_compile_handler_index.compileHandler(materialized, this, exactDuplicate && require_compile_aot.Compiled.hasProgram(this["~programId"]));
1216
1324
  } catch (error) {
1217
1325
  const routeError = new Error(`[Elysia] Failed to compile route ${materialized[0]} ${materialized[1]}: ${error?.message ?? error}`, { cause: error });
1218
1326
  const finalize = this["~finalizeError"];
1219
1327
  if (finalize) return finalize(context, routeError);
1220
1328
  throw routeError;
1221
1329
  }
1222
- this.#compiled[index] = handler;
1223
- let releasedNow = false;
1224
- if (this.#jitColdRemaining !== void 0 && --this.#jitColdRemaining === 0) {
1225
- require_compile_aot.Compiled.release(this["~programId"]);
1226
- this.#jitColdRemaining = void 0;
1227
- releasedNow = true;
1330
+ this.compiled[index] = handler;
1331
+ if (!exactDuplicate) {
1332
+ const aliases = this.jitAliases?.[index] ?? (table ? this.#staticAliases(table, index) : void 0);
1333
+ if (aliases) {
1334
+ this.#initMap();
1335
+ const map = this["~map"][aliases.method] ??= require_utils.nullObject();
1336
+ for (let p = 0; p < aliases.paths.length; p++) map[aliases.paths[p]] = handler;
1337
+ } else this.#saveHandler(materialized[0], materialized[1], handler);
1228
1338
  }
1229
- const aliases = this.#jitAliases?.[index];
1230
- if (aliases) {
1231
- this.#initMap();
1232
- const map = this["~map"][aliases.method] ??= require_utils.nullObject();
1233
- for (let p = 0; p < aliases.paths.length; p++) map[aliases.paths[p]] = handler;
1234
- } else this.#saveHandler(materialized[0], materialized[1], handler);
1235
- if (releasedNow) {
1236
- this.#jitRoute = void 0;
1237
- this.#jitStatic = void 0;
1238
- this.#jitAliases = void 0;
1239
- this.#jitTable = void 0;
1240
- } else {
1241
- if (this.#jitRoute) this.#jitRoute[index] = void 0;
1242
- if (this.#jitStatic) this.#jitStatic[index] = void 0;
1243
- if (this.#jitAliases) this.#jitAliases[index] = void 0;
1339
+ if (!(table ? this.#satisfyJit(table, index) : false)) {
1340
+ if (this.jitRoute) this.jitRoute[index] = void 0;
1341
+ if (this.jitAliases) this.jitAliases[index] = void 0;
1244
1342
  }
1245
1343
  return handler(context);
1246
1344
  }
@@ -1257,7 +1355,7 @@ var Elysia = class Elysia {
1257
1355
  if (typeof response === "string") return true;
1258
1356
  if (response && typeof response === "object") {
1259
1357
  const record = response;
1260
- if (!("~kind" in record) && !("~elyAcl" in record) && !("~standard" in record)) {
1358
+ if (!isSingleSchema(record)) {
1261
1359
  for (const status in record) if (typeof record[status] === "string") return true;
1262
1360
  }
1263
1361
  }
@@ -1271,16 +1369,41 @@ var Elysia = class Elysia {
1271
1369
  }
1272
1370
  return false;
1273
1371
  }
1274
- #routeMayHaveModelRef(table, i) {
1275
- const macroScope = table.macroScope?.get(i);
1276
- const owner = table.owner[i];
1277
- const candidate = macroScope ?? owner ?? this;
1278
- if ((candidate === this ? this : require_compile_handler_index.localMacroRoot(candidate, this))["~ext"]?.macro) return true;
1279
- if (Elysia.#hookHasString(table.localHook[i])) return true;
1280
- const appHook = table.appHook[i];
1281
- if (appHook !== void 0 && appHook.refs) return true;
1282
- const inheritedChain = table.inheritedChain[i];
1283
- return inheritedChain !== void 0 && inheritedChain.refs;
1372
+ static #slotHasTypeBox(value, models, record) {
1373
+ if (typeof value === "string") value = models?.[value];
1374
+ if (!value || typeof value !== "object") return false;
1375
+ if ("~kind" in value || "~elyAcl" in value) return true;
1376
+ if (!record || "~standard" in value) return false;
1377
+ for (const status in value) if (Elysia.#slotHasTypeBox(value[status], models)) return true;
1378
+ return false;
1379
+ }
1380
+ static #hookHasTypeBox(h, models) {
1381
+ if (!h || typeof h !== "object") return false;
1382
+ for (const key in h) {
1383
+ if (key === "schemas") {
1384
+ const schemas = h[key];
1385
+ if (Array.isArray(schemas)) {
1386
+ for (let s = 0; s < schemas.length; s++) if (Elysia.#hookHasTypeBox(schemas[s], models)) return true;
1387
+ }
1388
+ continue;
1389
+ }
1390
+ if (require_utils.schemaProperties.has(key) && Elysia.#slotHasTypeBox(h[key], models, key === "response")) return true;
1391
+ }
1392
+ return false;
1393
+ }
1394
+ static #chainHasTypeBox(node, models, seen, resolve) {
1395
+ while (node) {
1396
+ if (seen.has(node)) return false;
1397
+ seen.add(node);
1398
+ if ("added" in node) {
1399
+ if (Elysia.#hookHasTypeBox(resolve ? resolve(node) : node.added, models)) return true;
1400
+ node = node.parent;
1401
+ } else {
1402
+ if (Elysia.#chainHasTypeBox(node.combine, models, seen, resolve) || Elysia.#chainHasTypeBox(node.tail, models, seen, resolve)) return true;
1403
+ node = node.over;
1404
+ }
1405
+ }
1406
+ return false;
1284
1407
  }
1285
1408
  #assertRouteModelRefs(route, method) {
1286
1409
  const models = this["~ext"]?.models;
@@ -1294,7 +1417,7 @@ var Elysia = class Elysia {
1294
1417
  if (!models || !(v in models)) throw new Error(`[Elysia] Unknown model reference "${v}" for ${key} on route ${method} ${path}.`);
1295
1418
  } else if (key === "response" && v && typeof v === "object") {
1296
1419
  const record = v;
1297
- if ("~kind" in record || "~elyAcl" in record || "~standard" in record) continue;
1420
+ if (isSingleSchema(record)) continue;
1298
1421
  for (const status in record) {
1299
1422
  const r = record[status];
1300
1423
  if (typeof r === "string" && (!models || !(r in models))) throw new Error(`[Elysia] Unknown model reference "${r}" for response ${status} on route ${method} ${path}.`);
@@ -1307,30 +1430,27 @@ var Elysia = class Elysia {
1307
1430
  const schemas = hook?.schemas;
1308
1431
  if (Array.isArray(schemas)) for (let s = 0; s < schemas.length; s++) checkSlots(schemas[s]);
1309
1432
  }
1310
- #routerBuilt = false;
1311
1433
  #buildRouter(seal = false) {
1312
- if (this.#routerBuilt) {
1434
+ if (this.routerBuilt) {
1313
1435
  if (seal && this["~generation"] === void 0) this.#publishGeneration();
1314
1436
  return;
1315
1437
  }
1316
1438
  const compilerSession = require_compile_aot.beginCompilerSession(this);
1317
1439
  const previousMap = this["~map"];
1318
1440
  const previousRouter = this["~router"];
1319
- const previousCompiled = this.#compiled;
1320
- const previousJitTable = this.#jitTable;
1321
- const previousJitRoute = this.#jitRoute;
1322
- const previousJitStatic = this.#jitStatic;
1323
- const previousJitAliases = this.#jitAliases;
1441
+ const previousCompiled = this.compiled;
1442
+ const previousJitTable = this.jitTable;
1443
+ const previousJitRoute = this.jitRoute;
1444
+ const previousJitAliases = this.jitAliases;
1324
1445
  const previousHasDynamicWS = this["~hasDynamicWS"];
1325
1446
  const previousGeneration = this["~generation"];
1326
1447
  this["~map"] = void 0;
1327
1448
  this["~router"] = void 0;
1328
- this.#compiled = previousCompiled?.slice();
1329
- this.#jitColdRemaining = void 0;
1330
- this.#jitTable = void 0;
1331
- this.#jitRoute = void 0;
1332
- this.#jitStatic = void 0;
1333
- this.#jitAliases = void 0;
1449
+ this.compiled = previousCompiled?.slice();
1450
+ this.jitColdRemaining = void 0;
1451
+ this.jitTable = void 0;
1452
+ this.jitRoute = void 0;
1453
+ this.jitAliases = void 0;
1334
1454
  this["~hasDynamicWS"] = void 0;
1335
1455
  this["~generation"] = void 0;
1336
1456
  let buildSucceeded = false;
@@ -1346,18 +1466,17 @@ var Elysia = class Elysia {
1346
1466
  Object.assign(previousRouter, nextRouter);
1347
1467
  this["~router"] = previousRouter;
1348
1468
  }
1349
- this.#routerBuilt = true;
1469
+ this.routerBuilt = true;
1350
1470
  if (seal) this.#publishGeneration();
1351
1471
  buildSucceeded = true;
1352
1472
  } catch (error) {
1353
1473
  this["~map"] = previousMap;
1354
1474
  this["~router"] = previousRouter;
1355
- this.#compiled = previousCompiled;
1356
- this.#jitColdRemaining = void 0;
1357
- this.#jitTable = previousJitTable;
1358
- this.#jitRoute = previousJitRoute;
1359
- this.#jitStatic = previousJitStatic;
1360
- this.#jitAliases = previousJitAliases;
1475
+ this.compiled = previousCompiled;
1476
+ this.jitColdRemaining = void 0;
1477
+ this.jitTable = previousJitTable;
1478
+ this.jitRoute = previousJitRoute;
1479
+ this.jitAliases = previousJitAliases;
1361
1480
  this["~hasDynamicWS"] = previousHasDynamicWS;
1362
1481
  this["~generation"] = previousGeneration;
1363
1482
  throw error;
@@ -1378,26 +1497,31 @@ var Elysia = class Elysia {
1378
1497
  "~wsConfig": this["~wsConfig"]
1379
1498
  };
1380
1499
  if (require_universal_is_production.isProduction() && !require_compile_aot.Capture.isAotBuildEnv()) {
1381
- if (this["~config"]?.precompile) require_compile_aot.Compiled.release(this["~programId"]);
1500
+ if (this["~config"]?.precompile) this.#releaseJit();
1382
1501
  else {
1383
1502
  const table = this["~routeTable"];
1384
- const routeCount = table?.length ?? this["~routes"].length;
1385
- let cold = routeCount;
1386
- const compiled = this.#compiled;
1387
- for (let i = 0; i < routeCount; i++) if (table !== void 0 && (table.flags[i] & require_route_table.RouteFlag.WS) !== 0 || compiled?.[i] !== void 0) cold--;
1388
- if (cold <= 0) require_compile_aot.Compiled.release(this["~programId"]);
1389
- else this.#jitColdRemaining = cold;
1503
+ const routeCount = table.length;
1504
+ const compiled = this.compiled;
1505
+ let cold = 0;
1506
+ for (let i = 0; i < routeCount; i++) {
1507
+ const satisfied = (table.flags[i] & require_route_table.RouteFlag.JITSatisfied) !== 0;
1508
+ table.flags[i] &= ~(require_route_table.RouteFlag.JITCold | require_route_table.RouteFlag.JITSatisfied);
1509
+ const flags = table.flags[i];
1510
+ if (satisfied || (flags & (require_route_table.RouteFlag.WS | require_route_table.RouteFlag.ExactDuplicate)) !== 0 || compiled?.[i] !== void 0) continue;
1511
+ table.flags[i] |= require_route_table.RouteFlag.JITCold;
1512
+ cold++;
1513
+ }
1514
+ if (cold === 0) this.#releaseJit();
1515
+ else this.jitColdRemaining = cold;
1390
1516
  }
1391
- require_compile_analysis_cache.clearAuthoringAnalysisCaches(this);
1392
- if (!this["~ext"]?.macro && !this["~scopeChildren"]) this.#declaredRoutes = void 0;
1517
+ require_compile_handler_index.clearHandlerAnalysisCaches(this);
1518
+ require_utils.clearFlattenChainMemo(this);
1519
+ if (!this["~ext"]?.macro && !this["~scopeChildren"]) this.declaredRoutes = void 0;
1393
1520
  }
1394
- }
1395
- ["~newGeneration"]() {
1396
- this.#fetchFn = void 0;
1397
- this.#routerBuilt = false;
1398
- this["~generation"] = void 0;
1399
- this.#buildRouter(true);
1400
- return this;
1521
+ const ext = this["~ext"];
1522
+ if (ext?.hoc) extCallbackIndexes.delete(ext.hoc);
1523
+ if (ext?.setup) extCallbackIndexes.delete(ext.setup);
1524
+ if (ext?.cleanup) extCallbackIndexes.delete(ext.cleanup);
1401
1525
  }
1402
1526
  #buildRouterUnsafe() {
1403
1527
  const precompile = this["~config"]?.precompile;
@@ -1413,21 +1537,52 @@ var Elysia = class Elysia {
1413
1537
  const length = table.length;
1414
1538
  const appChain = this["~hookChain"];
1415
1539
  if (this["~ext"]?.macro || this["~scopeChildren"] || appChain !== void 0 && appChain.refs) for (let i = 0; i < length; i++) this.#assertRouteModelRefs(require_route_table.routeRow(table, i), method[i]);
1416
- else for (let i = 0; i < length; i++) if (this.#routeMayHaveModelRef(table, i)) this.#assertRouteModelRefs(require_route_table.routeRow(table, i), method[i]);
1417
- if (length) require_compile_aot.Compiled.claim(this["~programId"], this["~aotFingerprint"] = require_compile_aot.createAotFingerprint());
1540
+ else for (let i = 0; i < length; i++) {
1541
+ const macroScope = table.macroScope?.get(i);
1542
+ const owner = table.owner[i];
1543
+ const candidate = macroScope ?? owner ?? this;
1544
+ if ((candidate === this ? this : require_compile_handler_index.localMacroRoot(candidate, this))["~ext"]?.macro || Elysia.#hookHasString(table.localHook[i]) || table.appHook[i]?.refs || table.inheritedChain[i]?.refs) this.#assertRouteModelRefs(require_route_table.routeRow(table, i), method[i]);
1545
+ }
1546
+ let hasTypeBoxSchema = false;
1547
+ if (length) {
1548
+ const models = this["~ext"]?.models;
1549
+ const resolve = require_compile_handler_index.chainResolver(this);
1550
+ const rootChain = this["~hookChain"];
1551
+ const seen = /* @__PURE__ */ new Set();
1552
+ const { localHook, appHook, inheritedChain, owner } = table;
1553
+ for (let i = 0; i < table.length; i++) if (Elysia.#hookHasTypeBox(localHook[i], models) || Elysia.#chainHasTypeBox(appHook[i], models, seen, resolve) || Elysia.#chainHasTypeBox(inheritedChain[i], models, seen, resolve) || owner[i] !== this && Elysia.#chainHasTypeBox(rootChain, models, seen, resolve)) {
1554
+ hasTypeBoxSchema = true;
1555
+ break;
1556
+ }
1557
+ }
1558
+ if (hasTypeBoxSchema) try {
1559
+ require_type_bridge.warmTypebox();
1560
+ } catch {}
1561
+ let markDuplicates = false;
1562
+ if (length) {
1563
+ const programId = this["~programId"];
1564
+ if (!require_compile_aot.Compiled.claim(programId, this["~aotFingerprint"] = require_compile_aot.createAotFingerprint())) require_compile_aot.Compiled.assertUncontested(programId);
1565
+ markDuplicates = require_compile_aot.Compiled.hasProgram(programId) || precompile !== true && require_universal_is_production.isProduction() && !require_compile_aot.Capture.isAotBuildEnv();
1566
+ }
1418
1567
  const isLoose = this["~config"]?.strictPath !== true;
1568
+ const collectExplicit = isLoose && table.hasLoose;
1419
1569
  let explicitPaths;
1420
- if (isLoose && table.hasLoose) {
1421
- explicitPaths = /* @__PURE__ */ new Map();
1570
+ if (markDuplicates || collectExplicit) {
1571
+ const seen = /* @__PURE__ */ new Map();
1572
+ if (collectExplicit) explicitPaths = seen;
1422
1573
  for (let i = 0; i < length; i++) {
1423
1574
  const m = method[i];
1424
1575
  const p = path[i];
1425
- let set = explicitPaths.get(m);
1426
- if (!set) explicitPaths.set(m, set = /* @__PURE__ */ new Set());
1427
- set.add(p);
1428
- if ((flags[i] & require_route_table.RouteFlag.Encode) !== 0) {
1576
+ let paths = seen.get(m);
1577
+ if (!paths) seen.set(m, paths = /* @__PURE__ */ new Map());
1578
+ if (markDuplicates && (flags[i] & require_route_table.RouteFlag.WS) === 0) {
1579
+ const displaced = paths.get(p);
1580
+ if (displaced !== void 0 && displaced >= 0) flags[displaced] |= require_route_table.RouteFlag.ExactDuplicate;
1581
+ paths.set(p, i);
1582
+ } else paths.set(p, -1);
1583
+ if (collectExplicit && (flags[i] & require_route_table.RouteFlag.Encode) !== 0) {
1429
1584
  const encoded = encodeURI(p);
1430
- if (encoded !== p) set.add(encoded);
1585
+ if (encoded !== p && !paths.has(encoded)) paths.set(encoded, -1);
1431
1586
  }
1432
1587
  }
1433
1588
  }
@@ -1440,54 +1595,40 @@ var Elysia = class Elysia {
1440
1595
  const handler = ws[0];
1441
1596
  const options = ws[1];
1442
1597
  if (wsConfig === void 0 && wsCap.options) wsConfig = wsCap.options;
1598
+ const wsNeedsEncode = (routeFlags & require_route_table.RouteFlag.Encode) !== 0;
1443
1599
  if ((routeFlags & require_route_table.RouteFlag.Dynamic) !== 0) {
1444
- (this["~router"] ??= new memoirist.default({ loosePath: isLoose })).add("WS", routePath, handler);
1600
+ const wsRouter = this["~router"] ??= new memoirist.default({ loosePath: isLoose });
1601
+ const wsPaths = expandPaths(routePath, wsNeedsEncode, false, void 0);
1602
+ for (let v = 0; v < wsPaths.length; v++) wsRouter.add("WS", wsPaths[v], handler);
1445
1603
  this["~hasDynamicWS"] = true;
1446
1604
  } else {
1447
- this.#initMap();
1448
1605
  const wsMap = this["~map"]["WS"] ??= require_utils.nullObject();
1449
- wsMap[routePath] = handler;
1450
- if (isLoose) {
1451
- const loose = require_utils.getLoosePath(routePath);
1452
- if (loose !== routePath && !explicitPaths?.get("WS")?.has(loose)) wsMap[loose] = handler;
1453
- }
1606
+ const wsPaths = expandPaths(routePath, wsNeedsEncode, isLoose, isLoose ? explicitPaths?.get("WS") : void 0);
1607
+ for (let v = 0; v < wsPaths.length; v++) wsMap[wsPaths[v]] = handler;
1454
1608
  }
1455
1609
  if (options && require_utils.isNotEmpty(options)) {
1456
1610
  wsConfig ??= require_utils.nullObject();
1457
- wsCap.provider.accumulateOptions(wsConfig, options, routePath);
1611
+ wsCap.provider.accumulateOptions(wsConfig, options);
1458
1612
  }
1459
1613
  continue;
1460
1614
  }
1461
1615
  const isDynamic = (routeFlags & require_route_table.RouteFlag.Dynamic) !== 0;
1462
1616
  const needsEncode = (routeFlags & require_route_table.RouteFlag.Encode) !== 0;
1463
1617
  const registerLoose = isLoose && canRegisterLoose(routePath, isDynamic);
1464
- const explicitMain = registerLoose ? explicitPaths?.get(routeMethod) : void 0;
1465
1618
  if (!isDynamic && !needsEncode && !registerLoose) {
1466
1619
  const map = methods[routeMethod] ??= require_utils.nullObject();
1467
- map[routePath] = this.handler(i, precompile, void 0, void 0, void 0, table);
1620
+ map[routePath] = this.handler(i, precompile, void 0, void 0, table);
1468
1621
  continue;
1469
1622
  }
1470
- const variants = [routePath];
1471
- if (needsEncode) {
1472
- const encoded = encodeURI(routePath);
1473
- if (encoded !== routePath) variants.push(encoded);
1474
- }
1475
- const paths = [];
1476
- for (let v = 0; v < variants.length; v++) {
1477
- const p = variants[v];
1478
- paths.push(p);
1479
- if (registerLoose) {
1480
- const loose = require_utils.getLoosePath(p);
1481
- if (loose !== p && !explicitMain?.has(loose)) paths.push(loose);
1482
- }
1483
- }
1623
+ const paths = expandPaths(routePath, needsEncode, registerLoose, registerLoose ? explicitPaths?.get(routeMethod) : void 0);
1484
1624
  if (isDynamic) {
1625
+ if (require_utils.isHTMLBundle(table.handler[i])) continue;
1485
1626
  const router = this["~router"] ??= new memoirist.default({ loosePath: isLoose });
1486
- const handler = this.handler(i, precompile, void 0, void 0, void 0, table);
1627
+ const handler = this.handler(i, precompile, void 0, void 0, table);
1487
1628
  for (let p = 0; p < paths.length; p++) router.add(routeMethod, paths[p], handler);
1488
1629
  } else {
1489
1630
  const map = methods[routeMethod] ??= require_utils.nullObject();
1490
- const handler = this.handler(i, precompile, void 0, void 0, {
1631
+ const handler = this.handler(i, precompile, void 0, {
1491
1632
  method: routeMethod,
1492
1633
  paths
1493
1634
  }, table);
@@ -1496,19 +1637,28 @@ var Elysia = class Elysia {
1496
1637
  }
1497
1638
  this["~wsConfig"] = wsConfig;
1498
1639
  }
1499
- #fetchFn;
1500
1640
  get fetch() {
1501
- if (this.#fetchFn) return this.#fetchFn;
1502
- this.#buildRouter(!this.#pending);
1503
- return this.#fetchFn ??= require_handler_fetch.applyHoc(this, require_handler_fetch.createFetchHandler(this));
1641
+ if (this.fetchFn) return this.fetchFn;
1642
+ if (this.ready) return (request, ...rest) => {
1643
+ const run = () => this.fetch(request, ...rest);
1644
+ return this.ready ? this.modules.then(run, run) : run();
1645
+ };
1646
+ return this.#dispatch();
1647
+ }
1648
+ #dispatch() {
1649
+ this.#buildRouter(!this._pending);
1650
+ if (this.ready) return require_handler_fetch.applyHoc(this, require_handler_fetch.createFetchHandler(this));
1651
+ return this.fetchFn ??= require_handler_fetch.applyHoc(this, require_handler_fetch.createFetchHandler(this));
1504
1652
  }
1505
- #handle;
1506
1653
  get handle() {
1507
- return this.#handle ??= async (requestOrUrl, options) => this.fetch(typeof requestOrUrl === "string" ? new Request(requestOrUrl.charCodeAt(0) === 47 ? `http://e.ly${requestOrUrl}` : requestOrUrl, options) : requestOrUrl);
1654
+ 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);
1508
1655
  }
1509
1656
  listen(options, callback) {
1510
1657
  const listen = (this["~config"]?.adapter ?? (require_universal_constants.isBun ? require_adapter_bun_index.BunAdapter : void 0))?.listen;
1511
- if (!listen) throw new Error("No adapter provided for listen()");
1658
+ if (!listen) {
1659
+ const deno = typeof Deno !== "undefined";
1660
+ 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"}`);
1661
+ }
1512
1662
  if (!require_compile_aot.Capture.isAotBuildEnv()) listen(this, options, callback);
1513
1663
  return this;
1514
1664
  }
@@ -1529,8 +1679,8 @@ var Elysia = class Elysia {
1529
1679
  */
1530
1680
  wrap(callback) {
1531
1681
  this.#assertMutable("wrap");
1532
- if (this.#fetchFn && !this.#pending) console.warn("[Elysia] .wrap() was called after the fetch handler was built");
1533
- const ext = this.#ext;
1682
+ if (this.fetchFn && !this._pending) console.warn("[Elysia] .wrap() was called after the fetch handler was built");
1683
+ const ext = this.ext;
1534
1684
  (ext.hoc ??= []).push(callback);
1535
1685
  return this;
1536
1686
  }
@@ -1548,31 +1698,49 @@ var Elysia = class Elysia {
1548
1698
  * ```
1549
1699
  */
1550
1700
  cleanup(handler) {
1701
+ if (this["~ext"]?.cleanupEpoch?.(handler)) return this;
1551
1702
  this.#assertMutable("cleanup");
1552
- const arr = this.#ext.cleanup ??= [];
1553
- if (Array.isArray(handler)) arr.push(...handler);
1554
- else arr.push(handler);
1703
+ (this.ext.cleanup ??= []).push(...[].concat(handler));
1555
1704
  return this;
1556
1705
  }
1557
1706
  /**
1558
- * Stop the underlying server (if any), running every `cleanup` handler once
1559
- * it has stopped. Mirrors `Server.stop()`.
1707
+ * Stop the underlying server (if any), then run every `cleanup` handler
1560
1708
  *
1561
- * @param closeActiveConnections Pass `true` to terminate in-flight
1562
- * requests and WebSocket connections immediately. Defaults to
1563
- * draining gracefully.
1709
+ * Awaiting `stop()` from inside a `setup`, `cleanup` or WebSocket lifecycle
1710
+ * callback is only supported while that callback is still synchronous
1711
+ *
1712
+ * @param closeActiveConnections Pass `true` to terminate active
1713
+ * transports. Omit (or pass `false`) to drain safely.
1564
1714
  */
1565
1715
  stop(closeActiveConnections) {
1716
+ const stop = this["~ext"]?.stop;
1717
+ if (stop) return stop(closeActiveConnections);
1566
1718
  const server = this.server;
1567
1719
  if (!server) return;
1568
- const r = server.stop?.(closeActiveConnections);
1569
- this.server = void 0;
1720
+ let result;
1721
+ const errors = [];
1722
+ try {
1723
+ result = server.stop?.(closeActiveConnections);
1724
+ } catch (error) {
1725
+ errors.push(error);
1726
+ } finally {
1727
+ this.server = void 0;
1728
+ }
1570
1729
  const handlers = this["~ext"]?.cleanup;
1571
- const fire = handlers ? async () => {
1572
- for (let i = 0; i < handlers.length; i++) await handlers[i](this);
1573
- } : void 0;
1574
- if (r && typeof r.then === "function") return fire ? r.then(fire) : r;
1575
- return fire?.();
1730
+ if (!errors.length && !handlers?.length) return result && typeof result.then === "function" ? result : void 0;
1731
+ return (async () => {
1732
+ try {
1733
+ await result;
1734
+ } catch (error) {
1735
+ errors.push(error);
1736
+ }
1737
+ if (handlers) for (let i = 0; i < handlers.length; i++) try {
1738
+ await handlers[i](this);
1739
+ } catch (error) {
1740
+ errors.push(error);
1741
+ }
1742
+ require_utils.throwLifecycleErrors(errors);
1743
+ })();
1576
1744
  }
1577
1745
  };
1578
1746