elysia 2.0.0-beta.2 → 2.0.0-beta.4

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 (175) hide show
  1. package/dist/adapter/bun/index.d.ts +1 -1
  2. package/dist/adapter/bun/index.js +320 -56
  3. package/dist/adapter/bun/index.mjs +321 -57
  4. package/dist/adapter/utils.js +51 -30
  5. package/dist/adapter/utils.mjs +52 -31
  6. package/dist/adapter/web-standard/handler.js +5 -2
  7. package/dist/adapter/web-standard/handler.mjs +5 -2
  8. package/dist/base.d.ts +18 -7
  9. package/dist/base.js +213 -101
  10. package/dist/base.mjs +215 -103
  11. package/dist/compile/aot-capture.js +1 -1
  12. package/dist/compile/aot-capture.mjs +1 -1
  13. package/dist/compile/aot.d.ts +5 -4
  14. package/dist/compile/aot.js +17 -9
  15. package/dist/compile/aot.mjs +18 -9
  16. package/dist/compile/handler/descriptor.js +1 -1
  17. package/dist/compile/handler/descriptor.mjs +1 -1
  18. package/dist/compile/handler/frozen-validator.d.ts +4 -1
  19. package/dist/compile/handler/frozen-validator.js +8 -3
  20. package/dist/compile/handler/frozen-validator.mjs +5 -3
  21. package/dist/compile/handler/index.d.ts +2 -2
  22. package/dist/compile/handler/index.js +8 -6
  23. package/dist/compile/handler/index.mjs +8 -6
  24. package/dist/compile/handler/reconstruct.d.ts +1 -1
  25. package/dist/compile/handler/reconstruct.js +4 -3
  26. package/dist/compile/handler/reconstruct.mjs +4 -3
  27. package/dist/compile/handler/utils.js +2 -2
  28. package/dist/compile/handler/utils.mjs +3 -3
  29. package/dist/compile/jit-probe.d.ts +2 -2
  30. package/dist/context.d.ts +1 -1
  31. package/dist/context.js +8 -5
  32. package/dist/context.mjs +8 -5
  33. package/dist/cookie/config.d.ts +1 -0
  34. package/dist/cookie/config.js +3 -2
  35. package/dist/cookie/config.mjs +3 -2
  36. package/dist/cookie/cookie.d.ts +1 -0
  37. package/dist/cookie/cookie.js +6 -5
  38. package/dist/cookie/cookie.mjs +6 -5
  39. package/dist/cookie/crypto.d.ts +8 -7
  40. package/dist/cookie/crypto.js +38 -31
  41. package/dist/cookie/crypto.mjs +38 -32
  42. package/dist/cookie/error.js +1 -2
  43. package/dist/cookie/error.mjs +1 -2
  44. package/dist/cookie/lib.js +5 -1
  45. package/dist/cookie/lib.mjs +5 -1
  46. package/dist/cookie/serialize.js +1 -1
  47. package/dist/cookie/serialize.mjs +1 -1
  48. package/dist/cookie/types.d.ts +10 -0
  49. package/dist/cookie/utils.js +28 -14
  50. package/dist/cookie/utils.mjs +29 -15
  51. package/dist/handler/error.d.ts +23 -12
  52. package/dist/handler/error.js +24 -21
  53. package/dist/handler/error.mjs +20 -21
  54. package/dist/handler/fetch.js +6 -7
  55. package/dist/handler/fetch.mjs +7 -8
  56. package/dist/handler/utils.js +4 -2
  57. package/dist/handler/utils.mjs +5 -3
  58. package/dist/index.js +1 -8
  59. package/dist/package.js +1 -1
  60. package/dist/package.mjs +1 -1
  61. package/dist/plugin/aot/core.d.ts +33 -6
  62. package/dist/plugin/aot/core.js +120 -40
  63. package/dist/plugin/aot/core.mjs +121 -43
  64. package/dist/plugin/aot/esbuild.js +8 -2
  65. package/dist/plugin/aot/esbuild.mjs +9 -3
  66. package/dist/plugin/aot/hooks.d.ts +2 -1
  67. package/dist/plugin/aot/hooks.js +3 -3
  68. package/dist/plugin/aot/hooks.mjs +4 -4
  69. package/dist/plugin/aot/source.d.ts +4 -1
  70. package/dist/plugin/aot/source.js +21 -14
  71. package/dist/plugin/aot/source.mjs +21 -14
  72. package/dist/plugin/aot/worker.js +2 -2
  73. package/dist/plugin/aot/worker.mjs +2 -2
  74. package/dist/route-table.d.ts +3 -0
  75. package/dist/route-table.js +16 -11
  76. package/dist/route-table.mjs +16 -11
  77. package/dist/schema-snapshot.js +17 -22
  78. package/dist/schema-snapshot.mjs +17 -22
  79. package/dist/type/bridge-live.d.ts +1 -1
  80. package/dist/type/bridge-live.js +2 -2
  81. package/dist/type/bridge-live.mjs +2 -2
  82. package/dist/type/bridge.d.ts +1 -1
  83. package/dist/type/coerce-plan.js +3 -4
  84. package/dist/type/coerce-plan.mjs +1 -1
  85. package/dist/type/coerce.d.ts +1 -1
  86. package/dist/type/compat.d.ts +3 -0
  87. package/dist/type/compat.js +32 -14
  88. package/dist/type/compat.mjs +27 -8
  89. package/dist/type/constants.d.ts +1 -1
  90. package/dist/type/elysia/array-buffer.js +2 -3
  91. package/dist/type/elysia/array-buffer.mjs +1 -1
  92. package/dist/type/elysia/array-string.js +7 -4
  93. package/dist/type/elysia/array-string.mjs +9 -5
  94. package/dist/type/elysia/boolean-string.js +2 -3
  95. package/dist/type/elysia/boolean-string.mjs +1 -1
  96. package/dist/type/elysia/boolean.js +4 -3
  97. package/dist/type/elysia/boolean.mjs +4 -2
  98. package/dist/type/elysia/cookie.js +2 -1
  99. package/dist/type/elysia/cookie.mjs +2 -1
  100. package/dist/type/elysia/date.js +7 -7
  101. package/dist/type/elysia/date.mjs +3 -2
  102. package/dist/type/elysia/file-type.d.ts +9 -2
  103. package/dist/type/elysia/file-type.js +16 -1
  104. package/dist/type/elysia/file-type.mjs +16 -2
  105. package/dist/type/elysia/file.js +8 -4
  106. package/dist/type/elysia/file.mjs +8 -3
  107. package/dist/type/elysia/files.js +6 -4
  108. package/dist/type/elysia/files.mjs +5 -2
  109. package/dist/type/elysia/form.js +2 -3
  110. package/dist/type/elysia/form.mjs +3 -3
  111. package/dist/type/elysia/integer-string.js +4 -5
  112. package/dist/type/elysia/integer-string.mjs +3 -3
  113. package/dist/type/elysia/integer.js +4 -3
  114. package/dist/type/elysia/integer.mjs +4 -2
  115. package/dist/type/elysia/maybe-empty.js +3 -4
  116. package/dist/type/elysia/maybe-empty.mjs +1 -1
  117. package/dist/type/elysia/nullable.js +2 -3
  118. package/dist/type/elysia/nullable.mjs +1 -1
  119. package/dist/type/elysia/number.js +4 -3
  120. package/dist/type/elysia/number.mjs +4 -2
  121. package/dist/type/elysia/numeric-enum.js +2 -3
  122. package/dist/type/elysia/numeric-enum.mjs +1 -1
  123. package/dist/type/elysia/numeric.js +5 -5
  124. package/dist/type/elysia/numeric.mjs +3 -2
  125. package/dist/type/elysia/object-string.js +7 -4
  126. package/dist/type/elysia/object-string.mjs +9 -5
  127. package/dist/type/elysia/string.js +4 -3
  128. package/dist/type/elysia/string.mjs +4 -2
  129. package/dist/type/elysia/uint8-array.js +2 -3
  130. package/dist/type/elysia/uint8-array.mjs +1 -1
  131. package/dist/type/elysia/union-enum.js +2 -3
  132. package/dist/type/elysia/union-enum.mjs +1 -1
  133. package/dist/type/elysia/utils.js +2 -3
  134. package/dist/type/elysia/utils.mjs +1 -1
  135. package/dist/type/exports.js +10 -1
  136. package/dist/type/exports.mjs +7 -0
  137. package/dist/type/index.d.ts +9 -1
  138. package/dist/type/index.js +41 -13
  139. package/dist/type/index.mjs +44 -9
  140. package/dist/type/typebox-type-live.d.ts +9 -0
  141. package/dist/type/typebox-type-live.js +31 -0
  142. package/dist/type/typebox-type-live.mjs +16 -0
  143. package/dist/type/typebox-type.d.ts +24 -0
  144. package/dist/type/typebox-type.js +130 -0
  145. package/dist/type/typebox-type.mjs +66 -0
  146. package/dist/type/typebox-value-live.d.ts +9 -0
  147. package/dist/type/typebox-value-live.js +27 -0
  148. package/dist/type/typebox-value-live.mjs +11 -0
  149. package/dist/type/typebox-value.d.ts +30 -0
  150. package/dist/type/typebox-value.js +147 -0
  151. package/dist/type/typebox-value.mjs +62 -0
  152. package/dist/type/validator/custom-error.js +4 -5
  153. package/dist/type/validator/custom-error.mjs +3 -3
  154. package/dist/type/validator/default-precompute.js +4 -5
  155. package/dist/type/validator/default-precompute.mjs +1 -1
  156. package/dist/type/validator/index.d.ts +1 -1
  157. package/dist/type/validator/index.js +51 -42
  158. package/dist/type/validator/index.mjs +33 -23
  159. package/dist/type/validator/validator-cache.d.ts +1 -1
  160. package/dist/type/validator/validator-cache.js +27 -14
  161. package/dist/type/validator/validator-cache.mjs +27 -13
  162. package/dist/universal/constants.d.ts +2 -1
  163. package/dist/universal/constants.js +3 -1
  164. package/dist/universal/constants.mjs +2 -1
  165. package/dist/utils.d.ts +19 -1
  166. package/dist/utils.js +25 -3
  167. package/dist/utils.mjs +19 -3
  168. package/dist/validator/index.js +2 -1
  169. package/dist/validator/index.mjs +2 -1
  170. package/dist/ws/context.d.ts +8 -1
  171. package/dist/ws/context.js +65 -29
  172. package/dist/ws/context.mjs +64 -29
  173. package/dist/ws/route.js +176 -28
  174. package/dist/ws/route.mjs +179 -31
  175. package/package.json +5 -3
