elysia 2.0.0-beta.3 → 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 (69) 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 +48 -27
  5. package/dist/adapter/utils.mjs +48 -27
  6. package/dist/base.d.ts +16 -5
  7. package/dist/base.js +171 -74
  8. package/dist/base.mjs +172 -75
  9. package/dist/compile/aot-capture.js +1 -1
  10. package/dist/compile/aot-capture.mjs +1 -1
  11. package/dist/compile/aot.d.ts +1 -0
  12. package/dist/compile/aot.js +3 -0
  13. package/dist/compile/aot.mjs +3 -0
  14. package/dist/compile/handler/frozen-validator.d.ts +4 -1
  15. package/dist/compile/handler/frozen-validator.js +8 -3
  16. package/dist/compile/handler/frozen-validator.mjs +5 -3
  17. package/dist/compile/handler/index.d.ts +1 -1
  18. package/dist/compile/handler/index.js +3 -3
  19. package/dist/compile/handler/index.mjs +3 -3
  20. package/dist/compile/handler/reconstruct.d.ts +1 -1
  21. package/dist/compile/handler/reconstruct.js +4 -3
  22. package/dist/compile/handler/reconstruct.mjs +4 -3
  23. package/dist/compile/jit-probe.d.ts +2 -2
  24. package/dist/context.d.ts +1 -1
  25. package/dist/context.js +5 -1
  26. package/dist/context.mjs +5 -1
  27. package/dist/package.js +1 -1
  28. package/dist/package.mjs +1 -1
  29. package/dist/plugin/aot/core.d.ts +11 -5
  30. package/dist/plugin/aot/core.js +86 -38
  31. package/dist/plugin/aot/core.mjs +87 -41
  32. package/dist/plugin/aot/esbuild.js +8 -2
  33. package/dist/plugin/aot/esbuild.mjs +9 -3
  34. package/dist/plugin/aot/hooks.d.ts +2 -1
  35. package/dist/plugin/aot/hooks.js +3 -3
  36. package/dist/plugin/aot/hooks.mjs +4 -4
  37. package/dist/plugin/aot/source.d.ts +4 -1
  38. package/dist/plugin/aot/source.js +21 -14
  39. package/dist/plugin/aot/source.mjs +21 -14
  40. package/dist/plugin/aot/worker.js +2 -2
  41. package/dist/plugin/aot/worker.mjs +2 -2
  42. package/dist/route-table.d.ts +3 -0
  43. package/dist/route-table.js +4 -1
  44. package/dist/route-table.mjs +4 -1
  45. package/dist/schema-snapshot.js +17 -22
  46. package/dist/schema-snapshot.mjs +17 -22
  47. package/dist/type/bridge-live.js +1 -1
  48. package/dist/type/bridge-live.mjs +1 -1
  49. package/dist/type/coerce.d.ts +1 -1
  50. package/dist/type/constants.d.ts +1 -1
  51. package/dist/type/typebox-value-live.d.ts +2 -2
  52. package/dist/type/typebox-value-live.js +2 -2
  53. package/dist/type/typebox-value-live.mjs +2 -2
  54. package/dist/type/typebox-value.d.ts +6 -6
  55. package/dist/type/validator/index.d.ts +1 -1
  56. package/dist/type/validator/index.js +10 -7
  57. package/dist/type/validator/index.mjs +10 -7
  58. package/dist/type/validator/validator-cache.d.ts +1 -1
  59. package/dist/type/validator/validator-cache.js +17 -9
  60. package/dist/type/validator/validator-cache.mjs +17 -9
  61. package/dist/utils.d.ts +10 -1
  62. package/dist/utils.js +8 -1
  63. package/dist/utils.mjs +6 -1
  64. package/dist/ws/context.d.ts +4 -1
  65. package/dist/ws/context.js +21 -3
  66. package/dist/ws/context.mjs +20 -3
  67. package/dist/ws/route.js +57 -12
  68. package/dist/ws/route.mjs +59 -14
  69. package/package.json +2 -2
