elysia 2.0.0-exp.28 → 2.0.0-exp.29

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 (47) hide show
  1. package/dist/base.js +66 -4
  2. package/dist/base.mjs +66 -4
  3. package/dist/compile/aot.d.ts +17 -3
  4. package/dist/compile/aot.js +51 -0
  5. package/dist/compile/aot.mjs +49 -1
  6. package/dist/compile/handler/frozen-validator.d.ts +2 -12
  7. package/dist/compile/handler/frozen-validator.js +27 -39
  8. package/dist/compile/handler/frozen-validator.mjs +28 -40
  9. package/dist/compile/handler/utils.js +17 -59
  10. package/dist/compile/handler/utils.mjs +1 -43
  11. package/dist/compile/lexer.d.ts +7 -0
  12. package/dist/compile/lexer.js +53 -0
  13. package/dist/compile/lexer.mjs +48 -0
  14. package/dist/error.js +2 -2
  15. package/dist/error.mjs +2 -2
  16. package/dist/trace.js +3 -3
  17. package/dist/trace.mjs +1 -1
  18. package/dist/type/coerce-plan.d.ts +33 -0
  19. package/dist/type/coerce-plan.js +92 -0
  20. package/dist/type/coerce-plan.mjs +86 -0
  21. package/dist/type/coerce.d.ts +4 -18
  22. package/dist/type/coerce.js +8 -51
  23. package/dist/type/coerce.mjs +5 -48
  24. package/dist/type/elysia/array-string.js +1 -1
  25. package/dist/type/elysia/array-string.mjs +1 -1
  26. package/dist/type/elysia/files.js +1 -1
  27. package/dist/type/elysia/files.mjs +1 -1
  28. package/dist/type/elysia/integer-string.js +1 -1
  29. package/dist/type/elysia/integer-string.mjs +1 -1
  30. package/dist/type/elysia/numeric-enum.js +1 -1
  31. package/dist/type/elysia/numeric-enum.mjs +1 -1
  32. package/dist/type/elysia/numeric.js +1 -1
  33. package/dist/type/elysia/numeric.mjs +1 -1
  34. package/dist/type/exports.js +5 -5
  35. package/dist/type/exports.mjs +5 -5
  36. package/dist/type/index.js +5 -5
  37. package/dist/type/index.mjs +5 -5
  38. package/dist/type/validator/default-precompute.js +3 -3
  39. package/dist/type/validator/default-precompute.mjs +3 -3
  40. package/dist/type/validator/index.js +1 -1
  41. package/dist/type/validator/index.mjs +2 -2
  42. package/dist/type/validator/string-codec-aot.d.ts +1 -2
  43. package/dist/type/validator/string-codec-aot.js +2 -43
  44. package/dist/type/validator/string-codec-aot.mjs +1 -42
  45. package/dist/ws/route.js +1 -1
  46. package/dist/ws/route.mjs +1 -1
  47. package/package.json +1 -1
