@amritk/validation 0.2.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.
Files changed (84) hide show
  1. package/AI.md +92 -0
  2. package/LICENSE +21 -0
  3. package/README.md +184 -0
  4. package/dist/generate.d.ts +135 -0
  5. package/dist/generate.js +74 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +5 -0
  8. package/dist/parsers/generators/assert-supported-keywords.d.ts +30 -0
  9. package/dist/parsers/generators/assert-supported-keywords.js +87 -0
  10. package/dist/parsers/generators/build-schema.d.ts +111 -0
  11. package/dist/parsers/generators/build-schema.js +74 -0
  12. package/dist/parsers/generators/generate-deep-equal-check.d.ts +24 -0
  13. package/dist/parsers/generators/generate-deep-equal-check.js +35 -0
  14. package/dist/parsers/generators/generate-discriminated-union-validation.d.ts +15 -0
  15. package/dist/parsers/generators/generate-discriminated-union-validation.js +28 -0
  16. package/dist/parsers/generators/generate-enum-check.d.ts +35 -0
  17. package/dist/parsers/generators/generate-enum-check.js +31 -0
  18. package/dist/parsers/generators/generate-files.d.ts +138 -0
  19. package/dist/parsers/generators/generate-files.js +89 -0
  20. package/dist/parsers/generators/generate-non-discriminated-union-validation.d.ts +6 -0
  21. package/dist/parsers/generators/generate-non-discriminated-union-validation.js +26 -0
  22. package/dist/parsers/generators/generate-parser-function.d.ts +120 -0
  23. package/dist/parsers/generators/generate-parser-function.js +1973 -0
  24. package/dist/parsers/generators/generate-schema-checks.d.ts +5 -0
  25. package/dist/parsers/generators/generate-schema-checks.js +260 -0
  26. package/dist/parsers/generators/generate-strict-assertion.d.ts +155 -0
  27. package/dist/parsers/generators/generate-strict-assertion.js +759 -0
  28. package/dist/parsers/generators/generate-type-checks.d.ts +148 -0
  29. package/dist/parsers/generators/generate-type-checks.js +454 -0
  30. package/dist/parsers/generators/generate-unique-items-check.d.ts +16 -0
  31. package/dist/parsers/generators/generate-unique-items-check.js +32 -0
  32. package/dist/parsers/generators/generate-validation-expression.d.ts +79 -0
  33. package/dist/parsers/generators/generate-validation-expression.js +416 -0
  34. package/dist/parsers/generators/subschema-match.d.ts +48 -0
  35. package/dist/parsers/generators/subschema-match.js +485 -0
  36. package/dist/parsers/generators/unevaluated-match.d.ts +19 -0
  37. package/dist/parsers/generators/unevaluated-match.js +206 -0
  38. package/dist/parsers/helpers/apply-schema-extensions.d.ts +35 -0
  39. package/dist/parsers/helpers/apply-schema-extensions.js +20 -0
  40. package/dist/parsers/helpers/collect-helpers.d.ts +15 -0
  41. package/dist/parsers/helpers/collect-helpers.js +64 -0
  42. package/dist/parsers/helpers/collect-imports.d.ts +104 -0
  43. package/dist/parsers/helpers/collect-imports.js +227 -0
  44. package/dist/parsers/helpers/find-discriminator.d.ts +6 -0
  45. package/dist/parsers/helpers/find-discriminator.js +27 -0
  46. package/dist/parsers/helpers/get-discriminator-value.d.ts +6 -0
  47. package/dist/parsers/helpers/get-discriminator-value.js +18 -0
  48. package/dist/parsers/index.d.ts +2 -0
  49. package/dist/parsers/index.js +4 -0
  50. package/dist/parsers/types/schema-extensions.d.ts +34 -0
  51. package/dist/parsers/types/schema-extensions.js +0 -0
  52. package/dist/rehome-parser-file.d.ts +25 -0
  53. package/dist/rehome-parser-file.js +74 -0
  54. package/dist/validators/generators/assert-generatable-refs.d.ts +19 -0
  55. package/dist/validators/generators/assert-generatable-refs.js +12 -0
  56. package/dist/validators/generators/assert-unevaluated-generatable.d.ts +30 -0
  57. package/dist/validators/generators/assert-unevaluated-generatable.js +71 -0
  58. package/dist/validators/generators/build-schema.d.ts +61 -0
  59. package/dist/validators/generators/build-schema.js +666 -0
  60. package/dist/validators/generators/collect-emitted-refs.d.ts +26 -0
  61. package/dist/validators/generators/collect-emitted-refs.js +112 -0
  62. package/dist/validators/generators/collect-validator-imports.d.ts +86 -0
  63. package/dist/validators/generators/collect-validator-imports.js +62 -0
  64. package/dist/validators/generators/emit-format-checks.d.ts +50 -0
  65. package/dist/validators/generators/emit-format-checks.js +248 -0
  66. package/dist/validators/generators/enforced-keywords.d.ts +33 -0
  67. package/dist/validators/generators/enforced-keywords.js +70 -0
  68. package/dist/validators/generators/folds-to-constant.d.ts +15 -0
  69. package/dist/validators/generators/folds-to-constant.js +43 -0
  70. package/dist/validators/generators/generate-coerce-function.d.ts +22 -0
  71. package/dist/validators/generators/generate-coerce-function.js +208 -0
  72. package/dist/validators/generators/generate-files.d.ts +92 -0
  73. package/dist/validators/generators/generate-files.js +86 -0
  74. package/dist/validators/generators/generate-repair-function.d.ts +27 -0
  75. package/dist/validators/generators/generate-repair-function.js +102 -0
  76. package/dist/validators/generators/generate-validator-function.d.ts +58 -0
  77. package/dist/validators/generators/generate-validator-function.js +1691 -0
  78. package/dist/validators/generators/tuple-shape.d.ts +31 -0
  79. package/dist/validators/generators/tuple-shape.js +20 -0
  80. package/dist/validators/generators/unevaluated-match.d.ts +29 -0
  81. package/dist/validators/generators/unevaluated-match.js +223 -0
  82. package/dist/validators/index.d.ts +2 -0
  83. package/dist/validators/index.js +4 -0
  84. package/package.json +92 -0