package/dist/base.mjs CHANGED
@@ -1,5 +1,5 @@
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
5
  import { Capture, Compiled, beginCompilerSession, createAotFingerprint, endCompilerSession } from "./compile/aot.mjs";
@@ -7,6 +7,7 @@ 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,6 +16,23 @@ 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);
19
37
  /**
20
38
  * Every key a single route must answer to, in registration order.
@@ -752,27 +770,12 @@ var Elysia = class Elysia {
752
770
  }
753
771
  if (error) if (ext.error) for (const [code, handler] of error) ext.error.set(code, handler);
754
772
  else ext.error = new Map(error);
755
- if (hoc) if (ext.hoc) {
756
- const seen = new Set(ext.hoc);
757
- for (const fn of hoc) if (!seen.has(fn)) {
758
- seen.add(fn);
759
- ext.hoc.push(fn);
760
- }
761
- } else ext.hoc = hoc.slice();
762
- if (setup) if (ext.setup) {
763
- const seen = new Set(ext.setup);
764
- for (const fn of setup) if (!seen.has(fn)) {
765
- seen.add(fn);
766
- ext.setup.push(fn);
767
- }
768
- } else ext.setup = setup.slice();
769
- if (cleanup) if (ext.cleanup) {
770
- const seen = new Set(ext.cleanup);
771
- for (const fn of cleanup) if (!seen.has(fn)) {
772
- seen.add(fn);
773
- ext.cleanup.push(fn);
774
- }
775
- } 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();
776
779
  if (capability) {
777
780
  const target = ext.capability ??= nullObject();
778
781
  if (capability.trace) {
@@ -951,11 +954,11 @@ var Elysia = class Elysia {
951
954
  get modules() {
952
955
  const ready = this.#ready;
953
956
  if (!ready) {
954
- if (this.#error !== void 0) return Promise.reject(this.#error);
957
+ if (this.#error !== void 0) return Promise.reject(this.#error.error);
955
958
  return Promise.resolve();
956
959
  }
957
960
  return ready.then(() => {
958
- if (this.#error !== void 0) throw this.#error;
961
+ if (this.#error !== void 0) throw this.#error.error;
959
962
  if (this.#ready && this.#ready !== ready) return this.modules;
960
963
  });
961
964
  }
@@ -970,13 +973,13 @@ var Elysia = class Elysia {
970
973
  if (plugin) try {
971
974
  this.use(plugin);
972
975
  } catch (err) {
973
- this.#error ??= err;
976
+ this.#error ??= { error: err };
974
977
  console.error(err);
975
978
  }
976
979
  }).finally(() => {
977
980
  this.#pending--;
978
981
  }).then(() => {}, (err) => {
979
- this.#error ??= err;
982
+ this.#error ??= { error: err };
980
983
  console.error(err);
981
984
  }).finally(() => this.#tryDrain(next));
982
985
  this.#ready = next;
@@ -985,10 +988,20 @@ var Elysia = class Elysia {
985
988
  #tryDrain(sentinel) {
986
989
  if (this.#pending > 0) return;
987
990
  if (this.#ready !== sentinel) return;
991
+ const previousCompiled = this.#compiled;
992
+ const previousJitColdRemaining = this.#jitColdRemaining;
988
993
  this.#ready = void 0;
994
+ this.#compiled = void 0;
989
995
  this.#fetchFn = void 0;
990
996
  this.#routerBuilt = false;
991
- 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
+ }
992
1005
  }
993
1006
  #add(method, path, hookOrFn, fn, hasHook = fn !== void 0) {
994
1007
  if (this["~Prefix"]) path = joinPath(this["~Prefix"], path);
@@ -1208,20 +1221,22 @@ var Elysia = class Elysia {
1208
1221
  }
1209
1222
  handler(index, immediate, route, precomputedStatic, aliases, table) {
1210
1223
  if (this.#compiled?.[index]) return this.#compiled[index];
1211
- const compiled = this.#compiled ??= new Array(table?.length ?? this["~routes"].length);
1224
+ const indexedTable = table ?? (this.#routerBuilt ? this["~routeTable"] : void 0);
1225
+ const compiled = this.#compiled ??= new Array(indexedTable?.length ?? this["~routes"].length);
1212
1226
  if (immediate ?? this["~config"]?.precompile) {
1213
- const row = route ?? (table ? routeRow(table, index) : this["~routes"][index]);
1227
+ const row = route ?? (indexedTable ? routeRow(indexedTable, index) : this["~routes"][index]);
1228
+ const exactDuplicate = ((indexedTable?.flags[index] ?? 0) & RouteFlag.ExactDuplicate) !== 0;
1214
1229
  let handler;
1215
1230
  try {
1216
- handler = compileHandler(row, this, precomputedStatic);
1231
+ handler = compileHandler(row, this, precomputedStatic, exactDuplicate && Compiled.hasProgram(this["~programId"]));
1217
1232
  } catch (error) {
1218
1233
  throw new Error(`[Elysia] Failed to compile route ${row[0]} ${row[1]}: ${error?.message ?? error}`, { cause: error });
1219
1234
  }
1220
1235
  compiled[index] = handler;
1221
- this.#saveHandler(row[0], row[1], handler);
1236
+ if (indexedTable) this.#satisfyJit(indexedTable, index);
1222
1237
  return handler;
1223
1238
  }
1224
- 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);
1225
1240
  }
1226
1241
  #jitHandler(index, route, precomputedStatic, aliases, table) {
1227
1242
  if (table !== void 0) this.#jitTable = table;
@@ -1230,12 +1245,58 @@ var Elysia = class Elysia {
1230
1245
  if (aliases !== void 0) (this.#jitAliases ??= [])[index] = aliases;
1231
1246
  return (context) => this.#jitDispatch(index, context);
1232
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
+ }
1233
1291
  #jitDispatch(index, context) {
1234
1292
  if (this.#compiled[index]) return this.#compiled[index](context);
1235
- 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;
1236
1297
  let handler;
1237
1298
  try {
1238
- handler = compileHandler(materialized, this, this.#jitStatic?.[index]);
1299
+ handler = compileHandler(materialized, this, this.#jitStatic?.[index], exactDuplicate && Compiled.hasProgram(this["~programId"]));
1239
1300
  } catch (error) {
1240
1301
  const routeError = new Error(`[Elysia] Failed to compile route ${materialized[0]} ${materialized[1]}: ${error?.message ?? error}`, { cause: error });
1241
1302
  const finalize = this["~finalizeError"];
@@ -1243,24 +1304,15 @@ var Elysia = class Elysia {
1243
1304
  throw routeError;
1244
1305
  }
1245
1306
  this.#compiled[index] = handler;
1246
- let releasedNow = false;
1247
- if (this.#jitColdRemaining !== void 0 && --this.#jitColdRemaining === 0) {
1248
- Compiled.release(this["~programId"]);
1249
- this.#jitColdRemaining = void 0;
1250
- 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);
1251
1314
  }
1252
- const aliases = this.#jitAliases?.[index];
1253
- if (aliases) {
1254
- this.#initMap();
1255
- const map = this["~map"][aliases.method] ??= nullObject();
1256
- for (let p = 0; p < aliases.paths.length; p++) map[aliases.paths[p]] = handler;
1257
- } else this.#saveHandler(materialized[0], materialized[1], handler);
1258
- if (releasedNow) {
1259
- this.#jitRoute = void 0;
1260
- this.#jitStatic = void 0;
1261
- this.#jitAliases = void 0;
1262
- this.#jitTable = void 0;
1263
- } else {
1315
+ if (!(table ? this.#satisfyJit(table, index) : false)) {
1264
1316
  if (this.#jitRoute) this.#jitRoute[index] = void 0;
1265
1317
  if (this.#jitStatic) this.#jitStatic[index] = void 0;
1266
1318
  if (this.#jitAliases) this.#jitAliases[index] = void 0;
@@ -1401,19 +1453,30 @@ var Elysia = class Elysia {
1401
1453
  "~wsConfig": this["~wsConfig"]
1402
1454
  };
1403
1455
  if (isProduction() && !Capture.isAotBuildEnv()) {
1404
- if (this["~config"]?.precompile) Compiled.release(this["~programId"]);
1456
+ if (this["~config"]?.precompile) this.#releaseJit();
1405
1457
  else {
1406
1458
  const table = this["~routeTable"];
1407
- const routeCount = table?.length ?? this["~routes"].length;
1408
- let cold = routeCount;
1459
+ const routeCount = table.length;
1409
1460
  const compiled = this.#compiled;
1410
- for (let i = 0; i < routeCount; i++) if (table !== void 0 && (table.flags[i] & RouteFlag.WS) !== 0 || compiled?.[i] !== void 0) cold--;
1411
- 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();
1412
1471
  else this.#jitColdRemaining = cold;
1413
1472
  }
1414
1473
  clearAuthoringAnalysisCaches(this);
1415
1474
  if (!this["~ext"]?.macro && !this["~scopeChildren"]) this.#declaredRoutes = void 0;
1416
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);
1417
1480
  }
1418
1481
  ["~newGeneration"]() {
1419
1482
  this.#fetchFn = void 0;
@@ -1437,23 +1500,31 @@ var Elysia = class Elysia {
1437
1500
  const appChain = this["~hookChain"];
1438
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]);
1439
1502
  else for (let i = 0; i < length; i++) if (this.#routeMayHaveModelRef(table, i)) this.#assertRouteModelRefs(routeRow(table, i), method[i]);
1503
+ let markDuplicates = false;
1440
1504
  if (length) {
1441
1505
  const programId = this["~programId"];
1442
1506
  if (!Compiled.claim(programId, this["~aotFingerprint"] = createAotFingerprint())) Compiled.assertUncontested(programId);
1507
+ markDuplicates = Compiled.hasProgram(programId) || precompile !== true && isProduction() && !Capture.isAotBuildEnv();
1443
1508
  }
1444
1509
  const isLoose = this["~config"]?.strictPath !== true;
1510
+ const collectExplicit = isLoose && table.hasLoose;
1445
1511
  let explicitPaths;
1446
- if (isLoose && table.hasLoose) {
1447
- explicitPaths = /* @__PURE__ */ new Map();
1512
+ if (markDuplicates || collectExplicit) {
1513
+ const seen = /* @__PURE__ */ new Map();
1514
+ if (collectExplicit) explicitPaths = seen;
1448
1515
  for (let i = 0; i < length; i++) {
1449
1516
  const m = method[i];
1450
1517
  const p = path[i];
1451
- let set = explicitPaths.get(m);
1452
- if (!set) explicitPaths.set(m, set = /* @__PURE__ */ new Set());
1453
- set.add(p);
1454
- 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) {
1455
1526
  const encoded = encodeURI(p);
1456
- if (encoded !== p) set.add(encoded);
1527
+ if (encoded !== p && !paths.has(encoded)) paths.set(encoded, -1);
1457
1528
  }
1458
1529
  }
