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.
- package/dist/base.js +66 -4
- package/dist/base.mjs +66 -4
- package/dist/compile/aot.d.ts +17 -3
- package/dist/compile/aot.js +51 -0
- package/dist/compile/aot.mjs +49 -1
- package/dist/compile/handler/frozen-validator.d.ts +2 -12
- package/dist/compile/handler/frozen-validator.js +27 -39
- package/dist/compile/handler/frozen-validator.mjs +28 -40
- package/dist/compile/handler/utils.js +17 -59
- package/dist/compile/handler/utils.mjs +1 -43
- package/dist/compile/lexer.d.ts +7 -0
- package/dist/compile/lexer.js +53 -0
- package/dist/compile/lexer.mjs +48 -0
- package/dist/error.js +2 -2
- package/dist/error.mjs +2 -2
- package/dist/trace.js +3 -3
- package/dist/trace.mjs +1 -1
- package/dist/type/coerce-plan.d.ts +33 -0
- package/dist/type/coerce-plan.js +92 -0
- package/dist/type/coerce-plan.mjs +86 -0
- package/dist/type/coerce.d.ts +4 -18
- package/dist/type/coerce.js +8 -51
- package/dist/type/coerce.mjs +5 -48
- package/dist/type/elysia/array-string.js +1 -1
- package/dist/type/elysia/array-string.mjs +1 -1
- package/dist/type/elysia/files.js +1 -1
- package/dist/type/elysia/files.mjs +1 -1
- package/dist/type/elysia/integer-string.js +1 -1
- package/dist/type/elysia/integer-string.mjs +1 -1
- package/dist/type/elysia/numeric-enum.js +1 -1
- package/dist/type/elysia/numeric-enum.mjs +1 -1
- package/dist/type/elysia/numeric.js +1 -1
- package/dist/type/elysia/numeric.mjs +1 -1
- package/dist/type/exports.js +5 -5
- package/dist/type/exports.mjs +5 -5
- package/dist/type/index.js +5 -5
- package/dist/type/index.mjs +5 -5
- package/dist/type/validator/default-precompute.js +3 -3
- package/dist/type/validator/default-precompute.mjs +3 -3
- package/dist/type/validator/index.js +1 -1
- package/dist/type/validator/index.mjs +2 -2
- package/dist/type/validator/string-codec-aot.d.ts +1 -2
- package/dist/type/validator/string-codec-aot.js +2 -43
- package/dist/type/validator/string-codec-aot.mjs +1 -42
- package/dist/ws/route.js +1 -1
- package/dist/ws/route.mjs +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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()) {
|
package/dist/compile/aot.d.ts
CHANGED
|
@@ -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 };
|
package/dist/compile/aot.js
CHANGED
|
@@ -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;
|
package/dist/compile/aot.mjs
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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,
|
|
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
|
|
177
|
-
|
|
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.
|
|
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;
|