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