@@ -0,0 +1,1691 @@
1
+ import { regexFlagsFor, regexLiteral } from "@amritk/helpers/escape-regex-pattern";
2
+ import { getMjstInstanceOf, getMjstPrimitive, MJST_EXTENSION_KEY } from "@amritk/helpers/mjst-extension";
3
+ import { multipleOfFailExpr, multipleOfPassExpr } from "@amritk/helpers/multiple-of-check";
4
+ import { boundFailExpr, boundOperator, boundPassExpr } from "@amritk/helpers/numeric-bound-check";
5
+ import { declaresKey, readKey } from "@amritk/helpers/read-key";
6
+ import { refToName } from "@amritk/helpers/ref-to-name";
7
+ import { safeAccessor } from "@amritk/helpers/safe-accessor";
8
+ import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasDependentRequired, hasEnum, hasExclusiveMaximum, hasExclusiveMinimum, hasItems, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasOneOf, hasPattern, hasProperties, hasPropertyNames, hasRef, hasRequired, hasStrictExclusiveMaximum, hasStrictExclusiveMinimum, hasType, hasUniqueItems, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
9
+ import { maxLengthFailExpr, maxLengthPassExpr, minLengthFailExpr, minLengthPassExpr } from "@amritk/helpers/string-length-check";
10
+ import { unknownKeyCheck } from "@amritk/helpers/unknown-key-check";
11
+ import { DEFAULT_UNKNOWN_KEYS } from "@amritk/helpers/unknown-keys-strategy";
12
+ import { assertGeneratableRefs } from "./assert-generatable-refs.js";
13
+ import { assertUnevaluatedGeneratable, UNPROVABLE_COVERAGE_MESSAGE } from "./assert-unevaluated-generatable.js";
14
+ import { formatCheckName, formatFamily } from "./emit-format-checks.js";
15
+ import { declaresKeywordOutside, enforcesFormat, NO_FORMATS } from "./enforced-keywords.js";
16
+ import { tupleShapeOf } from "./tuple-shape.js";
17
+ import { unevaluatedItemsExpr, unevaluatedPropertiesExpr } from "./unevaluated-match.js";
18
+ const validatorName = (typeName) => `validate${typeName}`;
19
+ const checkerName = (typeName) => `check${typeName}`;
20
+ const propertyRead = (objVar, key) => safeAccessor(objVar, key);
21
+ const PROTOTYPE_MEMBERS = new Set(Object.getOwnPropertyNames(Object.prototype));
22
+ const protoLocalName = (key, depth) => `_own${depth}_${key}`;
23
+ const hasOwnCheck = (objVar, key) => {
24
+ if (PROTOTYPE_MEMBERS.has(key))
25
+ return `Object.hasOwn(${objVar}, ${JSON.stringify(key)})`;
26
+ return `${JSON.stringify(key)} in ${objVar}`;
27
+ };
28
+ const missingCheck = (objVar, key) => `!(${hasOwnCheck(objVar, key)})`;
29
+ const typeofString = (type) => {
30
+ if (type === "integer")
31
+ return "number";
32
+ return type;
33
+ };
34
+ const constMismatchCondition = (accessor, value) => {
35
+ if (value === null || typeof value !== "object") {
36
+ return `${accessor} !== ${JSON.stringify(value)}`;
37
+ }
38
+ return `!valuesEqual(${accessor}, ${JSON.stringify(value)})`;
39
+ };
40
+ const enumMembershipExpr = (values, acc) => {
41
+ if (values.length === 0)
42
+ return "false";
43
+ const parts = values.map((value) => {
44
+ if (typeof value === "number" && Number.isNaN(value))
45
+ return `Number.isNaN(${acc})`;
46
+ if (value !== null && typeof value === "object")
47
+ return `valuesEqual(${acc}, ${JSON.stringify(value)})`;
48
+ return `${acc} === ${JSON.stringify(value)}`;
49
+ });
50
+ return `(${parts.join(" || ")})`;
51
+ };
52
+ const FALSE_SCHEMA_MESSAGE = "boolean schema is false";
53
+ const SCALAR_ITEM_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null"]);
54
+ const schemaIsScalarOnly = (schema) => {
55
+ if (!isSchemaObject(schema))
56
+ return false;
57
+ const t = readKey(schema, "type");
58
+ if (t === void 0)
59
+ return false;
60
+ const types = Array.isArray(t) ? t : [t];
61
+ return types.length > 0 && types.every((x) => typeof x === "string" && SCALAR_ITEM_TYPES.has(x));
62
+ };
63
+ const arrayItemsAreScalarOnly = (schema) => {
64
+ const { tuple, tail, tailIsClosed } = tupleShapeOf(schema);
65
+ if (tuple !== void 0) {
66
+ if (!tuple.every((p) => schemaIsScalarOnly(p)))
67
+ return false;
68
+ if (tailIsClosed)
69
+ return true;
70
+ return schemaIsScalarOnly(tail);
71
+ }
72
+ return schemaIsScalarOnly(tail);
73
+ };
74
+ const wrongTypeCondition = (accessor, type) => {
75
+ switch (type) {
76
+ case "string":
77
+ return `typeof ${accessor} !== 'string'`;
78
+ case "number":
79
+ return `typeof ${accessor} !== 'number'`;
80
+ case "integer":
81
+ return `typeof ${accessor} !== 'number' || !Number.isInteger(${accessor})`;
82
+ case "boolean":
83
+ return `typeof ${accessor} !== 'boolean'`;
84
+ case "array":
85
+ return `!Array.isArray(${accessor})`;
86
+ case "null":
87
+ return `${accessor} !== null`;
88
+ case "object":
89
+ return `typeof ${accessor} !== 'object' || ${accessor} === null || Array.isArray(${accessor})`;
90
+ default:
91
+ return "";
92
+ }
93
+ };
94
+ const declaresObjectType = (schema) => hasType(schema) && schema.type === "object";
95
+ const objectRootIsSelfContained = (schema) => !hasRef(schema) && !hasConst(schema) && !hasEnum(schema) && getMjstInstanceOf(schema) === void 0 && getMjstPrimitive(schema) === void 0;
96
+ const getTypeArray = (schema) => {
97
+ if (!isSchemaObject(schema) || !declaresKey(schema, "type") || !Array.isArray(schema.type))
98
+ return null;
99
+ return schema.type;
100
+ };
101
+ const ROOT_ERROR_SINK = "(errors ??= [])";
102
+ const ACCUMULATING = { on: false, blocked: false };
103
+ const FAIL_FAST_SINK = "\0fail-fast";
104
+ const LITERAL_CONDITION = /^[\s()!&|]*(?:(?:true|false)[\s()!&|]*)+$/;
105
+ const blockFailFast = (ctx) => {
106
+ if (ctx.sink === FAIL_FAST_SINK)
107
+ ctx.failFast.blocked = true;
108
+ };
109
+ const hoistPrefix = (ctx) => ctx.failFast.on ? "_check" : "";
110
+ const typeTestAccessor = (raw, ctx) => ctx.sink === FAIL_FAST_SINK ? `(${raw} as unknown)` : raw;
111
+ const emittedName = (typeName, failFast) => failFast.on ? checkerName(typeName) : validatorName(typeName);
112
+ const errorsBinding = (failFast) => failFast.on ? [] : [" let errors: ValidationError[] | undefined"];
113
+ const errorsVerdict = (failFast) => failFast.on ? " return true" : " return errors !== undefined ? { valid: false, errors } : true";
114
+ const returnError = (message, path, keyword, params = "{}") => `return { valid: false, errors: [{ message: ${message}, path: ${path}, keyword: ${JSON.stringify(keyword)}, params: ${params} }] }`;
115
+ const pushError = (sink, message, path, keyword, params = "{}") => sink === FAIL_FAST_SINK ? returnError(message, path, keyword, params) : `${sink}.push({ message: ${message}, path: ${path}, keyword: ${JSON.stringify(keyword)}, params: ${params} })`;
116
+ const MATCH_ERROR_SINK = "_m";
117
+ const createRootContext = (rootSchema, formats = NO_FORMATS, branchErrors = false, failFast = ACCUMULATING) => ({
118
+ objVar: "obj",
119
+ pathPrefix: "${_path}",
120
+ depth: 0,
121
+ hoisted: [],
122
+ rootSchema,
123
+ sink: failFast.on ? FAIL_FAST_SINK : ROOT_ERROR_SINK,
124
+ formats,
125
+ // A fail-fast body reports one error and stops, so there is no second error for
126
+ // a branch explanation to sit beside. Leaving the option on would emit the
127
+ // `selectBranchErrors` sweep *after* the `return` that ends the function, which
128
+ // is unreachable code the consumer's build rejects.
129
+ branchErrors: branchErrors && !failFast.on,
130
+ failFast
131
+ });
132
+ const pointerSegment = (key) => {
133
+ const pointer = key.replace(/~/g, "~0").replace(/\//g, "~1");
134
+ return JSON.stringify(pointer).slice(1, -1).replace(/[`$]/g, "\\$&");
135
+ };
136
+ const patternPropertySources = (schema) => {
137
+ if (!isSchemaObject(schema) || !declaresKey(schema, "patternProperties"))
138
+ return [];
139
+ const patterns = schema.patternProperties;
140
+ if (typeof patterns !== "object" || patterns === null)
141
+ return [];
142
+ return Object.keys(patterns);
143
+ };
144
+ const readsBinding = (name, text) => new RegExp(`\\b${name}\\b`).test(text);
145
+ const readsObjBinding = (text) => readsBinding("obj", text);
146
+ const withHoisted = (hoisted, text) => {
147
+ const kept = hoisted.filter((entry) => text.includes(entry.reference)).map((entry) => entry.declaration);
148
+ const firstBreak = text.indexOf("\n");
149
+ const signature = firstBreak === -1 ? text : text.slice(0, firstBreak);
150
+ const rest = firstBreak === -1 ? "" : text.slice(firstBreak);
151
+ const body = /\binput\b/.test(rest) ? text : signature.replace("(input: unknown", "(_input: unknown") + rest;
152
+ return kept.length > 0 ? `${kept.join("\n")}
153
+
154
+ ${body}` : body;
155
+ };
156
+ const generateStrictKeyChecks = (schema, ctx) => {
157
+ if (!isSchemaObject(schema))
158
+ return [];
159
+ if (!hasAdditionalProperties(schema) || schema.additionalProperties !== false)
160
+ return [];
161
+ const known = Object.keys(hasProperties(schema) ? schema.properties : {});
162
+ const d = ctx.depth;
163
+ const knownKeysName = `${hoistPrefix(ctx)}_knownKeys${ctx.hoisted.length}`;
164
+ const check = unknownKeyCheck(known, knownKeysName);
165
+ const unknownTest = check.isUnknown(`_key${d}`);
166
+ for (const declaration of check.declarations) {
167
+ ctx.hoisted.push({ declaration, reference: unknownTest });
168
+ }
169
+ const patterns = patternPropertySources(schema);
170
+ let patternGuard = "";
171
+ if (patterns.length > 0) {
172
+ const patternsName = `${hoistPrefix(ctx)}_patterns${ctx.hoisted.length}`;
173
+ const compiled = patterns.map((p) => `new RegExp(${JSON.stringify(p)}, ${JSON.stringify(regexFlagsFor(p))})`);
174
+ patternGuard = ` && !${patternsName}.some((re) => re.test(_key${d}))`;
175
+ ctx.hoisted.push({
176
+ declaration: `const ${patternsName} = [${compiled.join(", ")}]`,
177
+ reference: patternGuard
178
+ });
179
+ }
180
+ return [
181
+ ` for (const _key${d} in ${ctx.objVar}) {`,
182
+ ` if (${unknownTest}${patternGuard}) {`,
183
+ ` ${pushError(ctx.sink, "'must NOT have additional properties'", `\`${ctx.pathPrefix}/\${escapePointer(_key${d})}\``, "additionalProperties", `{ additionalProperty: _key${d} }`)}`,
184
+ ` }`,
185
+ ` }`
186
+ ];
187
+ };
188
+ const generateMissingRequiredChecks = (schema, ctx) => {
189
+ if (!isSchemaObject(schema) || !hasRequired(schema))
190
+ return [];
191
+ const props = hasProperties(schema) ? schema.properties : {};
192
+ const parentPath = ctx.depth === 0 ? "_path" : `\`${ctx.pathPrefix}\``;
193
+ const lines = [];
194
+ for (const key of schema.required) {
195
+ if (Object.hasOwn(props, key))
196
+ continue;
197
+ lines.push(` if (${missingCheck(ctx.objVar, key)}) {`);
198
+ lines.push(` ${pushError(ctx.sink, JSON.stringify(`must have required property '${key}'`), parentPath, "required", JSON.stringify({ missingProperty: key }))}`);
199
+ lines.push(` }`);
200
+ }
201
+ return lines;
202
+ };
203
+ const generatePropertyChecks = (key, propSchema, isRequired, suffix, ctx) => {
204
+ if (propSchema === false) {
205
+ const path = `\`${ctx.pathPrefix}/${pointerSegment(key)}\``;
206
+ const parentPath = ctx.depth === 0 ? "_path" : `\`${ctx.pathPrefix}\``;
207
+ if (isRequired) {
208
+ blockFailFast(ctx);
209
+ return [
210
+ ` if (${missingCheck(ctx.objVar, key)}) {`,
211
+ ` ${pushError(ctx.sink, JSON.stringify(`must have required property '${key}'`), parentPath, "required", JSON.stringify({ missingProperty: key }))}`,
212
+ ` } else {`,
213
+ ` ${pushError(ctx.sink, JSON.stringify(FALSE_SCHEMA_MESSAGE), path, "false schema")}`,
214
+ ` }`
215
+ ];
216
+ }
217
+ return [
218
+ ` if (${hasOwnCheck(ctx.objVar, key)}) {`,
219
+ ` ${pushError(ctx.sink, JSON.stringify(FALSE_SCHEMA_MESSAGE), path, "false schema")}`,
220
+ ` }`
221
+ ];
222
+ }
223
+ const raw = PROTOTYPE_MEMBERS.has(key) ? protoLocalName(key, ctx.depth) : propertyRead(ctx.objVar, key);
224
+ const lines = [
225
+ ...generatePropertyCheckLines(key, propSchema, isRequired, suffix, ctx),
226
+ // `unevaluated*` is a sibling of everything else the property declares, and
227
+ // several of the branches above return early, so it is appended here rather
228
+ // than emitted inside them. The checks carry their own runtime type guard, so
229
+ // an absent optional property is inert and needs no presence gate.
230
+ ...generateUnevaluatedChecks(raw, `\`${ctx.pathPrefix}/${pointerSegment(key)}\``, propSchema, suffix, ctx)
231
+ ];
232
+ if (lines.length === 0 || !PROTOTYPE_MEMBERS.has(key))
233
+ return lines;
234
+ return [` const ${protoLocalName(key, ctx.depth)} = ${propertyRead(ctx.objVar, key)}`, ...lines];
235
+ };
236
+ const singleIfBlock = (lines) => {
237
+ if (lines.length < 3)
238
+ return null;
239
+ const opener = /^ {2}if \((.*)\) \{$/.exec(lines[0]);
240
+ if (opener === null || lines[lines.length - 1] !== " }")
241
+ return null;
242
+ for (let i = 1; i < lines.length - 1; i++) {
243
+ if (!lines[i].startsWith(" "))
244
+ return null;
245
+ }
246
+ return { condition: opener[1], body: lines.slice(1, -1) };
247
+ };
248
+ const generateKeywordChecks = (key, raw, path, schema, suffix, ctx, presence) => {
249
+ if (!isSchemaObject(schema))
250
+ return [];
251
+ const lines = [];
252
+ if (hasRef(schema)) {
253
+ const target = refToName(schema.$ref, suffix);
254
+ const delegate = ctx.sink === FAIL_FAST_SINK ? [` const _r = ${checkerName(target)}(${raw}, ${path})`, ` if (_r !== true) return _r`] : [
255
+ ` const _r = ${validatorName(target)}(${raw}, ${path})`,
256
+ ` if (_r !== true) ${ctx.sink}.push(..._r.errors)`
257
+ ];
258
+ if (presence === "")
259
+ lines.push(` {`, ...delegate.map((line) => ` ${line}`), ` }`);
260
+ else
261
+ lines.push(` if (${raw} !== undefined) {`, ...delegate.map((line) => ` ${line}`), ` }`);
262
+ }
263
+ const instanceOf = getMjstInstanceOf(schema);
264
+ if (instanceOf) {
265
+ lines.push(` if (${presence}!(${raw} instanceof ${instanceOf})) {`);
266
+ lines.push(` ${pushError(ctx.sink, `'must be ${instanceOf}'`, path, "type", JSON.stringify({ type: instanceOf }))}`);
267
+ lines.push(` }`);
268
+ }
269
+ const primitive = getMjstPrimitive(schema);
270
+ if (primitive) {
271
+ lines.push(` if (${presence}typeof ${raw} !== "${primitive}") {`);
272
+ lines.push(` ${pushError(ctx.sink, `'must be ${primitive}'`, path, "type", JSON.stringify({ type: primitive }))}`);
273
+ lines.push(` }`);
274
+ }
275
+ if (hasConst(schema)) {
276
+ const mismatch = constMismatchCondition(raw, schema.const);
277
+ const msg = JSON.stringify(`must be ${JSON.stringify(schema.const)}`);
278
+ lines.push(` if (${presence}${mismatch}) {`);
279
+ lines.push(` ${pushError(ctx.sink, msg, path, "const", JSON.stringify({ allowedValue: schema.const }))}`);
280
+ lines.push(` }`);
281
+ }
282
+ if (hasEnum(schema)) {
283
+ const label = schema.enum.map((v) => JSON.stringify(v)).join(", ");
284
+ lines.push(` if (${presence}!${enumMembershipExpr(schema.enum, raw)}) {`);
285
+ lines.push(` ${pushError(ctx.sink, JSON.stringify(`must be one of: ${label}`), path, "enum", JSON.stringify({ allowedValues: schema.enum }))}`);
286
+ lines.push(` }`);
287
+ }
288
+ if (instanceOf === void 0 && primitive === void 0) {
289
+ if (hasType(schema)) {
290
+ const t = schema.type;
291
+ const wrongType = wrongTypeCondition(typeTestAccessor(raw, ctx), t);
292
+ if (wrongType) {
293
+ lines.push(` if (${presence === "" ? wrongType : `${presence}(${wrongType})`}) {`);
294
+ lines.push(` ${pushError(ctx.sink, `'must be ${typeofString(t)}'`, path, "type", JSON.stringify({ type: t }))}`);
295
+ lines.push(` }`);
296
+ }
297
+ }
298
+ const typeArray = getTypeArray(schema);
299
+ if (typeArray) {
300
+ const allWrong = typeArray.map((t) => wrongTypeCondition(typeTestAccessor(raw, ctx), t)).filter((c) => c !== "").map((c) => `(${c})`).join(" && ");
301
+ if (allWrong) {
302
+ const label = typeArray.map((t) => typeofString(t)).join(" or ");
303
+ lines.push(` if (${presence === "" ? allWrong : `${presence}(${allWrong})`}) {`);
304
+ lines.push(` ${pushError(ctx.sink, JSON.stringify(`must be ${label}`), path, "type", JSON.stringify({ type: schema.type }))}`);
305
+ lines.push(` }`);
306
+ }
307
+ }
308
+ }
309
+ lines.push(...generateConstraintChecks(key, raw, path, schema, suffix, ctx));
310
+ const combinators = generateCombinatorChecks(key, raw, path, schema, suffix, ctx);
311
+ if (combinators.length > 0) {
312
+ if (presence === "")
313
+ lines.push(...combinators);
314
+ else
315
+ lines.push(` if (${raw} !== undefined) {`, ...combinators.map((line) => ` ${line}`), ` }`);
316
+ }
317
+ return lines;
318
+ };
319
+ const generatePropertyCheckLines = (key, propSchema, isRequired, suffix, ctx) => {
320
+ const parentPath = ctx.depth === 0 ? "_path" : `\`${ctx.pathPrefix}\``;
321
+ const missing = [
322
+ ` if (${missingCheck(ctx.objVar, key)}) {`,
323
+ ` ${pushError(ctx.sink, JSON.stringify(`must have required property '${key}'`), parentPath, "required", JSON.stringify({ missingProperty: key }))}`
324
+ ];
325
+ if (!isSchemaObject(propSchema)) {
326
+ if (isRequired && propSchema === true)
327
+ return [...missing, ` }`];
328
+ return [];
329
+ }
330
+ const raw = PROTOTYPE_MEMBERS.has(key) ? protoLocalName(key, ctx.depth) : propertyRead(ctx.objVar, key);
331
+ const path = `\`${ctx.pathPrefix}/${pointerSegment(key)}\``;
332
+ if (isRequired) {
333
+ const valueLines = generateKeywordChecks(key, raw, path, propSchema, suffix, ctx, "");
334
+ if (valueLines.length === 0)
335
+ return [...missing, ` }`];
336
+ const single = singleIfBlock(valueLines);
337
+ if (single !== null)
338
+ return [...missing, ` } else if (${single.condition}) {`, ...single.body, ` }`];
339
+ return [...missing, ` } else {`, ...valueLines.map((line) => ` ${line}`), ` }`];
340
+ }
341
+ return generateKeywordChecks(key, raw, path, propSchema, suffix, ctx, `${raw} !== undefined && `);
342
+ };
343
+ const generateConstraintChecks = (key, raw, path, propSchema, suffix, ctx) => {
344
+ if (!isSchemaObject(propSchema))
345
+ return [];
346
+ const sp = propSchema;
347
+ const lines = [];
348
+ if (isSchemaObject(propSchema) && enforcesFormat(propSchema, ctx.formats)) {
349
+ const format = propSchema.format;
350
+ const family = formatFamily(format);
351
+ if (family !== void 0) {
352
+ lines.push(` if (typeof ${raw} === '${family}' && !${formatCheckName(format)}(${raw})) {`);
353
+ lines.push(` ${pushError(ctx.sink, JSON.stringify(`must match format "${format}"`), path, "format", JSON.stringify({ format }))}`);
354
+ lines.push(` }`);
355
+ }
356
+ }
357
+ if (hasPattern(propSchema) || hasMinLength(propSchema) || hasMaxLength(propSchema)) {
358
+ if (hasPattern(propSchema)) {
359
+ const re = regexLiteral(propSchema.pattern);
360
+ const msg = JSON.stringify(`must match pattern ${propSchema.pattern}`);
361
+ lines.push(` if (typeof ${raw} === 'string' && !${re}.test(${raw})) {`);
362
+ lines.push(` ${pushError(ctx.sink, msg, path, "pattern", JSON.stringify({ pattern: propSchema.pattern }))}`);
363
+ lines.push(` }`);
364
+ }
365
+ if (hasMinLength(propSchema) && propSchema.minLength > 0) {
366
+ lines.push(` if (typeof ${raw} === 'string' && ${minLengthFailExpr(raw, propSchema.minLength)}) {`);
367
+ lines.push(` ${pushError(ctx.sink, `'must have at least ${propSchema.minLength} characters'`, path, "minLength", JSON.stringify({ limit: propSchema.minLength }))}`);
368
+ lines.push(` }`);
369
+ }
370
+ if (hasMaxLength(propSchema)) {
371
+ lines.push(` if (typeof ${raw} === 'string' && ${maxLengthFailExpr(raw, propSchema.maxLength)}) {`);
372
+ lines.push(` ${pushError(ctx.sink, `'must have at most ${propSchema.maxLength} characters'`, path, "maxLength", JSON.stringify({ limit: propSchema.maxLength }))}`);
373
+ lines.push(` }`);
374
+ }
375
+ }
376
+ if (hasMinimum(propSchema) || hasMaximum(propSchema) || hasExclusiveMinimum(propSchema) || hasExclusiveMaximum(propSchema) || hasMultipleOf(propSchema)) {
377
+ if (hasMinimum(propSchema)) {
378
+ const strict = hasStrictExclusiveMinimum(propSchema);
379
+ lines.push(` if (typeof ${raw} === 'number' && ${boundFailExpr(raw, "minimum", propSchema.minimum, strict)}) {`);
380
+ lines.push(` ${pushError(ctx.sink, `'must be ${boundOperator("minimum", strict)} ${propSchema.minimum}'`, path, strict ? "exclusiveMinimum" : "minimum", JSON.stringify({ comparison: boundOperator("minimum", strict), limit: propSchema.minimum }))}`);
381
+ lines.push(` }`);
382
+ }
383
+ if (hasMaximum(propSchema)) {
384
+ const strict = hasStrictExclusiveMaximum(propSchema);
385
+ lines.push(` if (typeof ${raw} === 'number' && ${boundFailExpr(raw, "maximum", propSchema.maximum, strict)}) {`);
386
+ lines.push(` ${pushError(ctx.sink, `'must be ${boundOperator("maximum", strict)} ${propSchema.maximum}'`, path, strict ? "exclusiveMaximum" : "maximum", JSON.stringify({ comparison: boundOperator("maximum", strict), limit: propSchema.maximum }))}`);
387
+ lines.push(` }`);
388
+ }
389
+ if (hasExclusiveMinimum(propSchema)) {
390
+ lines.push(` if (typeof ${raw} === 'number' && ${boundFailExpr(raw, "minimum", propSchema.exclusiveMinimum, true)}) {`);
391
+ lines.push(` ${pushError(ctx.sink, `'must be > ${propSchema.exclusiveMinimum}'`, path, "exclusiveMinimum", JSON.stringify({ comparison: ">", limit: propSchema.exclusiveMinimum }))}`);
392
+ lines.push(` }`);
393
+ }
394
+ if (hasExclusiveMaximum(propSchema)) {
395
+ lines.push(` if (typeof ${raw} === 'number' && ${boundFailExpr(raw, "maximum", propSchema.exclusiveMaximum, true)}) {`);
396
+ lines.push(` ${pushError(ctx.sink, `'must be < ${propSchema.exclusiveMaximum}'`, path, "exclusiveMaximum", JSON.stringify({ comparison: "<", limit: propSchema.exclusiveMaximum }))}`);
397
+ lines.push(` }`);
398
+ }
399
+ if (hasMultipleOf(propSchema)) {
400
+ lines.push(` if (typeof ${raw} === 'number' && ${multipleOfFailExpr(raw, propSchema.multipleOf)}) {`);
401
+ lines.push(` ${pushError(ctx.sink, `'must be a multiple of ${propSchema.multipleOf}'`, path, "multipleOf", JSON.stringify({ multipleOf: propSchema.multipleOf }))}`);
402
+ lines.push(` }`);
403
+ }
404
+ }
405
+ const { tuple, tail, tailIsClosed } = tupleShapeOf(sp);
406
+ if (tail !== void 0) {
407
+ const itemSchema = tail;
408
+ const firstTailIndex = tuple !== void 0 ? tuple.length : 0;
409
+ const iv = `_i${ctx.depth}`;
410
+ const itemPath = `\`${path.slice(1, -1)}/\${${iv}}\``;
411
+ if (hasRef(itemSchema) && !declaresKeywordOutside(itemSchema, ["$ref"])) {
412
+ const failFast = ctx.sink === FAIL_FAST_SINK;
413
+ const target = refToName(itemSchema.$ref, suffix);
414
+ lines.push(` if (Array.isArray(${raw})) {`);
415
+ lines.push(` for (let ${iv} = ${firstTailIndex}; ${iv} < ${raw}.length; ${iv}++) {`);
416
+ lines.push(` const _ir = ${failFast ? checkerName(target) : validatorName(target)}(${raw}[${iv}], ${itemPath})`);
417
+ lines.push(` if (_ir !== true) ${failFast ? "return _ir" : `${ctx.sink}.push(..._ir.errors)`}`);
418
+ lines.push(` }`);
419
+ lines.push(` }`);
420
+ } else if (isSchemaObject(itemSchema)) {
421
+ const itemVar = `_item${ctx.depth}`;
422
+ const detail = generateValueChecks("", itemVar, itemPath, itemSchema, suffix, ctx, true);
423
+ if (detail.length > 0) {
424
+ lines.push(` if (Array.isArray(${raw})) {`);
425
+ lines.push(` for (let ${iv} = ${firstTailIndex}; ${iv} < ${raw}.length; ${iv}++) {`);
426
+ if (readsBinding(itemVar, detail.join("\n")))
427
+ lines.push(` const ${itemVar} = ${raw}[${iv}]`);
428
+ lines.push(...detail.map((l) => ` ${l}`));
429
+ lines.push(` }`);
430
+ lines.push(` }`);
431
+ }
432
+ }
433
+ }
434
+ if (hasMinItems(propSchema) || hasMaxItems(propSchema) || hasUniqueItems(propSchema) && propSchema.uniqueItems === true || declaresKey(sp, "contains") || tuple !== void 0 || tail === false && tuple === void 0) {
435
+ if (tail === false && tuple === void 0) {
436
+ lines.push(` if (Array.isArray(${raw}) && ${raw}.length > 0) {`);
437
+ lines.push(` ${pushError(ctx.sink, `'must NOT have more than 0 items'`, path, "items", "{ limit: 0 }")}`);
438
+ lines.push(` }`);
439
+ }
440
+ if (hasMinItems(propSchema)) {
441
+ lines.push(` if (Array.isArray(${raw}) && ${raw}.length < ${propSchema.minItems}) {`);
442
+ lines.push(` ${pushError(ctx.sink, `'must have at least ${propSchema.minItems} items'`, path, "minItems", JSON.stringify({ limit: propSchema.minItems }))}`);
443
+ lines.push(` }`);
444
+ }
445
+ if (hasMaxItems(propSchema)) {
446
+ lines.push(` if (Array.isArray(${raw}) && ${raw}.length > ${propSchema.maxItems}) {`);
447
+ lines.push(` ${pushError(ctx.sink, `'must have at most ${propSchema.maxItems} items'`, path, "maxItems", JSON.stringify({ limit: propSchema.maxItems }))}`);
448
+ lines.push(` }`);
449
+ }
450
+ if (hasUniqueItems(propSchema) && propSchema.uniqueItems === true) {
451
+ const dupCond = arrayItemsAreScalarOnly(sp) ? `new Set(${raw} as unknown[]).size !== ${raw}.length` : `!allUnique(${raw} as unknown[])`;
452
+ lines.push(` if (Array.isArray(${raw}) && ${dupCond}) {`);
453
+ lines.push(` ${pushError(ctx.sink, `'must NOT have duplicate items'`, path, "uniqueItems")}`);
454
+ lines.push(` }`);
455
+ }
456
+ if (declaresKey(sp, "contains")) {
457
+ const declaredMin = readKey(sp, "minContains");
458
+ const declaredMax = readKey(sp, "maxContains");
459
+ const min = typeof declaredMin === "number" ? declaredMin : 1;
460
+ const max = typeof declaredMax === "number" ? declaredMax : void 0;
461
+ if (min > 0 || max !== void 0) {
462
+ const matchExpr = generateMatchesExpr("_c", readKey(sp, "contains"), suffix, ctx, true);
463
+ const report = pushError(ctx.sink, `'array does not contain the required matching items'`, path, "contains");
464
+ if (matchExpr === "true") {
465
+ const bound = max !== void 0 ? `${raw}.length < ${min} || ${raw}.length > ${max}` : `${raw}.length < ${min}`;
466
+ lines.push(` if (Array.isArray(${raw}) && (${bound})) {`);
467
+ lines.push(` ${report}`);
468
+ lines.push(` }`);
469
+ } else if (matchExpr === "false") {
470
+ if (0 < min || max !== void 0 && 0 > max) {
471
+ lines.push(` if (Array.isArray(${raw})) {`);
472
+ lines.push(` ${report}`);
473
+ lines.push(` }`);
474
+ }
475
+ } else {
476
+ const bound = max !== void 0 ? `_cn < ${min} || _cn > ${max}` : `_cn < ${min}`;
477
+ lines.push(` if (Array.isArray(${raw})) {`);
478
+ lines.push(` const _ca = ${raw} as unknown[]`);
479
+ lines.push(` let _cn = 0`);
480
+ lines.push(` for (let _ci = 0; _ci < _ca.length; _ci++) {`);
481
+ lines.push(` const _c = _ca[_ci]`);
482
+ lines.push(` if (${matchExpr}) _cn++`);
483
+ if (max === void 0)
484
+ lines.push(` if (_cn >= ${min}) break`);
485
+ lines.push(` }`);
486
+ lines.push(` if (${bound}) {`);
487
+ lines.push(` ${report}`);
488
+ lines.push(` }`);
489
+ lines.push(` }`);
490
+ }
491
+ }
492
+ }
493
+ if (tuple !== void 0) {
494
+ lines.push(` if (Array.isArray(${raw})) {`);
495
+ for (let i = 0; i < tuple.length; i++) {
496
+ const itemChecks = generateValueChecks("", `${raw}[${i}]`, `\`${path.slice(1, -1)}/${i}\``, tuple[i], suffix, ctx, true);
497
+ if (itemChecks.length > 0) {
498
+ lines.push(` if (${raw}.length > ${i}) {`);
499
+ lines.push(...itemChecks.map((l) => ` ${l}`));
500
+ lines.push(` }`);
501
+ }
502
+ }
503
+ if (tailIsClosed) {
504
+ lines.push(` if (${raw}.length > ${tuple.length}) {`);
505
+ lines.push(` ${pushError(ctx.sink, `'must NOT have more than ${tuple.length} items'`, path, "items", JSON.stringify({ limit: tuple.length }))}`);
506
+ lines.push(` }`);
507
+ }
508
+ lines.push(` }`);
509
+ }
510
+ }
511
+ lines.push(...generateInlineObjectChecks(key, propSchema, raw, suffix, ctx));
512
+ return lines;
513
+ };
514
+ const generateValueChecks = (key, raw, path, propSchema, suffix, ctx, required = false) => [
515
+ ...generateValueCheckLines(key, raw, path, propSchema, suffix, ctx, required),
516
+ // Appended rather than folded into the checks above because several of those
517
+ // branches (`$ref`, `const`, `enum`, x-mjst) return early: `unevaluated*` is a
518
+ // sibling of whatever else the node declares, and dropping it would leave the
519
+ // validator accepting documents the interpreter rejects.
520
+ ...generateUnevaluatedChecks(raw, path, propSchema, suffix, ctx)
521
+ ];
522
+ const generateValueCheckLines = (_key, raw, path, propSchema, suffix, ctx, required = false) => {
523
+ const lines = [];
524
+ if (propSchema === false) {
525
+ const report = pushError(ctx.sink, JSON.stringify(FALSE_SCHEMA_MESSAGE), path, "false schema");
526
+ if (required)
527
+ blockFailFast(ctx);
528
+ if (required)
529
+ return [` {`, ` ${report}`, ` }`];
530
+ return [` if (${raw} !== undefined) {`, ` ${report}`, ` }`];
531
+ }
532
+ if (!isSchemaObject(propSchema))
533
+ return [];
534
+ const presence = required ? "" : `${raw} !== undefined && `;
535
+ const valueCtx = {
536
+ branchErrors: ctx.branchErrors,
537
+ failFast: ctx.failFast,
538
+ objVar: ctx.objVar,
539
+ pathPrefix: path.slice(1, -1),
540
+ depth: ctx.depth + 1,
541
+ hoisted: ctx.hoisted,
542
+ rootSchema: ctx.rootSchema,
543
+ sink: ctx.sink,
544
+ formats: ctx.formats
545
+ };
546
+ lines.push(...generateKeywordChecks("", raw, path, propSchema, suffix, valueCtx, presence));
547
+ return lines;
548
+ };
549
+ const NARROWABLE_REFERENCE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[[A-Za-z_$][\w$]*\]|\[\d+\])*$/;
550
+ const generateMatchesExpr = (raw, sub, suffix, ctx, required = false, branch) => {
551
+ if (sub === true)
552
+ return "true";
553
+ if (sub === false)
554
+ return "false";
555
+ if (!isSchemaObject(sub))
556
+ return "true";
557
+ const value = NARROWABLE_REFERENCE.test(raw) ? raw : `_mv${ctx.depth}`;
558
+ const valuePath = branch === void 0 ? "`${_path}`" : branch.path;
559
+ const checks = generateValueChecks("", value, valuePath, sub, suffix, { ...ctx, sink: MATCH_ERROR_SINK }, required);
560
+ if (checks.length === 0)
561
+ return "true";
562
+ const body = checks.join("\n");
563
+ const binding = value === raw ? "" : `const ${value}: unknown = ${raw}
564
+ `;
565
+ const collect = branch === void 0 ? "" : `
566
+ if (_m.length !== 0) (${branch.buffer} ??= []).push(_m)`;
567
+ return `((): boolean => { const _m: ValidationError[] = []
568
+ ${binding}${body}${collect}
569
+ return _m.length === 0 })()`;
570
+ };
571
+ const unevaluatedMatcher = (suffix, ctx) => (accessor, schema, depth) => (
572
+ // Always `required`: every accessor handed to this matcher is a value the
573
+ // surrounding test has already established is there — the whole instance
574
+ // inside its type guard, or a key/index the sweep is walking. In optional
575
+ // mode a property whose value is `undefined` matched every branch, so
576
+ // `{ a: undefined }` satisfied an `unevaluatedProperties: { type: 'string' }`
577
+ // that Ajv and the interpreter both reject.
578
+ generateMatchesExpr(accessor, schema, suffix, { ...ctx, depth: ctx.depth + depth + 1 }, true)
579
+ );
580
+ const generateUnevaluatedChecks = (raw, path, schema, suffix, ctx) => {
581
+ if (!isSchemaObject(schema))
582
+ return [];
583
+ const s = schema;
584
+ if (!declaresKey(s, "unevaluatedProperties") && !declaresKey(s, "unevaluatedItems"))
585
+ return [];
586
+ const match = unevaluatedMatcher(suffix, ctx);
587
+ const lines = [];
588
+ const properties = unevaluatedPropertiesExpr(raw, schema, ctx.rootSchema, ctx.depth, match);
589
+ if (properties === null)
590
+ throw new Error(UNPROVABLE_COVERAGE_MESSAGE("unevaluatedProperties"));
591
+ if (properties !== void 0) {
592
+ if (LITERAL_CONDITION.test(`!(${properties.expr})`))
593
+ blockFailFast(ctx);
594
+ lines.push(` if (typeof ${raw} === 'object' && ${raw} !== null && !Array.isArray(${raw})) {`);
595
+ for (const statement of properties.setup)
596
+ lines.push(` ${statement}`);
597
+ lines.push(` if (!(${properties.expr})) {`);
598
+ lines.push(` ${pushError(ctx.sink, `'must NOT have unevaluated properties'`, path, "unevaluatedProperties")}`);
599
+ lines.push(` }`);
600
+ lines.push(` }`);
601
+ }
602
+ const items = unevaluatedItemsExpr(raw, schema, ctx.rootSchema, ctx.depth, match);
603
+ if (items === null)
604
+ throw new Error(UNPROVABLE_COVERAGE_MESSAGE("unevaluatedItems"));
605
+ if (items !== void 0) {
606
+ if (LITERAL_CONDITION.test(`!(${items.expr})`))
607
+ blockFailFast(ctx);
608
+ lines.push(` if (Array.isArray(${raw})) {`);
609
+ for (const statement of items.setup)
610
+ lines.push(` ${statement}`);
611
+ lines.push(` if (!(${items.expr})) {`);
612
+ lines.push(` ${pushError(ctx.sink, `'must NOT have unevaluated items'`, path, "unevaluatedItems")}`);
613
+ lines.push(` }`);
614
+ lines.push(` }`);
615
+ }
616
+ return lines;
617
+ };
618
+ const BRANCH_BUFFER = "_br";
619
+ const selectBranchErrorsLine = (path, ctx) => `for (const _e of selectBranchErrors(${BRANCH_BUFFER} ?? [], ${path})) ${ctx.sink}.push(_e)`;
620
+ const branchBufferBlock = (guard, message, keyword, path, ctx) => [
621
+ ` {`,
622
+ ` let ${BRANCH_BUFFER}: ValidationError[][] | null = null`,
623
+ ` if (${guard}) {`,
624
+ ` ${pushError(ctx.sink, message, path, keyword)}`,
625
+ ` ${selectBranchErrorsLine(path, ctx)}`,
626
+ ` }`,
627
+ ` }`
628
+ ];
629
+ const generateCombinatorChecks = (key, raw, path, schema, suffix, ctx) => {
630
+ if (!isSchemaObject(schema))
631
+ return [];
632
+ const lines = [];
633
+ if (hasAllOf(schema)) {
634
+ for (const branch of schema.allOf)
635
+ lines.push(...generateValueChecks(key, raw, path, branch, suffix, ctx, true));
636
+ }
637
+ if (hasAnyOf(schema) && schema.anyOf.length > 0) {
638
+ const collector = ctx.branchErrors ? { buffer: BRANCH_BUFFER, path } : void 0;
639
+ const conds = schema.anyOf.map((b) => generateMatchesExpr(raw, b, suffix, ctx, true, collector));
640
+ if (!conds.includes("true")) {
641
+ if (ctx.branchErrors) {
642
+ lines.push(...branchBufferBlock(`!(${conds.join(" || ")})`, `'must match a schema in anyOf'`, "anyOf", path, ctx));
643
+ } else {
644
+ const condition = `!(${conds.join(" || ")})`;
645
+ if (LITERAL_CONDITION.test(condition))
646
+ blockFailFast(ctx);
647
+ lines.push(` if (${condition}) {`);
648
+ lines.push(` ${pushError(ctx.sink, `'must match a schema in anyOf'`, path, "anyOf")}`);
649
+ lines.push(` }`);
650
+ }
651
+ }
652
+ }
653
+ if (hasOneOf(schema) && schema.oneOf.length > 0) {
654
+ const collector = ctx.branchErrors ? { buffer: BRANCH_BUFFER, path } : void 0;
655
+ const conds = schema.oneOf.map((b) => `(${generateMatchesExpr(raw, b, suffix, ctx, true, collector)} ? 1 : 0)`);
656
+ const message = `'must match exactly one schema in oneOf'`;
657
+ if (!ctx.branchErrors) {
658
+ lines.push(` if ((${conds.join(" + ")}) !== 1) {`);
659
+ lines.push(` ${pushError(ctx.sink, message, path, "oneOf")}`);
660
+ lines.push(` }`);
661
+ } else {
662
+ lines.push(` {`);
663
+ lines.push(` let ${BRANCH_BUFFER}: ValidationError[][] | null = null`);
664
+ lines.push(` const _n = ${conds.join(" + ")}`);
665
+ lines.push(` if (_n === 0) {`);
666
+ lines.push(` ${pushError(ctx.sink, message, path, "oneOf")}`);
667
+ lines.push(` ${selectBranchErrorsLine(path, ctx)}`);
668
+ lines.push(` } else if (_n !== 1) {`);
669
+ lines.push(` ${pushError(ctx.sink, message, path, "oneOf")}`);
670
+ lines.push(` }`);
671
+ lines.push(` }`);
672
+ }
673
+ }
674
+ const not = readKey(schema, "not");
675
+ if (not !== void 0 && (isSchemaObject(not) || typeof not === "boolean")) {
676
+ const cond = generateMatchesExpr(raw, not, suffix, ctx, true);
677
+ if (cond !== "false") {
678
+ if (LITERAL_CONDITION.test(cond))
679
+ blockFailFast(ctx);
680
+ lines.push(` if (${cond}) {`);
681
+ lines.push(` ${pushError(ctx.sink, `'must NOT match the schema in not'`, path, "not")}`);
682
+ lines.push(` }`);
683
+ }
684
+ }
685
+ const ifSchema = readKey(schema, "if");
686
+ if (ifSchema !== void 0 && (isSchemaObject(ifSchema) || typeof ifSchema === "boolean")) {
687
+ const thenSchema = readKey(schema, "then");
688
+ const elseSchema = readKey(schema, "else");
689
+ const thenLines = thenSchema !== void 0 ? generateValueChecks(key, raw, path, thenSchema, suffix, ctx, true) : [];
690
+ const elseLines = elseSchema !== void 0 ? generateValueChecks(key, raw, path, elseSchema, suffix, ctx, true) : [];
691
+ if (thenLines.length > 0 || elseLines.length > 0) {
692
+ const cond = generateMatchesExpr(raw, ifSchema, suffix, ctx, true);
693
+ if (cond === "true") {
694
+ lines.push(...thenLines);
695
+ } else if (cond === "false") {
696
+ lines.push(...elseLines);
697
+ } else {
698
+ lines.push(` if (${cond}) {`);
699
+ lines.push(...thenLines);
700
+ lines.push(` } else {`);
701
+ lines.push(...elseLines);
702
+ lines.push(` }`);
703
+ }
704
+ }
705
+ }
706
+ return lines;
707
+ };
708
+ const generatePatternAndAdditionalChecks = (schema, suffix, ctx) => {
709
+ if (!isSchemaObject(schema))
710
+ return [];
711
+ const obj = ctx.objVar;
712
+ const d = ctx.depth;
713
+ const patternsRecord = declaresKey(schema, "patternProperties") && typeof schema.patternProperties === "object" && schema.patternProperties !== null ? schema.patternProperties : {};
714
+ const patterns = Object.keys(patternsRecord);
715
+ const kv = `_pk${d}`;
716
+ const valueChecksFor = (sub) => (
717
+ // `required` mode: the key came out of the loop over the object, so its value
718
+ // is there to be judged. Optional mode wrote `obj[_pk0] !== undefined &&` in
719
+ // front of every check, and a property whose value *is* `undefined` then
720
+ // satisfied all of them — `{ a: undefined }` passed a
721
+ // `patternProperties: { "^a": { type: "string" } }` that Ajv and the
722
+ // interpreter both reject.
723
+ generateValueChecks(`\${${kv}}`, `${obj}[${kv}]`, `\`${ctx.pathPrefix}/\${escapePointer(${kv})}\``, sub, suffix, ctx, true)
724
+ );
725
+ const body = [];
726
+ for (const [pattern, sub] of Object.entries(patternsRecord)) {
727
+ const valueChecks = valueChecksFor(sub);
728
+ if (valueChecks.length === 0)
729
+ continue;
730
+ body.push(` if (${regexLiteral(pattern)}.test(${kv})) {`);
731
+ body.push(...valueChecks.map((line) => ` ${line}`));
732
+ body.push(` }`);
733
+ }
734
+ if (hasAdditionalProperties(schema) && isSchemaObject(schema.additionalProperties)) {
735
+ const valueChecks = valueChecksFor(schema.additionalProperties);
736
+ if (valueChecks.length > 0) {
737
+ const known = Object.keys(hasProperties(schema) ? schema.properties : {});
738
+ if (known.length > 0) {
739
+ const check = unknownKeyCheck(known, `${hoistPrefix(ctx)}_knownKeys${ctx.hoisted.length}`);
740
+ const knownTest = check.isKnown(kv);
741
+ for (const declaration of check.declarations) {
742
+ ctx.hoisted.push({ declaration, reference: knownTest });
743
+ }
744
+ body.push(` if (${knownTest}) continue`);
745
+ }
746
+ for (const pattern of patterns) {
747
+ body.push(` if (${regexLiteral(pattern)}.test(${kv})) continue`);
748
+ }
749
+ body.push(...valueChecks.map((line) => ` ${line}`));
750
+ }
751
+ }
752
+ if (body.length === 0)
753
+ return [];
754
+ return [` for (const ${kv} in ${obj}) {`, ...body, ` }`];
755
+ };
756
+ const generateInlineObjectChecks = (key, propSchema, raw, suffix, ctx) => {
757
+ if (!isSchemaObject(propSchema))
758
+ return [];
759
+ const child = {
760
+ branchErrors: ctx.branchErrors,
761
+ failFast: ctx.failFast,
762
+ objVar: `_obj${ctx.depth + 1}`,
763
+ // When `key` is empty the value is located AT `ctx.pathPrefix` already (e.g. an
764
+ // inline object reached through a combinator branch or a dynamic-key value), so
765
+ // appending `/${key}` would emit a spurious `//` or trailing `/` in error paths.
766
+ pathPrefix: key === "" ? ctx.pathPrefix : `${ctx.pathPrefix}/${pointerSegment(key)}`,
767
+ depth: ctx.depth + 1,
768
+ hoisted: ctx.hoisted,
769
+ rootSchema: ctx.rootSchema,
770
+ sink: ctx.sink,
771
+ formats: ctx.formats
772
+ };
773
+ const required = new Set(hasRequired(propSchema) ? propSchema.required : []);
774
+ const properties = hasProperties(propSchema) ? propSchema.properties : {};
775
+ const innerLines = [];
776
+ for (const [childKey, childSchema] of Object.entries(properties)) {
777
+ innerLines.push(...generatePropertyChecks(childKey, childSchema, required.has(childKey), suffix, child));
778
+ }
779
+ innerLines.push(...generateMissingRequiredChecks(propSchema, child));
780
+ innerLines.push(...generatePatternAndAdditionalChecks(propSchema, suffix, child));
781
+ innerLines.push(...generateStrictKeyChecks(propSchema, child));
782
+ innerLines.push(...generateDependentRequiredChecks(propSchema, child));
783
+ innerLines.push(...generateDependentSchemasChecks(propSchema, suffix, child));
784
+ innerLines.push(...generateDependenciesChecks(propSchema, suffix, child));
785
+ innerLines.push(...generateMinMaxPropertiesChecks(propSchema, child));
786
+ if (hasPropertyNames(propSchema)) {
787
+ innerLines.push(...generatePropertyNameChecks(propSchema.propertyNames, suffix, child));
788
+ }
789
+ if (innerLines.length === 0)
790
+ return [];
791
+ return [
792
+ ` if (typeof ${raw} === 'object' && ${raw} !== null && !Array.isArray(${raw})) {`,
793
+ ` const ${child.objVar} = ${raw} as Record<string, unknown>`,
794
+ ...innerLines.map((line) => ` ${line}`),
795
+ ` }`
796
+ ];
797
+ };
798
+ const generatePropertyNameChecks = (nameSchema, suffix, ctx) => {
799
+ const at = `\`${ctx.pathPrefix}/\${escapePointer(_name)}\``;
800
+ const nameCtx = {
801
+ branchErrors: ctx.branchErrors,
802
+ failFast: ctx.failFast,
803
+ objVar: ctx.objVar,
804
+ pathPrefix: `${ctx.pathPrefix}/\${escapePointer(_name)}`,
805
+ depth: ctx.depth + 1,
806
+ hoisted: ctx.hoisted,
807
+ rootSchema: ctx.rootSchema,
808
+ sink: ctx.sink,
809
+ formats: ctx.formats
810
+ };
811
+ const checks = generateValueChecks("", "_name", at, nameSchema, suffix, nameCtx, true);
812
+ if (checks.length === 0)
813
+ return [];
814
+ return [` for (const _name of Object.keys(${ctx.objVar})) {`, ...checks.map((line) => ` ${line}`), ` }`];
815
+ };
816
+ const generateDependentRequiredChecks = (schema, ctx) => {
817
+ if (!isSchemaObject(schema) || !hasDependentRequired(schema))
818
+ return [];
819
+ const obj = ctx.objVar;
820
+ const at = ctx.depth === 0 ? "_path" : `\`${ctx.pathPrefix}\``;
821
+ const lines = [];
822
+ for (const [trigger, deps] of Object.entries(schema.dependentRequired)) {
823
+ if (!Array.isArray(deps))
824
+ continue;
825
+ for (const dep of deps) {
826
+ const msg = JSON.stringify(`must have property '${dep}' when '${trigger}' is present`);
827
+ lines.push(` if (${hasOwnCheck(obj, trigger)} && ${missingCheck(obj, dep)}) {`);
828
+ lines.push(` ${pushError(ctx.sink, msg, at, "dependentRequired", JSON.stringify({ missingProperty: dep, property: trigger, depsCount: 1 }))}`);
829
+ lines.push(` }`);
830
+ }
831
+ }
832
+ return lines;
833
+ };
834
+ const dependentSelfBinding = (objVar, depth) => {
835
+ const name = `_dep${depth}`;
836
+ return { name, declaration: ` const ${name}: unknown = ${objVar}` };
837
+ };
838
+ const generateDependentSchemasChecks = (schema, suffix, ctx) => {
839
+ if (!isSchemaObject(schema))
840
+ return [];
841
+ const dep = readKey(schema, "dependentSchemas");
842
+ if (typeof dep !== "object" || dep === null || Array.isArray(dep))
843
+ return [];
844
+ const obj = ctx.objVar;
845
+ const at = ctx.depth === 0 ? "_path" : `\`${ctx.pathPrefix}\``;
846
+ const objPath = `\`${ctx.pathPrefix}\``;
847
+ const lines = [];
848
+ for (const [trigger, sub] of Object.entries(dep)) {
849
+ if (sub === true)
850
+ continue;
851
+ if (sub === false) {
852
+ const msg = JSON.stringify(`must NOT have property '${trigger}'`);
853
+ lines.push(` if (${hasOwnCheck(obj, trigger)}) {`);
854
+ lines.push(` ${pushError(ctx.sink, msg, at, "dependentSchemas", JSON.stringify({ property: trigger }))}`);
855
+ lines.push(` }`);
856
+ continue;
857
+ }
858
+ if (!isSchemaObject(sub))
859
+ continue;
860
+ const self = dependentSelfBinding(obj, ctx.depth);
861
+ const checks = generateValueChecks("", self.name, objPath, sub, suffix, ctx);
862
+ if (checks.length === 0)
863
+ continue;
864
+ lines.push(` if (${hasOwnCheck(obj, trigger)}) {`);
865
+ lines.push(` ${self.declaration}`);
866
+ lines.push(...checks.map((line) => ` ${line}`));
867
+ lines.push(` }`);
868
+ }
869
+ return lines;
870
+ };
871
+ const generateDependenciesChecks = (schema, suffix, ctx) => {
872
+ if (!isSchemaObject(schema))
873
+ return [];
874
+ const dep = readKey(schema, "dependencies");
875
+ if (typeof dep !== "object" || dep === null || Array.isArray(dep))
876
+ return [];
877
+ const obj = ctx.objVar;
878
+ const at = ctx.depth === 0 ? "_path" : `\`${ctx.pathPrefix}\``;
879
+ const objPath = `\`${ctx.pathPrefix}\``;
880
+ const lines = [];
881
+ for (const [trigger, value] of Object.entries(dep)) {
882
+ if (Array.isArray(value)) {
883
+ for (const key of value) {
884
+ if (typeof key !== "string")
885
+ continue;
886
+ const msg = JSON.stringify(`must have property '${key}' when '${trigger}' is present`);
887
+ lines.push(` if (${hasOwnCheck(obj, trigger)} && ${missingCheck(obj, key)}) {`);
888
+ lines.push(` ${pushError(ctx.sink, msg, at, "dependencies", JSON.stringify({ missingProperty: key, property: trigger, depsCount: 1 }))}`);
889
+ lines.push(` }`);
890
+ }
891
+ continue;
892
+ }
893
+ if (value === true)
894
+ continue;
895
+ if (value === false) {
896
+ const msg = JSON.stringify(`must NOT have property '${trigger}'`);
897
+ lines.push(` if (${hasOwnCheck(obj, trigger)}) {`);
898
+ lines.push(` ${pushError(ctx.sink, msg, at, "dependencies", JSON.stringify({ property: trigger }))}`);
899
+ lines.push(` }`);
900
+ continue;
901
+ }
902
+ if (!isSchemaObject(value))
903
+ continue;
904
+ const self = dependentSelfBinding(obj, ctx.depth);
905
+ const checks = generateValueChecks("", self.name, objPath, value, suffix, ctx);
906
+ if (checks.length === 0)
907
+ continue;
908
+ lines.push(` if (${hasOwnCheck(obj, trigger)}) {`);
909
+ lines.push(` ${self.declaration}`);
910
+ lines.push(...checks.map((line) => ` ${line}`));
911
+ lines.push(` }`);
912
+ }
913
+ return lines;
914
+ };
915
+ const generateMinMaxPropertiesChecks = (schema, ctx) => {
916
+ if (!isSchemaObject(schema))
917
+ return [];
918
+ const hasMin = hasMinProperties(schema);
919
+ const hasMax = hasMaxProperties(schema);
920
+ if (!hasMin && !hasMax)
921
+ return [];
922
+ const obj = ctx.objVar;
923
+ const at = ctx.depth === 0 ? "_path" : `\`${ctx.pathPrefix}\``;
924
+ const count = `_pc${ctx.depth}`;
925
+ const lines = [` const ${count} = Object.keys(${obj}).length`];
926
+ if (hasMin) {
927
+ const msg = JSON.stringify(`must have at least ${schema.minProperties} properties`);
928
+ lines.push(` if (${count} < ${schema.minProperties}) {`);
929
+ lines.push(` ${pushError(ctx.sink, msg, at, "minProperties", JSON.stringify({ limit: schema.minProperties }))}`);
930
+ lines.push(` }`);
931
+ }
932
+ if (hasMax) {
933
+ const msg = JSON.stringify(`must have at most ${schema.maxProperties} properties`);
934
+ lines.push(` if (${count} > ${schema.maxProperties}) {`);
935
+ lines.push(` ${pushError(ctx.sink, msg, at, "maxProperties", JSON.stringify({ limit: schema.maxProperties }))}`);
936
+ lines.push(` }`);
937
+ }
938
+ return lines;
939
+ };
940
+ const hasObjectLevelCombinator = (schema) => {
941
+ if (!isSchemaObject(schema))
942
+ return false;
943
+ return hasAllOf(schema) || hasAnyOf(schema) || hasOneOf(schema) || declaresKey(schema, "not") || declaresKey(schema, "if");
944
+ };
945
+ const carriesUnevaluated = (schema) => {
946
+ if (!isSchemaObject(schema))
947
+ return false;
948
+ const s = schema;
949
+ return declaresKey(s, "unevaluatedProperties") && s["unevaluatedProperties"] !== true || declaresKey(s, "unevaluatedItems") && s["unevaluatedItems"] !== true;
950
+ };
951
+ const createGuardContext = (unknownKeys, formats = NO_FORMATS) => ({ unknownKeys, formats, locals: 0, loops: 0 });
952
+ const guardFormatPass = (schema, acc, ctx) => {
953
+ if (!enforcesFormat(schema, ctx.formats))
954
+ return null;
955
+ const format = schema.format;
956
+ return formatFamily(format) === void 0 ? null : `${formatCheckName(format)}(${acc})`;
957
+ };
958
+ const emptyGuardBlock = () => ({ conditions: [], nested: [], keyChecks: [] });
959
+ const isFlatGuardBlock = (block) => block.nested.length === 0 && block.keyChecks.length === 0;
960
+ const keySetCheckLines = (check, bail, indent, unknownKeys) => {
961
+ const key = `_k${check.index}`;
962
+ if (check.count === null) {
963
+ const unknown = unknownKeyCheck(check.known, "", Number.POSITIVE_INFINITY).isUnknown(key);
964
+ return [`${indent}for (const ${key} in ${check.obj}) if (${unknown}) ${bail}`];
965
+ }
966
+ if (unknownKeys === "count-keys") {
967
+ return [`${indent}if (Object.keys(${check.obj}).length !== ${check.count}) ${bail}`];
968
+ }
969
+ const count = `_c${check.index}`;
970
+ return [
971
+ `${indent}let ${count} = 0`,
972
+ `${indent}for (const ${key} in ${check.obj}) ${count}++`,
973
+ `${indent}if (${count} !== ${check.count}) ${bail}`
974
+ ];
975
+ };
976
+ const renderChainBail = (conditions, bail, indent, layout) => layout === "inline" ? [`if (!(${conditions.join(" && ")})) ${bail}`] : [`${indent}if (!(`, conditions.map((condition) => `${indent} ${condition}`).join(" &&\n"), `${indent})) ${bail}`];
977
+ const renderGuardBlock = (block, bail, indent, layout, unknownKeys) => {
978
+ const lines = [];
979
+ if (block.conditions.length > 0)
980
+ lines.push(...renderChainBail(block.conditions, bail, indent, layout));
981
+ for (const nested of block.nested) {
982
+ lines.push(`${indent}const ${nested.local} = ${nested.read} as Record<string, unknown>`);
983
+ if (nested.optional) {
984
+ lines.push(`${indent}if (${nested.local} !== undefined) {`);
985
+ lines.push(...renderGuardBlock(nested.block, bail, `${indent} `, layout, unknownKeys));
986
+ lines.push(`${indent}}`);
987
+ } else {
988
+ lines.push(...renderGuardBlock(nested.block, bail, indent, layout, unknownKeys));
989
+ }
990
+ }
991
+ for (const check of block.keyChecks)
992
+ lines.push(...keySetCheckLines(check, bail, indent, unknownKeys));
993
+ return lines;
994
+ };
995
+ const joinInline = (lines) => {
996
+ const trimmed = lines.map((line) => line.trim());
997
+ return trimmed.reduce((text, line, i) => {
998
+ if (i === 0)
999
+ return line;
1000
+ const previous = trimmed[i - 1];
1001
+ return `${text}${previous.endsWith("{") || line === "}" ? " " : "; "}${line}`;
1002
+ }, "");
1003
+ };
1004
+ const guardPropConditions = (key, propSchema, objAcc, block, ctx) => {
1005
+ if (!isSchemaObject(propSchema))
1006
+ return null;
1007
+ const raw = safeAccessor(objAcc, key);
1008
+ if (hasRef(propSchema) || hasEnum(propSchema) || hasConst(propSchema) || hasOneOf(propSchema) || hasAnyOf(propSchema) || hasAllOf(propSchema) || declaresKey(propSchema, "not") || declaresKey(propSchema, "if") || getMjstInstanceOf(propSchema) !== void 0 || getMjstPrimitive(propSchema) !== void 0 || hasPattern(propSchema) || hasMinLength(propSchema) || hasMaxLength(propSchema) || hasMinimum(propSchema) || hasMaximum(propSchema) || hasExclusiveMinimum(propSchema) || hasExclusiveMaximum(propSchema) || hasMultipleOf(propSchema) || hasItems(propSchema) || carriesUnevaluated(propSchema)) {
1009
+ return null;
1010
+ }
1011
+ if (!hasType(propSchema))
1012
+ return null;
1013
+ switch (propSchema.type) {
1014
+ case "string":
1015
+ return [`typeof ${raw} === 'string'`];
1016
+ case "number":
1017
+ return [`typeof ${raw} === 'number'`];
1018
+ case "integer":
1019
+ return [`typeof ${raw} === 'number'`, `Number.isInteger(${raw})`];
1020
+ case "boolean":
1021
+ return [`typeof ${raw} === 'boolean'`];
1022
+ case "null":
1023
+ return [`${raw} === null`];
1024
+ case "object": {
1025
+ const local = `_n${ctx.locals++}`;
1026
+ const nested = guardObjectConditions(propSchema, local, local, ctx);
1027
+ if (nested === null)
1028
+ return null;
1029
+ block.nested.push({ local, read: raw, optional: false, block: nested });
1030
+ return [];
1031
+ }
1032
+ // Arrays need a per-item loop the guard can't express, and any other type
1033
+ // (null, multi-type, untyped) is left to the slow path.
1034
+ default:
1035
+ return null;
1036
+ }
1037
+ };
1038
+ const ARRAY_INDEX_KEY = /^(0|[1-9]\d*)$/;
1039
+ const TYPEOF_CHECKABLE_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "object"]);
1040
+ const arrayRejectedByRequiredProp = (keys, required, properties) => {
1041
+ for (const key of keys) {
1042
+ if (!required.has(key) || key === "length" || ARRAY_INDEX_KEY.test(key))
1043
+ continue;
1044
+ const propSchema = properties[key];
1045
+ if (propSchema !== void 0 && isSchemaObject(propSchema) && hasType(propSchema) && TYPEOF_CHECKABLE_TYPES.has(propSchema.type)) {
1046
+ return true;
1047
+ }
1048
+ }
1049
+ return false;
1050
+ };
1051
+ const guardObjectConditions = (schema, raw, objAcc, ctx) => {
1052
+ if (!isObjectSchema(schema))
1053
+ return null;
1054
+ if (hasDependentRequired(schema) || hasPropertyNames(schema) || declaresKey(schema, "dependentSchemas"))
1055
+ return null;
1056
+ if (hasMinProperties(schema) || hasMaxProperties(schema) || declaresKey(schema, "dependencies"))
1057
+ return null;
1058
+ if (isSchemaObject(schema) && declaresKey(schema, "patternProperties"))
1059
+ return null;
1060
+ if (hasObjectLevelCombinator(schema))
1061
+ return null;
1062
+ if (carriesUnevaluated(schema))
1063
+ return null;
1064
+ let strict = false;
1065
+ if (hasAdditionalProperties(schema)) {
1066
+ if (schema.additionalProperties === false)
1067
+ strict = true;
1068
+ else
1069
+ return null;
1070
+ }
1071
+ const required = new Set(hasRequired(schema) ? schema.required : []);
1072
+ const properties = hasProperties(schema) ? schema.properties : {};
1073
+ const keys = Object.keys(properties);
1074
+ for (const key of required) {
1075
+ if (!Object.hasOwn(properties, key))
1076
+ return null;
1077
+ }
1078
+ const arrayCheck = arrayRejectedByRequiredProp(keys, required, properties) ? "" : ` && !Array.isArray(${raw})`;
1079
+ const block = emptyGuardBlock();
1080
+ block.conditions.push(`typeof ${raw} === 'object' && ${raw} !== null${arrayCheck}`);
1081
+ for (const key of keys) {
1082
+ if (!required.has(key))
1083
+ return null;
1084
+ const propConditions = guardPropConditions(key, properties[key], objAcc, block, ctx);
1085
+ if (propConditions === null)
1086
+ return null;
1087
+ block.conditions.push(...propConditions);
1088
+ }
1089
+ if (strict) {
1090
+ if (!keys.every((key) => required.has(key)))
1091
+ return null;
1092
+ block.keyChecks.push({ obj: objAcc, count: keys.length, known: keys, index: ctx.loops++ });
1093
+ }
1094
+ return block;
1095
+ };
1096
+ const generateObjectValidator = (schema, typeName, suffix, rootSchema, unknownKeys, formats, branchErrors, failFast) => {
1097
+ const vName = emittedName(typeName, failFast);
1098
+ const required = new Set(hasRequired(schema) ? schema.required : []);
1099
+ const properties = hasProperties(schema) ? schema.properties : {};
1100
+ const ctx = createRootContext(rootSchema, formats, branchErrors, failFast);
1101
+ const propertyLines = [];
1102
+ for (const [key, propSchema] of Object.entries(properties)) {
1103
+ const checks = generatePropertyChecks(key, propSchema, required.has(key), suffix, ctx);
1104
+ if (checks.length > 0) {
1105
+ propertyLines.push(...checks);
1106
+ }
1107
+ }
1108
+ propertyLines.push(...generateMissingRequiredChecks(schema, ctx));
1109
+ propertyLines.push(...generatePatternAndAdditionalChecks(schema, suffix, ctx));
1110
+ propertyLines.push(...generateStrictKeyChecks(schema, ctx));
1111
+ propertyLines.push(...generateDependentRequiredChecks(schema, ctx));
1112
+ propertyLines.push(...generateDependentSchemasChecks(schema, suffix, ctx));
1113
+ propertyLines.push(...generateDependenciesChecks(schema, suffix, ctx));
1114
+ propertyLines.push(...generateMinMaxPropertiesChecks(schema, ctx));
1115
+ if (hasPropertyNames(schema)) {
1116
+ propertyLines.push(...generatePropertyNameChecks(schema.propertyNames, suffix, ctx));
1117
+ }
1118
+ const objectCombinators = generateCombinatorChecks("", "_root", "`${_path}`", schema, suffix, ctx);
1119
+ if (objectCombinators.length > 0) {
1120
+ if (readsBinding("_root", objectCombinators.join("\n")))
1121
+ propertyLines.push(` const _root: unknown = input`);
1122
+ propertyLines.push(...objectCombinators);
1123
+ }
1124
+ const body = propertyLines.length > 0 ? "\n" + propertyLines.join("\n") + "\n" : "";
1125
+ const guard = guardObjectConditions(schema, "input", "obj", createGuardContext(unknownKeys, formats));
1126
+ const objBinding = readsObjBinding(body) ? [` const obj = input as Record<string, unknown>`] : [];
1127
+ const collectBody = (name, exported) => [
1128
+ `${exported ? "export " : ""}const ${name} = (input: unknown, _path = ''): ValidationResult => {`,
1129
+ ...objBinding,
1130
+ ` if (typeof input !== 'object' || input === null || Array.isArray(input)) {`,
1131
+ ` ${returnError(`'must be object'`, "_path", "type", JSON.stringify({ type: "object" }))}`,
1132
+ ` }`,
1133
+ // The blank line separates the shape check from the `errors` declaration
1134
+ // under it; a fail-fast body declares nothing there, and two blank lines in
1135
+ // a row is just untidy output.
1136
+ ...failFast.on ? [] : [""],
1137
+ ...errorsBinding(failFast),
1138
+ body,
1139
+ errorsVerdict(failFast),
1140
+ `}`
1141
+ ].join("\n");
1142
+ if (!guard) {
1143
+ return withHoisted(ctx.hoisted, collectBody(vName, true));
1144
+ }
1145
+ const collectName = `${vName}Errors`;
1146
+ const bail = `return ${collectName}(input, _path)`;
1147
+ const guardLines = isFlatGuardBlock(guard) ? [
1148
+ ` if (`,
1149
+ guard.conditions.map((condition) => ` ${condition}`).join(" &&\n"),
1150
+ ` ) {`,
1151
+ ` return true`,
1152
+ ` }`,
1153
+ ` ${bail}`
1154
+ ] : [...renderGuardBlock(guard, bail, " ", "lines", unknownKeys), ` return true`];
1155
+ return withHoisted(ctx.hoisted, [
1156
+ collectBody(collectName, false),
1157
+ ``,
1158
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1159
+ ...readsObjBinding(guardLines.join("\n")) ? [` const obj = input as Record<string, unknown>`] : [],
1160
+ ...guardLines,
1161
+ `}`
1162
+ ].join("\n"));
1163
+ };
1164
+ const guardName = (typeName) => `is${typeName}`;
1165
+ const valueCanHaveType = (value, type) => {
1166
+ switch (type) {
1167
+ case "string":
1168
+ return typeof value === "string";
1169
+ case "number":
1170
+ return typeof value === "number";
1171
+ case "integer":
1172
+ return typeof value === "number" && Number.isInteger(value);
1173
+ case "boolean":
1174
+ return typeof value === "boolean";
1175
+ case "null":
1176
+ return value === null;
1177
+ case "array":
1178
+ return Array.isArray(value);
1179
+ case "object":
1180
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1181
+ default:
1182
+ return true;
1183
+ }
1184
+ };
1185
+ const flatLeafBails = (schema) => hasRef(schema) || hasConst(schema) || hasOneOf(schema) || declaresKey(schema, "anyOf") || declaresKey(schema, "allOf") || declaresKey(schema, "not") || declaresKey(schema, "if") || declaresKey(schema, "contains") || declaresKey(schema, "prefixItems") || // A draft-07 tuple: the fixed positions live in an *array* `items` and the
1186
+ // tail in `additionalItems`. Neither is expressible flat, and reading past
1187
+ // them let the guard accept tuples `validateX` rejects.
1188
+ Array.isArray(readKey(schema, "items")) || declaresKey(schema, "additionalItems") || carriesUnevaluated(schema) || getMjstInstanceOf(schema) !== void 0 || getMjstPrimitive(schema) !== void 0;
1189
+ const isNestedObjectLeaf = (schema) => isSchemaObject(schema) && !flatLeafBails(schema) && hasType(schema) && schema.type === "object";
1190
+ const booleanLeafExpr = (schema, acc, ctx) => {
1191
+ if (!isSchemaObject(schema))
1192
+ return null;
1193
+ if (flatLeafBails(schema))
1194
+ return null;
1195
+ if (!hasType(schema)) {
1196
+ if (!hasEnum(schema))
1197
+ return null;
1198
+ if (declaresKeywordOutside(schema, ["enum"], ctx.formats))
1199
+ return null;
1200
+ return enumMembershipExpr(schema.enum, acc);
1201
+ }
1202
+ const t = schema.type;
1203
+ const membership = hasEnum(schema) ? enumMembershipExpr(schema.enum.filter((member) => valueCanHaveType(member, t)), acc) : null;
1204
+ const withMembership = (expr) => expr === null || membership === null ? expr : `${expr} && ${membership}`;
1205
+ switch (t) {
1206
+ // Each constraint is the exact negation of the validator's error condition,
1207
+ // so edge values — `NaN` above all, which compares `false` against every
1208
+ // operator — get the identical verdict on both paths. The numeric bounds are
1209
+ // written as the pass condition (`x >= min`) because the validator's error
1210
+ // condition is its negation; the length checks come from the same
1211
+ // `string-length-check` emitter the validator uses, for the same reason.
1212
+ // A constrained check reads `acc` again after the `typeof`, and TypeScript
1213
+ // narrows it there because every `acc` is a *reference* — `obj.a`, `_n0.b`,
1214
+ // `_it` — never a cast expression: nested members read through a hoisted
1215
+ // local (see {@link NestedGuard}), and a narrowing does not survive two
1216
+ // spellings of `(obj.a as Record<string, unknown>).b`.
1217
+ case "string": {
1218
+ const parts = [`typeof ${acc} === 'string'`];
1219
+ const formatPass = guardFormatPass(schema, acc, ctx);
1220
+ if (formatPass !== null)
1221
+ parts.push(formatPass);
1222
+ if (hasPattern(schema))
1223
+ parts.push(`${regexLiteral(schema.pattern)}.test(${acc})`);
1224
+ if (hasMinLength(schema))
1225
+ parts.push(minLengthPassExpr(acc, schema.minLength));
1226
+ if (hasMaxLength(schema))
1227
+ parts.push(maxLengthPassExpr(acc, schema.maxLength));
1228
+ return withMembership(parts.join(" && "));
1229
+ }
1230
+ case "number":
1231
+ case "integer": {
1232
+ const parts = [`typeof ${acc} === 'number'`];
1233
+ if (t === "integer")
1234
+ parts.push(`Number.isInteger(${acc})`);
1235
+ const formatPass = guardFormatPass(schema, acc, ctx);
1236
+ if (formatPass !== null)
1237
+ parts.push(formatPass);
1238
+ if (hasMinimum(schema))
1239
+ parts.push(boundPassExpr(acc, "minimum", schema.minimum, hasStrictExclusiveMinimum(schema)));
1240
+ if (hasMaximum(schema))
1241
+ parts.push(boundPassExpr(acc, "maximum", schema.maximum, hasStrictExclusiveMaximum(schema)));
1242
+ if (hasExclusiveMinimum(schema))
1243
+ parts.push(boundPassExpr(acc, "minimum", schema.exclusiveMinimum, true));
1244
+ if (hasExclusiveMaximum(schema))
1245
+ parts.push(boundPassExpr(acc, "maximum", schema.exclusiveMaximum, true));
1246
+ if (hasMultipleOf(schema))
1247
+ parts.push(multipleOfPassExpr(acc, schema.multipleOf));
1248
+ return withMembership(parts.join(" && "));
1249
+ }
1250
+ case "boolean":
1251
+ return withMembership(`typeof ${acc} === 'boolean'`);
1252
+ case "null":
1253
+ return withMembership(`${acc} === null`);
1254
+ case "object":
1255
+ return null;
1256
+ case "array":
1257
+ return withMembership(booleanArrayExpr(schema, acc, ctx));
1258
+ default:
1259
+ return null;
1260
+ }
1261
+ };
1262
+ const booleanArrayExpr = (schema, acc, ctx) => {
1263
+ const parts = [`Array.isArray(${acc})`];
1264
+ if (hasMinItems(schema))
1265
+ parts.push(`${acc}.length >= ${schema.minItems}`);
1266
+ if (hasMaxItems(schema))
1267
+ parts.push(`${acc}.length <= ${schema.maxItems}`);
1268
+ if (hasUniqueItems(schema) && schema.uniqueItems === true) {
1269
+ parts.push(arrayItemsAreScalarOnly(schema) ? `new Set(${acc} as unknown[]).size === ${acc}.length` : `allUnique(${acc} as unknown[])`);
1270
+ }
1271
+ const base = parts.join(" && ");
1272
+ if (readKey(schema, "items") === false)
1273
+ return `${base} && ${acc}.length === 0`;
1274
+ if (!hasItems(schema))
1275
+ return base;
1276
+ const items = schema.items;
1277
+ if (!isSchemaObject(items))
1278
+ return base;
1279
+ if (hasRef(items))
1280
+ return null;
1281
+ if (isNestedObjectLeaf(items)) {
1282
+ const local = `_n${ctx.locals++}`;
1283
+ const block = booleanObjectParts(items, local, local, ctx);
1284
+ if (block === null)
1285
+ return null;
1286
+ const body = joinInline([
1287
+ `const ${local} = _it as Record<string, unknown>`,
1288
+ ...renderGuardBlock(block, "return false", "", "inline", ctx.unknownKeys),
1289
+ `return true`
1290
+ ]);
1291
+ return `${base} && everyItem(${acc} as unknown[], (_it) => { ${body} })`;
1292
+ }
1293
+ const itemExpr = booleanLeafExpr(items, "_it", ctx);
1294
+ if (itemExpr === null)
1295
+ return null;
1296
+ return `${base} && everyItem(${acc} as unknown[], (_it) => (${itemExpr}))`;
1297
+ };
1298
+ const booleanObjectParts = (schema, raw, objAcc, ctx) => {
1299
+ if (!isObjectSchema(schema))
1300
+ return null;
1301
+ if (hasRef(schema) || hasConst(schema) || hasEnum(schema))
1302
+ return null;
1303
+ if (getMjstInstanceOf(schema) !== void 0 || getMjstPrimitive(schema) !== void 0)
1304
+ return null;
1305
+ if (hasDependentRequired(schema) || hasPropertyNames(schema) || declaresKey(schema, "dependentSchemas"))
1306
+ return null;
1307
+ if (hasMinProperties(schema) || hasMaxProperties(schema) || declaresKey(schema, "dependencies"))
1308
+ return null;
1309
+ if (isSchemaObject(schema) && declaresKey(schema, "patternProperties"))
1310
+ return null;
1311
+ if (hasObjectLevelCombinator(schema))
1312
+ return null;
1313
+ if (carriesUnevaluated(schema))
1314
+ return null;
1315
+ let strict = false;
1316
+ if (hasAdditionalProperties(schema)) {
1317
+ if (schema.additionalProperties === false)
1318
+ strict = true;
1319
+ else
1320
+ return null;
1321
+ }
1322
+ const required = new Set(hasRequired(schema) ? schema.required : []);
1323
+ const properties = hasProperties(schema) ? schema.properties : {};
1324
+ const keys = Object.keys(properties);
1325
+ const arrayCheck = arrayRejectedByRequiredProp(keys, required, properties) ? "" : ` && !Array.isArray(${raw})`;
1326
+ const block = emptyGuardBlock();
1327
+ block.conditions.push(`typeof ${raw} === 'object' && ${raw} !== null${arrayCheck}`);
1328
+ for (const key of keys) {
1329
+ const propSchema = properties[key];
1330
+ if (propSchema === void 0 || !isSchemaObject(propSchema))
1331
+ return null;
1332
+ if (PROTOTYPE_MEMBERS.has(key))
1333
+ return null;
1334
+ const member = safeAccessor(objAcc, key);
1335
+ if (isNestedObjectLeaf(propSchema)) {
1336
+ const local = `_n${ctx.locals++}`;
1337
+ const nested = booleanObjectParts(propSchema, local, local, ctx);
1338
+ if (nested === null)
1339
+ return null;
1340
+ block.nested.push({ local, read: member, optional: !required.has(key), block: nested });
1341
+ continue;
1342
+ }
1343
+ const expr = booleanLeafExpr(propSchema, member, ctx);
1344
+ if (expr === null)
1345
+ return null;
1346
+ block.conditions.push(required.has(key) ? expr : `(${member} === undefined || (${expr}))`);
1347
+ }
1348
+ for (const key of required) {
1349
+ if (Object.hasOwn(properties, key))
1350
+ continue;
1351
+ block.conditions.push(hasOwnCheck(objAcc, key));
1352
+ }
1353
+ if (strict) {
1354
+ const allRequired = keys.every((key) => required.has(key));
1355
+ block.keyChecks.push({ obj: objAcc, count: allRequired ? keys.length : null, known: keys, index: ctx.loops++ });
1356
+ }
1357
+ return block;
1358
+ };
1359
+ const IMPLICIT_OBJECT_KEYWORDS = ["properties", "patternProperties", "additionalProperties"];
1360
+ const typeDescribesEveryAcceptedValue = (schema) => {
1361
+ if (!isSchemaObject(schema))
1362
+ return true;
1363
+ const s = schema;
1364
+ if (declaresKey(s, "type") || declaresKey(s, "enum") || declaresKey(s, "const") || declaresKey(s, "$ref"))
1365
+ return true;
1366
+ for (const keyword of ["allOf", "anyOf", "oneOf"]) {
1367
+ const branches = readKey(s, keyword);
1368
+ if (!Array.isArray(branches))
1369
+ continue;
1370
+ if (!branches.every((branch) => typeDescribesEveryAcceptedValue(branch)))
1371
+ return false;
1372
+ }
1373
+ return !IMPLICIT_OBJECT_KEYWORDS.some((keyword) => declaresKey(s, keyword));
1374
+ };
1375
+ const generateBooleanGuard = (schema, typeName, _suffix = "", unknownKeys = DEFAULT_UNKNOWN_KEYS, formats = NO_FORMATS) => {
1376
+ const name = guardName(typeName);
1377
+ const returns = typeDescribesEveryAcceptedValue(rewriteNullable(schema)) ? `input is ${typeName}` : "boolean";
1378
+ const fallback = `export const ${name} = (input: unknown): ${returns} => ${validatorName(typeName)}(input) === true`;
1379
+ const rewritten = rewriteNullable(schema);
1380
+ const ctx = createGuardContext(unknownKeys, formats);
1381
+ if (declaresObjectType(rewritten)) {
1382
+ if (!objectRootIsSelfContained(rewritten))
1383
+ return fallback;
1384
+ const block = booleanObjectParts(rewritten, "input", "obj", ctx);
1385
+ if (block === null)
1386
+ return fallback;
1387
+ const body = isFlatGuardBlock(block) ? [` return (`, block.conditions.map((part) => ` ${part}`).join(" &&\n"), ` )`] : [...renderGuardBlock(block, "return false", " ", "lines", unknownKeys), ` return true`];
1388
+ return [
1389
+ `export const ${name} = (input: unknown): ${returns} => {`,
1390
+ // Same unused-local as the validator's hot guard: a node with no property
1391
+ // to read guards on the shape alone and never touches the narrowing.
1392
+ ...readsObjBinding(body.join("\n")) ? [` const obj = input as Record<string, unknown>`] : [],
1393
+ ...body,
1394
+ `}`
1395
+ ].join("\n");
1396
+ }
1397
+ const expr = booleanLeafExpr(rewritten, "input", ctx);
1398
+ if (expr === null)
1399
+ return fallback;
1400
+ return `export const ${name} = (input: unknown): ${returns} => ${expr}`;
1401
+ };
1402
+ const generateScalarValidator = (schema, typeName, suffix, rootSchema, formats, branchErrors, failFast) => {
1403
+ const vName = emittedName(typeName, failFast);
1404
+ if (!isSchemaObject(schema)) {
1405
+ if (schema === false) {
1406
+ return [
1407
+ `export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`,
1408
+ ` ${returnError(JSON.stringify(FALSE_SCHEMA_MESSAGE), "_path", "false schema")}`,
1409
+ `}`
1410
+ ].join("\n");
1411
+ }
1412
+ return [`export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`, ` return true`, `}`].join("\n");
1413
+ }
1414
+ const generalRoot = () => generateGeneralRootValidator(schema, typeName, suffix, rootSchema, formats, branchErrors, failFast);
1415
+ if (hasRef(schema)) {
1416
+ if (declaresKeywordOutside(schema, ["$ref"]))
1417
+ return generalRoot();
1418
+ const delegateName = emittedName(refToName(schema.$ref, suffix), failFast);
1419
+ return [
1420
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1421
+ ` return ${delegateName}(input, _path)`,
1422
+ `}`
1423
+ ].join("\n");
1424
+ }
1425
+ const instanceOf = getMjstInstanceOf(schema);
1426
+ if (instanceOf) {
1427
+ if (declaresKeywordOutside(schema, [MJST_EXTENSION_KEY, "type"]))
1428
+ return generalRoot();
1429
+ return [
1430
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1431
+ ` if (!(input instanceof ${instanceOf})) {`,
1432
+ ` ${returnError(`'must be ${instanceOf}'`, "_path", "type", JSON.stringify({ type: instanceOf }))}`,
1433
+ ` }`,
1434
+ ` return true`,
1435
+ `}`
1436
+ ].join("\n");
1437
+ }
1438
+ const primitive = getMjstPrimitive(schema);
1439
+ if (primitive) {
1440
+ if (declaresKeywordOutside(schema, [MJST_EXTENSION_KEY, "type"]))
1441
+ return generalRoot();
1442
+ return [
1443
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1444
+ ` if (typeof input !== "${primitive}") {`,
1445
+ ` ${returnError(`'must be ${primitive}'`, "_path", "type", JSON.stringify({ type: primitive }))}`,
1446
+ ` }`,
1447
+ ` return true`,
1448
+ `}`
1449
+ ].join("\n");
1450
+ }
1451
+ if (hasConst(schema)) {
1452
+ if (declaresKeywordOutside(schema, ["const"]))
1453
+ return generalRoot();
1454
+ const mismatch = constMismatchCondition("input", schema.const);
1455
+ const msg = JSON.stringify(`must be ${JSON.stringify(schema.const)}`);
1456
+ return [
1457
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1458
+ ` if (${mismatch}) {`,
1459
+ ` ${returnError(msg, "_path", "const", JSON.stringify({ allowedValue: schema.const }))}`,
1460
+ ` }`,
1461
+ ` return true`,
1462
+ `}`
1463
+ ].join("\n");
1464
+ }
1465
+ if (hasEnum(schema)) {
1466
+ if (declaresKeywordOutside(schema, ["enum"]))
1467
+ return generalRoot();
1468
+ const label = schema.enum.map((v) => JSON.stringify(v)).join(", ");
1469
+ return [
1470
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1471
+ ` if (!${enumMembershipExpr(schema.enum, "input")}) {`,
1472
+ ` ${returnError(JSON.stringify(`must be one of: ${label}`), "_path", "enum", JSON.stringify({ allowedValues: schema.enum }))}`,
1473
+ ` }`,
1474
+ ` return true`,
1475
+ `}`
1476
+ ].join("\n");
1477
+ }
1478
+ if (hasAllOf(schema) || hasAnyOf(schema) || hasOneOf(schema) || declaresKey(schema, "not") || declaresKey(schema, "if")) {
1479
+ const ctx = createRootContext(rootSchema, formats, branchErrors, failFast);
1480
+ const checks = [];
1481
+ const rootPath = "`${_path}`";
1482
+ const rootTypeArray2 = getTypeArray(schema);
1483
+ if (rootTypeArray2) {
1484
+ const allWrong = rootTypeArray2.map((t) => wrongTypeCondition(typeTestAccessor("input", ctx), t)).filter((c) => c !== "").map((c) => `(${c})`).join(" && ");
1485
+ if (allWrong) {
1486
+ const label = rootTypeArray2.map((t) => typeofString(t)).join(" or ");
1487
+ checks.push(` if (${allWrong}) {`);
1488
+ checks.push(` ${pushError(ctx.sink, JSON.stringify(`must be ${label}`), rootPath, "type", JSON.stringify({ type: schema.type }))}`);
1489
+ checks.push(` }`);
1490
+ }
1491
+ } else if (hasType(schema)) {
1492
+ const t = schema.type;
1493
+ const wrongType = wrongTypeCondition(typeTestAccessor("input", ctx), t);
1494
+ if (wrongType) {
1495
+ checks.push(` if (${wrongType}) {`);
1496
+ checks.push(` ${pushError(ctx.sink, `'must be ${typeofString(t)}'`, rootPath, "type", JSON.stringify({ type: t }))}`);
1497
+ checks.push(` }`);
1498
+ }
1499
+ }
1500
+ checks.push(...generateConstraintChecks("", "input", rootPath, schema, suffix, ctx));
1501
+ checks.push(...generateCombinatorChecks("", "input", rootPath, schema, suffix, ctx));
1502
+ const body = checks.join("\n");
1503
+ return withHoisted(ctx.hoisted, [
1504
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1505
+ ...errorsBinding(failFast),
1506
+ body,
1507
+ errorsVerdict(failFast),
1508
+ `}`
1509
+ ].join("\n"));
1510
+ }
1511
+ const rootTypeArray = getTypeArray(schema);
1512
+ if (rootTypeArray) {
1513
+ const ctx = createRootContext(rootSchema, formats, branchErrors, failFast);
1514
+ const rootPath = "`${_path}`";
1515
+ const checks = [];
1516
+ const allWrong = rootTypeArray.map((t) => wrongTypeCondition(typeTestAccessor("input", ctx), t)).filter((c) => c !== "").map((c) => `(${c})`).join(" && ");
1517
+ if (allWrong) {
1518
+ const label = rootTypeArray.map((t) => typeofString(t)).join(" or ");
1519
+ checks.push(` if (${allWrong}) {`);
1520
+ checks.push(` ${pushError(ctx.sink, JSON.stringify(`must be ${label}`), rootPath, "type", JSON.stringify({ type: schema.type }))}`);
1521
+ checks.push(` }`);
1522
+ }
1523
+ checks.push(...generateConstraintChecks("", "input", rootPath, schema, suffix, ctx));
1524
+ if (checks.length === 0) {
1525
+ return [
1526
+ `export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`,
1527
+ ` return true`,
1528
+ `}`
1529
+ ].join("\n");
1530
+ }
1531
+ const body = checks.join("\n");
1532
+ return withHoisted(ctx.hoisted, [
1533
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1534
+ ...errorsBinding(failFast),
1535
+ body,
1536
+ errorsVerdict(failFast),
1537
+ `}`
1538
+ ].join("\n"));
1539
+ }
1540
+ if (hasType(schema)) {
1541
+ const t = schema.type;
1542
+ const wrongType = wrongTypeCondition("input", t);
1543
+ const typLabel = typeofString(t);
1544
+ const rootCtx = createRootContext(rootSchema, formats, branchErrors, failFast);
1545
+ const constraintLines = generateConstraintChecks("", "_root", "`${_path}`", schema, suffix, rootCtx);
1546
+ if (!wrongType) {
1547
+ return [
1548
+ `export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`,
1549
+ ` return true`,
1550
+ `}`
1551
+ ].join("\n");
1552
+ }
1553
+ if (constraintLines.length === 0) {
1554
+ return [
1555
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1556
+ ` if (${wrongType}) {`,
1557
+ ` ${returnError(`'must be ${typLabel}'`, "_path", "type", JSON.stringify({ type: schema.type }))}`,
1558
+ ` }`,
1559
+ ` return true`,
1560
+ `}`
1561
+ ].join("\n");
1562
+ }
1563
+ return withHoisted(rootCtx.hoisted, [
1564
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1565
+ ` if (${wrongType}) {`,
1566
+ ` ${returnError(`'must be ${typLabel}'`, "_path", "type", JSON.stringify({ type: schema.type }))}`,
1567
+ ` }`,
1568
+ ...readsBinding("_root", constraintLines.join("\n")) ? [` const _root: unknown = input`] : [],
1569
+ ...errorsBinding(failFast),
1570
+ constraintLines.join("\n"),
1571
+ errorsVerdict(failFast),
1572
+ `}`
1573
+ ].join("\n"));
1574
+ }
1575
+ const typelessCtx = createRootContext(rootSchema, formats, branchErrors, failFast);
1576
+ const typelessChecks = generateConstraintChecks("", "input", "`${_path}`", schema, suffix, typelessCtx);
1577
+ if (typelessChecks.length === 0) {
1578
+ return [`export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`, ` return true`, `}`].join("\n");
1579
+ }
1580
+ return withHoisted(typelessCtx.hoisted, [
1581
+ `export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
1582
+ ...errorsBinding(failFast),
1583
+ typelessChecks.join("\n"),
1584
+ errorsVerdict(failFast),
1585
+ `}`
1586
+ ].join("\n"));
1587
+ };
1588
+ const generateGeneralRootValidator = (schema, typeName, suffix, rootSchema, formats, branchErrors, failFast) => {
1589
+ const ctx = createRootContext(rootSchema, formats, branchErrors, failFast);
1590
+ const checks = generateValueChecks("", "input", "`${_path}`", schema, suffix, ctx, true);
1591
+ const body = checks.join("\n");
1592
+ return withHoisted(ctx.hoisted, [
1593
+ `export const ${emittedName(typeName, failFast)} = (input: unknown, _path = ''): ValidationResult => {`,
1594
+ ...errorsBinding(failFast),
1595
+ body,
1596
+ errorsVerdict(failFast),
1597
+ `}`
1598
+ ].join("\n"));
1599
+ };
1600
+ const SINGLE_SUBSCHEMA_KEYS = /* @__PURE__ */ new Set([
1601
+ "additionalProperties",
1602
+ "additionalItems",
1603
+ "contains",
1604
+ "propertyNames",
1605
+ "not",
1606
+ "if",
1607
+ "then",
1608
+ "else",
1609
+ "unevaluatedProperties",
1610
+ "unevaluatedItems"
1611
+ ]);
1612
+ const SUBSCHEMA_LIST_KEYS = /* @__PURE__ */ new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
1613
+ const SUBSCHEMA_MAP_KEYS = /* @__PURE__ */ new Set(["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"]);
1614
+ const defineEntry = (target, key, value) => {
1615
+ if (key === "__proto__") {
1616
+ Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
1617
+ return;
1618
+ }
1619
+ target[key] = value;
1620
+ };
1621
+ const rewriteNullable = (node) => {
1622
+ if (typeof node !== "object" || node === null || Array.isArray(node))
1623
+ return node;
1624
+ const src = node;
1625
+ const out = {};
1626
+ for (const [key, value] of Object.entries(src)) {
1627
+ if (key === "nullable")
1628
+ continue;
1629
+ if (SINGLE_SUBSCHEMA_KEYS.has(key)) {
1630
+ defineEntry(out, key, rewriteNullable(value));
1631
+ } else if (key === "items") {
1632
+ defineEntry(out, key, Array.isArray(value) ? value.map(rewriteNullable) : rewriteNullable(value));
1633
+ } else if (SUBSCHEMA_LIST_KEYS.has(key) && Array.isArray(value)) {
1634
+ defineEntry(out, key, value.map(rewriteNullable));
1635
+ } else if (SUBSCHEMA_MAP_KEYS.has(key) && typeof value === "object" && value !== null) {
1636
+ const mapped = {};
1637
+ for (const [name, sub] of Object.entries(value)) {
1638
+ defineEntry(mapped, name, rewriteNullable(sub));
1639
+ }
1640
+ defineEntry(out, key, mapped);
1641
+ } else if (key === "dependencies" && typeof value === "object" && value !== null) {
1642
+ const mapped = {};
1643
+ for (const [name, sub] of Object.entries(value)) {
1644
+ defineEntry(mapped, name, Array.isArray(sub) ? sub : rewriteNullable(sub));
1645
+ }
1646
+ defineEntry(out, key, mapped);
1647
+ } else {
1648
+ defineEntry(out, key, value);
1649
+ }
1650
+ }
1651
+ if (readKey(src, "nullable") === true)
1652
+ return { anyOf: [{ type: "null" }, out] };
1653
+ return out;
1654
+ };
1655
+ const generateValidatorFunction = (schema, typeName, suffix = "", rootSchema, unknownKeys = DEFAULT_UNKNOWN_KEYS, formats = NO_FORMATS, branchErrors = false) => generateValidatorSource(schema, typeName, suffix, rootSchema, unknownKeys, formats, branchErrors, {
1656
+ on: false,
1657
+ blocked: false
1658
+ });
1659
+ const generateCheckFunction = (schema, typeName, suffix = "", rootSchema, unknownKeys = DEFAULT_UNKNOWN_KEYS, formats = NO_FORMATS) => {
1660
+ const failFast = { on: true, blocked: false };
1661
+ const source = generateValidatorSource(schema, typeName, suffix, rootSchema, unknownKeys, formats, false, failFast);
1662
+ if (source.includes(FAIL_FAST_SINK)) {
1663
+ throw new Error(`the fail-fast validator for "${typeName}" emitted the error sink verbatim, which means some emitter reported through \`ctx.sink\` without going through \`pushError\`. That report has to take the fail-fast form (a \`return\`) or stay on the accumulating path.`);
1664
+ }
1665
+ if (!failFast.blocked)
1666
+ return source;
1667
+ return [
1668
+ `export const ${checkerName(typeName)} = (input: unknown, _path = ''): ValidationResult => {`,
1669
+ ` const _r = ${validatorName(typeName)}(input, _path)`,
1670
+ ` return _r === true ? true : { valid: false, errors: _r.errors.slice(0, 1) }`,
1671
+ `}`
1672
+ ].join("\n");
1673
+ };
1674
+ const generateValidatorSource = (schema, typeName, suffix, rootSchema, unknownKeys, formats, branchErrors, failFast) => {
1675
+ assertGeneratableRefs(schema, typeName);
1676
+ const rewritten = rewriteNullable(schema);
1677
+ const document = rootSchema ?? schema;
1678
+ assertUnevaluatedGeneratable(rewritten, typeName, document, unevaluatedMatcher(suffix, createRootContext(document, formats, branchErrors, failFast)));
1679
+ if (carriesUnevaluated(rewritten)) {
1680
+ return generateGeneralRootValidator(rewritten, typeName, suffix, document, formats, branchErrors, failFast);
1681
+ }
1682
+ if (declaresObjectType(rewritten) && objectRootIsSelfContained(rewritten)) {
1683
+ return generateObjectValidator(rewritten, typeName, suffix, document, unknownKeys, formats, branchErrors, failFast);
1684
+ }
1685
+ return generateScalarValidator(rewritten, typeName, suffix, document, formats, branchErrors, failFast);
1686
+ };
1687
+ export {
1688
+ generateBooleanGuard,
1689
+ generateCheckFunction,
1690
+ generateValidatorFunction
1691
+ };