package/dist/base.js CHANGED
@@ -9,6 +9,7 @@ const require_generation = require('./generation.js');
9
9
  const require_compile_handler_index = require('./compile/handler/index.js');
10
10
  const require_route_table = require('./route-table.js');
11
11
  const require_adapter_bun_index = require('./adapter/bun/index.js');
12
+ const require_context = require('./context.js');
12
13
  const require_handler_fetch = require('./handler/fetch.js');
13
14
  const require_compile_analysis_cache = require('./compile/analysis-cache.js');
14
15
  const require_schema_snapshot = require('./schema-snapshot.js');
@@ -18,7 +19,47 @@ memoirist = require_runtime.__toESM(memoirist);
18
19
  //#region src/base.ts
19
20
  const useNodesBuffer = [];
20
21
  const emptyHistory = Object.freeze([]);
22
+ const extCallbackIndexes = /* @__PURE__ */ new WeakMap();
23
+ const mergeExtCallbacks = (target, incoming) => {
24
+ let index = extCallbackIndexes.get(target);
25
+ if (!index) {
26
+ index = {
27
+ indexedLength: target.length,
28
+ seen: new Set(target)
29
+ };
30
+ extCallbackIndexes.set(target, index);
31
+ } else for (let i = index.indexedLength; i < target.length; i++) index.seen.add(target[i]);
32
+ for (const fn of incoming) {
33
+ if (index.seen.has(fn)) continue;
34
+ target.push(fn);
35
+ index.seen.add(fn);
36
+ }
37
+ index.indexedLength = target.length;
38
+ };
21
39
  const canRegisterLoose = (path, isDynamic) => !isDynamic && (path.length === 0 || path.charCodeAt(path.length - 1) === 47);