1459
1530
  }
@@ -1560,6 +1631,7 @@ var Elysia = class Elysia {
1560
1631
  * ```
1561
1632
  */
1562
1633
  cleanup(handler) {
1634
+ if (this["~ext"]?.cleanupEpoch?.(handler)) return this;
1563
1635
  this.#assertMutable("cleanup");
1564
1636
  const arr = this.#ext.cleanup ??= [];
1565
1637
  if (Array.isArray(handler)) arr.push(...handler);
@@ -1567,24 +1639,49 @@ var Elysia = class Elysia {
1567
1639
  return this;
1568
1640
  }
1569
1641
  /**
1570
- * Stop the underlying server (if any), running every `cleanup` handler once
1571
- * 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()`.
1572
1652
  *
1573
- * @param closeActiveConnections Pass `true` to terminate in-flight
1574
- * requests and WebSocket connections immediately. Defaults to
1575
- * draining gracefully.
1653
+ * @param closeActiveConnections Pass `true` to terminate active
1654
+ * transports. Omit (or pass `false`) to drain safely.
1576
1655
  */
1577
1656
  stop(closeActiveConnections) {
1657
+ const stop = this["~ext"]?.stop;
1658
+ if (stop) return stop(closeActiveConnections);
1578
1659
  const server = this.server;
1579
1660
  if (!server) return;
1580
- const r = server.stop?.(closeActiveConnections);
1581
- 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
+ }
1582
1670
  const handlers = this["~ext"]?.cleanup;
1583
- const fire = handlers ? async () => {
1584
- for (let i = 0; i < handlers.length; i++) await handlers[i](this);
1585
- } : void 0;
1586
- if (r && typeof r.then === "function") return fire ? r.then(fire) : r;
1587
- 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
+ })();
1588
1685
  }
