@amritk/validation 0.3.2 → 0.4.1
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/AI.md +25 -7
- package/README.md +109 -2
- package/dist/parsers/generators/build-schema.js +12 -2
- package/dist/parsers/generators/generate-exact-half.d.ts +50 -0
- package/dist/parsers/generators/generate-exact-half.js +165 -0
- package/dist/parsers/generators/generate-files.d.ts +8 -0
- package/dist/parsers/generators/generate-files.js +11 -1
- package/dist/parsers/generators/generate-parser-function.d.ts +6 -0
- package/dist/parsers/generators/generate-parser-function.js +48 -10
- package/dist/parsers/helpers/collect-helpers.d.ts +1 -1
- package/dist/parsers/helpers/collect-helpers.js +25 -2
- package/dist/validators/generators/build-schema.d.ts +1 -1
- package/dist/validators/generators/build-schema.js +46 -12
- package/dist/validators/generators/collect-validator-imports.d.ts +7 -0
- package/dist/validators/generators/collect-validator-imports.js +4 -1
- package/dist/validators/generators/generate-coerce-function.d.ts +46 -1
- package/dist/validators/generators/generate-coerce-function.js +181 -53
- package/dist/validators/generators/generate-files.js +13 -5
- package/dist/validators/generators/generate-repair-function.d.ts +6 -1
- package/dist/validators/generators/generate-repair-function.js +70 -11
- package/dist/validators/generators/generate-validator-function.d.ts +48 -0
- package/dist/validators/generators/generate-validator-function.js +165 -51
- package/package.json +2 -2
|
@@ -108,7 +108,7 @@ const blockFailFast = (ctx) => {
|
|
|
108
108
|
if (returnsOnReport(ctx.sink))
|
|
109
109
|
ctx.failFast.blocked = true;
|
|
110
110
|
};
|
|
111
|
-
const hoistPrefix = (ctx) => ctx.
|
|
111
|
+
const hoistPrefix = (ctx) => ctx.hoistNamespace;
|
|
112
112
|
const typeTestAccessor = (raw, ctx) => returnsOnReport(ctx.sink) ? `(${raw} as unknown)` : raw;
|
|
113
113
|
const emittedName = (typeName, failFast) => failFast.on ? checkerName(typeName) : validatorName(typeName);
|
|
114
114
|
const errorsBinding = (failFast) => failFast.on ? [] : [" let errors: ValidationError[] | undefined"];
|
|
@@ -129,7 +129,10 @@ const createRootContext = (rootSchema, formats = NO_FORMATS, branchErrors = fals
|
|
|
129
129
|
// `selectBranchErrors` sweep *after* the `return` that ends the function, which
|
|
130
130
|
// is unreachable code the consumer's build rejects.
|
|
131
131
|
branchErrors: branchErrors && !failFast.on,
|
|
132
|
-
failFast
|
|
132
|
+
failFast,
|
|
133
|
+
hoistNamespace: failFast.on ? "_check" : "",
|
|
134
|
+
refGuard: guardName,
|
|
135
|
+
namedMatchers: /* @__PURE__ */ new Map()
|
|
133
136
|
});
|
|
134
137
|
const pointerSegment = (key) => {
|
|
135
138
|
const pointer = key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
@@ -145,8 +148,22 @@ const patternPropertySources = (schema) => {
|
|
|
145
148
|
};
|
|
146
149
|
const readsBinding = (name, text) => new RegExp(`\\b${name}\\b`).test(text);
|
|
147
150
|
const readsObjBinding = (text) => readsBinding("obj", text);
|
|
151
|
+
const keptHoisted = (hoisted, text) => {
|
|
152
|
+
const kept = /* @__PURE__ */ new Set();
|
|
153
|
+
const scan = (source) => {
|
|
154
|
+
for (const entry of hoisted) {
|
|
155
|
+
if (kept.has(entry) || !source.includes(entry.reference))
|
|
156
|
+
continue;
|
|
157
|
+
kept.add(entry);
|
|
158
|
+
if (entry.calls === true)
|
|
159
|
+
scan(entry.declaration);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
scan(text);
|
|
163
|
+
return hoisted.filter((entry) => kept.has(entry)).map((entry) => entry.declaration);
|
|
164
|
+
};
|
|
148
165
|
const withHoisted = (hoisted, text) => {
|
|
149
|
-
const kept = hoisted
|
|
166
|
+
const kept = keptHoisted(hoisted, text);
|
|
150
167
|
const firstBreak = text.indexOf("\n");
|
|
151
168
|
const signature = firstBreak === -1 ? text : text.slice(0, firstBreak);
|
|
152
169
|
const rest = firstBreak === -1 ? "" : text.slice(firstBreak);
|
|
@@ -253,7 +270,7 @@ const generateKeywordChecks = (key, raw, path, schema, suffix, ctx, presence) =>
|
|
|
253
270
|
const lines = [];
|
|
254
271
|
if (hasRef(schema)) {
|
|
255
272
|
const target = refToName(schema.$ref, suffix);
|
|
256
|
-
const delegate = ctx.sink === FAIL_FAST_SINK ? [` const _r = ${checkerName(target)}(${raw}, ${path})`, ` if (_r !== true) return _r`] : ctx.sink === MATCH_BOOL_SINK ? [`
|
|
273
|
+
const delegate = ctx.sink === FAIL_FAST_SINK ? [` const _r = ${checkerName(target)}(${raw}, ${path})`, ` if (_r !== true) return _r`] : ctx.sink === MATCH_BOOL_SINK ? [` if (!${ctx.refGuard(target)}(${raw})) return false`] : [
|
|
257
274
|
` const _r = ${validatorName(target)}(${raw}, ${path})`,
|
|
258
275
|
` if (_r !== true) ${ctx.sink}.push(..._r.errors)`
|
|
259
276
|
];
|
|
@@ -275,7 +292,7 @@ const generateKeywordChecks = (key, raw, path, schema, suffix, ctx, presence) =>
|
|
|
275
292
|
lines.push(` }`);
|
|
276
293
|
}
|
|
277
294
|
if (hasConst(schema)) {
|
|
278
|
-
const mismatch = constMismatchCondition(raw, schema.const);
|
|
295
|
+
const mismatch = constMismatchCondition(typeTestAccessor(raw, ctx), schema.const);
|
|
279
296
|
const msg = JSON.stringify(`must be ${JSON.stringify(schema.const)}`);
|
|
280
297
|
lines.push(` if (${presence}${mismatch}) {`);
|
|
281
298
|
lines.push(` ${pushError(ctx.sink, msg, path, "const", JSON.stringify({ allowedValue: schema.const }))}`);
|
|
@@ -283,7 +300,7 @@ const generateKeywordChecks = (key, raw, path, schema, suffix, ctx, presence) =>
|
|
|
283
300
|
}
|
|
284
301
|
if (hasEnum(schema)) {
|
|
285
302
|
const label = schema.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
286
|
-
lines.push(` if (${presence}!${enumMembershipExpr(schema.enum, raw)}) {`);
|
|
303
|
+
lines.push(` if (${presence}!${enumMembershipExpr(schema.enum, typeTestAccessor(raw, ctx))}) {`);
|
|
287
304
|
lines.push(` ${pushError(ctx.sink, JSON.stringify(`must be one of: ${label}`), path, "enum", JSON.stringify({ allowedValues: schema.enum }))}`);
|
|
288
305
|
lines.push(` }`);
|
|
289
306
|
}
|
|
@@ -413,11 +430,15 @@ const generateConstraintChecks = (key, raw, path, propSchema, suffix, ctx) => {
|
|
|
413
430
|
if (hasRef(itemSchema) && !declaresKeywordOutside(itemSchema, ["$ref"])) {
|
|
414
431
|
const failFast = ctx.sink === FAIL_FAST_SINK;
|
|
415
432
|
const target = refToName(itemSchema.$ref, suffix);
|
|
416
|
-
const onFailure = failFast ? "return _ir" :
|
|
433
|
+
const onFailure = failFast ? "return _ir" : `${ctx.sink}.push(..._ir.errors)`;
|
|
417
434
|
lines.push(` if (Array.isArray(${raw})) {`);
|
|
418
435
|
lines.push(` for (let ${iv} = ${firstTailIndex}; ${iv} < ${raw}.length; ${iv}++) {`);
|
|
419
|
-
|
|
420
|
-
|
|
436
|
+
if (ctx.sink === MATCH_BOOL_SINK) {
|
|
437
|
+
lines.push(` if (!${ctx.refGuard(target)}(${raw}[${iv}])) return false`);
|
|
438
|
+
} else {
|
|
439
|
+
lines.push(` const _ir = ${emittedName(target, ctx.failFast)}(${raw}[${iv}], ${itemPath})`);
|
|
440
|
+
lines.push(` if (_ir !== true) ${onFailure}`);
|
|
441
|
+
}
|
|
421
442
|
lines.push(` }`);
|
|
422
443
|
lines.push(` }`);
|
|
423
444
|
} else if (isSchemaObject(itemSchema)) {
|
|
@@ -544,13 +565,75 @@ const generateValueCheckLines = (_key, raw, path, propSchema, suffix, ctx, requi
|
|
|
544
565
|
hoisted: ctx.hoisted,
|
|
545
566
|
rootSchema: ctx.rootSchema,
|
|
546
567
|
sink: ctx.sink,
|
|
547
|
-
formats: ctx.formats
|
|
568
|
+
formats: ctx.formats,
|
|
569
|
+
hoistNamespace: ctx.hoistNamespace,
|
|
570
|
+
refGuard: ctx.refGuard,
|
|
571
|
+
namedMatchers: ctx.namedMatchers
|
|
548
572
|
};
|
|
549
573
|
lines.push(...generateKeywordChecks("", raw, path, propSchema, suffix, valueCtx, presence));
|
|
550
574
|
return lines;
|
|
551
575
|
};
|
|
552
576
|
const NARROWABLE_REFERENCE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[[A-Za-z_$][\w$]*\]|\[\d+\])*$/;
|
|
553
|
-
const
|
|
577
|
+
const namedMatcher = (sub, suffix, ctx) => {
|
|
578
|
+
const key = JSON.stringify(sub);
|
|
579
|
+
const existing = ctx.namedMatchers.get(key);
|
|
580
|
+
if (existing !== void 0)
|
|
581
|
+
return existing;
|
|
582
|
+
const state = { on: false, blocked: false };
|
|
583
|
+
const fnCtx = {
|
|
584
|
+
...ctx,
|
|
585
|
+
objVar: "obj",
|
|
586
|
+
pathPrefix: "${_path}",
|
|
587
|
+
depth: 0,
|
|
588
|
+
sink: MATCH_BOOL_SINK,
|
|
589
|
+
failFast: state,
|
|
590
|
+
branchErrors: false
|
|
591
|
+
};
|
|
592
|
+
const checks = generateValueChecks("", "input", "`${_path}`", sub, suffix, fnCtx, true);
|
|
593
|
+
if (checks.length === 0)
|
|
594
|
+
return "true";
|
|
595
|
+
if (state.blocked)
|
|
596
|
+
return null;
|
|
597
|
+
const body = checks.join("\n");
|
|
598
|
+
const name = `${hoistPrefix(ctx)}_match${ctx.namedMatchers.size}`;
|
|
599
|
+
const declaration = [
|
|
600
|
+
`const ${name} = (${readsBinding("input", body) ? "input" : "_input"}: unknown): boolean => {`,
|
|
601
|
+
// Some checks build a path for a key or an index before they know whether
|
|
602
|
+
// they fail. Nothing reports it here, so it is rooted at nothing.
|
|
603
|
+
...readsBinding("_path", body) ? [` const _path = ''`] : [],
|
|
604
|
+
body,
|
|
605
|
+
` return true`,
|
|
606
|
+
`}`
|
|
607
|
+
].join("\n");
|
|
608
|
+
ctx.namedMatchers.set(key, name);
|
|
609
|
+
ctx.hoisted.push({ declaration, reference: `${name}(`, calls: true });
|
|
610
|
+
return name;
|
|
611
|
+
};
|
|
612
|
+
const namedBranchErrors = (sub, suffix, ctx) => {
|
|
613
|
+
if (!isSchemaObject(sub))
|
|
614
|
+
return null;
|
|
615
|
+
const key = `explain:${JSON.stringify(sub)}`;
|
|
616
|
+
const existing = ctx.namedMatchers.get(key);
|
|
617
|
+
if (existing !== void 0)
|
|
618
|
+
return existing;
|
|
619
|
+
const fnCtx = { ...ctx, objVar: "obj", pathPrefix: "${_path}", depth: 0, sink: MATCH_ERROR_SINK };
|
|
620
|
+
const checks = generateValueChecks("", "input", "`${_path}`", sub, suffix, fnCtx, true);
|
|
621
|
+
if (checks.length === 0)
|
|
622
|
+
return null;
|
|
623
|
+
const body = checks.join("\n");
|
|
624
|
+
const name = `${hoistPrefix(ctx)}_explain${ctx.namedMatchers.size}`;
|
|
625
|
+
const declaration = [
|
|
626
|
+
`const ${name} = (${readsBinding("input", body) ? "input" : "_input"}: unknown, _path: string): ValidationError[] => {`,
|
|
627
|
+
` const ${MATCH_ERROR_SINK}: ValidationError[] = []`,
|
|
628
|
+
body,
|
|
629
|
+
` return ${MATCH_ERROR_SINK}`,
|
|
630
|
+
`}`
|
|
631
|
+
].join("\n");
|
|
632
|
+
ctx.namedMatchers.set(key, name);
|
|
633
|
+
ctx.hoisted.push({ declaration, reference: `${name}(`, calls: true });
|
|
634
|
+
return name;
|
|
635
|
+
};
|
|
636
|
+
const generateMatchesExpr = (raw, sub, suffix, ctx, required = false) => {
|
|
554
637
|
if (sub === true)
|
|
555
638
|
return "true";
|
|
556
639
|
if (sub === false)
|
|
@@ -558,28 +641,26 @@ const generateMatchesExpr = (raw, sub, suffix, ctx, required = false, branch) =>
|
|
|
558
641
|
if (!isSchemaObject(sub))
|
|
559
642
|
return "true";
|
|
560
643
|
const value = NARROWABLE_REFERENCE.test(raw) ? raw : `_mv${ctx.depth}`;
|
|
561
|
-
const valuePath =
|
|
644
|
+
const valuePath = "`${_path}`";
|
|
562
645
|
const binding = value === raw ? "" : `const ${value}: unknown = ${raw}
|
|
563
646
|
`;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
647
|
+
const named = namedMatcher(sub, suffix, ctx);
|
|
648
|
+
if (named !== null)
|
|
649
|
+
return named === "true" ? "true" : `${named}(${raw})`;
|
|
650
|
+
const matchState = { on: ctx.failFast.on, blocked: false };
|
|
651
|
+
const matchCtx = { ...ctx, sink: MATCH_BOOL_SINK, failFast: matchState, branchErrors: false };
|
|
652
|
+
const boolChecks = generateValueChecks("", value, valuePath, sub, suffix, matchCtx, required);
|
|
653
|
+
if (boolChecks.length === 0)
|
|
654
|
+
return "true";
|
|
655
|
+
if (!matchState.blocked)
|
|
656
|
+
return `((): boolean => {
|
|
572
657
|
${binding}${boolChecks.join("\n")}
|
|
573
658
|
return true })()`;
|
|
574
|
-
}
|
|
575
659
|
const checks = generateValueChecks("", value, valuePath, sub, suffix, { ...ctx, sink: MATCH_ERROR_SINK }, required);
|
|
576
660
|
if (checks.length === 0)
|
|
577
661
|
return "true";
|
|
578
|
-
const body = checks.join("\n");
|
|
579
|
-
const collect = branch === void 0 ? "" : `
|
|
580
|
-
if (_m.length !== 0) (${branch.buffer} ??= []).push(_m)`;
|
|
581
662
|
return `((): boolean => { const _m: ValidationError[] = []
|
|
582
|
-
${binding}${
|
|
663
|
+
${binding}${checks.join("\n")}
|
|
583
664
|
return _m.length === 0 })()`;
|
|
584
665
|
};
|
|
585
666
|
const unevaluatedMatcher = (suffix, ctx) => (accessor, schema, depth) => (
|
|
@@ -629,17 +710,18 @@ const generateUnevaluatedChecks = (raw, path, schema, suffix, ctx) => {
|
|
|
629
710
|
}
|
|
630
711
|
return lines;
|
|
631
712
|
};
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
713
|
+
const branchErrorsReport = (guard, raw, explain, message, keyword, path, ctx) => [
|
|
714
|
+
` if (${guard}) {`,
|
|
715
|
+
` ${pushError(ctx.sink, message, path, keyword)}`,
|
|
716
|
+
...explain.length === 0 ? [] : [
|
|
717
|
+
` const _bp = ${path}`,
|
|
718
|
+
` const _br: ValidationError[][] = []`,
|
|
719
|
+
` for (const _b of [${explain.map((name) => `${name}(${raw}, _bp)`).join(", ")}]) if (_b.length !== 0) _br.push(_b)`,
|
|
720
|
+
` for (const _e of selectBranchErrors(_br, _bp)) ${ctx.sink}.push(_e)`
|
|
721
|
+
],
|
|
641
722
|
` }`
|
|
642
723
|
];
|
|
724
|
+
const branchExplainers = (branches, suffix, ctx) => branches.map((branch) => namedBranchErrors(branch, suffix, ctx)).filter((name) => name !== null);
|
|
643
725
|
const generateCombinatorChecks = (key, raw, path, schema, suffix, ctx) => {
|
|
644
726
|
if (!isSchemaObject(schema))
|
|
645
727
|
return [];
|
|
@@ -649,11 +731,10 @@ const generateCombinatorChecks = (key, raw, path, schema, suffix, ctx) => {
|
|
|
649
731
|
lines.push(...generateValueChecks(key, raw, path, branch, suffix, ctx, true));
|
|
650
732
|
}
|
|
651
733
|
if (hasAnyOf(schema) && schema.anyOf.length > 0) {
|
|
652
|
-
const
|
|
653
|
-
const conds = schema.anyOf.map((b) => generateMatchesExpr(raw, b, suffix, ctx, true, collector));
|
|
734
|
+
const conds = schema.anyOf.map((b) => generateMatchesExpr(raw, b, suffix, ctx, true));
|
|
654
735
|
if (!conds.includes("true")) {
|
|
655
736
|
if (ctx.branchErrors) {
|
|
656
|
-
lines.push(...
|
|
737
|
+
lines.push(...branchErrorsReport(`!(${conds.join(" || ")})`, raw, branchExplainers(schema.anyOf, suffix, ctx), `'must match a schema in anyOf'`, "anyOf", path, ctx));
|
|
657
738
|
} else {
|
|
658
739
|
const condition = `!(${conds.join(" || ")})`;
|
|
659
740
|
if (LITERAL_CONDITION.test(condition))
|
|
@@ -665,20 +746,18 @@ const generateCombinatorChecks = (key, raw, path, schema, suffix, ctx) => {
|
|
|
665
746
|
}
|
|
666
747
|
}
|
|
667
748
|
if (hasOneOf(schema) && schema.oneOf.length > 0) {
|
|
668
|
-
const
|
|
669
|
-
const conds = schema.oneOf.map((b) => `(${generateMatchesExpr(raw, b, suffix, ctx, true, collector)} ? 1 : 0)`);
|
|
749
|
+
const conds = schema.oneOf.map((b) => `(${generateMatchesExpr(raw, b, suffix, ctx, true)} ? 1 : 0)`);
|
|
670
750
|
const message = `'must match exactly one schema in oneOf'`;
|
|
671
751
|
if (!ctx.branchErrors) {
|
|
672
752
|
lines.push(` if ((${conds.join(" + ")}) !== 1) {`);
|
|
673
753
|
lines.push(` ${pushError(ctx.sink, message, path, "oneOf")}`);
|
|
674
754
|
lines.push(` }`);
|
|
675
755
|
} else {
|
|
756
|
+
const explain = branchExplainers(schema.oneOf, suffix, ctx);
|
|
676
757
|
lines.push(` {`);
|
|
677
|
-
lines.push(` let ${BRANCH_BUFFER}: ValidationError[][] | null = null`);
|
|
678
758
|
lines.push(` const _n = ${conds.join(" + ")}`);
|
|
679
|
-
|
|
680
|
-
lines.push(
|
|
681
|
-
lines.push(` ${selectBranchErrorsLine(path, ctx)}`);
|
|
759
|
+
const report = branchErrorsReport("_n === 0", raw, explain, message, "oneOf", path, ctx).map((line) => ` ${line}`);
|
|
760
|
+
lines.push(...report.slice(0, -1));
|
|
682
761
|
lines.push(` } else if (_n !== 1) {`);
|
|
683
762
|
lines.push(` ${pushError(ctx.sink, message, path, "oneOf")}`);
|
|
684
763
|
lines.push(` }`);
|
|
@@ -782,7 +861,10 @@ const generateInlineObjectChecks = (key, propSchema, raw, suffix, ctx) => {
|
|
|
782
861
|
hoisted: ctx.hoisted,
|
|
783
862
|
rootSchema: ctx.rootSchema,
|
|
784
863
|
sink: ctx.sink,
|
|
785
|
-
formats: ctx.formats
|
|
864
|
+
formats: ctx.formats,
|
|
865
|
+
hoistNamespace: ctx.hoistNamespace,
|
|
866
|
+
refGuard: ctx.refGuard,
|
|
867
|
+
namedMatchers: ctx.namedMatchers
|
|
786
868
|
};
|
|
787
869
|
const required = new Set(hasRequired(propSchema) ? propSchema.required : []);
|
|
788
870
|
const properties = hasProperties(propSchema) ? propSchema.properties : {};
|
|
@@ -820,7 +902,10 @@ const generatePropertyNameChecks = (nameSchema, suffix, ctx) => {
|
|
|
820
902
|
hoisted: ctx.hoisted,
|
|
821
903
|
rootSchema: ctx.rootSchema,
|
|
822
904
|
sink: ctx.sink,
|
|
823
|
-
formats: ctx.formats
|
|
905
|
+
formats: ctx.formats,
|
|
906
|
+
hoistNamespace: ctx.hoistNamespace,
|
|
907
|
+
refGuard: ctx.refGuard,
|
|
908
|
+
namedMatchers: ctx.namedMatchers
|
|
824
909
|
};
|
|
825
910
|
const checks = generateValueChecks("", "_name", at, nameSchema, suffix, nameCtx, true);
|
|
826
911
|
if (checks.length === 0)
|
|
@@ -1386,7 +1471,8 @@ const typeDescribesEveryAcceptedValue = (schema) => {
|
|
|
1386
1471
|
}
|
|
1387
1472
|
return !IMPLICIT_OBJECT_KEYWORDS.some((keyword) => declaresKey(s, keyword));
|
|
1388
1473
|
};
|
|
1389
|
-
const generateBooleanGuard = (schema, typeName, _suffix = "", unknownKeys = DEFAULT_UNKNOWN_KEYS, formats = NO_FORMATS) =>
|
|
1474
|
+
const generateBooleanGuard = (schema, typeName, _suffix = "", unknownKeys = DEFAULT_UNKNOWN_KEYS, formats = NO_FORMATS) => generateBooleanGuardSource(schema, typeName, unknownKeys, formats).code;
|
|
1475
|
+
const generateBooleanGuardSource = (schema, typeName, unknownKeys = DEFAULT_UNKNOWN_KEYS, formats = NO_FORMATS) => {
|
|
1390
1476
|
const name = guardName(typeName);
|
|
1391
1477
|
const returns = typeDescribesEveryAcceptedValue(rewriteNullable(schema)) ? `input is ${typeName}` : "boolean";
|
|
1392
1478
|
const fallback = `export const ${name} = (input: unknown): ${returns} => ${validatorName(typeName)}(input) === true`;
|
|
@@ -1394,12 +1480,12 @@ const generateBooleanGuard = (schema, typeName, _suffix = "", unknownKeys = DEFA
|
|
|
1394
1480
|
const ctx = createGuardContext(unknownKeys, formats);
|
|
1395
1481
|
if (declaresObjectType(rewritten)) {
|
|
1396
1482
|
if (!objectRootIsSelfContained(rewritten))
|
|
1397
|
-
return fallback;
|
|
1483
|
+
return { code: fallback, standalone: false };
|
|
1398
1484
|
const block = booleanObjectParts(rewritten, "input", "obj", ctx);
|
|
1399
1485
|
if (block === null)
|
|
1400
|
-
return fallback;
|
|
1486
|
+
return { code: fallback, standalone: false };
|
|
1401
1487
|
const body = isFlatGuardBlock(block) ? [` return (`, block.conditions.map((part) => ` ${part}`).join(" &&\n"), ` )`] : [...renderGuardBlock(block, "return false", " ", "lines", unknownKeys), ` return true`];
|
|
1402
|
-
|
|
1488
|
+
const code = [
|
|
1403
1489
|
`export const ${name} = (input: unknown): ${returns} => {`,
|
|
1404
1490
|
// Same unused-local as the validator's hot guard: a node with no property
|
|
1405
1491
|
// to read guards on the shape alone and never touches the narrowing.
|
|
@@ -1407,11 +1493,12 @@ const generateBooleanGuard = (schema, typeName, _suffix = "", unknownKeys = DEFA
|
|
|
1407
1493
|
...body,
|
|
1408
1494
|
`}`
|
|
1409
1495
|
].join("\n");
|
|
1496
|
+
return { code, standalone: true };
|
|
1410
1497
|
}
|
|
1411
1498
|
const expr = booleanLeafExpr(rewritten, "input", ctx);
|
|
1412
1499
|
if (expr === null)
|
|
1413
|
-
return fallback;
|
|
1414
|
-
return `export const ${name} = (input: unknown): ${returns} => ${expr}
|
|
1500
|
+
return { code: fallback, standalone: false };
|
|
1501
|
+
return { code: `export const ${name} = (input: unknown): ${returns} => ${expr}`, standalone: true };
|
|
1415
1502
|
};
|
|
1416
1503
|
const generateScalarValidator = (schema, typeName, suffix, rootSchema, formats, branchErrors, failFast) => {
|
|
1417
1504
|
const vName = emittedName(typeName, failFast);
|
|
@@ -1693,6 +1780,31 @@ const generateCheckFunction = (schema, typeName, suffix = "", rootSchema, unknow
|
|
|
1693
1780
|
`}`
|
|
1694
1781
|
].join("\n");
|
|
1695
1782
|
};
|
|
1783
|
+
const createSubschemaMatcher = (suffix, rootSchema, formats, hoistNamespace, refGuard = guardName) => {
|
|
1784
|
+
const ctx = { ...createRootContext(rootSchema, formats), hoistNamespace, refGuard };
|
|
1785
|
+
return {
|
|
1786
|
+
test: (sub) => {
|
|
1787
|
+
if (sub === false)
|
|
1788
|
+
return false;
|
|
1789
|
+
const rewritten = rewriteNullable(sub);
|
|
1790
|
+
const state = { on: false, blocked: false };
|
|
1791
|
+
const checks = generateValueChecks("", "input", "`${_path}`", rewritten, suffix, { ...ctx, sink: MATCH_BOOL_SINK, failFast: state, branchErrors: false }, true);
|
|
1792
|
+
if (checks.length === 0)
|
|
1793
|
+
return true;
|
|
1794
|
+
if (!state.blocked)
|
|
1795
|
+
return `${checks.join("\n")}
|
|
1796
|
+
return true`;
|
|
1797
|
+
return ` return ${generateMatchesExpr("input", rewritten, suffix, ctx, true)}`;
|
|
1798
|
+
},
|
|
1799
|
+
named: (sub) => {
|
|
1800
|
+
if (sub === false)
|
|
1801
|
+
return false;
|
|
1802
|
+
const name = namedMatcher(rewriteNullable(sub), suffix, ctx);
|
|
1803
|
+
return name === "true" ? true : name;
|
|
1804
|
+
},
|
|
1805
|
+
declarations: (text) => keptHoisted(ctx.hoisted, text)
|
|
1806
|
+
};
|
|
1807
|
+
};
|
|
1696
1808
|
const generateValidatorSource = (schema, typeName, suffix, rootSchema, unknownKeys, formats, branchErrors, failFast) => {
|
|
1697
1809
|
assertGeneratableRefs(schema, typeName);
|
|
1698
1810
|
const rewritten = rewriteNullable(schema);
|
|
@@ -1707,7 +1819,9 @@ const generateValidatorSource = (schema, typeName, suffix, rootSchema, unknownKe
|
|
|
1707
1819
|
return generateScalarValidator(rewritten, typeName, suffix, document, formats, branchErrors, failFast);
|
|
1708
1820
|
};
|
|
1709
1821
|
export {
|
|
1822
|
+
createSubschemaMatcher,
|
|
1710
1823
|
generateBooleanGuard,
|
|
1824
|
+
generateBooleanGuardSource,
|
|
1711
1825
|
generateCheckFunction,
|
|
1712
1826
|
generateValidatorFunction
|
|
1713
1827
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/validation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "One generator surface over mjst's types, validators, coercers, repairers and parsers.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@amritk/helpers": "^0.
|
|
76
|
+
"@amritk/helpers": "^0.24.0",
|
|
77
77
|
"json-schema-typed": "^8.0.1"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|