40
+ /**
41
+ * Every key a single route must answer to, in registration order.
42
+ *
43
+ * `registerLoose` is the caller's call, not this helper's: HTTP gates it on
44
+ * `canRegisterLoose` because its lookup retries the trailing slash, while WS
45
+ * has no such retry and must own both directions outright
46
+ */
47
+ const expandPaths = (path, needsEncode, registerLoose, explicit) => {
48
+ const variants = [path];
49
+ if (needsEncode) {
50
+ const encoded = encodeURI(path);
51
+ if (encoded !== path) variants.push(encoded);
52
+ }
53
+ if (!registerLoose) return variants;
54
+ const paths = [];
55
+ for (let v = 0; v < variants.length; v++) {
56
+ const p = variants[v];
57
+ paths.push(p);
58
+ const loose = require_utils.getLoosePath(p);
59
+ if (loose !== p && !explicit?.has(loose)) paths.push(loose);
60
+ }
61
+ return paths;
62
+ };
22
63
  var Elysia = class Elysia {
23
64
  #hasPlugin;
24
65
  #hasGlobal;
@@ -80,11 +121,14 @@ var Elysia = class Elysia {
80
121
  #jitRoute;
81
122
  #jitStatic;
82
123
  #jitAliases;
124
+ get ["~programId"]() {
125
+ return this;
126
+ }
83
127
  constructor(config) {
84
128
  /**
85
129
  * ! Don't tune
86
130
  *
87
- * Interesting JSC internal: These `= undefined` are load-bearing
131
+ * Interesting JSC internal: These `= undefined` shouldn't be removed
88
132
  *
89
133
  * Inline slots live inside the object cell, so JSC must pick their
90
134
  * count at allocation before any initializer runs
@@ -102,7 +146,8 @@ var Elysia = class Elysia {
102
146
  * Don't omitted field either for monomorphic access
103
147
  *
104
148
  * @see test/memory/instance-footprint.test.ts
105
- * Est. Overflow 37 -> 21 = butterfly 64 -> 32 slots = 738B -> 450B/instance
149
+ * Est. overflow 37 -> 21 = butterfly 64 -> 32 slots = 738B -> 450B;
150
+ * reusing the app as its program identity brings the current cell to ~386B.
106
151
  **/
107
152
  this["~config"] = config;
108
153
  this["~ext"] = void 0;
@@ -122,7 +167,6 @@ var Elysia = class Elysia {
122
167
  this["~introspect"] = void 0;
123
168
  this["~scopeChild"] = void 0;
124
169
  this["~scopeChildren"] = void 0;
125
- this["~programId"] = require_compile_aot.createProgramId();
126
170
  if (config) {
127
171
  const { prefix, name, seed, adapter } = config;
128
172
  this["~Prefix"] = prefix ? prefix.charCodeAt(0) === 47 ? prefix : `/${prefix}` : void 0;
@@ -729,27 +773,12 @@ var Elysia = class Elysia {
729
773
  }
730
774
  if (error) if (ext.error) for (const [code, handler] of error) ext.error.set(code, handler);
731
775
  else ext.error = new Map(error);
732
- if (hoc) if (ext.hoc) {
733
- const seen = new Set(ext.hoc);
734
- for (const fn of hoc) if (!seen.has(fn)) {
735
- seen.add(fn);
736
- ext.hoc.push(fn);
737
- }
738
- } else ext.hoc = hoc.slice();
739
- if (setup) if (ext.setup) {
740
- const seen = new Set(ext.setup);
741
- for (const fn of setup) if (!seen.has(fn)) {
742
- seen.add(fn);
743
- ext.setup.push(fn);
744
- }
745
- } else ext.setup = setup.slice();
746
- if (cleanup) if (ext.cleanup) {
747
- const seen = new Set(ext.cleanup);
748
- for (const fn of cleanup) if (!seen.has(fn)) {
749
- seen.add(fn);
750
- ext.cleanup.push(fn);
751
- }
752
- } else ext.cleanup = cleanup.slice();
776
+ if (hoc) if (ext.hoc) mergeExtCallbacks(ext.hoc, hoc);
777
+ else ext.hoc = hoc.slice();
778
+ if (setup) if (ext.setup) mergeExtCallbacks(ext.setup, setup);
779
+ else ext.setup = setup.slice();
780
+ if (cleanup) if (ext.cleanup) mergeExtCallbacks(ext.cleanup, cleanup);
781
+ else ext.cleanup = cleanup.slice();
753
782
  if (capability) {
754
783
  const target = ext.capability ??= require_utils.nullObject();
755
784
  if (capability.trace) {
@@ -928,11 +957,11 @@ var Elysia = class Elysia {
928
957
  get modules() {
929
958
  const ready = this.#ready;
930
959
  if (!ready) {
931
- if (this.#error !== void 0) return Promise.reject(this.#error);
960
+ if (this.#error !== void 0) return Promise.reject(this.#error.error);
932
961
  return Promise.resolve();
933
962
  }
934
963
  return ready.then(() => {
935
- if (this.#error !== void 0) throw this.#error;
964
+ if (this.#error !== void 0) throw this.#error.error;
936
965
  if (this.#ready && this.#ready !== ready) return this.modules;
937
966
  });
938
967
  }
@@ -947,13 +976,13 @@ var Elysia = class Elysia {
947
976
  if (plugin) try {
948
977
  this.use(plugin);
949
978
  } catch (err) {
950
- this.#error ??= err;
979
+ this.#error ??= { error: err };
951
980
  console.error(err);
952
981
  }
953
982
  }).finally(() => {
954
983
  this.#pending--;
955
984
  }).then(() => {}, (err) => {
956
- this.#error ??= err;
985
+ this.#error ??= { error: err };
957
986
  console.error(err);
958
987
  }).finally(() => this.#tryDrain(next));
959
988
  this.#ready = next;
@@ -962,10 +991,20 @@ var Elysia = class Elysia {
962
991
  #tryDrain(sentinel) {
963
992
  if (this.#pending > 0) return;
964
993
  if (this.#ready !== sentinel) return;
994
+ const previousCompiled = this.#compiled;
995
+ const previousJitColdRemaining = this.#jitColdRemaining;
965
996
  this.#ready = void 0;
997
+ this.#compiled = void 0;
966
998
  this.#fetchFn = void 0;
967
999
  this.#routerBuilt = false;
968
- this.#buildRouter(false);
1000
+ require_context.clearContextCache(this);
1001
+ try {
1002
+ this.#buildRouter(false);
1003
+ } catch (error) {
1004
+ this.#compiled = previousCompiled;
1005
+ this.#jitColdRemaining = previousJitColdRemaining;
1006
+ this.#error ??= { error };
1007
+ }
969
1008
  }
970
1009
  #add(method, path, hookOrFn, fn, hasHook = fn !== void 0) {
971
1010
  if (this["~Prefix"]) path = require_utils.joinPath(this["~Prefix"], path);
@@ -1183,22 +1222,24 @@ var Elysia = class Elysia {
1183
1222
  this.fetch;
1184
1223
  return this;
1185
1224
  }
1186
- handler(index, immediate = this["~config"]?.precompile, route, precomputedStatic, aliases, table) {
1225
+ handler(index, immediate, route, precomputedStatic, aliases, table) {
1187
1226
  if (this.#compiled?.[index]) return this.#compiled[index];
1188
- const compiled = this.#compiled ??= new Array(table?.length ?? this["~routes"].length);
1189
- if (immediate) {
1190
- const row = route ?? (table ? require_route_table.routeRow(table, index) : this["~routes"][index]);
1227
+ const indexedTable = table ?? (this.#routerBuilt ? this["~routeTable"] : void 0);
1228
+ const compiled = this.#compiled ??= new Array(indexedTable?.length ?? this["~routes"].length);
1229
+ if (immediate ?? this["~config"]?.precompile) {
1230
+ const row = route ?? (indexedTable ? require_route_table.routeRow(indexedTable, index) : this["~routes"][index]);
1231
+ const exactDuplicate = ((indexedTable?.flags[index] ?? 0) & require_route_table.RouteFlag.ExactDuplicate) !== 0;
1191
1232
  let handler;
1192
1233
  try {
1193
- handler = require_compile_handler_index.compileHandler(row, this, precomputedStatic);
1234
+ handler = require_compile_handler_index.compileHandler(row, this, precomputedStatic, exactDuplicate && require_compile_aot.Compiled.hasProgram(this["~programId"]));
1194
1235
  } catch (error) {
1195
1236
  throw new Error(`[Elysia] Failed to compile route ${row[0]} ${row[1]}: ${error?.message ?? error}`, { cause: error });
1196
1237
  }
1197
1238
  compiled[index] = handler;
1198
- this.#saveHandler(row[0], row[1], handler);
1239
+ if (indexedTable) this.#satisfyJit(indexedTable, index);
1199
1240
  return handler;
1200
1241
  }
1201
- return this.#jitHandler(index, route ?? (table ? void 0 : this["~routes"][index]), precomputedStatic, aliases, table);
1242
+ return this.#jitHandler(index, route ?? (indexedTable ? void 0 : this["~routes"][index]), precomputedStatic, aliases, indexedTable);
1202
1243
  }
1203
1244
  #jitHandler(index, route, precomputedStatic, aliases, table) {
1204
1245
  if (table !== void 0) this.#jitTable = table;
@@ -1207,12 +1248,58 @@ var Elysia = class Elysia {
1207
1248
  if (aliases !== void 0) (this.#jitAliases ??= [])[index] = aliases;
1208
1249
  return (context) => this.#jitDispatch(index, context);
1209
1250
  }
1251
+ #staticAliases(table, index) {
1252
+ const flags = table.flags[index];
1253
+ if ((flags & (require_route_table.RouteFlag.WS | require_route_table.RouteFlag.Dynamic)) !== 0) return;
1254
+ const path = table.path[index];
1255
+ const method = table.method[index];
1256
+ const needsEncode = (flags & require_route_table.RouteFlag.Encode) !== 0;
1257
+ const registerLoose = this["~config"]?.strictPath !== true && canRegisterLoose(path, false);
1258
+ if (!needsEncode && !registerLoose) return;
1259
+ let explicit;
1260
+ if (registerLoose) {
1261
+ explicit = /* @__PURE__ */ new Set();
1262
+ for (let i = 0; i < table.length; i++) {
1263
+ if (table.method[i] !== method) continue;
1264
+ const declared = table.path[i];
1265
+ explicit.add(declared);
1266
+ if ((table.flags[i] & require_route_table.RouteFlag.Encode) !== 0) explicit.add(encodeURI(declared));
1267
+ }
1268
+ }
1269
+ return {
1270
+ method,
1271
+ paths: expandPaths(path, needsEncode, registerLoose, explicit)
1272
+ };
1273
+ }
1274
+ #releaseJit() {
1275
+ require_compile_aot.Compiled.release(this["~programId"]);
1276
+ this.#jitColdRemaining = void 0;
1277
+ this.#jitRoute = void 0;
1278
+ this.#jitStatic = void 0;
1279
+ this.#jitAliases = void 0;
1280
+ this.#jitTable = void 0;
1281
+ }
1282
+ #satisfyJit(table, index) {
1283
+ if ((table.flags[index] & require_route_table.RouteFlag.ExactDuplicate) !== 0) return false;
1284
+ if (this.#jitColdRemaining === void 0) {
1285
+ if (this["~generation"] === void 0 && this["~config"]?.precompile !== true && require_universal_is_production.isProduction() && !require_compile_aot.Capture.isAotBuildEnv()) table.flags[index] |= require_route_table.RouteFlag.JITSatisfied;
1286
+ return false;
1287
+ }
1288
+ if ((table.flags[index] & require_route_table.RouteFlag.JITCold) === 0) return false;
1289
+ table.flags[index] &= ~require_route_table.RouteFlag.JITCold;
1290
+ if (--this.#jitColdRemaining > 0) return false;
1291
+ this.#releaseJit();
1292
+ return true;
1293
+ }
1210
1294
  #jitDispatch(index, context) {
1211
1295
  if (this.#compiled[index]) return this.#compiled[index](context);
1212
- const materialized = this.#jitRoute?.[index] ?? require_route_table.routeRow(this.#jitTable, index);
1296
+ const route = this.#jitRoute?.[index];
1297
+ const table = this.#jitTable ?? this["~routeTable"];
1298
+ const materialized = route ?? require_route_table.routeRow(table, index);
1299
+ const exactDuplicate = ((table?.flags[index] ?? 0) & require_route_table.RouteFlag.ExactDuplicate) !== 0;
1213
1300
  let handler;
1214
1301
  try {
1215
- handler = require_compile_handler_index.compileHandler(materialized, this, this.#jitStatic?.[index]);
1302
+ handler = require_compile_handler_index.compileHandler(materialized, this, this.#jitStatic?.[index], exactDuplicate && require_compile_aot.Compiled.hasProgram(this["~programId"]));
1216
1303
  } catch (error) {
1217
1304
  const routeError = new Error(`[Elysia] Failed to compile route ${materialized[0]} ${materialized[1]}: ${error?.message ?? error}`, { cause: error });
1218
1305
  const finalize = this["~finalizeError"];
@@ -1220,24 +1307,15 @@ var Elysia = class Elysia {
1220
1307
  throw routeError;
1221
1308
  }
1222
1309
  this.#compiled[index] = handler;
1223
- let releasedNow = false;
1224
- if (this.#jitColdRemaining !== void 0 && --this.#jitColdRemaining === 0) {
1225
- require_compile_aot.Compiled.release(this["~programId"]);
1226
- this.#jitColdRemaining = void 0;
1227
- releasedNow = true;
1310
+ if (!exactDuplicate) {
1311
+ const aliases = this.#jitAliases?.[index] ?? (table ? this.#staticAliases(table, index) : void 0);
1312
+ if (aliases) {
1313
+ this.#initMap();
1314
+ const map = this["~map"][aliases.method] ??= require_utils.nullObject();
1315
+ for (let p = 0; p < aliases.paths.length; p++) map[aliases.paths[p]] = handler;
1316
+ } else this.#saveHandler(materialized[0], materialized[1], handler);
1228
1317
  }
1229
- const aliases = this.#jitAliases?.[index];
1230
- if (aliases) {
1231
- this.#initMap();
1232
- const map = this["~map"][aliases.method] ??= require_utils.nullObject();
1233
- for (let p = 0; p < aliases.paths.length; p++) map[aliases.paths[p]] = handler;
1234
- } else this.#saveHandler(materialized[0], materialized[1], handler);
1235
- if (releasedNow) {
1236
- this.#jitRoute = void 0;
1237
- this.#jitStatic = void 0;
1238
- this.#jitAliases = void 0;
1239
- this.#jitTable = void 0;
1240
- } else {
1318
+ if (!(table ? this.#satisfyJit(table, index) : false)) {
1241
1319
  if (this.#jitRoute) this.#jitRoute[index] = void 0;
1242
1320
  if (this.#jitStatic) this.#jitStatic[index] = void 0;
1243
1321
  if (this.#jitAliases) this.#jitAliases[index] = void 0;
@@ -1378,19 +1456,30 @@ var Elysia = class Elysia {
1378
1456
  "~wsConfig": this["~wsConfig"]
1379
1457
  };
1380
1458
  if (require_universal_is_production.isProduction() && !require_compile_aot.Capture.isAotBuildEnv()) {
1381
- if (this["~config"]?.precompile) require_compile_aot.Compiled.release(this["~programId"]);
1459
+ if (this["~config"]?.precompile) this.#releaseJit();
1382
1460
  else {
1383
1461
  const table = this["~routeTable"];
1384
- const routeCount = table?.length ?? this["~routes"].length;
1385
- let cold = routeCount;
1462
+ const routeCount = table.length;
1386
1463
  const compiled = this.#compiled;
1387
- for (let i = 0; i < routeCount; i++) if (table !== void 0 && (table.flags[i] & require_route_table.RouteFlag.WS) !== 0 || compiled?.[i] !== void 0) cold--;
1388
- if (cold <= 0) require_compile_aot.Compiled.release(this["~programId"]);
1464
+ let cold = 0;
1465
+ for (let i = 0; i < routeCount; i++) {
1466
+ const satisfied = (table.flags[i] & require_route_table.RouteFlag.JITSatisfied) !== 0;
1467
+ table.flags[i] &= ~(require_route_table.RouteFlag.JITCold | require_route_table.RouteFlag.JITSatisfied);
1468
+ const flags = table.flags[i];
1469
+ if (satisfied || (flags & (require_route_table.RouteFlag.WS | require_route_table.RouteFlag.ExactDuplicate)) !== 0 || compiled?.[i] !== void 0) continue;
1470
+ table.flags[i] |= require_route_table.RouteFlag.JITCold;
1471
+ cold++;
1472
+ }
1473
+ if (cold === 0) this.#releaseJit();
1389
1474
  else this.#jitColdRemaining = cold;
1390
1475
  }
1391
1476
  require_compile_analysis_cache.clearAuthoringAnalysisCaches(this);
1392
1477
  if (!this["~ext"]?.macro && !this["~scopeChildren"]) this.#declaredRoutes = void 0;
1393
1478
  }
1479
+ const ext = this["~ext"];
1480
+ if (ext?.hoc) extCallbackIndexes.delete(ext.hoc);
1481
+ if (ext?.setup) extCallbackIndexes.delete(ext.setup);
1482
+ if (ext?.cleanup) extCallbackIndexes.delete(ext.cleanup);
1394
1483
  }
1395
1484
  ["~newGeneration"]() {
1396
1485
  this.#fetchFn = void 0;
@@ -1414,20 +1503,31 @@ var Elysia = class Elysia {
1414
1503
  const appChain = this["~hookChain"];
1415
1504
  if (this["~ext"]?.macro || this["~scopeChildren"] || appChain !== void 0 && appChain.refs) for (let i = 0; i < length; i++) this.#assertRouteModelRefs(require_route_table.routeRow(table, i), method[i]);
1416
1505
  else for (let i = 0; i < length; i++) if (this.#routeMayHaveModelRef(table, i)) this.#assertRouteModelRefs(require_route_table.routeRow(table, i), method[i]);
1417
- if (length) require_compile_aot.Compiled.claim(this["~programId"], this["~aotFingerprint"] = require_compile_aot.createAotFingerprint());
1506
+ let markDuplicates = false;
1507
+ if (length) {
1508
+ const programId = this["~programId"];
1509
+ if (!require_compile_aot.Compiled.claim(programId, this["~aotFingerprint"] = require_compile_aot.createAotFingerprint())) require_compile_aot.Compiled.assertUncontested(programId);
1510
+ markDuplicates = require_compile_aot.Compiled.hasProgram(programId) || precompile !== true && require_universal_is_production.isProduction() && !require_compile_aot.Capture.isAotBuildEnv();
1511
+ }
1418
1512
  const isLoose = this["~config"]?.strictPath !== true;
1513
+ const collectExplicit = isLoose && table.hasLoose;
1419
1514
  let explicitPaths;
1420
- if (isLoose && table.hasLoose) {
1421
- explicitPaths = /* @__PURE__ */ new Map();
1515
+ if (markDuplicates || collectExplicit) {
1516
+ const seen = /* @__PURE__ */ new Map();
1517
+ if (collectExplicit) explicitPaths = seen;
1422
1518
  for (let i = 0; i < length; i++) {
1423
1519
  const m = method[i];
1424
1520
  const p = path[i];
1425
- let set = explicitPaths.get(m);
1426
- if (!set) explicitPaths.set(m, set = /* @__PURE__ */ new Set());
1427
- set.add(p);
1428
- if ((flags[i] & require_route_table.RouteFlag.Encode) !== 0) {
1521
+ let paths = seen.get(m);
1522
+ if (!paths) seen.set(m, paths = /* @__PURE__ */ new Map());
1523
+ if (markDuplicates && (flags[i] & require_route_table.RouteFlag.WS) === 0) {
1524
+ const displaced = paths.get(p);
1525
+ if (displaced !== void 0 && displaced >= 0) flags[displaced] |= require_route_table.RouteFlag.ExactDuplicate;
1526
+ paths.set(p, i);
1527
+ } else paths.set(p, -1);
1528
+ if (collectExplicit && (flags[i] & require_route_table.RouteFlag.Encode) !== 0) {
1429
1529
  const encoded = encodeURI(p);
1430
- if (encoded !== p) set.add(encoded);
1530
+ if (encoded !== p && !paths.has(encoded)) paths.set(encoded, -1);
1431
1531
  }
1432
1532
  }
1433
1533
  }
@@ -1440,17 +1540,17 @@ var Elysia = class Elysia {
1440
1540
  const handler = ws[0];
1441
1541
  const options = ws[1];
1442
1542
  if (wsConfig === void 0 && wsCap.options) wsConfig = wsCap.options;
1543
+ const wsNeedsEncode = (routeFlags & require_route_table.RouteFlag.Encode) !== 0;
1443
1544
  if ((routeFlags & require_route_table.RouteFlag.Dynamic) !== 0) {
1444
- (this["~router"] ??= new memoirist.default({ loosePath: isLoose })).add("WS", routePath, handler);
1545
+ const wsRouter = this["~router"] ??= new memoirist.default({ loosePath: isLoose });
1546
+ const wsPaths = expandPaths(routePath, wsNeedsEncode, false, void 0);
1547
+ for (let v = 0; v < wsPaths.length; v++) wsRouter.add("WS", wsPaths[v], handler);
1445
1548
  this["~hasDynamicWS"] = true;
1446
1549
  } else {
1447
1550
  this.#initMap();
1448
1551
  const wsMap = this["~map"]["WS"] ??= require_utils.nullObject();
1449
- wsMap[routePath] = handler;
1450
- if (isLoose) {
1451
- const loose = require_utils.getLoosePath(routePath);
1452
- if (loose !== routePath && !explicitPaths?.get("WS")?.has(loose)) wsMap[loose] = handler;
1453
- }
1552
+ const wsPaths = expandPaths(routePath, wsNeedsEncode, isLoose, isLoose ? explicitPaths?.get("WS") : void 0);
1553
+ for (let v = 0; v < wsPaths.length; v++) wsMap[wsPaths[v]] = handler;
1454
1554
  }
1455
1555
  if (options && require_utils.isNotEmpty(options)) {
1456
1556
  wsConfig ??= require_utils.nullObject();
@@ -1461,26 +1561,12 @@ var Elysia = class Elysia {
1461
1561
  const isDynamic = (routeFlags & require_route_table.RouteFlag.Dynamic) !== 0;
1462
1562
  const needsEncode = (routeFlags & require_route_table.RouteFlag.Encode) !== 0;
1463
1563
  const registerLoose = isLoose && canRegisterLoose(routePath, isDynamic);
1464
- const explicitMain = registerLoose ? explicitPaths?.get(routeMethod) : void 0;
1465
1564
  if (!isDynamic && !needsEncode && !registerLoose) {
1466
1565
  const map = methods[routeMethod] ??= require_utils.nullObject();
1467
1566
  map[routePath] = this.handler(i, precompile, void 0, void 0, void 0, table);
1468
1567
  continue;
1469
1568
  }
1470
- const variants = [routePath];
1471
- if (needsEncode) {
1472
- const encoded = encodeURI(routePath);
1473
- if (encoded !== routePath) variants.push(encoded);
1474
- }
1475
- const paths = [];
1476
- for (let v = 0; v < variants.length; v++) {
1477
- const p = variants[v];
1478
- paths.push(p);
1479
- if (registerLoose) {
1480
- const loose = require_utils.getLoosePath(p);
1481
- if (loose !== p && !explicitMain?.has(loose)) paths.push(loose);
1482
- }
1483
- }
1569
+ const paths = expandPaths(routePath, needsEncode, registerLoose, registerLoose ? explicitPaths?.get(routeMethod) : void 0);
1484
1570
  if (isDynamic) {
1485
1571
  const router = this["~router"] ??= new memoirist.default({ loosePath: isLoose });
1486
1572
  const handler = this.handler(i, precompile, void 0, void 0, void 0, table);
@@ -1548,6 +1634,7 @@ var Elysia = class Elysia {
1548
1634
  * ```
1549
1635
  */
1550
1636
  cleanup(handler) {
1637
+ if (this["~ext"]?.cleanupEpoch?.(handler)) return this;
1551
1638
  this.#assertMutable("cleanup");
1552
1639
  const arr = this.#ext.cleanup ??= [];
1553
1640
  if (Array.isArray(handler)) arr.push(...handler);
@@ -1555,24 +1642,49 @@ var Elysia = class Elysia {
1555
1642
  return this;
1556
1643
  }
1557
1644
  /**
1558
- * Stop the underlying server (if any), running every `cleanup` handler once
1559
- * it has stopped. Mirrors `Server.stop()`.
1645
+ * Stop the underlying server (if any), then run every `cleanup` handler.
1646
+ * Omitted and `false` are the same graceful stop: new requests are gated,
1647
+ * tracked WebSockets are settled and active HTTP is drained, then cleanup
1648
+ * runs and the epoch is released. `true` force-closes the transport
1649
+ * instead of draining it, and escalates a graceful stop already in flight.
1650
+ *
1651
+ * Awaiting `stop()` from inside a `setup`, `cleanup` or WebSocket lifecycle
1652
+ * callback is only supported while that callback is still synchronous; the
1653
+ * teardown is waiting on the callback, so after an `await` it can only be
1654
+ * issued as `void app.stop()`.
1560
1655
  *
1561
- * @param closeActiveConnections Pass `true` to terminate in-flight
1562
- * requests and WebSocket connections immediately. Defaults to
1563
- * draining gracefully.
1656
+ * @param closeActiveConnections Pass `true` to terminate active
1657
+ * transports. Omit (or pass `false`) to drain safely.
1564
1658
  */
1565
1659
  stop(closeActiveConnections) {
1660
+ const stop = this["~ext"]?.stop;
1661
+ if (stop) return stop(closeActiveConnections);
1566
1662
  const server = this.server;
1567
1663
  if (!server) return;
1568
- const r = server.stop?.(closeActiveConnections);
1569
- this.server = void 0;
1664
+ let result;
1665
+ const errors = [];
1666
+ try {
1667
+ result = server.stop?.(closeActiveConnections);
1668
+ } catch (error) {
1669
+ errors.push(error);
1670
+ } finally {
1671
+ this.server = void 0;
1672
+ }
1570
1673
  const handlers = this["~ext"]?.cleanup;
1571
- const fire = handlers ? async () => {
1572
- for (let i = 0; i < handlers.length; i++) await handlers[i](this);
1573
- } : void 0;
1574
- if (r && typeof r.then === "function") return fire ? r.then(fire) : r;
1575
- return fire?.();
1674
+ if (!errors.length && !handlers?.length) return result && typeof result.then === "function" ? result : void 0;
1675
+ return (async () => {
1676
+ try {
1677
+ await result;
1678
+ } catch (error) {
1679
+ errors.push(error);
1680
+ }
1681
+ if (handlers) for (let i = 0; i < handlers.length; i++) try {
1682
+ await handlers[i](this);
1683
+ } catch (error) {
1684
+ errors.push(error);
1685
+ }
1686
+ require_utils.throwLifecycleErrors(errors);
1687
+ })();
1576
1688
  }
1577
1689
  };
1578
1690