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.mjs CHANGED
@@ -1,12 +1,13 @@
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";
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, throwLifecycleErrors } from "./utils.mjs";
3
3
  import { Ref } 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
7
  import { 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
12
  import { clearAuthoringAnalysisCaches } from "./compile/analysis-cache.mjs";
12
13
  import { snapshotHookSchemas, snapshotSchema } from "./schema-snapshot.mjs";
@@ -15,7 +16,47 @@ import Memoirist from "memoirist";
15
16
  //#region src/base.ts
16
17
  const useNodesBuffer = [];
17
18
  const emptyHistory = Object.freeze([]);
19
+ const extCallbackIndexes = /* @__PURE__ */ new WeakMap();
20
+ const mergeExtCallbacks = (target, incoming) => {
21
+ let index = extCallbackIndexes.get(target);
22
+ if (!index) {
23
+ index = {
24
+ indexedLength: target.length,
25
+ seen: new Set(target)
26
+ };
27
+ extCallbackIndexes.set(target, index);
28
+ } else for (let i = index.indexedLength; i < target.length; i++) index.seen.add(target[i]);
29
+ for (const fn of incoming) {
30
+ if (index.seen.has(fn)) continue;
31
+ target.push(fn);
32
+ index.seen.add(fn);
33
+ }
34
+ index.indexedLength = target.length;
35
+ };
18
36
  const canRegisterLoose = (path, isDynamic) => !isDynamic && (path.length === 0 || path.charCodeAt(path.length - 1) === 47);
37
+ /**
38
+ * Every key a single route must answer to, in registration order.
39
+ *
40
+ * `registerLoose` is the caller's call, not this helper's: HTTP gates it on
41
+ * `canRegisterLoose` because its lookup retries the trailing slash, while WS
42
+ * has no such retry and must own both directions outright
43
+ */
44
+ const expandPaths = (path, needsEncode, registerLoose, explicit) => {
45
+ const variants = [path];
46
+ if (needsEncode) {
47
+ const encoded = encodeURI(path);
48
+ if (encoded !== path) variants.push(encoded);
49
+ }
50
+ if (!registerLoose) return variants;
51
+ const paths = [];
52
+ for (let v = 0; v < variants.length; v++) {
53
+ const p = variants[v];
54
+ paths.push(p);
55
+ const loose = getLoosePath(p);
56
+ if (loose !== p && !explicit?.has(loose)) paths.push(loose);
57
+ }
58
+ return paths;
59
+ };
19
60
  var Elysia = class Elysia {
20
61
  #hasPlugin;
21
62
  #hasGlobal;
@@ -77,11 +118,14 @@ var Elysia = class Elysia {
77
118
  #jitRoute;
78
119
  #jitStatic;
79
120
  #jitAliases;
121
+ get ["~programId"]() {
122
+ return this;
123
+ }
80
124
  constructor(config) {
81
125
  /**
82
126
  * ! Don't tune
83
127
  *
84
- * Interesting JSC internal: These `= undefined` are load-bearing
128
+ * Interesting JSC internal: These `= undefined` shouldn't be removed
85
129
  *
86
130
  * Inline slots live inside the object cell, so JSC must pick their
87
131
  * count at allocation before any initializer runs
@@ -99,7 +143,8 @@ var Elysia = class Elysia {
99
143
  * Don't omitted field either for monomorphic access
100
144
  *
101
145
  * @see test/memory/instance-footprint.test.ts
102
- * Est. Overflow 37 -> 21 = butterfly 64 -> 32 slots = 738B -> 450B/instance
146
+ * Est. overflow 37 -> 21 = butterfly 64 -> 32 slots = 738B -> 450B;
147
+ * reusing the app as its program identity brings the current cell to ~386B.
103
148
  **/
104
149
  this["~config"] = config;
105
150
  this["~ext"] = void 0;
@@ -119,7 +164,6 @@ var Elysia = class Elysia {
119
164
  this["~introspect"] = void 0;
120
165
  this["~scopeChild"] = void 0;
121
166
  this["~scopeChildren"] = void 0;
122
- this["~programId"] = createProgramId();
123
167
  if (config) {
124
168
  const { prefix, name, seed, adapter } = config;
125
169
  this["~Prefix"] = prefix ? prefix.charCodeAt(0) === 47 ? prefix : `/${prefix}` : void 0;
@@ -726,27 +770,12 @@ var Elysia = class Elysia {
726
770
  }
727
771
  if (error) if (ext.error) for (const [code, handler] of error) ext.error.set(code, handler);
728
772
  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();
773
+ if (hoc) if (ext.hoc) mergeExtCallbacks(ext.hoc, hoc);
774
+ else ext.hoc = hoc.slice();
775
+ if (setup) if (ext.setup) mergeExtCallbacks(ext.setup, setup);
776
+ else ext.setup = setup.slice();
777
+ if (cleanup) if (ext.cleanup) mergeExtCallbacks(ext.cleanup, cleanup);
778
+ else ext.cleanup = cleanup.slice();
750
779
  if (capability) {
751
780
  const target = ext.capability ??= nullObject();
752
781
  if (capability.trace) {
@@ -925,11 +954,11 @@ var Elysia = class Elysia {
925
954
  get modules() {
926
955
  const ready = this.#ready;
927
956
  if (!ready) {
928
- if (this.#error !== void 0) return Promise.reject(this.#error);
957
+ if (this.#error !== void 0) return Promise.reject(this.#error.error);
929
958
  return Promise.resolve();
930
959
  }
931
960
  return ready.then(() => {
932
- if (this.#error !== void 0) throw this.#error;
961
+ if (this.#error !== void 0) throw this.#error.error;
933
962
  if (this.#ready && this.#ready !== ready) return this.modules;
934
963
  });
935
964
  }
@@ -944,13 +973,13 @@ var Elysia = class Elysia {
944
973
  if (plugin) try {
945
974
  this.use(plugin);
946
975
  } catch (err) {
947
- this.#error ??= err;
976
+ this.#error ??= { error: err };
948
977
  console.error(err);
949
978
  }
950
979
  }).finally(() => {
951
980
  this.#pending--;
952
981
  }).then(() => {}, (err) => {
953
- this.#error ??= err;
982
+ this.#error ??= { error: err };
954
983
  console.error(err);
955
984
  }).finally(() => this.#tryDrain(next));
956
985
  this.#ready = next;
@@ -959,10 +988,20 @@ var Elysia = class Elysia {
959
988
  #tryDrain(sentinel) {
960
989
  if (this.#pending > 0) return;
961
990
  if (this.#ready !== sentinel) return;
991
+ const previousCompiled = this.#compiled;
992
+ const previousJitColdRemaining = this.#jitColdRemaining;
962
993
  this.#ready = void 0;
994
+ this.#compiled = void 0;
963
995
  this.#fetchFn = void 0;
964
996
  this.#routerBuilt = false;
965
- this.#buildRouter(false);
997
+ clearContextCache(this);
998
+ try {
999
+ this.#buildRouter(false);
1000
+ } catch (error) {
1001
+ this.#compiled = previousCompiled;
1002
+ this.#jitColdRemaining = previousJitColdRemaining;
1003
+ this.#error ??= { error };
1004
+ }
966
1005
  }
967
1006
  #add(method, path, hookOrFn, fn, hasHook = fn !== void 0) {
968
1007
  if (this["~Prefix"]) path = joinPath(this["~Prefix"], path);
@@ -1180,22 +1219,24 @@ var Elysia = class Elysia {
1180
1219
  this.fetch;
1181
1220
  return this;
1182
1221
  }
1183
- handler(index, immediate = this["~config"]?.precompile, route, precomputedStatic, aliases, table) {
1222
+ handler(index, immediate, route, precomputedStatic, aliases, table) {
1184
1223
  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]);
1224
+ const indexedTable = table ?? (this.#routerBuilt ? this["~routeTable"] : void 0);
1225
+ const compiled = this.#compiled ??= new Array(indexedTable?.length ?? this["~routes"].length);
1226
+ if (immediate ?? this["~config"]?.precompile) {
1227
+ const row = route ?? (indexedTable ? routeRow(indexedTable, index) : this["~routes"][index]);
1228
+ const exactDuplicate = ((indexedTable?.flags[index] ?? 0) & RouteFlag.ExactDuplicate) !== 0;
1188
1229
  let handler;
1189
1230
  try {
1190
- handler = compileHandler(row, this, precomputedStatic);
1231
+ handler = compileHandler(row, this, precomputedStatic, exactDuplicate && Compiled.hasProgram(this["~programId"]));
1191
1232
  } catch (error) {
1192
1233
  throw new Error(`[Elysia] Failed to compile route ${row[0]} ${row[1]}: ${error?.message ?? error}`, { cause: error });
1193
1234
  }
1194
1235
  compiled[index] = handler;
1195
- this.#saveHandler(row[0], row[1], handler);
1236
+ if (indexedTable) this.#satisfyJit(indexedTable, index);
1196
1237
  return handler;
1197
1238
  }
1198
- return this.#jitHandler(index, route ?? (table ? void 0 : this["~routes"][index]), precomputedStatic, aliases, table);
1239
+ return this.#jitHandler(index, route ?? (indexedTable ? void 0 : this["~routes"][index]), precomputedStatic, aliases, indexedTable);
1199
1240
  }
1200
1241
  #jitHandler(index, route, precomputedStatic, aliases, table) {
1201
1242
  if (table !== void 0) this.#jitTable = table;
@@ -1204,12 +1245,58 @@ var Elysia = class Elysia {
1204
1245
  if (aliases !== void 0) (this.#jitAliases ??= [])[index] = aliases;
1205
1246
  return (context) => this.#jitDispatch(index, context);
1206
1247
  }
1248
+ #staticAliases(table, index) {
1249
+ const flags = table.flags[index];
1250
+ if ((flags & (RouteFlag.WS | RouteFlag.Dynamic)) !== 0) return;
1251
+ const path = table.path[index];
1252
+ const method = table.method[index];
1253
+ const needsEncode = (flags & RouteFlag.Encode) !== 0;
1254
+ const registerLoose = this["~config"]?.strictPath !== true && canRegisterLoose(path, false);
1255
+ if (!needsEncode && !registerLoose) return;
1256
+ let explicit;
1257
+ if (registerLoose) {
1258
+ explicit = /* @__PURE__ */ new Set();
1259
+ for (let i = 0; i < table.length; i++) {
1260
+ if (table.method[i] !== method) continue;
1261
+ const declared = table.path[i];
1262
+ explicit.add(declared);
1263
+ if ((table.flags[i] & RouteFlag.Encode) !== 0) explicit.add(encodeURI(declared));
1264
+ }
1265
+ }
1266
+ return {
1267
+ method,
1268
+ paths: expandPaths(path, needsEncode, registerLoose, explicit)
1269
+ };
1270
+ }
1271
+ #releaseJit() {
1272
+ Compiled.release(this["~programId"]);
1273
+ this.#jitColdRemaining = void 0;
1274
+ this.#jitRoute = void 0;
1275
+ this.#jitStatic = void 0;
1276
+ this.#jitAliases = void 0;
1277
+ this.#jitTable = void 0;
1278
+ }
1279
+ #satisfyJit(table, index) {
1280
+ if ((table.flags[index] & RouteFlag.ExactDuplicate) !== 0) return false;
1281
+ if (this.#jitColdRemaining === void 0) {
1282
+ if (this["~generation"] === void 0 && this["~config"]?.precompile !== true && isProduction() && !Capture.isAotBuildEnv()) table.flags[index] |= RouteFlag.JITSatisfied;
1283
+ return false;
1284
+ }
1285
+ if ((table.flags[index] & RouteFlag.JITCold) === 0) return false;
1286
+ table.flags[index] &= ~RouteFlag.JITCold;
1287
+ if (--this.#jitColdRemaining > 0) return false;
1288
+ this.#releaseJit();
1289
+ return true;
1290
+ }
1207
1291
  #jitDispatch(index, context) {
1208
1292
  if (this.#compiled[index]) return this.#compiled[index](context);
1209
- const materialized = this.#jitRoute?.[index] ?? routeRow(this.#jitTable, index);
1293
+ const route = this.#jitRoute?.[index];
1294
+ const table = this.#jitTable ?? this["~routeTable"];
1295
+ const materialized = route ?? routeRow(table, index);
1296
+ const exactDuplicate = ((table?.flags[index] ?? 0) & RouteFlag.ExactDuplicate) !== 0;
1210
1297
  let handler;
1211
1298
  try {
1212
- handler = compileHandler(materialized, this, this.#jitStatic?.[index]);
1299
+ handler = compileHandler(materialized, this, this.#jitStatic?.[index], exactDuplicate && Compiled.hasProgram(this["~programId"]));
1213
1300
  } catch (error) {
1214
1301
  const routeError = new Error(`[Elysia] Failed to compile route ${materialized[0]} ${materialized[1]}: ${error?.message ?? error}`, { cause: error });
1215
1302
  const finalize = this["~finalizeError"];
@@ -1217,24 +1304,15 @@ var Elysia = class Elysia {
1217
1304
  throw routeError;
1218
1305
  }
1219
1306
  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;
1307
+ if (!exactDuplicate) {
1308
+ const aliases = this.#jitAliases?.[index] ?? (table ? this.#staticAliases(table, index) : void 0);
1309
+ if (aliases) {
1310
+ this.#initMap();
1311
+ const map = this["~map"][aliases.method] ??= nullObject();
1312
+ for (let p = 0; p < aliases.paths.length; p++) map[aliases.paths[p]] = handler;
1313
+ } else this.#saveHandler(materialized[0], materialized[1], handler);
1225
1314
  }
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 {
1315
+ if (!(table ? this.#satisfyJit(table, index) : false)) {
1238
1316
  if (this.#jitRoute) this.#jitRoute[index] = void 0;
1239
1317
  if (this.#jitStatic) this.#jitStatic[index] = void 0;
1240
1318
  if (this.#jitAliases) this.#jitAliases[index] = void 0;
@@ -1375,19 +1453,30 @@ var Elysia = class Elysia {
1375
1453
  "~wsConfig": this["~wsConfig"]
1376
1454
  };
1377
1455
  if (isProduction() && !Capture.isAotBuildEnv()) {
1378
- if (this["~config"]?.precompile) Compiled.release(this["~programId"]);
1456
+ if (this["~config"]?.precompile) this.#releaseJit();
1379
1457
  else {
1380
1458
  const table = this["~routeTable"];
1381
- const routeCount = table?.length ?? this["~routes"].length;
1382
- let cold = routeCount;
1459
+ const routeCount = table.length;
1383
1460
  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"]);
1461
+ let cold = 0;
1462
+ for (let i = 0; i < routeCount; i++) {
1463
+ const satisfied = (table.flags[i] & RouteFlag.JITSatisfied) !== 0;
1464
+ table.flags[i] &= ~(RouteFlag.JITCold | RouteFlag.JITSatisfied);
1465
+ const flags = table.flags[i];
1466
+ if (satisfied || (flags & (RouteFlag.WS | RouteFlag.ExactDuplicate)) !== 0 || compiled?.[i] !== void 0) continue;
1467
+ table.flags[i] |= RouteFlag.JITCold;
1468
+ cold++;
1469
+ }
1470
+ if (cold === 0) this.#releaseJit();
1386
1471
  else this.#jitColdRemaining = cold;
1387
1472
  }
1388
1473
  clearAuthoringAnalysisCaches(this);
1389
1474
  if (!this["~ext"]?.macro && !this["~scopeChildren"]) this.#declaredRoutes = void 0;
1390
1475
  }
1476
+ const ext = this["~ext"];
1477
+ if (ext?.hoc) extCallbackIndexes.delete(ext.hoc);
1478
+ if (ext?.setup) extCallbackIndexes.delete(ext.setup);
1479
+ if (ext?.cleanup) extCallbackIndexes.delete(ext.cleanup);
1391
1480
  }
1392
1481
  ["~newGeneration"]() {
1393
1482
  this.#fetchFn = void 0;
@@ -1411,20 +1500,31 @@ var Elysia = class Elysia {
1411
1500
  const appChain = this["~hookChain"];
1412
1501
  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
1502
  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());
1503
+ let markDuplicates = false;
1504
+ if (length) {
1505
+ const programId = this["~programId"];
1506
+ if (!Compiled.claim(programId, this["~aotFingerprint"] = createAotFingerprint())) Compiled.assertUncontested(programId);
1507
+ markDuplicates = Compiled.hasProgram(programId) || precompile !== true && isProduction() && !Capture.isAotBuildEnv();
1508
+ }
1415
1509
  const isLoose = this["~config"]?.strictPath !== true;
1510
+ const collectExplicit = isLoose && table.hasLoose;
1416
1511
  let explicitPaths;
1417
- if (isLoose && table.hasLoose) {
1418
- explicitPaths = /* @__PURE__ */ new Map();
1512
+ if (markDuplicates || collectExplicit) {
1513
+ const seen = /* @__PURE__ */ new Map();
1514
+ if (collectExplicit) explicitPaths = seen;
1419
1515
  for (let i = 0; i < length; i++) {
1420
1516
  const m = method[i];
1421
1517
  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) {
1518
+ let paths = seen.get(m);
1519
+ if (!paths) seen.set(m, paths = /* @__PURE__ */ new Map());
1520
+ if (markDuplicates && (flags[i] & RouteFlag.WS) === 0) {
1521
+ const displaced = paths.get(p);
1522
+ if (displaced !== void 0 && displaced >= 0) flags[displaced] |= RouteFlag.ExactDuplicate;
1523
+ paths.set(p, i);
1524
+ } else paths.set(p, -1);
1525
+ if (collectExplicit && (flags[i] & RouteFlag.Encode) !== 0) {
1426
1526
  const encoded = encodeURI(p);
1427
- if (encoded !== p) set.add(encoded);
1527
+ if (encoded !== p && !paths.has(encoded)) paths.set(encoded, -1);
1428
1528
  }
1429
1529
  }
1430
1530
  }
@@ -1437,17 +1537,17 @@ var Elysia = class Elysia {
1437
1537
  const handler = ws[0];
1438
1538
  const options = ws[1];
1439
1539
  if (wsConfig === void 0 && wsCap.options) wsConfig = wsCap.options;
1540
+ const wsNeedsEncode = (routeFlags & RouteFlag.Encode) !== 0;
1440
1541
  if ((routeFlags & RouteFlag.Dynamic) !== 0) {
1441
- (this["~router"] ??= new Memoirist({ loosePath: isLoose })).add("WS", routePath, handler);
1542
+ const wsRouter = this["~router"] ??= new Memoirist({ loosePath: isLoose });
1543
+ const wsPaths = expandPaths(routePath, wsNeedsEncode, false, void 0);
1544
+ for (let v = 0; v < wsPaths.length; v++) wsRouter.add("WS", wsPaths[v], handler);
1442
1545
  this["~hasDynamicWS"] = true;
1443
1546
  } else {
1444
1547
  this.#initMap();
1445
1548
  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
- }
1549
+ const wsPaths = expandPaths(routePath, wsNeedsEncode, isLoose, isLoose ? explicitPaths?.get("WS") : void 0);
1550
+ for (let v = 0; v < wsPaths.length; v++) wsMap[wsPaths[v]] = handler;
1451
1551
  }
1452
1552
  if (options && isNotEmpty(options)) {
1453
1553
  wsConfig ??= nullObject();
@@ -1458,26 +1558,12 @@ var Elysia = class Elysia {
1458
1558
  const isDynamic = (routeFlags & RouteFlag.Dynamic) !== 0;
1459
1559
  const needsEncode = (routeFlags & RouteFlag.Encode) !== 0;
1460
1560
  const registerLoose = isLoose && canRegisterLoose(routePath, isDynamic);
1461
- const explicitMain = registerLoose ? explicitPaths?.get(routeMethod) : void 0;
1462
1561
  if (!isDynamic && !needsEncode && !registerLoose) {
1463
1562
  const map = methods[routeMethod] ??= nullObject();
1464
1563
  map[routePath] = this.handler(i, precompile, void 0, void 0, void 0, table);
1465
1564
  continue;
1466
1565
  }
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
- }
1566
+ const paths = expandPaths(routePath, needsEncode, registerLoose, registerLoose ? explicitPaths?.get(routeMethod) : void 0);
1481
1567
  if (isDynamic) {
1482
1568
  const router = this["~router"] ??= new Memoirist({ loosePath: isLoose });
1483
1569
  const handler = this.handler(i, precompile, void 0, void 0, void 0, table);
@@ -1545,6 +1631,7 @@ var Elysia = class Elysia {
1545
1631
  * ```
1546
1632
  */
1547
1633
  cleanup(handler) {
1634
+ if (this["~ext"]?.cleanupEpoch?.(handler)) return this;
1548
1635
  this.#assertMutable("cleanup");
1549
1636
  const arr = this.#ext.cleanup ??= [];
1550
1637
  if (Array.isArray(handler)) arr.push(...handler);
@@ -1552,24 +1639,49 @@ var Elysia = class Elysia {
1552
1639
  return this;
1553
1640
  }
1554
1641
  /**
1555
- * Stop the underlying server (if any), running every `cleanup` handler once
1556
- * it has stopped. Mirrors `Server.stop()`.
1642
+ * Stop the underlying server (if any), then run every `cleanup` handler.
1643
+ * Omitted and `false` are the same graceful stop: new requests are gated,
1644
+ * tracked WebSockets are settled and active HTTP is drained, then cleanup
1645
+ * runs and the epoch is released. `true` force-closes the transport
1646
+ * instead of draining it, and escalates a graceful stop already in flight.
1647
+ *
1648
+ * Awaiting `stop()` from inside a `setup`, `cleanup` or WebSocket lifecycle
1649
+ * callback is only supported while that callback is still synchronous; the
1650
+ * teardown is waiting on the callback, so after an `await` it can only be
1651
+ * issued as `void app.stop()`.
1557
1652
  *
1558
- * @param closeActiveConnections Pass `true` to terminate in-flight
1559
- * requests and WebSocket connections immediately. Defaults to
1560
- * draining gracefully.
1653
+ * @param closeActiveConnections Pass `true` to terminate active
1654
+ * transports. Omit (or pass `false`) to drain safely.
1561
1655
  */
1562
1656
  stop(closeActiveConnections) {
1657
+ const stop = this["~ext"]?.stop;
1658
+ if (stop) return stop(closeActiveConnections);
1563
1659
  const server = this.server;
1564
1660
  if (!server) return;
1565
- const r = server.stop?.(closeActiveConnections);
1566
- this.server = void 0;
1661
+ let result;
1662
+ const errors = [];
1663
+ try {
1664
+ result = server.stop?.(closeActiveConnections);
1665
+ } catch (error) {
1666
+ errors.push(error);
1667
+ } finally {
1668
+ this.server = void 0;
1669
+ }
1567
1670
  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?.();
1671
+ if (!errors.length && !handlers?.length) return result && typeof result.then === "function" ? result : void 0;
1672
+ return (async () => {
1673
+ try {
1674
+ await result;
1675
+ } catch (error) {
1676
+ errors.push(error);
1677
+ }
1678
+ if (handlers) for (let i = 0; i < handlers.length; i++) try {
1679
+ await handlers[i](this);
1680
+ } catch (error) {
1681
+ errors.push(error);
1682
+ }
1683
+ throwLifecycleErrors(errors);
1684
+ })();
1573
1685
  }
1574
1686
  };
1575
1687
 
@@ -13,8 +13,8 @@ const require_type_validator_frozen_check = require('../type/validator/frozen-ch
13
13
  const require_type_validator_custom_error = require('../type/validator/custom-error.js');
14
14
  const require_type_validator_exact_mirror = require('../type/validator/exact-mirror.js');
15
15
  const require_type_validator_string_codec_aot = require('../type/validator/string-codec-aot.js');
16
- let typebox_value = require("typebox/value");
17
16
  let typebox_schema = require("typebox/schema");
17
+ let typebox_value = require("typebox/value");
18
18
 
19
19
  //#region src/compile/aot-capture.ts
20
20
  function emptyContainers(node, depth) {
@@ -11,8 +11,8 @@ import { buildFrozenCheck } from "../type/validator/frozen-check.mjs";
11
11
  import { captureCustomErrors } from "../type/validator/custom-error.mjs";
12
12
  import { getExactMirror } from "../type/validator/exact-mirror.mjs";
13
13
  import { captureStringCodecEntries } from "../type/validator/string-codec-aot.mjs";
14
- import { Default } from "typebox/value";
15
14
  import { Build, Compile } from "typebox/schema";
15
+ import { Default } from "typebox/value";
16
16
 
17
17
  //#region src/compile/aot-capture.ts
18
18
  function emptyContainers(node, depth) {
@@ -8,8 +8,7 @@ interface AotFingerprint {
8
8
  interface ProgramId {
9
9
  readonly _: unique symbol;
10
10
  }
11
- declare const createProgramId: () => ProgramId;
12
- declare function createAotFingerprint(): AotFingerprint;
11
+ declare const createAotFingerprint: () => AotFingerprint;
13
12
  interface CompilerSession {
14
13
  readonly sucroseCache: Map<number, {
15
14
  content: string;
@@ -91,7 +90,7 @@ interface HandlerManifest {
91
90
  }
92
91
  interface CompiledSnapshot {
93
92
  registered: CompiledProgramRegistration | undefined;
94
- claimed: boolean;
93
+ claimed: WeakRef<ProgramId> | undefined;
95
94
  programs: WeakMap<ProgramId, CompiledProgram>;
96
95
  }
97
96
  interface CompiledProgramRegistration {
@@ -131,9 +130,11 @@ declare function reconstruct(): ReconstructImpl;
131
130
  declare abstract class Compiled {
132
131
  static register(manifest: CompiledProgramRegistration): void;
133
132
  static claim(id: ProgramId, fingerprint: AotFingerprint): boolean;
133
+ static assertUncontested(id: ProgramId): void;
134
134
  static get reconstruct(): ReconstructImpl | undefined;
135
135
  static set reconstruct(impl: ReconstructImpl | undefined);
136
136
  static getHandler(id: ProgramId | undefined, method: string, path: string): FrozenHandler | undefined;
137
+ static hasProgram(id: ProgramId | undefined): boolean;
137
138
  static getValidator(method: string, path: string, slot: ValidatorSlot, id?: ProgramId): FrozenValidator | undefined;
138
139
  static hasValidator(method: string, path: string, slot: ValidatorSlot, id?: ProgramId): boolean;
139
140
  static getPlanRebuilder(id?: ProgramId): ((original: unknown, plan: CoercePlan) => any) | undefined;
@@ -284,4 +285,4 @@ interface CaptureImpl {
284
285
  declare let captureImpl: CaptureImpl | undefined;
285
286
  declare function setCaptureImpl(impl: CaptureImpl | undefined): void;
286
287
  //#endregion
287
- export { AOT_ABI, AOT_MANIFEST_FORMAT, AotFingerprint, Capture, CaptureImpl, CapturedHandler, CapturedMirror, CapturedValidator, CheckBuildResult, Compiled, CompiledProgramRegistration, CompiledSnapshot, CompilerSession, CompilerState, EMPTY_EXTERNALS, FrozenBothFactory, FrozenCheckFactory, FrozenHandler, FrozenMirror, FrozenMirrorFactory, FrozenValidator, HandlerManifest, ProgramId, ReconstructImpl, StringCodecNode, ValidatorManifest, ValidatorSlot, aotActivationError, beginCompilerSession, captureImpl, createAotFingerprint, createProgramId, endCompilerSession, getCompilerSession, reconstruct, setCaptureImpl };
288
+ export { AOT_ABI, AOT_MANIFEST_FORMAT, AotFingerprint, Capture, CaptureImpl, CapturedHandler, CapturedMirror, CapturedValidator, CheckBuildResult, Compiled, CompiledProgramRegistration, CompiledSnapshot, CompilerSession, CompilerState, EMPTY_EXTERNALS, FrozenBothFactory, FrozenCheckFactory, FrozenHandler, FrozenMirror, FrozenMirrorFactory, FrozenValidator, HandlerManifest, ProgramId, ReconstructImpl, StringCodecNode, ValidatorManifest, ValidatorSlot, aotActivationError, beginCompilerSession, captureImpl, createAotFingerprint, endCompilerSession, getCompilerSession, reconstruct, setCaptureImpl };
@@ -6,11 +6,8 @@ const require_package = require('../package.js');
6
6
  //#region src/compile/aot.ts
7
7
  const AOT_MANIFEST_FORMAT = 3;
8
8
  const AOT_ABI = `${require_package.version}:${3}`;
9
- const createProgramId = () => ({});
10
9
  const sharedAotFingerprint = Object.freeze({ abi: AOT_ABI });
11
- function createAotFingerprint() {
12
- return sharedAotFingerprint;
13
- }
10
+ const createAotFingerprint = () => sharedAotFingerprint;
14
11
  let activeSession;
15
12
  const newCompilerSession = () => ({ sucroseCache: /* @__PURE__ */ new Map() });
16
13
  function beginCompilerSession(app) {
@@ -41,7 +38,12 @@ function reconstruct() {
41
38
  return reconstructImpl;
42
39
  }
43
40
  let registered;
44
- let claimed = false;
41
+ /**
42
+ * App that consumed this process's manifest, held weakly: it only has to
43
+ * recognise a re-claim by the *same* app, and `assertUncontested` always
44
+ * compares against a live id, so a cleared ref answers as a strong one would
45
+ */
46
+ let claimed;
45
47
  let programs = /* @__PURE__ */ new WeakMap();
46
48
  const programFor = (id) => id ? programs.get(id) : void 0;
47
49
  const fingerprintMismatch = (manifest, actual) => {
@@ -54,14 +56,14 @@ const fingerprintMismatch = (manifest, actual) => {
54
56
  var Compiled = class {
55
57
  static register(manifest) {
56
58
  registered = manifest;
57
- claimed = false;
59
+ claimed = void 0;
58
60
  }
59
61
  static claim(id, fingerprint) {
60
62
  if (!registered || claimed) return false;
61
63
  const manifest = registered;
62
64
  const differences = fingerprintMismatch(manifest, fingerprint);
63
65
  if (differences.length) throw new Error(`[elysia-aot] Registered manifest fingerprint mismatch: ${differences.join("; ")}.`);
64
- claimed = true;
66
+ claimed = new WeakRef(id);
65
67
  registered = void 0;
66
68
  programs.set(id, {
67
69
  ...manifest,
@@ -69,6 +71,10 @@ var Compiled = class {
69
71
  });
70
72
  return true;
71
73
  }
74
+ static assertUncontested(id) {
75
+ if (claimed === void 0 || claimed.deref() === id || isAotBuildEnv()) return;
76
+ throw new Error("[elysia-aot] A second Elysia app built a router in this process. An AOT build seals one app per process.");
77
+ }
72
78
  static get reconstruct() {
73
79
  return reconstructImpl;
74
80
  }
@@ -78,6 +84,9 @@ var Compiled = class {
78
84
  static getHandler(id, method, path) {
79
85
  return programFor(id)?.handlers?.[method]?.[path];
80
86
  }
87
+ static hasProgram(id) {
88
+ return programFor(id) !== void 0;
89
+ }
81
90
  static getValidator(method, path, slot, id) {
82
91
  const program = programFor(id);
83
92
  if (!program) return void 0;
@@ -116,7 +125,7 @@ var Compiled = class {
116
125
  /** @internal test isolation */
117
126
  static clear() {
118
127
  registered = void 0;
119
- claimed = false;
128
+ claimed = void 0;
120
129
  programs = /* @__PURE__ */ new WeakMap();
121
130
  }
122
131
  };
@@ -213,7 +222,6 @@ Object.defineProperty(exports, 'captureImpl', {
213
222
  }
214
223
  });
215
224
  exports.createAotFingerprint = createAotFingerprint;
216
- exports.createProgramId = createProgramId;
217
225
  exports.endCompilerSession = endCompilerSession;
218
226
  exports.getCompilerSession = getCompilerSession;
219
227
  exports.reconstruct = reconstruct;