1589
1686
  };
1590
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) {
@@ -134,6 +134,7 @@ declare abstract class Compiled {
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;
@@ -84,6 +84,9 @@ var Compiled = class {
84
84
  static getHandler(id, method, path) {
85
85
  return programFor(id)?.handlers?.[method]?.[path];
86
86
  }
87
+ static hasProgram(id) {
88
+ return programFor(id) !== void 0;
89
+ }
87
90
  static getValidator(method, path, slot, id) {
88
91
  const program = programFor(id);
89
92
  if (!program) return void 0;
@@ -83,6 +83,9 @@ var Compiled = class {
83
83
  static getHandler(id, method, path) {
84
84
  return programFor(id)?.handlers?.[method]?.[path];
85
85
  }
86
+ static hasProgram(id) {
87
+ return programFor(id) !== void 0;
88
+ }
86
89
  static getValidator(method, path, slot, id) {
87
90
  const program = programFor(id);
88
91
  if (!program) return void 0;
@@ -32,9 +32,12 @@ interface FrozenRouteValidatorShape {
32
32
  cookie?: FrozenSlotValidator;
33
33
  response?: Record<number, FrozenSlotValidator>;
34
34
  }
35
+ declare const REQUEST_SLOTS: readonly ["body", "headers", "query", "params", "cookie"];
35
36
  declare const isStandardSchema: (schema: unknown) => schema is object & Record<"~standard", unknown>;
36
37
  declare function mergeSchemasAllStandard(schemas: Array<Record<string, unknown>> | undefined): boolean;
38
+ declare function resolveModelRef(schema: unknown, root: AnyElysia): unknown;
37
39
  declare function buildFrozenRouteValidator(hook: AnyLocalHook, root: AnyElysia, method: HTTPMethod, path: string): FrozenRouteValidatorShape | undefined;
40
+ declare const isResponseMap: (schema: unknown) => schema is Record<string, unknown>;
38
41
  declare function isCapturedBridgeFree(c: CapturedValidator, schema: unknown, coerced?: unknown): boolean;
39
42
  //#endregion
40
- export { buildFrozenRouteValidator, isBridgeNotInitialized, isCapturedBridgeFree, isCompactDiagnosable, isCompactWalkable, isStandardSchema, mergeSchemasAllStandard };
43
+ export { REQUEST_SLOTS, buildFrozenRouteValidator, isBridgeNotInitialized, isCapturedBridgeFree, isCompactDiagnosable, isCompactWalkable, isResponseMap, isStandardSchema, mergeSchemasAllStandard, resolveModelRef };
@@ -294,7 +294,9 @@ function buildFrozenRouteValidator(hook, root, method, path) {
294
294
  }
295
295
  const response = hook?.response;
296
296
  if (response) {
297
- const statuses = isResponseMap(response) ? response : { 200: response };
297
+ const resolved = resolveModelRef(response, root);
298
+ if (!resolved) return void 0;
299
+ const statuses = isResponseMap(resolved) ? resolved : { 200: resolved };
298
300
  const responseOut = {};
299
301
  for (const status in statuses) {
300
302
  const raw = statuses[status];
@@ -313,7 +315,7 @@ function buildFrozenRouteValidator(hook, root, method, path) {
313
315
  }
314
316
  return out;
315
317
  }
316
- const isResponseMap = (schema) => !("~kind" in schema || "~elyAcl" in schema || "~standard" in schema);
318
+ const isResponseMap = (schema) => schema !== null && typeof schema === "object" && !("~kind" in schema || "~elyAcl" in schema || "~standard" in schema);
317
319
  const capturedBothMarker = (() => ({}));
318
320
  function isCapturedBridgeFree(c, schema, coerced = schema) {
319
321
  if (typeof schema === "string") return false;
@@ -340,10 +342,13 @@ function isCapturedBridgeFree(c, schema, coerced = schema) {
340
342
  }
341
343
 
342
344
  //#endregion
345
+ exports.REQUEST_SLOTS = REQUEST_SLOTS;
343
346
  exports.buildFrozenRouteValidator = buildFrozenRouteValidator;
344
347
  exports.isBridgeNotInitialized = isBridgeNotInitialized;
345
348
  exports.isCapturedBridgeFree = isCapturedBridgeFree;
346
349
  exports.isCompactDiagnosable = isCompactDiagnosable;
347
350
  exports.isCompactWalkable = isCompactWalkable;
351
+ exports.isResponseMap = isResponseMap;
348
352
  exports.isStandardSchema = isStandardSchema;
349
- exports.mergeSchemasAllStandard = mergeSchemasAllStandard;
353
+ exports.mergeSchemasAllStandard = mergeSchemasAllStandard;
354
+ exports.resolveModelRef = resolveModelRef;
@@ -293,7 +293,9 @@ function buildFrozenRouteValidator(hook, root, method, path) {
293
293
  }
294
294
  const response = hook?.response;
295
295
  if (response) {
296
- const statuses = isResponseMap(response) ? response : { 200: response };
296
+ const resolved = resolveModelRef(response, root);
297
+ if (!resolved) return void 0;
298
+ const statuses = isResponseMap(resolved) ? resolved : { 200: resolved };
297
299
  const responseOut = {};
298
300
  for (const status in statuses) {
299
301
  const raw = statuses[status];
@@ -312,7 +314,7 @@ function buildFrozenRouteValidator(hook, root, method, path) {
312
314
  }
313
315
  return out;
314
316
  }
315
- const isResponseMap = (schema) => !("~kind" in schema || "~elyAcl" in schema || "~standard" in schema);
317
+ const isResponseMap = (schema) => schema !== null && typeof schema === "object" && !("~kind" in schema || "~elyAcl" in schema || "~standard" in schema);
316
318
  const capturedBothMarker = (() => ({}));
317
319
  function isCapturedBridgeFree(c, schema, coerced = schema) {
318
320
  if (typeof schema === "string") return false;
@@ -339,4 +341,4 @@ function isCapturedBridgeFree(c, schema, coerced = schema) {
339
341
  }
340
342
 
341
343
  //#endregion
342
- export { buildFrozenRouteValidator, isBridgeNotInitialized, isCapturedBridgeFree, isCompactDiagnosable, isCompactWalkable, isStandardSchema, mergeSchemasAllStandard };
344
+ export { REQUEST_SLOTS, buildFrozenRouteValidator, isBridgeNotInitialized, isCapturedBridgeFree, isCompactDiagnosable, isCompactWalkable, isResponseMap, isStandardSchema, mergeSchemasAllStandard, resolveModelRef };
@@ -14,6 +14,6 @@ declare function clearHandlerAnalysisCaches(root: AnyElysia): void;
14
14
  declare const localMacroRoot: (instance: AnyElysia, root: AnyElysia) => AnyElysia;
15
15
  declare function buildNativeStaticResponse(route: InternalRoute, root: AnyElysia): Response | undefined;
16
16
  declare function composeRouteHook(instance: AnyElysia, localHook: Partial<AnyLocalHook> | undefined, appHook: ChainNode | undefined, inheritedChain: ChainNode | undefined, root: AnyElysia, macroScope?: AnyElysia): AnyLocalHook | undefined;
17
- declare function compileHandler(route: InternalRoute, root: AnyElysia, precomputedStatic?: Response): CompiledHandler;
17
+ declare function compileHandler(route: InternalRoute, root: AnyElysia, precomputedStatic?: Response, liveOnly?: boolean): CompiledHandler;
18
18
  //#endregion
19
19
  export { buildNativeStaticResponse, clearHandlerAnalysisCaches, compileHandler, composeRouteHook, localMacroRoot, resolveLocalHook, resolveWSLocalHook, setCaptureHeaderShorthand };
@@ -274,14 +274,14 @@ function isContextFreeHandler(handler) {
274
274
  const source = Function.prototype.toString.call(handler);
275
275
  return isBareArrow.test(source) && !isSucroseOpaque.test(source);
276
276
  }
277
- function compileHandler(route, root, precomputedStatic) {
277
+ function compileHandler(route, root, precomputedStatic, liveOnly = false) {
278
278
  let [_method, path, handler, instance, localHook, appHook, inheritedChain, macroScope] = route;
279
279
  const frozenRoot = require_generation.frozenRootOf(root);
280
280
  const adapter = frozenRoot["~config"]?.adapter ?? require_adapter_constants.defaultAdapter;
281
281
  const method = _method;
282
282
  const mountMeta = typeof handler === "function" ? handler["~mount"] : void 0;
283
283
  if (mountMeta) handler = resolveMountHandler(mountMeta, path);
284
- const reconstructed = require_compile_aot.Compiled.getHandler(frozenRoot["~programId"], method, path);
284
+ const reconstructed = liveOnly ? void 0 : require_compile_aot.Compiled.getHandler(frozenRoot["~programId"], method, path);
285
285
  if (reconstructed && !precomputedStatic && typeof handler === "function" && !frozenRoot["~ext"]?.macro && !require_generation.frozenRootOf(localMacroRoot(macroScope ?? instance, root))["~ext"]?.macro && !reconstructNeedsHookState(reconstructed.a)) return reconstructed.f(handler, ...require_compile_handler_params.resolveHandlerParams(reconstructed.a, {
286
286
  root,
287
287
  parse: adapter.parse,
@@ -302,7 +302,7 @@ function compileHandler(route, root, precomputedStatic) {
302
302
  toArray("afterResponse", hook);
303
303
  toArray("error", hook);
304
304
  }
305
- const buildValidator = () => hook ? require_compile_handler_reconstruct.Reconstrct.validator(hook, root, method, path) : void 0;
305
+ const buildValidator = () => hook ? require_compile_handler_reconstruct.Reconstrct.validator(hook, root, method, path, liveOnly) : void 0;
306
306
  if (handler instanceof Error) {
307
307
  const error = handler;
308
308
  handler = () => {
@@ -273,14 +273,14 @@ function isContextFreeHandler(handler) {
273
273
  const source = Function.prototype.toString.call(handler);
274
274
  return isBareArrow.test(source) && !isSucroseOpaque.test(source);
275
275
  }
276
- function compileHandler(route, root, precomputedStatic) {
276
+ function compileHandler(route, root, precomputedStatic, liveOnly = false) {
277
277
  let [_method, path, handler, instance, localHook, appHook, inheritedChain, macroScope] = route;
278
278
  const frozenRoot = frozenRootOf(root);
279
279
  const adapter = frozenRoot["~config"]?.adapter ?? defaultAdapter;
280
280
  const method = _method;
281
281
  const mountMeta = typeof handler === "function" ? handler["~mount"] : void 0;
282
282
  if (mountMeta) handler = resolveMountHandler(mountMeta, path);
283
- const reconstructed = Compiled.getHandler(frozenRoot["~programId"], method, path);
283
+ const reconstructed = liveOnly ? void 0 : Compiled.getHandler(frozenRoot["~programId"], method, path);
284
284
  if (reconstructed && !precomputedStatic && typeof handler === "function" && !frozenRoot["~ext"]?.macro && !frozenRootOf(localMacroRoot(macroScope ?? instance, root))["~ext"]?.macro && !reconstructNeedsHookState(reconstructed.a)) return reconstructed.f(handler, ...resolveHandlerParams(reconstructed.a, {
285
285
  root,
286
286
  parse: adapter.parse,
@@ -301,7 +301,7 @@ function compileHandler(route, root, precomputedStatic) {
301
301
  toArray("afterResponse", hook);
302
302
  toArray("error", hook);
303
303
  }
304
- const buildValidator = () => hook ? Reconstrct.validator(hook, root, method, path) : void 0;
304
+ const buildValidator = () => hook ? Reconstrct.validator(hook, root, method, path, liveOnly) : void 0;
305
305
  if (handler instanceof Error) {
306
306
  const error = handler;
307
307
  handler = () => {
@@ -4,7 +4,7 @@ import { AnyElysia } from "../../base.js";
4
4
 
5
5
  //#region src/compile/handler/reconstruct.d.ts
6
6
  declare abstract class Reconstrct {
7
- static validator(hook: AnyLocalHook, root: AnyElysia, method: HTTPMethod, path: string): any;
7
+ static validator(hook: AnyLocalHook, root: AnyElysia, method: HTTPMethod, path: string, liveOnly?: boolean): any;
8
8
  static cookie(hook: AnyLocalHook, root: AnyElysia): CompiledCookieConfig;
9
9
  static trace(hook: AnyLocalHook, root: AnyElysia): any;
10
10
  }
@@ -7,9 +7,9 @@ const require_compile_handler_frozen_validator = require('./frozen-validator.js'
7
7
 
8
8
  //#region src/compile/handler/reconstruct.ts
9
9
  var Reconstrct = class {
10
- static validator(hook, root, method, path) {
10
+ static validator(hook, root, method, path, liveOnly = false) {
11
11
  const frozenRoot = require_generation.frozenRootOf(root);
12
- if (!require_type_bridge.isBridgeLive()) {
12
+ if (!liveOnly && !require_type_bridge.isBridgeLive()) {
13
13
  const frozen = require_compile_handler_frozen_validator.buildFrozenRouteValidator(hook, root, method, path);
14
14
  if (frozen) return frozen;
15
15
  }
@@ -20,7 +20,7 @@ var Reconstrct = class {
20
20
  normalize: frozenRoot["~config"]?.normalize,
21
21
  sanitize: frozenRoot["~config"]?.sanitize,
22
22
  schemas: hook?.schemas,
23
- aot: {
23
+ aot: liveOnly ? void 0 : {
24
24
  method,
25
25
  path
26
26
  },
@@ -28,6 +28,7 @@ var Reconstrct = class {
28
28
  });
29
29
  } catch (error) {
30
30
  if (!require_compile_handler_frozen_validator.isBridgeNotInitialized(error)) throw error;
31
+ if (liveOnly) throw new Error("Duplicate route must compile JIT but the TypeBox bridge is not initialized", { cause: error });
31
32
  const frozen = require_compile_handler_frozen_validator.buildFrozenRouteValidator(hook, root, method, path);
32
33
  if (frozen) return frozen;
33
34
  throw error;
@@ -6,9 +6,9 @@ import { buildFrozenRouteValidator, isBridgeNotInitialized } from "./frozen-vali
6
6
 
7
7
  //#region src/compile/handler/reconstruct.ts
8
8
  var Reconstrct = class {
9
- static validator(hook, root, method, path) {
9
+ static validator(hook, root, method, path, liveOnly = false) {
10
10
  const frozenRoot = frozenRootOf(root);
11
- if (!isBridgeLive()) {
11
+ if (!liveOnly && !isBridgeLive()) {
12
12
  const frozen = buildFrozenRouteValidator(hook, root, method, path);
13
13
  if (frozen) return frozen;
14
14
  }
@@ -19,7 +19,7 @@ var Reconstrct = class {
19
19
  normalize: frozenRoot["~config"]?.normalize,
20
20
  sanitize: frozenRoot["~config"]?.sanitize,
21
21
  schemas: hook?.schemas,
22
- aot: {
22
+ aot: liveOnly ? void 0 : {
23
23
  method,
24
24
  path
25
25
  },
@@ -27,6 +27,7 @@ var Reconstrct = class {
27
27
  });
28
28
  } catch (error) {
29
29
  if (!isBridgeNotInitialized(error)) throw error;
30
+ if (liveOnly) throw new Error("Duplicate route must compile JIT but the TypeBox bridge is not initialized", { cause: error });
30
31
  const frozen = buildFrozenRouteValidator(hook, root, method, path);
31
32
  if (frozen) return frozen;
32
33
  throw error;