package/dist/base.js CHANGED
@@ -834,7 +834,7 @@ var Elysia = class Elysia {
834
834
  * Registered reusable models (via `.model()`), keyed by name.
835
835
  */
836
836
  get models() {
837
- return this["~ext"]?.models ?? {};
837
+ return this["~ext"]?.models ?? require_utils.nullObject();
838
838
  }
839
839
  Ref(key) {
840
840
  return require_type_bridge.Ref(key);
@@ -944,7 +944,12 @@ var Elysia = class Elysia {
944
944
  if (this.#compiled?.[index]) return this.#compiled[index];
945
945
  const compiled = this.#compiled ??= new Array(this.#history.length);
946
946
  if (immediate) {
947
- const handler = require_compile_handler_index.compileHandler(this.#history[index], this, precomputedStatic);
947
+ let handler;
948
+ try {
949
+ handler = require_compile_handler_index.compileHandler(this.#history[index], this, precomputedStatic);
950
+ } catch (error) {
951
+ throw new Error(`[Elysia] Failed to compile route ${require_utils.mapMethodBack(route[0])} ${route[1]}: ${error?.message ?? error}`, { cause: error });
952
+ }
948
953
  compiled[index] = handler;
949
954
  this.#saveHandler(route[0], route[1], handler);
950
955
  return handler;
@@ -954,7 +959,12 @@ var Elysia = class Elysia {
954
959
  #jitHandler(index, route, precomputedStatic, aliases) {
955
960
  return (context) => {
956
961
  if (this.#compiled[index]) return this.#compiled[index](context);
957
- const handler = require_compile_handler_index.compileHandler(this.#history[index], this, precomputedStatic);
962
+ let handler;
963
+ try {
964
+ handler = require_compile_handler_index.compileHandler(this.#history[index], this, precomputedStatic);
965
+ } catch (error) {
966
+ throw new Error(`[Elysia] Failed to compile route ${require_utils.mapMethodBack(route[0])} ${route[1]}: ${error?.message ?? error}`, { cause: error });
967
+ }
958
968
  this.#compiled[index] = handler;
959
969
  if (aliases) {
960
970
  this.#initMap();
@@ -1000,6 +1010,58 @@ var Elysia = class Elysia {
1000
1010
  return r instanceof Promise ? r.then(Elysia.#toHeadResponse) : Elysia.#toHeadResponse(r);
1001
1011
  });
1002
1012
  }
1013
+ #chainRefMemo;
1014
+ static #slotHasString(h) {
1015
+ if (!h || typeof h !== "object") return false;
1016
+ for (const key of require_utils.schemaProperties) {
1017
+ const v = h[key];
1018
+ if (typeof v === "string") return true;
1019
+ if (key === "response" && v && typeof v === "object") {
1020
+ const record = v;
1021
+ if ("~kind" in record || "~elyAcl" in record || "~standard" in record) continue;
1022
+ for (const status in record) if (typeof record[status] === "string") return true;
1023
+ }
1024
+ }
1025
+ return false;
1026
+ }
1027
+ static #hookHasString(h) {
1028
+ if (Elysia.#slotHasString(h)) return true;
1029
+ const schemas = h?.schemas;
1030
+ if (Array.isArray(schemas)) {
1031
+ for (let s = 0; s < schemas.length; s++) if (Elysia.#slotHasString(schemas[s])) return true;
1032
+ }
1033
+ return false;
1034
+ }
1035
+ #chainHasModelRef(start) {
1036
+ if (!start) return false;
1037
+ const memo = this.#chainRefMemo ??= /* @__PURE__ */ new WeakMap();
1038
+ const cached = memo.get(start);
1039
+ if (cached !== void 0) return cached;
1040
+ let found = false;
1041
+ const stack = [start];
1042
+ while (stack.length) {
1043
+ const node = stack.pop();
1044
+ if (!node) continue;
1045
+ if ("combine" in node) {
1046
+ stack.push(node.combine);
1047
+ stack.push(node.over);
1048
+ } else {
1049
+ if (Elysia.#hookHasString(node.added)) {
1050
+ found = true;
1051
+ break;
1052
+ }
1053
+ stack.push(node.parent);
1054
+ }
1055
+ }
1056
+ memo.set(start, found);
1057
+ return found;
1058
+ }
1059
+ #routeMayHaveModelRef(route) {
1060
+ if (this["~ext"]?.macro || this["~scopeChildren"]) return true;
1061
+ if (require_compile_handler_index.localMacroRoot(route[7] ?? route[3] ?? this, this)["~ext"]?.macro) return true;
1062
+ if (Elysia.#hookHasString(route[4])) return true;
1063
+ return this.#chainHasModelRef(route[5]) || this.#chainHasModelRef(route[6]) || this.#chainHasModelRef(this["~hookChain"]);
1064
+ }
1003
1065
  #assertRouteModelRefs(route, method) {
1004
1066
  const models = this["~ext"]?.models;
1005
1067
  const path = route[1];
@@ -1040,7 +1102,7 @@ var Elysia = class Elysia {
1040
1102
  const route = this.#history[i];
1041
1103
  const m = route[0] === "WS" ? "WS" : require_utils.mapMethodBack(route[0]);
1042
1104
  const key = m + " " + route[1];
1043
- this.#assertRouteModelRefs(route, m);
1105
+ if (this.#routeMayHaveModelRef(route)) this.#assertRouteModelRefs(route, m);
1044
1106
  if (seen.has(key)) {
1045
1107
  hasDuplicate = true;
1046
1108
  if (!require_error.isProduction()) {
package/dist/base.mjs CHANGED
@@ -831,7 +831,7 @@ var Elysia = class Elysia {
831
831
  * Registered reusable models (via `.model()`), keyed by name.
832
832
  */
833
833
  get models() {
834
- return this["~ext"]?.models ?? {};
834
+ return this["~ext"]?.models ?? nullObject();
835
835
  }
836
836
  Ref(key) {
837
837
  return Ref(key);
@@ -941,7 +941,12 @@ var Elysia = class Elysia {
941
941
  if (this.#compiled?.[index]) return this.#compiled[index];
942
942
  const compiled = this.#compiled ??= new Array(this.#history.length);
943
943
  if (immediate) {
944
- const handler = compileHandler(this.#history[index], this, precomputedStatic);
944
+ let handler;
945
+ try {
946
+ handler = compileHandler(this.#history[index], this, precomputedStatic);
947
+ } catch (error) {
948
+ throw new Error(`[Elysia] Failed to compile route ${mapMethodBack(route[0])} ${route[1]}: ${error?.message ?? error}`, { cause: error });
949
+ }
945
950
  compiled[index] = handler;
946
951
  this.#saveHandler(route[0], route[1], handler);
947
952
  return handler;
@@ -951,7 +956,12 @@ var Elysia = class Elysia {
951
956
  #jitHandler(index, route, precomputedStatic, aliases) {
952
957
  return (context) => {
953
958
  if (this.#compiled[index]) return this.#compiled[index](context);
954
- const handler = compileHandler(this.#history[index], this, precomputedStatic);
959
+ let handler;
960
+ try {
961
+ handler = compileHandler(this.#history[index], this, precomputedStatic);
962
+ } catch (error) {
963
+ throw new Error(`[Elysia] Failed to compile route ${mapMethodBack(route[0])} ${route[1]}: ${error?.message ?? error}`, { cause: error });
964
+ }
955
965
  this.#compiled[index] = handler;
956
966
  if (aliases) {
957
967
  this.#initMap();
@@ -997,6 +1007,58 @@ var Elysia = class Elysia {
997
1007
  return r instanceof Promise ? r.then(Elysia.#toHeadResponse) : Elysia.#toHeadResponse(r);
998
1008
  });
999
1009
  }
1010
+ #chainRefMemo;
1011
+ static #slotHasString(h) {
1012
+ if (!h || typeof h !== "object") return false;
1013
+ for (const key of schemaProperties) {
1014
+ const v = h[key];
1015
+ if (typeof v === "string") return true;
1016
+ if (key === "response" && v && typeof v === "object") {
1017
+ const record = v;
1018
+ if ("~kind" in record || "~elyAcl" in record || "~standard" in record) continue;
1019
+ for (const status in record) if (typeof record[status] === "string") return true;
1020
+ }
1021
+ }
1022
+ return false;
1023
+ }
1024
+ static #hookHasString(h) {
1025
+ if (Elysia.#slotHasString(h)) return true;
1026
+ const schemas = h?.schemas;
1027
+ if (Array.isArray(schemas)) {
1028
+ for (let s = 0; s < schemas.length; s++) if (Elysia.#slotHasString(schemas[s])) return true;
1029
+ }
1030
+ return false;
1031
+ }
1032
+ #chainHasModelRef(start) {
1033
+ if (!start) return false;
1034
+ const memo = this.#chainRefMemo ??= /* @__PURE__ */ new WeakMap();
1035
+ const cached = memo.get(start);
1036
+ if (cached !== void 0) return cached;
1037
+ let found = false;
1038
+ const stack = [start];
1039
+ while (stack.length) {
1040
+ const node = stack.pop();
1041
+ if (!node) continue;
1042
+ if ("combine" in node) {
1043
+ stack.push(node.combine);
1044
+ stack.push(node.over);
1045
+ } else {
1046
+ if (Elysia.#hookHasString(node.added)) {
1047
+ found = true;
1048
+ break;
1049
+ }
1050
+ stack.push(node.parent);
1051
+ }
1052
+ }
1053
+ memo.set(start, found);
1054
+ return found;
1055
+ }
1056
+ #routeMayHaveModelRef(route) {
1057
+ if (this["~ext"]?.macro || this["~scopeChildren"]) return true;
1058
+ if (localMacroRoot(route[7] ?? route[3] ?? this, this)["~ext"]?.macro) return true;
1059
+ if (Elysia.#hookHasString(route[4])) return true;
1060
+ return this.#chainHasModelRef(route[5]) || this.#chainHasModelRef(route[6]) || this.#chainHasModelRef(this["~hookChain"]);
1061
+ }
1000
1062
  #assertRouteModelRefs(route, method) {
1001
1063
  const models = this["~ext"]?.models;
1002
1064
  const path = route[1];
@@ -1037,7 +1099,7 @@ var Elysia = class Elysia {
1037
1099
  const route = this.#history[i];
1038
1100
  const m = route[0] === "WS" ? "WS" : mapMethodBack(route[0]);
1039
1101
  const key = m + " " + route[1];
1040
- this.#assertRouteModelRefs(route, m);
1102
+ if (this.#routeMayHaveModelRef(route)) this.#assertRouteModelRefs(route, m);
1041
1103
  if (seen.has(key)) {
1042
1104
  hasDuplicate = true;
1043
1105
  if (!isProduction()) {
@@ -1,5 +1,4 @@
1
- import { CoercePlan } from "../type/coerce.js";
2
-
1
+ import { CoercePlan } from "../type/coerce-plan.js";
3
2
  //#region src/compile/aot.d.ts
4
3
  type ValidatorSlot = 'body' | 'query' | 'params' | 'headers' | 'cookie' | `response:${number}`;
5
4
  type FrozenCheckFactory = (External: unknown) => (value: unknown) => boolean;
@@ -115,6 +114,7 @@ declare const Source: {
115
114
  readonly mirrorFactory: (source: string, hasExternals: boolean) => string;
116
115
  readonly bothFactory: (identifier: string, checkDefs: string, checkValue: string, mirrorSource: string, mirrorHasExternals: boolean) => string;
117
116
  };
117
+ declare function collectMirrorUnions(schema: any, out?: unknown[][]): unknown[][];
118
118
  declare function instantiateFrozenMirror(frozen: FrozenMirror, schema: unknown): (value: unknown) => unknown;
119
119
  declare function instantiateFrozenDecodeMirror(frozen: FrozenMirror, schema: unknown, dir?: 'decode' | 'encode'): (value: unknown) => unknown;
120
120
  declare const instantiateFrozenEncodeMirror: (frozen: FrozenMirror, schema: unknown) => ((value: unknown) => unknown);
@@ -123,6 +123,20 @@ declare function instantiateFrozenBoth(frozen: FrozenValidator, checkSchema: unk
123
123
  check?: (value: unknown) => boolean;
124
124
  clean?: (value: unknown) => unknown;
125
125
  };
126
+ interface StringCodecNode {
127
+ inner: any;
128
+ codec: any;
129
+ open: number;
130
+ }
131
+ declare function collectStringCodecNodes(schema: any, out?: StringCodecNode[]): StringCodecNode[];
132
+ /**
133
+ * Overwrite every ObjectString/ArrayString node's `~refine[0].check` and
134
+ * `~codec.decode` with the baked `ic` closures. After this runs, the node no
135
+ * longer calls the constructor's live `typebox/value` Check/Decode
136
+ *
137
+ * Iterate in reverse so nested codecs reconstruct bottom-up.
138
+ */
139
+ declare function reconstructInnerCodecs(ic: NonNullable<FrozenValidator['ic']>, schema: any): void;
126
140
  /**
127
141
  * verify that mirror unions can be reconstructed in build time
128
142
  *
@@ -216,4 +230,4 @@ declare const Capture: {
216
230
  */
217
231
  declare const resetCaptureLifecycleForTests: () => void;
218
232
  //#endregion
219
- export { Capture, CapturedHandler, CapturedMirror, CapturedValidator, CheckBuildResult, Compiled, CompiledSnapshot, EMPTY_EXTERNALS, FrozenBothFactory, FrozenCheckFactory, FrozenHandler, FrozenMirror, FrozenMirrorFactory, FrozenValidator, HandlerManifest, Source, ValidatorManifest, ValidatorSlot, beginValidatorCapture, collectExternals, endHandlerCapture, endValidatorCapture, externalsMatch, instantiateFrozenBoth, instantiateFrozenDecodeMirror, instantiateFrozenEncodeMirror, instantiateFrozenMirror, reconstructCheck, resetCaptureLifecycleForTests };
233
+ export { Capture, CapturedHandler, CapturedMirror, CapturedValidator, CheckBuildResult, Compiled, CompiledSnapshot, EMPTY_EXTERNALS, FrozenBothFactory, FrozenCheckFactory, FrozenHandler, FrozenMirror, FrozenMirrorFactory, FrozenValidator, HandlerManifest, Source, StringCodecNode, ValidatorManifest, ValidatorSlot, beginValidatorCapture, collectExternals, collectMirrorUnions, collectStringCodecNodes, endHandlerCapture, endValidatorCapture, externalsMatch, instantiateFrozenBoth, instantiateFrozenDecodeMirror, instantiateFrozenEncodeMirror, instantiateFrozenMirror, reconstructCheck, reconstructInnerCodecs, resetCaptureLifecycleForTests };
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_utils = require('../utils.js');
3
+ const require_type_constants = require('../type/constants.js');
3
4
  const require_universal_env = require('../universal/env.js');
4
5
 
5
6
  //#region src/compile/aot.ts
@@ -201,6 +202,53 @@ const EMPTY_EXTERNALS = Object.freeze([]);
201
202
  function instantiateFrozenBoth(frozen, checkSchema, mirrorSchema) {
202
203
  return frozen.cm(frozen.e ? collectExternals(checkSchema) : EMPTY_EXTERNALS, frozen.u ? { unions: buildUnions(frozen.u, mirrorSchema) } : void 0);
203
204
  }
205
+ function collectStringCodecNodes(schema, out = []) {
206
+ if (!schema || typeof schema !== "object") return out;
207
+ const ely = schema["~elyTyp"];
208
+ if (ely === require_type_constants.ELYSIA_TYPES.ObjectString || ely === require_type_constants.ELYSIA_TYPES.ArrayString) {
209
+ const inner = schema.anyOf?.[0];
210
+ const codec = schema.anyOf?.[1];
211
+ if (inner && codec?.["~codec"] && codec["~refine"]) out.push({
212
+ inner,
213
+ codec,
214
+ open: ely === require_type_constants.ELYSIA_TYPES.ObjectString ? 123 : 91
215
+ });
216
+ }
217
+ if (schema.properties) for (const k in schema.properties) collectStringCodecNodes(schema.properties[k], out);
218
+ const items = schema.items;
219
+ if (Array.isArray(items)) for (const it of items) collectStringCodecNodes(it, out);
220
+ else if (items) collectStringCodecNodes(items, out);
221
+ if (Array.isArray(schema.anyOf)) for (const b of schema.anyOf) collectStringCodecNodes(b, out);
222
+ return out;
223
+ }
224
+ /**
225
+ * Overwrite every ObjectString/ArrayString node's `~refine[0].check` and
226
+ * `~codec.decode` with the baked `ic` closures. After this runs, the node no
227
+ * longer calls the constructor's live `typebox/value` Check/Decode
228
+ *
229
+ * Iterate in reverse so nested codecs reconstruct bottom-up.
230
+ */
231
+ function reconstructInnerCodecs(ic, schema) {
232
+ const nodes = collectStringCodecNodes(schema);
233
+ for (let i = nodes.length - 1; i >= 0; i--) {
234
+ const entry = ic[i];
235
+ const node = nodes[i];
236
+ if (!entry || !node) continue;
237
+ const innerSchema = node.inner;
238
+ const innerCheck = entry.c(entry.e ? collectExternals(innerSchema) : []);
239
+ const innerMirror = entry.d.x ? instantiateFrozenDecodeMirror(entry.d, innerSchema) : entry.d.s;
240
+ const open = entry.o;
241
+ node.codec["~refine"][0].check = (v) => {
242
+ if (v.charCodeAt(0) !== open) return false;
243
+ try {
244
+ return innerCheck(JSON.parse(v));
245
+ } catch {
246
+ return false;
247
+ }
248
+ };
249
+ node.codec["~codec"].decode = (v) => innerMirror(JSON.parse(v));
250
+ }
251
+ }
204
252
  /**
205
253
  * verify that mirror unions can be reconstructed in build time
206
254
  *
@@ -299,6 +347,8 @@ exports.EMPTY_EXTERNALS = EMPTY_EXTERNALS;
299
347
  exports.Source = Source;
300
348
  exports.beginValidatorCapture = beginValidatorCapture;
301
349
  exports.collectExternals = collectExternals;
350
+ exports.collectMirrorUnions = collectMirrorUnions;
351
+ exports.collectStringCodecNodes = collectStringCodecNodes;
302
352
  exports.endHandlerCapture = endHandlerCapture;
303
353
  exports.endValidatorCapture = endValidatorCapture;
304
354
  exports.externalsMatch = externalsMatch;
@@ -307,4 +357,5 @@ exports.instantiateFrozenDecodeMirror = instantiateFrozenDecodeMirror;
307
357
  exports.instantiateFrozenEncodeMirror = instantiateFrozenEncodeMirror;
308
358
  exports.instantiateFrozenMirror = instantiateFrozenMirror;
309
359
  exports.reconstructCheck = reconstructCheck;
360
+ exports.reconstructInnerCodecs = reconstructInnerCodecs;
310
361
  exports.resetCaptureLifecycleForTests = resetCaptureLifecycleForTests;
@@ -1,4 +1,5 @@
1
1
  import { nullObject } from "../utils.mjs";
2
+ import { ELYSIA_TYPES } from "../type/constants.mjs";
2
3
  import { env } from "../universal/env.mjs";
3
4
 
4
5
  //#region src/compile/aot.ts
@@ -200,6 +201,53 @@ const EMPTY_EXTERNALS = Object.freeze([]);
200
201
  function instantiateFrozenBoth(frozen, checkSchema, mirrorSchema) {
201
202
  return frozen.cm(frozen.e ? collectExternals(checkSchema) : EMPTY_EXTERNALS, frozen.u ? { unions: buildUnions(frozen.u, mirrorSchema) } : void 0);
202
203
  }
204
+ function collectStringCodecNodes(schema, out = []) {
205
+ if (!schema || typeof schema !== "object") return out;
206
+ const ely = schema["~elyTyp"];
207
+ if (ely === ELYSIA_TYPES.ObjectString || ely === ELYSIA_TYPES.ArrayString) {
208
+ const inner = schema.anyOf?.[0];
209
+ const codec = schema.anyOf?.[1];
210
+ if (inner && codec?.["~codec"] && codec["~refine"]) out.push({
211
+ inner,
212
+ codec,
213
+ open: ely === ELYSIA_TYPES.ObjectString ? 123 : 91
214
+ });
215
+ }
216
+ if (schema.properties) for (const k in schema.properties) collectStringCodecNodes(schema.properties[k], out);
217
+ const items = schema.items;
218
+ if (Array.isArray(items)) for (const it of items) collectStringCodecNodes(it, out);
219
+ else if (items) collectStringCodecNodes(items, out);
220
+ if (Array.isArray(schema.anyOf)) for (const b of schema.anyOf) collectStringCodecNodes(b, out);
221
+ return out;
222
+ }
223
+ /**
224
+ * Overwrite every ObjectString/ArrayString node's `~refine[0].check` and
225
+ * `~codec.decode` with the baked `ic` closures. After this runs, the node no
226
+ * longer calls the constructor's live `typebox/value` Check/Decode
227
+ *
228
+ * Iterate in reverse so nested codecs reconstruct bottom-up.
229
+ */
230
+ function reconstructInnerCodecs(ic, schema) {
231
+ const nodes = collectStringCodecNodes(schema);
232
+ for (let i = nodes.length - 1; i >= 0; i--) {
233
+ const entry = ic[i];
234
+ const node = nodes[i];
235
+ if (!entry || !node) continue;
236
+ const innerSchema = node.inner;
237
+ const innerCheck = entry.c(entry.e ? collectExternals(innerSchema) : []);
238
+ const innerMirror = entry.d.x ? instantiateFrozenDecodeMirror(entry.d, innerSchema) : entry.d.s;
239
+ const open = entry.o;
240
+ node.codec["~refine"][0].check = (v) => {
241
+ if (v.charCodeAt(0) !== open) return false;
242
+ try {
243
+ return innerCheck(JSON.parse(v));
244
+ } catch {
245
+ return false;
246
+ }
247
+ };
248
+ node.codec["~codec"].decode = (v) => innerMirror(JSON.parse(v));
249
+ }
250
+ }
203
251
  /**
204
252
  * verify that mirror unions can be reconstructed in build time
205
253
  *
@@ -292,4 +340,4 @@ const Capture = {
292
340
  const resetCaptureLifecycleForTests = () => void 0;
293
341
 
294
342
  //#endregion
295
- export { Capture, Compiled, EMPTY_EXTERNALS, Source, beginValidatorCapture, collectExternals, endHandlerCapture, endValidatorCapture, externalsMatch, instantiateFrozenBoth, instantiateFrozenDecodeMirror, instantiateFrozenEncodeMirror, instantiateFrozenMirror, reconstructCheck, resetCaptureLifecycleForTests };
343
+ export { Capture, Compiled, EMPTY_EXTERNALS, Source, beginValidatorCapture, collectExternals, collectMirrorUnions, collectStringCodecNodes, endHandlerCapture, endValidatorCapture, externalsMatch, instantiateFrozenBoth, instantiateFrozenDecodeMirror, instantiateFrozenEncodeMirror, instantiateFrozenMirror, reconstructCheck, reconstructInnerCodecs, resetCaptureLifecycleForTests };
@@ -8,7 +8,7 @@ declare class FrozenSlotValidator {
8
8
  #private;
9
9
  isAsync: boolean;
10
10
  schema: unknown;
11
- constructor(frozen: FrozenValidator, schema: unknown, normalize: boolean | 'exactMirror' | 'typebox' | undefined);
11
+ constructor(frozen: FrozenValidator, schema: unknown, raw: unknown, normalize: boolean | 'exactMirror' | 'typebox' | undefined);
12
12
  Check(value: unknown): boolean;
13
13
  Errors(): unknown[];
14
14
  From(value: unknown, type?: string): unknown;
@@ -22,19 +22,9 @@ interface FrozenRouteValidatorShape {
22
22
  cookie?: FrozenSlotValidator;
23
23
  response?: Record<number, FrozenSlotValidator>;
24
24
  }
25
- /**
26
- * Attempt to build an entire route validator bridge-free.
27
- *
28
- * Returns `undefined` (caller falls back to the wired `RouteValidator`) unless
29
- * EVERY declared slot — request and response — is bridge-free-complete. All or
30
- * nothing per route: a partial mix would still drag the bridge for one slot,
31
- * defeating the point, and the handler factory expects one uniform validator.
32
- */
33
25
  declare function buildFrozenRouteValidator(hook: AnyLocalHook, root: AnyElysia, method: HTTPMethod, path: string): FrozenRouteValidatorShape | undefined;
34
26
  /**
35
- * Build-time twin of `isBridgeFreeComplete`, operating on the captured entry +
36
- * its schema. The build plugin can aggregate this across every slot to decide
37
- * whether a manifest is "fully bridge-free"
27
+ * Build-time twin of `isBridgeFreeComplete`, operating on the captured entry & its schema
38
28
  */
39
29
  declare function isCapturedBridgeFree(c: CapturedValidator, schema: unknown): boolean;
40
30
  //#endregion
@@ -3,15 +3,24 @@ const require_utils = require('../../utils.js');
3
3
  const require_type_constants = require('../../type/constants.js');
4
4
  const require_error = require('../../error.js');
5
5
  const require_compile_aot = require('../aot.js');
6
+ const require_type_coerce_plan = require('../../type/coerce-plan.js');
6
7
 
7
8
  //#region src/compile/handler/frozen-validator.ts
8
9
  const isBridgeNotInitialized = (error) => error instanceof Error && error.message.startsWith("Typebox module isn't initialized");
9
- function codecCoercionBridgeFree(f) {
10
- return f.k === 1 && !!f.dm && !f.cp && !f.ic && !f.em && !f.ce && f.a !== 1 && !(f.d === 1 && f.ps !== 1);
10
+ function codecCoercionBridgeFree(f, coerced, raw) {
11
+ return f.k === 1 && !!f.dm && !f.em && !f.ce && f.a !== 1 && !(f.d === 1 && f.ps !== 1) && innerCodecsAligned(f.ic?.length, coerced) && mirrorUnionsAligned(f.u, raw) && mirrorUnionsAligned(f.dm.u, coerced);
11
12
  }
12
- function isBridgeFreeComplete(f, schema) {
13
+ const innerCodecsAligned = (icLength, coerced) => require_compile_aot.collectStringCodecNodes(coerced).length === (icLength ?? 0);
14
+ function mirrorUnionsAligned(u, schema) {
15
+ if (!u) return true;
16
+ const branches = require_compile_aot.collectMirrorUnions(schema);
17
+ if (branches.length !== u.length) return false;
18
+ for (let ui = 0; ui < u.length; ui++) if (branches[ui].length !== u[ui].length) return false;
19
+ return true;
20
+ }
21
+ function isBridgeFreeComplete(f, schema, raw) {
13
22
  if (!f.cm) return false;
14
- if (codecCoercionBridgeFree(f)) return true;
23
+ if (codecCoercionBridgeFree(f, schema, raw)) return true;
15
24
  if (f.e === 1 || f.u || f.dm || f.em || f.k === 1 || f.ce || f.ic || f.a === 1) return false;
16
25
  if (f.d === 1 && f.ps !== 1) return false;
17
26
  if (isFullyClosedObject(schema)) return false;
@@ -58,11 +67,12 @@ var FrozenSlotValidator = class {
58
67
  #defaultFastPath;
59
68
  #hasOptional;
60
69
  #noValidate;
61
- constructor(frozen, schema, normalize) {
70
+ constructor(frozen, schema, raw, normalize) {
62
71
  this.isAsync = false;
63
72
  this.schema = schema;
73
+ if (frozen.ic) require_compile_aot.reconstructInnerCodecs(frozen.ic, schema);
64
74
  if (frozen.k === 1 && frozen.dm) {
65
- const both = require_compile_aot.instantiateFrozenBoth(frozen, schema, schema);
75
+ const both = require_compile_aot.instantiateFrozenBoth(frozen, schema, raw);
66
76
  this.#check = both.check;
67
77
  this.#clean = normalize === false ? void 0 : both.clean;
68
78
  this.#decode = normalize === false ? void 0 : require_compile_aot.instantiateFrozenDecodeMirror(frozen.dm, schema);
@@ -132,36 +142,11 @@ const REQUEST_SLOTS = [
132
142
  "params",
133
143
  "cookie"
134
144
  ];
135
- /**
136
- * Resolve a slot schema to the concrete TypeBox schema the wired path validates.
137
- *
138
- * A slot can be a STRING model reference (`.post(path, { body: 'myModel' })`);
139
- * the wired path resolves it via `Validator.reference(schema, models)` before
140
- * building anything, so its closed-object / coverage checks and Clean see the
141
- * resolved object. The bridge-free path receives the RAW hook schema (the bare
142
- * string), so it must resolve the same way BEFORE any check — otherwise a closed
143
- * model ref (`type` is a string, not an object) dodges `isFullyClosedObject` and
144
- * builds bridge-free, diverging from the wired `#cleanRedundant` Clean-skip.
145
- *
146
- * Mirrors `Validator.reference` semantics without importing it (that module
147
- * statically pulls the TypeBox bridge this module must stay free of): non-string
148
- * passes through; a string that names a registered model resolves; a string that
149
- * names nothing returns `undefined` so the caller bails and the wired path
150
- * surfaces the "not found" error loudly instead of silently diverging.
151
- */
152
145
  function resolveModelRef(schema, root) {
153
146
  if (typeof schema !== "string") return schema;
154
147
  const models = root["~ext"]?.models;
155
148
  if (models && schema in models) return models[schema];
156
149
  }
157
- /**
158
- * Attempt to build an entire route validator bridge-free.
159
- *
160
- * Returns `undefined` (caller falls back to the wired `RouteValidator`) unless
161
- * EVERY declared slot — request and response — is bridge-free-complete. All or
162
- * nothing per route: a partial mix would still drag the bridge for one slot,
163
- * defeating the point, and the handler factory expects one uniform validator.
164
- */
165
150
  function buildFrozenRouteValidator(hook, root, method, path) {
166
151
  if (root["~config"]?.normalize === "typebox") return void 0;
167
152
  if (hook?.schemas) return void 0;
@@ -173,8 +158,10 @@ function buildFrozenRouteValidator(hook, root, method, path) {
173
158
  const schema = resolveModelRef(raw, root);
174
159
  if (!schema) return void 0;
175
160
  const frozen = require_compile_aot.Compiled.getValidator(method, path, slot);
176
- if (!frozen || !isBridgeFreeComplete(frozen, schema)) return void 0;
177
- out[slot] = new FrozenSlotValidator(frozen, schema, normalize);
161
+ if (!frozen) return void 0;
162
+ const coerced = frozen.cp ? require_type_coerce_plan.buildCoercedFromPlan(schema, frozen.cp) : schema;
163
+ if (!isBridgeFreeComplete(frozen, coerced, schema)) return void 0;
164
+ out[slot] = new FrozenSlotValidator(frozen, coerced, schema, normalize);
178
165
  }
179
166
  const response = hook?.response;
180
167
  if (response) {
@@ -186,8 +173,8 @@ function buildFrozenRouteValidator(hook, root, method, path) {
186
173
  const schema = resolveModelRef(raw, root);
187
174
  if (!schema) return void 0;
188
175
  const frozen = require_compile_aot.Compiled.getValidator(method, path, `response:${status}`);
189
- if (!frozen || !isBridgeFreeComplete(frozen, schema)) return void 0;
190
- responseOut[status] = new FrozenSlotValidator(frozen, schema, normalize);
176
+ if (!frozen || !isBridgeFreeComplete(frozen, schema, schema)) return void 0;
177
+ responseOut[status] = new FrozenSlotValidator(frozen, schema, schema, normalize);
191
178
  }
192
179
  out.response = responseOut;
193
180
  }
@@ -195,9 +182,7 @@ function buildFrozenRouteValidator(hook, root, method, path) {
195
182
  }
196
183
  const isResponseMap = (schema) => !("~kind" in schema || "~elyAcl" in schema || "~standard" in schema);
197
184
  /**
198
- * Build-time twin of `isBridgeFreeComplete`, operating on the captured entry +
199
- * its schema. The build plugin can aggregate this across every slot to decide
200
- * whether a manifest is "fully bridge-free"
185
+ * Build-time twin of `isBridgeFreeComplete`, operating on the captured entry & its schema
201
186
  */
202
187
  function isCapturedBridgeFree(c, schema) {
203
188
  if (typeof schema === "string") return false;
@@ -208,7 +193,10 @@ function isCapturedBridgeFree(c, schema) {
208
193
  * @see plugin/source.ts `entryParts`
209
194
  */
210
195
  if (!(c.checkValue && c.mirror)) return false;
211
- if (c.hasCodec && !!c.decodeMirror && !c.coercePlan && !c.innerCodecs?.length && !c.encodeMirror && !c.customErrors?.length && !c.async && !(c.hasDefault && !c.precomputeSafe)) return true;
196
+ if (c.hasCodec && !!c.decodeMirror && !c.encodeMirror && !c.customErrors?.length && !c.async && !(c.hasDefault && !c.precomputeSafe)) {
197
+ const coerced = c.coercePlan ? require_type_coerce_plan.buildCoercedFromPlan(schema, c.coercePlan) : schema;
198
+ return innerCodecsAligned(c.innerCodecs?.length, coerced) && mirrorUnionsAligned(c.mirror.u, schema) && mirrorUnionsAligned(c.decodeMirror.u, coerced);
199
+ }
212
200
  if (c.external || c.mirror.u || c.decodeMirror || c.encodeMirror || c.hasCodec || c.customErrors?.length || c.innerCodecs?.length || c.async) return false;
213
201
  if (c.hasDefault && !c.precomputeSafe) return false;
214
202
  if (isFullyClosedObject(schema)) return false;