typesea 0.2.0 → 0.3.0
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/CHANGELOG.md +18 -0
- package/README.md +45 -11
- package/dist/aot/index.d.ts +1 -1
- package/dist/aot/index.d.ts.map +1 -1
- package/dist/aot/index.js +22 -3
- package/dist/builders/composite.d.ts +6 -3
- package/dist/builders/composite.d.ts.map +1 -1
- package/dist/builders/composite.js +22 -13
- package/dist/builders/index.d.ts +6 -5
- package/dist/builders/index.d.ts.map +1 -1
- package/dist/builders/index.js +5 -4
- package/dist/builders/modifier.d.ts +6 -0
- package/dist/builders/modifier.d.ts.map +1 -1
- package/dist/builders/modifier.js +14 -0
- package/dist/builders/object/guard.d.ts +54 -2
- package/dist/builders/object/guard.d.ts.map +1 -1
- package/dist/builders/object/guard.js +117 -7
- package/dist/builders/object/index.d.ts +2 -2
- package/dist/builders/object/index.d.ts.map +1 -1
- package/dist/builders/object/index.js +1 -1
- package/dist/builders/object/schema.d.ts +33 -2
- package/dist/builders/object/schema.d.ts.map +1 -1
- package/dist/builders/object/schema.js +198 -8
- package/dist/builders/object/types.d.ts +15 -0
- package/dist/builders/object/types.d.ts.map +1 -1
- package/dist/builders/runtime.d.ts +40 -0
- package/dist/builders/runtime.d.ts.map +1 -0
- package/dist/builders/runtime.js +150 -0
- package/dist/builders/scalar.d.ts +20 -1
- package/dist/builders/scalar.d.ts.map +1 -1
- package/dist/builders/scalar.js +54 -1
- package/dist/builders/table.d.ts +31 -5
- package/dist/builders/table.d.ts.map +1 -1
- package/dist/builders/table.js +31 -5
- package/dist/builders/types.d.ts +6 -0
- package/dist/builders/types.d.ts.map +1 -1
- package/dist/compile/check-composite.d.ts +3 -1
- package/dist/compile/check-composite.d.ts.map +1 -1
- package/dist/compile/check-composite.js +143 -11
- package/dist/compile/check-scalar.d.ts +10 -0
- package/dist/compile/check-scalar.d.ts.map +1 -1
- package/dist/compile/check-scalar.js +138 -2
- package/dist/compile/check.d.ts.map +1 -1
- package/dist/compile/check.js +25 -3
- package/dist/compile/context.d.ts.map +1 -1
- package/dist/compile/context.js +2 -0
- package/dist/compile/first.d.ts +26 -0
- package/dist/compile/first.d.ts.map +1 -0
- package/dist/compile/first.js +850 -0
- package/dist/compile/graph-predicate.d.ts.map +1 -1
- package/dist/compile/graph-predicate.js +389 -18
- package/dist/compile/guard.d.ts +2 -1
- package/dist/compile/guard.d.ts.map +1 -1
- package/dist/compile/guard.js +54 -8
- package/dist/compile/predicate.d.ts.map +1 -1
- package/dist/compile/predicate.js +156 -5
- package/dist/compile/runtime.d.ts +20 -1
- package/dist/compile/runtime.d.ts.map +1 -1
- package/dist/compile/runtime.js +31 -0
- package/dist/compile/source.d.ts.map +1 -1
- package/dist/compile/source.js +27 -3
- package/dist/compile/types.d.ts +2 -0
- package/dist/compile/types.d.ts.map +1 -1
- package/dist/decoder/index.d.ts +60 -0
- package/dist/decoder/index.d.ts.map +1 -1
- package/dist/decoder/index.js +164 -1
- package/dist/evaluate/check-composite.d.ts +52 -2
- package/dist/evaluate/check-composite.d.ts.map +1 -1
- package/dist/evaluate/check-composite.js +193 -6
- package/dist/evaluate/check-scalar.d.ts +9 -0
- package/dist/evaluate/check-scalar.d.ts.map +1 -1
- package/dist/evaluate/check-scalar.js +92 -3
- package/dist/evaluate/check.d.ts.map +1 -1
- package/dist/evaluate/check.js +19 -4
- package/dist/evaluate/shared.d.ts +19 -0
- package/dist/evaluate/shared.d.ts.map +1 -1
- package/dist/evaluate/shared.js +35 -0
- package/dist/guard/array.d.ts +48 -0
- package/dist/guard/array.d.ts.map +1 -0
- package/dist/guard/array.js +84 -0
- package/dist/guard/base.d.ts +32 -2
- package/dist/guard/base.d.ts.map +1 -1
- package/dist/guard/base.js +74 -3
- package/dist/guard/date.d.ts +34 -0
- package/dist/guard/date.d.ts.map +1 -0
- package/dist/guard/date.js +60 -0
- package/dist/guard/index.d.ts +2 -0
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +2 -0
- package/dist/guard/number.d.ts +60 -0
- package/dist/guard/number.d.ts.map +1 -1
- package/dist/guard/number.js +129 -0
- package/dist/guard/read.d.ts +53 -1
- package/dist/guard/read.d.ts.map +1 -1
- package/dist/guard/read.js +102 -0
- package/dist/guard/string.d.ts +82 -0
- package/dist/guard/string.d.ts.map +1 -1
- package/dist/guard/string.js +213 -0
- package/dist/guard/types.d.ts +18 -0
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/ir/builder.d.ts +3 -3
- package/dist/ir/builder.d.ts.map +1 -1
- package/dist/ir/builder.js +5 -2
- package/dist/ir/freeze.js +7 -0
- package/dist/ir/types.d.ts +4 -1
- package/dist/ir/types.d.ts.map +1 -1
- package/dist/ir/validate.d.ts.map +1 -1
- package/dist/ir/validate.js +35 -1
- package/dist/issue/index.d.ts +1 -1
- package/dist/issue/index.d.ts.map +1 -1
- package/dist/issue/index.js +4 -0
- package/dist/json-schema/emit-composite.d.ts +6 -2
- package/dist/json-schema/emit-composite.d.ts.map +1 -1
- package/dist/json-schema/emit-composite.js +66 -12
- package/dist/json-schema/emit-scalar.d.ts.map +1 -1
- package/dist/json-schema/emit-scalar.js +54 -1
- package/dist/json-schema/emit.d.ts.map +1 -1
- package/dist/json-schema/emit.js +11 -2
- package/dist/json-schema/types.d.ts +7 -1
- package/dist/json-schema/types.d.ts.map +1 -1
- package/dist/kind/index.d.ts +25 -0
- package/dist/kind/index.d.ts.map +1 -1
- package/dist/kind/index.js +26 -3
- package/dist/lower/index.d.ts.map +1 -1
- package/dist/lower/index.js +52 -3
- package/dist/message/index.d.ts +18 -0
- package/dist/message/index.d.ts.map +1 -1
- package/dist/message/index.js +67 -0
- package/dist/optimize/domain.js +6 -2
- package/dist/optimize/map-node.d.ts.map +1 -1
- package/dist/optimize/map-node.js +3 -0
- package/dist/plan/cache.js +13 -1
- package/dist/plan/predicate.d.ts.map +1 -1
- package/dist/plan/predicate.js +33 -3
- package/dist/plan/schema-predicate.d.ts.map +1 -1
- package/dist/plan/schema-predicate.js +267 -8
- package/dist/schema/freeze.js +22 -0
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -1
- package/dist/schema/types.d.ts +89 -4
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +8 -1
- package/dist/schema/undefined.d.ts.map +1 -1
- package/dist/schema/undefined.js +5 -0
- package/dist/schema/validate.d.ts.map +1 -1
- package/dist/schema/validate.js +111 -4
- package/docs/api.md +71 -8
- package/docs/engine-notes.md +4 -0
- package/docs/index.html +1340 -722
- package/docs/ko/api.md +375 -0
- package/docs/ko/engine-notes.md +156 -0
- package/docs/ko/readme.md +378 -0
- package/package.json +3 -2
package/dist/compile/guard.js
CHANGED
|
@@ -8,7 +8,7 @@ import { BaseGuard, TypeSeaAssertionError } from "../guard/index.js";
|
|
|
8
8
|
import { finalizeIssueArray } from "../issue/index.js";
|
|
9
9
|
import { err, ok } from "../result/index.js";
|
|
10
10
|
import { freezeSchema, isSchemaValue } from "../schema/index.js";
|
|
11
|
-
import { makeDynamicCheck, makeDynamicIssueCheck, strictKeys } from "./runtime.js";
|
|
11
|
+
import { makeDynamicCheck, makeDynamicFirstIssueCheck, makeDynamicIssueCheck, strictKeys } from "./runtime.js";
|
|
12
12
|
import { emitCompiledSourceBundle } from "./source.js";
|
|
13
13
|
const trustedCollectors = new WeakSet();
|
|
14
14
|
const trustedCheckResults = new WeakSet();
|
|
@@ -24,7 +24,8 @@ export class CompiledBaseGuard extends BaseGuard {
|
|
|
24
24
|
#collect;
|
|
25
25
|
#trustedCollector;
|
|
26
26
|
#checkResult;
|
|
27
|
-
|
|
27
|
+
#checkFirstResult;
|
|
28
|
+
constructor(schema, test, collect, source, trustedCollector = false, checkResult, checkFirstResult) {
|
|
28
29
|
if (typeof test !== "function") {
|
|
29
30
|
throw new TypeError("compiled guard test must be a function");
|
|
30
31
|
}
|
|
@@ -42,9 +43,15 @@ export class CompiledBaseGuard extends BaseGuard {
|
|
|
42
43
|
trustedCheckResults.has(checkResult)
|
|
43
44
|
? checkResult
|
|
44
45
|
: undefined;
|
|
46
|
+
this.#checkFirstResult = checkFirstResult !== undefined &&
|
|
47
|
+
trustedCheckResults.has(checkFirstResult)
|
|
48
|
+
? checkFirstResult
|
|
49
|
+
: undefined;
|
|
45
50
|
defineReadonlyProperty(this, "source", source, true);
|
|
46
|
-
if (trustedPredicates.has(test) &&
|
|
47
|
-
|
|
51
|
+
if (trustedPredicates.has(test) &&
|
|
52
|
+
this.#checkResult !== undefined &&
|
|
53
|
+
this.#checkFirstResult !== undefined) {
|
|
54
|
+
defineTrustedHotMethods(this, test, this.#checkResult, this.#checkFirstResult);
|
|
48
55
|
}
|
|
49
56
|
Object.freeze(this);
|
|
50
57
|
}
|
|
@@ -57,6 +64,12 @@ export class CompiledBaseGuard extends BaseGuard {
|
|
|
57
64
|
}
|
|
58
65
|
return runCompiledCheck(this.#collect, this.#trustedCollector, value);
|
|
59
66
|
}
|
|
67
|
+
checkFirst(value) {
|
|
68
|
+
if (this.#checkFirstResult !== undefined) {
|
|
69
|
+
return this.#checkFirstResult(value);
|
|
70
|
+
}
|
|
71
|
+
return runCompiledCheckFirst(this.#collect, this.#trustedCollector, value);
|
|
72
|
+
}
|
|
60
73
|
assert(value) {
|
|
61
74
|
const result = this.#checkResult === undefined
|
|
62
75
|
? runCompiledCheck(this.#collect, this.#trustedCollector, value)
|
|
@@ -78,19 +91,21 @@ export function compile(guard, options) {
|
|
|
78
91
|
const bundle = emitCompiledSourceBundle(schema, name, mode);
|
|
79
92
|
// compile() intentionally emits source so V8 can optimize the validator body.
|
|
80
93
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
81
|
-
const factory = new Function("l", "r", "k", "u", "d", "m", "sk", bundle.source);
|
|
94
|
+
const factory = new Function("l", "r", "k", "u", "d", "m", "mf", "sk", bundle.source);
|
|
82
95
|
const dynamicCheck = makeDynamicCheck(bundle.dynamicSchemas);
|
|
83
|
-
const
|
|
96
|
+
const dynamicFirstIssueCheck = makeDynamicFirstIssueCheck(bundle.dynamicSchemas);
|
|
97
|
+
const runtime = factory(bundle.literals, bundle.regexps, bundle.keysets, bundle.strings, dynamicCheck, makeDynamicIssueCheck(bundle.dynamicSchemas), dynamicFirstIssueCheck, strictKeys);
|
|
84
98
|
trustedPredicates.add(runtime.is);
|
|
85
99
|
trustedCollectors.add(runtime.check);
|
|
86
100
|
trustedCheckResults.add(runtime.result);
|
|
87
|
-
|
|
101
|
+
trustedCheckResults.add(runtime.first);
|
|
102
|
+
return new CompiledBaseGuard(schema, runtime.is, runtime.check, bundle.source, true, runtime.result, runtime.first);
|
|
88
103
|
}
|
|
89
104
|
/**
|
|
90
105
|
* @brief Execute define trusted hot methods.
|
|
91
106
|
* @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
|
|
92
107
|
*/
|
|
93
|
-
function defineTrustedHotMethods(guard, test, checkResult) {
|
|
108
|
+
function defineTrustedHotMethods(guard, test, checkResult, checkFirstResult) {
|
|
94
109
|
const self = guard;
|
|
95
110
|
/*
|
|
96
111
|
* Compiled guards receive own methods after construction. The closure over
|
|
@@ -133,6 +148,17 @@ function defineTrustedHotMethods(guard, test, checkResult) {
|
|
|
133
148
|
throw new TypeSeaAssertionError(result.error);
|
|
134
149
|
}
|
|
135
150
|
}, false);
|
|
151
|
+
defineReadonlyProperty(guard, "checkFirst",
|
|
152
|
+
/**
|
|
153
|
+
* @brief Execute compiled trusted checkFirst.
|
|
154
|
+
* @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
|
|
155
|
+
*/
|
|
156
|
+
function compiledTrustedCheckFirst(value) {
|
|
157
|
+
if (this !== self) {
|
|
158
|
+
throw new TypeError("compiled guard method receiver is invalid");
|
|
159
|
+
}
|
|
160
|
+
return checkFirstResult(value);
|
|
161
|
+
}, false);
|
|
136
162
|
}
|
|
137
163
|
/**
|
|
138
164
|
* @brief Normalize the guard input for runtime compilation.
|
|
@@ -203,6 +229,26 @@ function runCompiledCheck(collect, trustedCollector, value) {
|
|
|
203
229
|
}
|
|
204
230
|
return err(issues);
|
|
205
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* @brief Convert a generated collector result into a one-issue public Result.
|
|
234
|
+
* @details This fallback is used only for manually constructed compiled guards.
|
|
235
|
+
* Trusted codegen normally installs a dedicated first-result function.
|
|
236
|
+
* @param collect Generated collector function.
|
|
237
|
+
* @param trustedCollector Whether the collector came from TypeSea codegen.
|
|
238
|
+
* @param value Candidate runtime value.
|
|
239
|
+
* @returns Success result or a frozen failure carrying at most one issue.
|
|
240
|
+
*/
|
|
241
|
+
function runCompiledCheckFirst(collect, trustedCollector, value) {
|
|
242
|
+
const result = runCompiledCheck(collect, trustedCollector, value);
|
|
243
|
+
if (result.ok || result.error.length <= 1) {
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
const first = result.error[0];
|
|
247
|
+
if (first === undefined) {
|
|
248
|
+
return ok(value);
|
|
249
|
+
}
|
|
250
|
+
return err(Object.freeze([first]));
|
|
251
|
+
}
|
|
206
252
|
/**
|
|
207
253
|
* @brief Execute define readonly property.
|
|
208
254
|
* @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predicate.d.ts","sourceRoot":"","sources":["../../src/compile/predicate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"predicate.d.ts","sourceRoot":"","sources":["../../src/compile/predicate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAYH,KAAK,MAAM,EACd,MAAM,oBAAoB,CAAC;AAO5B,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,YAAY,CAAC;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CAkBzE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAU1D;AAoCD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,GACrB,MAAM,CAiER;AAucD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACrB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,GACrB,MAAM,CAYR"}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @details Generated-source helpers keep the side-table ABI and JavaScript source shape
|
|
5
5
|
* stable across runtime and AOT emission.
|
|
6
6
|
*/
|
|
7
|
-
import { NumberCheckTag, ObjectModeTag, PresenceTag, SchemaTag, StringCheckTag } from "../kind/index.js";
|
|
8
|
-
import { UUID_PATTERN } from "../schema/index.js";
|
|
7
|
+
import { ArrayCheckTag, DateCheckTag, NumberCheckTag, ObjectModeTag, PresenceTag, SchemaTag, StringCheckTag } from "../kind/index.js";
|
|
8
|
+
import { EMAIL_PATTERN, IPV4_PATTERN, IPV6_PATTERN, ISO_DATETIME_PATTERN, ISO_DATE_PATTERN, ULID_PATTERN, URL_PATTERN, UUID_PATTERN } from "../schema/index.js";
|
|
9
9
|
import { pushLiteral, pushRegex, pushSchema, stringRef } from "./context.js";
|
|
10
10
|
/**
|
|
11
11
|
* @brief emit function.
|
|
@@ -65,11 +65,19 @@ export function emitFunctions(context) {
|
|
|
65
65
|
function emitBody(schema, value, context) {
|
|
66
66
|
switch (schema.tag) {
|
|
67
67
|
case SchemaTag.Array:
|
|
68
|
-
return emitArrayBody(schema
|
|
68
|
+
return emitArrayBody(schema, value, context);
|
|
69
69
|
case SchemaTag.Tuple:
|
|
70
|
+
if (schema.rest !== undefined) {
|
|
71
|
+
return `return d(${String(pushSchema(context, schema))},${value});`;
|
|
72
|
+
}
|
|
70
73
|
return emitTupleBody(schema.items, value, context);
|
|
71
74
|
case SchemaTag.Record:
|
|
72
75
|
return emitRecordBody(schema.value, value, context);
|
|
76
|
+
case SchemaTag.Map:
|
|
77
|
+
case SchemaTag.Set:
|
|
78
|
+
case SchemaTag.InstanceOf:
|
|
79
|
+
case SchemaTag.Property:
|
|
80
|
+
return `return d(${String(pushSchema(context, schema))},${value});`;
|
|
73
81
|
case SchemaTag.Object:
|
|
74
82
|
return emitObjectBody(schema, value, context);
|
|
75
83
|
case SchemaTag.DiscriminatedUnion:
|
|
@@ -97,6 +105,8 @@ export function emitExpression(schema, value, context) {
|
|
|
97
105
|
return emitString(schema, value, context);
|
|
98
106
|
case SchemaTag.Number:
|
|
99
107
|
return emitNumber(schema, value);
|
|
108
|
+
case SchemaTag.Date:
|
|
109
|
+
return emitDate(schema, value);
|
|
100
110
|
case SchemaTag.BigInt:
|
|
101
111
|
return `(typeof ${value}==="bigint")`;
|
|
102
112
|
case SchemaTag.Symbol:
|
|
@@ -112,9 +122,17 @@ export function emitExpression(schema, value, context) {
|
|
|
112
122
|
*/
|
|
113
123
|
return `${emitFunction(schema, context)}(${value})`;
|
|
114
124
|
case SchemaTag.Tuple:
|
|
125
|
+
if (schema.rest !== undefined) {
|
|
126
|
+
return `d(${String(pushSchema(context, schema))},${value})`;
|
|
127
|
+
}
|
|
115
128
|
return `${emitFunction(schema, context)}(${value})`;
|
|
116
129
|
case SchemaTag.Record:
|
|
117
130
|
return `${emitFunction(schema, context)}(${value})`;
|
|
131
|
+
case SchemaTag.Map:
|
|
132
|
+
case SchemaTag.Set:
|
|
133
|
+
case SchemaTag.InstanceOf:
|
|
134
|
+
case SchemaTag.Property:
|
|
135
|
+
return `d(${String(pushSchema(context, schema))},${value})`;
|
|
118
136
|
case SchemaTag.Object:
|
|
119
137
|
return `${emitFunction(schema, context)}(${value})`;
|
|
120
138
|
case SchemaTag.Union:
|
|
@@ -139,6 +157,34 @@ export function emitExpression(schema, value, context) {
|
|
|
139
157
|
return `d(${String(pushSchema(context, schema))},${value})`;
|
|
140
158
|
}
|
|
141
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* @brief Emit a Date predicate expression.
|
|
162
|
+
* @param schema Date schema with normalized checks.
|
|
163
|
+
* @param value Generated expression for the candidate value.
|
|
164
|
+
* @returns JavaScript expression that accepts valid Date objects within bounds.
|
|
165
|
+
*/
|
|
166
|
+
function emitDate(schema, value) {
|
|
167
|
+
const checks = schema.checks;
|
|
168
|
+
const parts = [
|
|
169
|
+
`(${value} instanceof Date)`,
|
|
170
|
+
`dg(${value})`
|
|
171
|
+
];
|
|
172
|
+
for (let index = 0; index < checks.length; index += 1) {
|
|
173
|
+
const check = checks[index];
|
|
174
|
+
if (check === undefined) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
switch (check.tag) {
|
|
178
|
+
case DateCheckTag.Min:
|
|
179
|
+
parts.push(`dt(${value})>=${String(check.value)}`);
|
|
180
|
+
break;
|
|
181
|
+
case DateCheckTag.Max:
|
|
182
|
+
parts.push(`dt(${value})<=${String(check.value)}`);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return `(${parts.join("&&")})`;
|
|
187
|
+
}
|
|
142
188
|
/**
|
|
143
189
|
* @brief emit array body.
|
|
144
190
|
* @details Generated-source helpers keep the side-table ABI and JavaScript source shape
|
|
@@ -148,7 +194,8 @@ export function emitExpression(schema, value, context) {
|
|
|
148
194
|
* @param context Shared code-generation context.
|
|
149
195
|
* @returns JavaScript source for an array predicate body.
|
|
150
196
|
*/
|
|
151
|
-
function emitArrayBody(
|
|
197
|
+
function emitArrayBody(schema, value, context) {
|
|
198
|
+
const item = schema.item;
|
|
152
199
|
const itemFunction = emitFunction(item, context);
|
|
153
200
|
/*
|
|
154
201
|
* Descriptor reads block accessor-backed slots without executing getters.
|
|
@@ -157,6 +204,7 @@ function emitArrayBody(item, value, context) {
|
|
|
157
204
|
*/
|
|
158
205
|
return [
|
|
159
206
|
`if(!Array.isArray(${value}))return false;`,
|
|
207
|
+
emitArrayLengthBody(value, schema.checks),
|
|
160
208
|
`for(let i=0;i<${value}.length;i+=1){`,
|
|
161
209
|
`const d=gp(${value},i);`,
|
|
162
210
|
`if(d!==undefined&&!h.call(d,"value"))return false;`,
|
|
@@ -165,6 +213,34 @@ function emitArrayBody(item, value, context) {
|
|
|
165
213
|
"return true;"
|
|
166
214
|
].join("");
|
|
167
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* @brief Emit array length checks for direct predicate bodies.
|
|
218
|
+
* @param value Generated expression for the candidate array.
|
|
219
|
+
* @param checks Normalized array length checks.
|
|
220
|
+
* @returns Straight-line early-return checks, or an empty string.
|
|
221
|
+
*/
|
|
222
|
+
function emitArrayLengthBody(value, checks) {
|
|
223
|
+
if (checks.length === 0) {
|
|
224
|
+
return "";
|
|
225
|
+
}
|
|
226
|
+
const chunks = [];
|
|
227
|
+
for (let index = 0; index < checks.length; index += 1) {
|
|
228
|
+
const check = checks[index];
|
|
229
|
+
if (check === undefined) {
|
|
230
|
+
chunks.push("return false;");
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
switch (check.tag) {
|
|
234
|
+
case ArrayCheckTag.Min:
|
|
235
|
+
chunks.push(`if(${value}.length<${String(check.value)})return false;`);
|
|
236
|
+
break;
|
|
237
|
+
case ArrayCheckTag.Max:
|
|
238
|
+
chunks.push(`if(${value}.length>${String(check.value)})return false;`);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return chunks.join("");
|
|
243
|
+
}
|
|
168
244
|
/**
|
|
169
245
|
* @brief emit record body.
|
|
170
246
|
* @details Generated-source helpers keep the side-table ABI and JavaScript source shape
|
|
@@ -251,9 +327,34 @@ function emitObjectBody(schema, value, context) {
|
|
|
251
327
|
chunks.push(`if(${descriptor}===undefined||!h.call(${descriptor},"value"))return false;`, `const ${itemValue}=${descriptor}.value;`, `if(!${emitExpression(entry.schema, itemValue, context)})return false;`);
|
|
252
328
|
}
|
|
253
329
|
}
|
|
330
|
+
chunks.push(emitObjectCatchallBody(schema, value, context));
|
|
254
331
|
chunks.push("return true;");
|
|
255
332
|
return chunks.join("");
|
|
256
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* @brief Emit catchall checks for undeclared object keys.
|
|
336
|
+
* @param schema Object schema with optional catchall.
|
|
337
|
+
* @param value Generated expression for the object value.
|
|
338
|
+
* @param context Shared code-generation context.
|
|
339
|
+
* @returns Generated source that validates extra own keys.
|
|
340
|
+
*/
|
|
341
|
+
function emitObjectCatchallBody(schema, value, context) {
|
|
342
|
+
if (schema.catchall === undefined) {
|
|
343
|
+
return "";
|
|
344
|
+
}
|
|
345
|
+
const keys = schema.keys;
|
|
346
|
+
const membership = keyMembershipExpression("key", keys, context);
|
|
347
|
+
return [
|
|
348
|
+
`const cx=Reflect.ownKeys(${value});`,
|
|
349
|
+
"for(let ci=0;ci<cx.length;ci+=1){",
|
|
350
|
+
"const key=cx[ci];",
|
|
351
|
+
`if(typeof key==="string"&&${membership})continue;`,
|
|
352
|
+
`const cd=gp(${value},key);`,
|
|
353
|
+
"if(cd===undefined||!h.call(cd,\"value\"))return false;",
|
|
354
|
+
`if(!${emitExpression(schema.catchall, "cd.value", context)})return false;`,
|
|
355
|
+
"}"
|
|
356
|
+
].join("");
|
|
357
|
+
}
|
|
257
358
|
/**
|
|
258
359
|
* @brief emit strict object key body.
|
|
259
360
|
* @details Emits a low-allocation known-key check specialized for one object shape.
|
|
@@ -263,7 +364,7 @@ function emitObjectBody(schema, value, context) {
|
|
|
263
364
|
* @returns Generated strict-key prelude, or an empty string for passthrough objects.
|
|
264
365
|
*/
|
|
265
366
|
function emitStrictObjectKeyBody(schema, value, context) {
|
|
266
|
-
if (schema.mode !== ObjectModeTag.Strict) {
|
|
367
|
+
if (schema.mode !== ObjectModeTag.Strict || schema.catchall !== undefined) {
|
|
267
368
|
return "";
|
|
268
369
|
}
|
|
269
370
|
const entries = schema.entries;
|
|
@@ -347,6 +448,27 @@ function emitString(schema, value, context) {
|
|
|
347
448
|
case StringCheckTag.Uuid:
|
|
348
449
|
parts.push(emitRegex(value, UUID_PATTERN, context));
|
|
349
450
|
break;
|
|
451
|
+
case StringCheckTag.Email:
|
|
452
|
+
parts.push(emitRegex(value, EMAIL_PATTERN, context));
|
|
453
|
+
break;
|
|
454
|
+
case StringCheckTag.Url:
|
|
455
|
+
parts.push(emitRegex(value, URL_PATTERN, context));
|
|
456
|
+
break;
|
|
457
|
+
case StringCheckTag.IsoDate:
|
|
458
|
+
parts.push(emitRegex(value, ISO_DATE_PATTERN, context));
|
|
459
|
+
break;
|
|
460
|
+
case StringCheckTag.IsoDateTime:
|
|
461
|
+
parts.push(emitRegex(value, ISO_DATETIME_PATTERN, context));
|
|
462
|
+
break;
|
|
463
|
+
case StringCheckTag.Ulid:
|
|
464
|
+
parts.push(emitRegex(value, ULID_PATTERN, context));
|
|
465
|
+
break;
|
|
466
|
+
case StringCheckTag.Ipv4:
|
|
467
|
+
parts.push(emitRegex(value, IPV4_PATTERN, context));
|
|
468
|
+
break;
|
|
469
|
+
case StringCheckTag.Ipv6:
|
|
470
|
+
parts.push(emitRegex(value, IPV6_PATTERN, context));
|
|
471
|
+
break;
|
|
350
472
|
}
|
|
351
473
|
}
|
|
352
474
|
return `(${parts.join("&&")})`;
|
|
@@ -386,6 +508,15 @@ function emitNumber(schema, value) {
|
|
|
386
508
|
case NumberCheckTag.Lte:
|
|
387
509
|
parts.push(`(${value}<=${String(check.value)})`);
|
|
388
510
|
break;
|
|
511
|
+
case NumberCheckTag.Gt:
|
|
512
|
+
parts.push(`(${value}>${String(check.value)})`);
|
|
513
|
+
break;
|
|
514
|
+
case NumberCheckTag.Lt:
|
|
515
|
+
parts.push(`(${value}<${String(check.value)})`);
|
|
516
|
+
break;
|
|
517
|
+
case NumberCheckTag.MultipleOf:
|
|
518
|
+
parts.push(`(${value}%${String(check.value)}===0)`);
|
|
519
|
+
break;
|
|
389
520
|
}
|
|
390
521
|
}
|
|
391
522
|
return `(${parts.join("&&")})`;
|
|
@@ -441,3 +572,23 @@ function emitRegex(value, regex, context) {
|
|
|
441
572
|
const access = `r[${String(index)}]`;
|
|
442
573
|
return `((${access}.lastIndex=0),${access}.test(${value}))`;
|
|
443
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* @brief Emit a side-table backed key membership expression.
|
|
577
|
+
* @param key Generated expression containing the candidate property key.
|
|
578
|
+
* @param keys Known object shape keys.
|
|
579
|
+
* @param context Shared code-generation context.
|
|
580
|
+
* @returns JavaScript expression that is true for known string keys.
|
|
581
|
+
*/
|
|
582
|
+
function keyMembershipExpression(key, keys, context) {
|
|
583
|
+
if (keys.length === 0) {
|
|
584
|
+
return "false";
|
|
585
|
+
}
|
|
586
|
+
const parts = new Array(keys.length);
|
|
587
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
588
|
+
const value = keys[index];
|
|
589
|
+
parts[index] = value === undefined
|
|
590
|
+
? "false"
|
|
591
|
+
: `${key}===${stringRef(context, value)}`;
|
|
592
|
+
}
|
|
593
|
+
return `(${parts.join("||")})`;
|
|
594
|
+
}
|
|
@@ -57,6 +57,16 @@ export type DynamicCheck = (schemaIndex: number, value: unknown) => boolean;
|
|
|
57
57
|
* @param issues Mutable issue buffer owned by the caller.
|
|
58
58
|
*/
|
|
59
59
|
export type DynamicIssueCheck = (schemaIndex: number, value: unknown, path: readonly PathSegment[], issues: Issue[]) => void;
|
|
60
|
+
/**
|
|
61
|
+
* @brief First-fault fallback for non-lowered schema fragments.
|
|
62
|
+
* @details Opaque lazy and refine schemas use the interpreter for correctness,
|
|
63
|
+
* then return only the first nested issue under the generated path prefix.
|
|
64
|
+
* @param schemaIndex Slot in the side table supplied to the factory.
|
|
65
|
+
* @param value Candidate value for that schema.
|
|
66
|
+
* @param path Path prefix owned by the generated collector.
|
|
67
|
+
* @returns First nested issue, or undefined when the fallback accepts the value.
|
|
68
|
+
*/
|
|
69
|
+
export type DynamicFirstIssueCheck = (schemaIndex: number, value: unknown, path: readonly PathSegment[]) => Issue | undefined;
|
|
60
70
|
/**
|
|
61
71
|
* @brief Runtime helper for strict-object excess key validation.
|
|
62
72
|
* @details Code generation can choose safe or fast property access strategies,
|
|
@@ -76,6 +86,7 @@ export interface RuntimeBundle {
|
|
|
76
86
|
readonly is: BooleanPredicate;
|
|
77
87
|
readonly check: IssueCollectorRoot;
|
|
78
88
|
readonly result: CheckResultRoot;
|
|
89
|
+
readonly first: CheckResultRoot;
|
|
79
90
|
}
|
|
80
91
|
/**
|
|
81
92
|
* @brief Factory signature consumed by `new Function` compiled modules.
|
|
@@ -91,7 +102,7 @@ export interface RuntimeBundle {
|
|
|
91
102
|
* @param strictKeys Shared strict-object excess key helper.
|
|
92
103
|
* @returns Runtime bundle exposed by the compiled guard.
|
|
93
104
|
*/
|
|
94
|
-
export type IsFactory = (literals: readonly LiteralValue[], regexps: readonly RegExp[], keysets: readonly (readonly string[])[], strings: readonly string[], dynamicCheck: DynamicCheck, dynamicIssueCheck: DynamicIssueCheck, strictKeys: StrictKeysCheck) => RuntimeBundle;
|
|
105
|
+
export type IsFactory = (literals: readonly LiteralValue[], regexps: readonly RegExp[], keysets: readonly (readonly string[])[], strings: readonly string[], dynamicCheck: DynamicCheck, dynamicIssueCheck: DynamicIssueCheck, dynamicFirstIssueCheck: DynamicFirstIssueCheck, strictKeys: StrictKeysCheck) => RuntimeBundle;
|
|
95
106
|
/**
|
|
96
107
|
* @brief Build the boolean fallback table reader for generated validators.
|
|
97
108
|
* @details Missing table slots fail closed. That preserves the security
|
|
@@ -110,6 +121,14 @@ export declare function makeDynamicCheck(schemas: readonly Schema[]): DynamicChe
|
|
|
110
121
|
* @returns Issue fallback callback used by emitted collectors.
|
|
111
122
|
*/
|
|
112
123
|
export declare function makeDynamicIssueCheck(schemas: readonly Schema[]): DynamicIssueCheck;
|
|
124
|
+
/**
|
|
125
|
+
* @brief Build the first-fault fallback table reader for generated validators.
|
|
126
|
+
* @details This path is entered only for opaque schema fragments that codegen
|
|
127
|
+
* cannot inline. The nested interpreter result is re-rooted once and returned.
|
|
128
|
+
* @param schemas Schema side table captured by the compiled guard.
|
|
129
|
+
* @returns First-issue fallback callback used by emitted collectors.
|
|
130
|
+
*/
|
|
131
|
+
export declare function makeDynamicFirstIssueCheck(schemas: readonly Schema[]): DynamicFirstIssueCheck;
|
|
113
132
|
/**
|
|
114
133
|
* @brief Safe strict-object key membership helper for generated validators.
|
|
115
134
|
* @details The default compiled mode uses Reflect.ownKeys so symbol and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/compile/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,KAAK,EAAE,GAAG,SAAS,CAAC;AAElF;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,WAAW,CAAC,OAAO,CAAC,CAAC;AAEvE;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAE5E;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC5B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,SAAS,WAAW,EAAE,EAC5B,MAAM,EAAE,KAAK,EAAE,KACd,IAAI,CAAC;AAEV;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,CAC1B,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,SAAS,MAAM,EAAE,KACtB,OAAO,CAAC;AAEb;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/compile/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,KAAK,EAAE,GAAG,SAAS,CAAC;AAElF;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,WAAW,CAAC,OAAO,CAAC,CAAC;AAEvE;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAE5E;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC5B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,SAAS,WAAW,EAAE,EAC5B,MAAM,EAAE,KAAK,EAAE,KACd,IAAI,CAAC;AAEV;;;;;;;;GAQG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACjC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,SAAS,WAAW,EAAE,KAC3B,KAAK,GAAG,SAAS,CAAC;AAEvB;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,CAC1B,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,SAAS,MAAM,EAAE,KACtB,OAAO,CAAC;AAEb;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;CACnC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,SAAS,GAAG,CACpB,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,EACvC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,YAAY,EAAE,YAAY,EAC1B,iBAAiB,EAAE,iBAAiB,EACpC,sBAAsB,EAAE,sBAAsB,EAC9C,UAAU,EAAE,eAAe,KAC1B,aAAa,CAAC;AAEnB;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,YAAY,CAKzE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,SAAS,MAAM,EAAE,GAC3B,iBAAiB,CA8BnB;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACtC,OAAO,EAAE,SAAS,MAAM,EAAE,GAC3B,sBAAsB,CA2BxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACtB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,SAAS,MAAM,EAAE,GACxB,OAAO,CAYT"}
|
package/dist/compile/runtime.js
CHANGED
|
@@ -58,6 +58,37 @@ export function makeDynamicIssueCheck(schemas) {
|
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* @brief Build the first-fault fallback table reader for generated validators.
|
|
63
|
+
* @details This path is entered only for opaque schema fragments that codegen
|
|
64
|
+
* cannot inline. The nested interpreter result is re-rooted once and returned.
|
|
65
|
+
* @param schemas Schema side table captured by the compiled guard.
|
|
66
|
+
* @returns First-issue fallback callback used by emitted collectors.
|
|
67
|
+
*/
|
|
68
|
+
export function makeDynamicFirstIssueCheck(schemas) {
|
|
69
|
+
return (schemaIndex, value, path) => {
|
|
70
|
+
const schema = schemas[schemaIndex];
|
|
71
|
+
if (schema === undefined) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const result = checkSchema(schema, value);
|
|
75
|
+
if (result.ok) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
const issue = result.error[0];
|
|
79
|
+
if (issue === undefined) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
const nestedPath = Object.freeze(path.concat(issue.path));
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
path: nestedPath,
|
|
85
|
+
code: issue.code,
|
|
86
|
+
expected: issue.expected,
|
|
87
|
+
actual: issue.actual,
|
|
88
|
+
message: issue.message
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
}
|
|
61
92
|
/**
|
|
62
93
|
* @brief Safe strict-object key membership helper for generated validators.
|
|
63
94
|
* @details The default compiled mode uses Reflect.ownKeys so symbol and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/compile/source.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/compile/source.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAMjD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAoB,GAC3B,oBAAoB,CAsDtB"}
|
package/dist/compile/source.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { emitCheckFunction, emitCheckFunctions } from "./check.js";
|
|
8
8
|
import { createEmitContext } from "./context.js";
|
|
9
|
+
import { emitFirstFunction, emitFirstFunctions } from "./first.js";
|
|
9
10
|
import { emitGraphFunction, emitGraphFunctions } from "./graph-predicate.js";
|
|
10
11
|
import { safeFunctionName } from "./names.js";
|
|
11
12
|
/**
|
|
@@ -27,25 +28,32 @@ export function emitCompiledSourceBundle(schema, name, mode = "safe") {
|
|
|
27
28
|
*/
|
|
28
29
|
const root = emitGraphFunction(schema, context, directRoot ? functionName : undefined);
|
|
29
30
|
const checkRoot = emitCheckFunction(schema, context);
|
|
31
|
+
const firstRoot = emitFirstFunction(schema, context);
|
|
30
32
|
const checkFunctionName = `${functionName}_check`;
|
|
31
33
|
const resultFunctionName = `${functionName}_result`;
|
|
34
|
+
const firstFunctionName = `${functionName}_first`;
|
|
32
35
|
const isProperty = directRoot
|
|
33
36
|
? `is:${root}`
|
|
34
37
|
: `is:function ${functionName}(x){return ${root}(x);}`;
|
|
35
38
|
const graphFunctions = emitGraphFunctions(context);
|
|
36
39
|
const checkFunctions = emitCheckFunctions(context);
|
|
40
|
+
const firstFunctions = emitFirstFunctions(context);
|
|
37
41
|
const rootPathIsFrozen = canReuseFrozenRootPath(checkFunctions);
|
|
42
|
+
const firstPathIsFrozen = canReuseFrozenRootPath(firstFunctions);
|
|
38
43
|
const rootPath = rootPathIsFrozen ? "z" : "[]";
|
|
44
|
+
const firstPath = firstPathIsFrozen ? "z" : "[]";
|
|
39
45
|
/*
|
|
40
46
|
* Boolean is() owns the hot path; check()/result() first reuse it so valid
|
|
41
47
|
* data avoids diagnostic allocation. Only failing inputs enter the check tree.
|
|
42
48
|
*/
|
|
43
49
|
const issueCollector = `check:function ${checkFunctionName}(x){if(${root}(x))return;const s=[];${checkRoot}(x,${rootPath},s);return s;}`;
|
|
44
50
|
const resultCollector = `result:function ${resultFunctionName}(x){if(${root}(x))return ${emitSuccessResult(mode, "x")};const s=[];${checkRoot}(x,${rootPath},s);return Object.freeze({ok:false,error:Object.freeze(s)});}`;
|
|
51
|
+
const firstCollector = `first:function ${firstFunctionName}(x){if(${root}(x))return ${emitSuccessResult(mode, "x")};const e=${firstRoot}(x,${firstPath});if(e===undefined)return ${emitSuccessResult(mode, "x")};return Object.freeze({ok:false,error:Object.freeze([e])});}`;
|
|
45
52
|
const body = [
|
|
46
53
|
graphFunctions,
|
|
47
54
|
checkFunctions,
|
|
48
|
-
|
|
55
|
+
firstFunctions,
|
|
56
|
+
`return {${isProperty},${issueCollector},${resultCollector},${firstCollector}};`
|
|
49
57
|
].join("");
|
|
50
58
|
const source = [
|
|
51
59
|
"\"use strict\";",
|
|
@@ -112,6 +120,7 @@ function isFactoryParameterName(name) {
|
|
|
112
120
|
case "u":
|
|
113
121
|
case "d":
|
|
114
122
|
case "m":
|
|
123
|
+
case "mf":
|
|
115
124
|
case "sk":
|
|
116
125
|
case "w":
|
|
117
126
|
return true;
|
|
@@ -164,15 +173,16 @@ function emitHelperPrelude(body, rootPathIsFrozen) {
|
|
|
164
173
|
pushHelper(chunks, needed, "hd", "const hd=function(v,k){if(!ph(v))return false;const d=gp(v,k);return d!==undefined&&h.call(d,\"value\");};");
|
|
165
174
|
pushHelper(chunks, needed, "fn", "const fn=function(v){return typeof v===\"number\"&&Number.isFinite(v);};");
|
|
166
175
|
pushHelper(chunks, needed, "nc", "const nc=function(x,y,gte){return typeof x===\"number\"&&typeof y===\"number\"&&(gte?x>=y:x<=y);};");
|
|
176
|
+
pushHelper(chunks, needed, "dg", "const __tg=Date.prototype.getTime;const dg=function(v){return v instanceof Date&&Number.isFinite(__tg.call(v));};const dt=function(v){return __tg.call(v);};");
|
|
167
177
|
pushHelper(chunks, needed, "sb", "const sb=function(v,b,min){return typeof v===\"string\"&&(min?v.length>=b:v.length<=b);};");
|
|
168
178
|
pushHelper(chunks, needed, "rx", "const rx=function(v,re){if(typeof v!==\"string\")return false;re.lastIndex=0;const ok=re.test(v);re.lastIndex=0;return ok;};");
|
|
169
179
|
pushHelper(chunks, needed, "ai", "const ai=function(k,n){if(k.length===0||k===\"length\")return false;const i=Number(k);return Number.isInteger(i)&&i>=0&&i<=4294967294&&i<n&&String(i)===k;};");
|
|
170
|
-
pushHelper(chunks, needed, "ea", "const ea=function(v,f){if(!Array.isArray(v))return false;for(let i=0;i<v.length;i+=1){const d=gp(v,i);if(
|
|
180
|
+
pushHelper(chunks, needed, "ea", "const ea=function(v,f){if(!Array.isArray(v))return false;for(let i=0;i<v.length;i+=1){const d=gp(v,i);if(!f(d===undefined?undefined:d.value))return false;}return true;};");
|
|
171
181
|
pushHelper(chunks, needed, "eu", "const eu=function(v,f){if(!Array.isArray(v))return false;const xs=Object.getOwnPropertyNames(v);for(let i=0;i<xs.length;i+=1){const k=xs[i];if(!ai(k,v.length))continue;const d=gp(v,k);if(d!==undefined&&!h.call(d,\"value\"))return false;if(d!==undefined&&!f(d.value))return false;}return true;};");
|
|
172
182
|
pushHelper(chunks, needed, "ev", "const ev=function(v,i,f){const d=gp(v,i);if(d!==undefined&&!h.call(d,\"value\"))return false;return f(d===undefined?undefined:d.value);};");
|
|
173
183
|
pushHelper(chunks, needed, "er", "const er=function(v,f){if(!o(v))return false;for(const key in v){if(!h.call(v,key))continue;const d=gp(v,key);if(d===undefined||!h.call(d,\"value\")||!f(d.value))return false;}return true;};");
|
|
174
184
|
pushHelper(chunks, needed, "dj", "const dj=function(v,key,ks){if(!o(v))return false;const d=g(v,key);if(d===undefined||typeof d.value!==\"string\")return false;const i=ks.indexOf(d.value);return i>=0&&arguments[i+3](v);};");
|
|
175
|
-
pushHelper(chunks, needed, "a", "const a=function(v){if(v===null)return \"null\";if(Array.isArray(v))return \"array\";if(typeof v===\"bigint\")return \"bigint\";if(typeof v===\"symbol\")return \"symbol\";if(typeof v===\"number\"&&Number.isNaN(v))return \"nan\";return typeof v;};");
|
|
185
|
+
pushHelper(chunks, needed, "a", "const a=function(v){if(v===null)return \"null\";if(Array.isArray(v))return \"array\";if(v instanceof Date)return \"date\";if(v instanceof Map)return \"map\";if(v instanceof Set)return \"set\";if(typeof v===\"bigint\")return \"bigint\";if(typeof v===\"symbol\")return \"symbol\";if(typeof v===\"number\"&&Number.isNaN(v))return \"nan\";return typeof v;};");
|
|
176
186
|
pushHelper(chunks, needed, "le", "const le=function(v){if(v===null)return \"null\";if(v===undefined)return \"undefined\";if(typeof v===\"string\")return JSON.stringify(v);if(typeof v===\"number\"&&Object.is(v,-0))return \"-0\";if(typeof v===\"symbol\")return String(v);return String(v);};");
|
|
177
187
|
pushHelper(chunks, needed, "w", "const w=function(){const x=new Array(u.length);for(let i=0;i<u.length;i+=1)x[i]=Object.freeze([u[i]]);return x;}();");
|
|
178
188
|
pushHelper(chunks, needed, "q", rootPathIsFrozen
|
|
@@ -187,6 +197,10 @@ function emitHelperPrelude(body, rootPathIsFrozen) {
|
|
|
187
197
|
pushHelper(chunks, needed, "q2", rootPathIsFrozen
|
|
188
198
|
? "const q2=function(s,p,a,b,c,e,x){s.push(Object.freeze({path:Object.freeze([a,b]),code:c,expected:e,actual:x,message:undefined}));};"
|
|
189
199
|
: "const q2=function(s,p,a,b,c,e,x){const n=p.length;const y=n===0?[a,b]:p.slice();if(n!==0){y.push(a);y.push(b);}Object.freeze(y);s.push(Object.freeze({path:y,code:c,expected:e,actual:x,message:undefined}));};");
|
|
200
|
+
pushHelper(chunks, needed, "fq", "const fq=function(p,c,e,x){const y=p.length===0?z:Object.freeze(p.slice());return Object.freeze({path:y,code:c,expected:e,actual:x,message:undefined});};");
|
|
201
|
+
pushHelper(chunks, needed, "fq1", "const fq1=function(p,k,c,e,x){const n=p.length;const y=n===0?[k]:p.slice();if(n!==0)y.push(k);Object.freeze(y);return Object.freeze({path:y,code:c,expected:e,actual:x,message:undefined});};");
|
|
202
|
+
pushHelper(chunks, needed, "fq1s", "const fq1s=function(p,i,c,e,x){if(p.length===0)return Object.freeze({path:w[i],code:c,expected:e,actual:x,message:undefined});const y=p.slice();y.push(u[i]);Object.freeze(y);return Object.freeze({path:y,code:c,expected:e,actual:x,message:undefined});};");
|
|
203
|
+
pushHelper(chunks, needed, "fq2", "const fq2=function(p,a,b,c,e,x){const n=p.length;const y=n===0?[a,b]:p.slice();if(n!==0){y.push(a);y.push(b);}Object.freeze(y);return Object.freeze({path:y,code:c,expected:e,actual:x,message:undefined});};");
|
|
190
204
|
return chunks.join("");
|
|
191
205
|
}
|
|
192
206
|
/**
|
|
@@ -281,8 +295,12 @@ function closeHelperDependencies(needed) {
|
|
|
281
295
|
changed = addDependencies(needed, "ev", ["gp", "h"]) || changed;
|
|
282
296
|
changed = addDependencies(needed, "er", ["o", "gp", "h"]) || changed;
|
|
283
297
|
changed = addDependencies(needed, "dj", ["o", "g"]) || changed;
|
|
298
|
+
changed = addDependencies(needed, "dt", ["dg"]) || changed;
|
|
284
299
|
changed = addDependencies(needed, "q", ["z"]) || changed;
|
|
285
300
|
changed = addDependencies(needed, "q1s", ["w"]) || changed;
|
|
301
|
+
changed = addDependencies(needed, "fq", ["z"]) || changed;
|
|
302
|
+
changed = addDependencies(needed, "fq1", ["z"]) || changed;
|
|
303
|
+
changed = addDependencies(needed, "fq1s", ["w"]) || changed;
|
|
286
304
|
}
|
|
287
305
|
}
|
|
288
306
|
/**
|
|
@@ -328,6 +346,8 @@ function isRuntimeHelperName(name) {
|
|
|
328
346
|
case "hd":
|
|
329
347
|
case "fn":
|
|
330
348
|
case "nc":
|
|
349
|
+
case "dg":
|
|
350
|
+
case "dt":
|
|
331
351
|
case "sb":
|
|
332
352
|
case "rx":
|
|
333
353
|
case "ai":
|
|
@@ -343,6 +363,10 @@ function isRuntimeHelperName(name) {
|
|
|
343
363
|
case "q1":
|
|
344
364
|
case "q1s":
|
|
345
365
|
case "q2":
|
|
366
|
+
case "fq":
|
|
367
|
+
case "fq1":
|
|
368
|
+
case "fq1s":
|
|
369
|
+
case "fq2":
|
|
346
370
|
return true;
|
|
347
371
|
default:
|
|
348
372
|
return false;
|
package/dist/compile/types.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface EmitContext {
|
|
|
22
22
|
readonly functionNames: Map<Schema, string>;
|
|
23
23
|
readonly checkFunctions: FunctionSource[];
|
|
24
24
|
readonly checkFunctionNames: Map<Schema, string>;
|
|
25
|
+
readonly firstFunctions: FunctionSource[];
|
|
26
|
+
readonly firstFunctionNames: Map<Schema, string>;
|
|
25
27
|
readonly stringIndexes: Map<string, number>;
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/compile/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;GAIG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,cAAc,EAAE,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAC1B,MAAM,EACN,SAAS,SAAS,QAAQ,GAAG,UAAU,CACzC,SAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;CAC1C"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/compile/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;GAIG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,cAAc,EAAE,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,QAAQ,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAC1B,MAAM,EACN,SAAS,SAAS,QAAQ,GAAG,UAAU,CACzC,SAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;CAC1C"}
|