@amritk/generate-parsers 0.24.0 → 0.25.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 (51) hide show
  1. package/README.md +37 -463
  2. package/dist/index.d.ts +22 -2
  3. package/dist/index.js +18 -1
  4. package/package.json +15 -40
  5. package/AI.md +0 -52
  6. package/dist/generators/assert-supported-keywords.d.ts +0 -30
  7. package/dist/generators/assert-supported-keywords.js +0 -87
  8. package/dist/generators/build-schema.d.ts +0 -111
  9. package/dist/generators/build-schema.js +0 -74
  10. package/dist/generators/generate-deep-equal-check.d.ts +0 -24
  11. package/dist/generators/generate-deep-equal-check.js +0 -35
  12. package/dist/generators/generate-default-from-pattern.d.ts +0 -26
  13. package/dist/generators/generate-default-from-pattern.js +0 -223
  14. package/dist/generators/generate-discriminated-union-validation.d.ts +0 -15
  15. package/dist/generators/generate-discriminated-union-validation.js +0 -28
  16. package/dist/generators/generate-enum-check.d.ts +0 -35
  17. package/dist/generators/generate-enum-check.js +0 -31
  18. package/dist/generators/generate-files.d.ts +0 -138
  19. package/dist/generators/generate-files.js +0 -89
  20. package/dist/generators/generate-non-discriminated-union-validation.d.ts +0 -6
  21. package/dist/generators/generate-non-discriminated-union-validation.js +0 -26
  22. package/dist/generators/generate-parser-function.d.ts +0 -120
  23. package/dist/generators/generate-parser-function.js +0 -1973
  24. package/dist/generators/generate-schema-checks.d.ts +0 -5
  25. package/dist/generators/generate-schema-checks.js +0 -260
  26. package/dist/generators/generate-strict-assertion.d.ts +0 -155
  27. package/dist/generators/generate-strict-assertion.js +0 -759
  28. package/dist/generators/generate-type-checks.d.ts +0 -148
  29. package/dist/generators/generate-type-checks.js +0 -454
  30. package/dist/generators/generate-unique-items-check.d.ts +0 -16
  31. package/dist/generators/generate-unique-items-check.js +0 -32
  32. package/dist/generators/generate-validation-expression.d.ts +0 -79
  33. package/dist/generators/generate-validation-expression.js +0 -416
  34. package/dist/generators/subschema-match.d.ts +0 -48
  35. package/dist/generators/subschema-match.js +0 -485
  36. package/dist/generators/unevaluated-match.d.ts +0 -19
  37. package/dist/generators/unevaluated-match.js +0 -206
  38. package/dist/helpers/apply-schema-extensions.d.ts +0 -35
  39. package/dist/helpers/apply-schema-extensions.js +0 -20
  40. package/dist/helpers/collect-helpers.d.ts +0 -15
  41. package/dist/helpers/collect-helpers.js +0 -64
  42. package/dist/helpers/collect-imports.d.ts +0 -104
  43. package/dist/helpers/collect-imports.js +0 -227
  44. package/dist/helpers/find-discriminator.d.ts +0 -6
  45. package/dist/helpers/find-discriminator.js +0 -27
  46. package/dist/helpers/get-default-value.d.ts +0 -7
  47. package/dist/helpers/get-default-value.js +0 -155
  48. package/dist/helpers/get-discriminator-value.d.ts +0 -6
  49. package/dist/helpers/get-discriminator-value.js +0 -18
  50. package/dist/types/schema-extensions.d.ts +0 -34
  51. package/dist/types/schema-extensions.js +0 -0
@@ -1,79 +0,0 @@
1
- import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
2
- /**
3
- * A boolean type check for an array element whose schema is a single scalar type
4
- * (the cases the element coercion below can handle). `integer` is treated as
5
- * `number` because the generated TS element type is `number`. Returns `null` for
6
- * objects, arrays, unions, `$ref`s, and anything richer.
7
- */
8
- export declare const scalarItemTypeCheck: (itemSchema: JSONSchema, accessor: string) => string | null;
9
- /**
10
- * {@link scalarItemTypeCheck}, but only for an item schema whose `type` is the
11
- * *whole* constraint — the one case where the bare `typeof` test is an exact
12
- * match rather than a weakening of it. Emit sites that need a check they can
13
- * trust in both directions (a true-sound fast-path guard, a strict assertion
14
- * that throws) take this one; the coercion path keeps the looser
15
- * {@link isCoercibleItemSchema}, which coerces each element through the full
16
- * per-element expression and so applies the constraints anyway.
17
- */
18
- export declare const plainScalarItemCheck: (itemSchema: JSONSchema, accessor: string) => string | null;
19
- /**
20
- * True when an array's `items` schema can be coerced element-by-element by the
21
- * mapping slow path: single scalar types (via {@link scalarItemTypeCheck}) and
22
- * enums (whose validation expression coerces a non-member to the first member).
23
- * Anything richer (objects, unions, `$ref`s) needs a real item parser instead.
24
- */
25
- export declare const isCoercibleItemSchema: (itemSchema: JSONSchema) => boolean;
26
- /**
27
- * The tuple positions a schema declares, else `null`. `prefixItems` (JSON
28
- * Schema 2020-12) positions each element against its own subschema; draft-07
29
- * spelled the same construct as an array-valued `items`, which every other item
30
- * path skips (they all read `items` as a single schema), so it is normalized
31
- * here rather than left unchecked.
32
- */
33
- export declare const getPrefixItems: (schema: JSONSchema) => readonly JSONSchema[] | null;
34
- /**
35
- * Wraps a per-element boolean check into the `.every(...)` call that proves it
36
- * for a whole array, skipping the positions `prefixItems` already owns.
37
- *
38
- * In 2020-12 `items` is only the *tail* schema: it applies from
39
- * `prefixItems.length` onward. Checking every element against it made
40
- * `{ prefixItems: [{type:'string'}], items: {type:'number'} }` reject
41
- * `["a", 1, 2]` — a value Ajv accepts, and one the `[string?, ...number[]]` type
42
- * this generator emits admits, so the strict parser threw on its own declared
43
- * type. The index guard is short-circuited rather than sliced so the check stays
44
- * allocation-free on the hot path.
45
- *
46
- * The draft-07 tuple spelling needs no guard: there `items` *is* the tuple and
47
- * the tail lives in `additionalItems`, which no item check reads.
48
- */
49
- export declare const everyTailItem: (accessor: string, itemCheck: string, schema: JSONSchema) => string;
50
- /**
51
- * True when a sibling `items: false` (2020-12) or `additionalItems: false`
52
- * (draft) caps a tuple's length — any element past the declared prefix is
53
- * disallowed, so the coercer drops it and strict mode rejects it.
54
- */
55
- export declare const prefixItemsCapsLength: (schema: JSONSchema) => boolean;
56
- /**
57
- * A boolean type check for a single JSON Schema primitive type name. Unlike
58
- * {@link scalarItemTypeCheck} this also covers `array`/`object` (as inline shape
59
- * checks, so no `isObject` import is assumed) and enforces `integer` with
60
- * `Number.isInteger` — a bare `typeof === "number"` accepts `1.5`. Used to build
61
- * the disjunction for an array-form `type` (e.g. `["string","null"]`).
62
- */
63
- export declare const singleTypeCheck: (accessor: string, type: string) => string | null;
64
- /**
65
- * Generates a validation expression that checks if a value matches the expected type.
66
- * Returns the value if valid, coerces to the correct type if invalid, or uses default if required and missing.
67
- * Supports type checking, pattern validation, enum validation, union types, and $ref resolution.
68
- */
69
- export declare const generateValidationExpression: (key: string, schema: JSONSchema, defaultValue: string, isRequired: boolean, rootSchema?: Record<string, unknown>, visitedRefs?: Set<string>, accessorOverride?: string, knownNotUndefined?: boolean, caseInsensitive?: boolean) => string;
70
- /**
71
- * Builds the coercion transform for a tuple `prefixItems` array: an index-matched
72
- * `.map` where each declared position runs through its own subschema's coercion
73
- * (present positions only — a shorter input keeps its absent trailing positions),
74
- * and every element past the tuple passes through unchanged. When `capLength` is
75
- * set (a sibling `items: false`), a trailing `.slice(0, N)` drops those extras.
76
- * `accessor` must already be known to be an array (the caller guards with
77
- * `Array.isArray`). `$ref` positions resolve against `rootSchema` when provided.
78
- */
79
- export declare const generatePrefixItemsMap: (accessor: string, prefix: readonly JSONSchema[], capLength: boolean, rootSchema?: Record<string, unknown>, visitedRefs?: Set<string>, caseInsensitive?: boolean) => string;
@@ -1,416 +0,0 @@
1
- import { regexLiteral } from "@amritk/helpers/escape-regex-pattern";
2
- import { getMjstInstanceOf, getMjstPrimitive } from "@amritk/helpers/mjst-extension";
3
- import { multipleOfPassExpr } from "@amritk/helpers/multiple-of-check";
4
- import { resolveRef } from "@amritk/helpers/resolve-ref";
5
- import { hasOwnCheck, safeAccessor } from "@amritk/helpers/safe-accessor";
6
- import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasEnum, hasExclusiveMaximum, hasExclusiveMinimum, hasItems, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasOneOf, hasPattern, hasProperties, hasRef, hasRequired, hasType, hasUniqueItems, isSchemaObject } from "@amritk/helpers/schema-guards";
7
- import { maxLengthPassExpr, minLengthPassExpr } from "@amritk/helpers/string-length-check";
8
- import { findDiscriminator } from "../helpers/find-discriminator.js";
9
- import { getDefaultValue } from "../helpers/get-default-value.js";
10
- import { generateDiscriminatedUnionValidation } from "./generate-discriminated-union-validation.js";
11
- import { generateEnumCaseInsensitiveCoercion, generateEnumCheck } from "./generate-enum-check.js";
12
- import { generateNonDiscriminatedUnionValidation } from "./generate-non-discriminated-union-validation.js";
13
- import { generateSchemaChecks } from "./generate-schema-checks.js";
14
- import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
15
- const getInstanceCoercion = (accessor, instanceOf, defaultValue) => {
16
- if (instanceOf === "Date")
17
- return `((_d) => Number.isNaN(_d.getTime()) ? ${defaultValue} : _d)(new Date(${accessor} as string | number | Date))`;
18
- return null;
19
- };
20
- const scalarItemTypeCheck = (itemSchema, accessor) => {
21
- if (!isSchemaObject(itemSchema) || !hasType(itemSchema) || hasEnum(itemSchema))
22
- return null;
23
- switch (itemSchema.type) {
24
- case "string":
25
- return `typeof ${accessor} === "string"`;
26
- case "number":
27
- return `typeof ${accessor} === "number"`;
28
- case "integer":
29
- return `typeof ${accessor} === "number" && Number.isInteger(${accessor})`;
30
- case "boolean":
31
- return `typeof ${accessor} === "boolean"`;
32
- case "null":
33
- return `${accessor} === null`;
34
- default:
35
- return null;
36
- }
37
- };
38
- const SCALAR_ITEM_CONSTRAINTS = [
39
- "pattern",
40
- "minLength",
41
- "maxLength",
42
- "minimum",
43
- "maximum",
44
- "exclusiveMinimum",
45
- "exclusiveMaximum",
46
- "multipleOf",
47
- "const",
48
- "$ref",
49
- "not",
50
- "allOf",
51
- "anyOf",
52
- "oneOf",
53
- "if"
54
- ];
55
- const plainScalarItemCheck = (itemSchema, accessor) => {
56
- if (!isSchemaObject(itemSchema))
57
- return null;
58
- const record = itemSchema;
59
- if (SCALAR_ITEM_CONSTRAINTS.some((keyword) => keyword in record))
60
- return null;
61
- return scalarItemTypeCheck(itemSchema, accessor);
62
- };
63
- const isCoercibleItemSchema = (itemSchema) => {
64
- if (scalarItemTypeCheck(itemSchema, "_it") !== null)
65
- return true;
66
- if (!isSchemaObject(itemSchema))
67
- return false;
68
- const itemTypes = itemSchema["type"];
69
- if (Array.isArray(itemTypes) && itemTypes.length > 0) {
70
- return itemTypes.every((type) => typeof type === "string" && scalarItemTypeCheck({ type }, "_it") !== null);
71
- }
72
- if (hasConst(itemSchema))
73
- return true;
74
- return hasEnum(itemSchema) && itemSchema.enum.length > 0;
75
- };
76
- const getPrefixItems = (schema) => {
77
- if (!isSchemaObject(schema))
78
- return null;
79
- const prefix = schema["prefixItems"];
80
- if (Array.isArray(prefix))
81
- return prefix;
82
- return Array.isArray(schema.items) ? schema.items : null;
83
- };
84
- const everyTailItem = (accessor, itemCheck, schema) => {
85
- const prefix = isSchemaObject(schema) ? schema["prefixItems"] : void 0;
86
- const tailStart = Array.isArray(prefix) ? prefix.length : 0;
87
- return tailStart === 0 ? `${accessor}.every((_it: unknown) => ${itemCheck})` : `${accessor}.every((_it: unknown, _ix: number) => _ix < ${tailStart} || (${itemCheck}))`;
88
- };
89
- const prefixItemsCapsLength = (schema) => {
90
- if (!isSchemaObject(schema))
91
- return false;
92
- const s = schema;
93
- return (Object.hasOwn(s, "items") ? s["items"] : void 0) === false || (Object.hasOwn(s, "additionalItems") ? s["additionalItems"] : void 0) === false;
94
- };
95
- const singleTypeCheck = (accessor, type) => {
96
- switch (type) {
97
- case "string":
98
- return `typeof ${accessor} === "string"`;
99
- case "number":
100
- return `typeof ${accessor} === "number"`;
101
- case "integer":
102
- return `typeof ${accessor} === "number" && Number.isInteger(${accessor})`;
103
- case "boolean":
104
- return `typeof ${accessor} === "boolean"`;
105
- case "null":
106
- return `${accessor} === null`;
107
- case "array":
108
- return `Array.isArray(${accessor})`;
109
- case "object":
110
- return `typeof ${accessor} === "object" && ${accessor} !== null && !Array.isArray(${accessor})`;
111
- default:
112
- return null;
113
- }
114
- };
115
- const getTypeArray = (schema) => {
116
- if (!isSchemaObject(schema) || !("type" in schema) || !Array.isArray(schema.type))
117
- return null;
118
- return schema.type;
119
- };
120
- const stringBoundChecks = (valueExpr, schema) => {
121
- const checks = [];
122
- if (hasPattern(schema))
123
- checks.push(`${regexLiteral(schema.pattern)}.test(${valueExpr})`);
124
- if (hasMinLength(schema))
125
- checks.push(minLengthPassExpr(valueExpr, schema.minLength));
126
- if (hasMaxLength(schema))
127
- checks.push(maxLengthPassExpr(valueExpr, schema.maxLength));
128
- return checks;
129
- };
130
- const numberBoundChecks = (valueExpr, schema) => {
131
- const checks = [];
132
- if (hasMinimum(schema))
133
- checks.push(`${valueExpr} >= ${schema.minimum}`);
134
- if (hasMaximum(schema))
135
- checks.push(`${valueExpr} <= ${schema.maximum}`);
136
- if (hasExclusiveMinimum(schema))
137
- checks.push(`${valueExpr} > ${schema.exclusiveMinimum}`);
138
- if (hasExclusiveMaximum(schema))
139
- checks.push(`${valueExpr} < ${schema.exclusiveMaximum}`);
140
- if (hasMultipleOf(schema))
141
- checks.push(multipleOfPassExpr(valueExpr, schema.multipleOf));
142
- return checks;
143
- };
144
- const BOOLEAN_TRUE_TOKENS = ["true", "yes", "y", "on", "1"];
145
- const BOOLEAN_FALSE_TOKENS = ["false", "no", "n", "off", "0", ""];
146
- const getBooleanCoercion = (accessor, defaultValue) => {
147
- const side = (tokens, literals) => [...tokens.map((token) => `"${token}"`), ...literals].map((term) => `_b === ${term}`).join(" || ");
148
- const normalized = `typeof ${accessor} === "string" ? ${accessor}.trim().toLowerCase() : ${accessor}`;
149
- const truthy = side(BOOLEAN_TRUE_TOKENS, ["1", "true"]);
150
- const falsy = side(BOOLEAN_FALSE_TOKENS, ["0", "false"]);
151
- return `((_b) => ${truthy} ? true : ${falsy} ? false : ${defaultValue})(${normalized})`;
152
- };
153
- const getTypeCoercion = (accessor, schema, defaultValue) => {
154
- if (!hasType(schema)) {
155
- return null;
156
- }
157
- switch (schema.type) {
158
- case "string": {
159
- const coerced = `String(${accessor})`;
160
- const bounds = stringBoundChecks("_s", schema);
161
- if (bounds.length === 0)
162
- return coerced;
163
- return `((_s) => ${bounds.join(" && ")} ? _s : ${defaultValue})(${coerced})`;
164
- }
165
- case "number":
166
- case "integer": {
167
- const wellFormed = schema.type === "integer" ? "Number.isInteger" : "Number.isFinite";
168
- const bounds = numberBoundChecks("_n", schema);
169
- const n = `Number(${accessor})`;
170
- if (bounds.length === 0)
171
- return `(${wellFormed}(${n}) ? ${n} : ${defaultValue})`;
172
- return `((_n) => ${[`${wellFormed}(_n)`, ...bounds].join(" && ")} ? _n : ${defaultValue})(${n})`;
173
- }
174
- case "boolean":
175
- return getBooleanCoercion(accessor, defaultValue);
176
- case "array":
177
- return defaultValue;
178
- case "object":
179
- return `typeof ${accessor} === "object" && ${accessor} !== null ? ${accessor} : ${defaultValue}`;
180
- default:
181
- return null;
182
- }
183
- };
184
- const generateValidationExpression = (key, schema, defaultValue, isRequired, rootSchema, visitedRefs, accessorOverride, knownNotUndefined, caseInsensitive) => {
185
- const accessor = accessorOverride ?? safeAccessor("input?", key);
186
- const checks = [];
187
- if (!isSchemaObject(schema)) {
188
- return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
189
- }
190
- const instanceOf = getMjstInstanceOf(schema);
191
- if (instanceOf) {
192
- const valid = `${accessor} instanceof ${instanceOf}`;
193
- if (isRequired) {
194
- const coercion2 = getInstanceCoercion(accessor, instanceOf, defaultValue);
195
- return coercion2 ? `${valid} ? ${accessor} : (${accessor} !== undefined ? ${coercion2} : ${defaultValue})` : `${valid} ? ${accessor} : ${defaultValue}`;
196
- }
197
- const coercion = getInstanceCoercion(accessor, instanceOf, "undefined");
198
- return coercion ? `${valid} ? ${accessor} : (${accessor} !== undefined ? ${coercion} : undefined)` : `${valid} ? ${accessor} : undefined`;
199
- }
200
- const primitive = getMjstPrimitive(schema);
201
- if (primitive) {
202
- const valid = `typeof ${accessor} === "${primitive}"`;
203
- return isRequired ? `${valid} ? ${accessor} : ${defaultValue}` : `${valid} ? ${accessor} : undefined`;
204
- }
205
- if (hasRef(schema) && rootSchema) {
206
- const ref = schema.$ref;
207
- if (visitedRefs?.has(ref)) {
208
- return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
209
- }
210
- const resolvedSchema = resolveRef(ref, rootSchema);
211
- if (resolvedSchema) {
212
- const visited = new Set(visitedRefs);
213
- visited.add(ref);
214
- return generateValidationExpression(key, resolvedSchema, defaultValue, isRequired, rootSchema, visited, void 0, void 0, caseInsensitive);
215
- }
216
- }
217
- if (hasOneOf(schema) && schema.oneOf.length > 0) {
218
- const discriminator = findDiscriminator(schema.oneOf);
219
- if (discriminator) {
220
- return generateDiscriminatedUnionValidation(accessor, schema.oneOf, discriminator, defaultValue, isRequired);
221
- }
222
- return generateNonDiscriminatedUnionValidation(accessor, schema.oneOf, defaultValue, isRequired);
223
- }
224
- if (hasAnyOf(schema) && schema.anyOf.length > 0) {
225
- const discriminator = findDiscriminator(schema.anyOf);
226
- if (discriminator) {
227
- return generateDiscriminatedUnionValidation(accessor, schema.anyOf, discriminator, defaultValue, isRequired);
228
- }
229
- return generateNonDiscriminatedUnionValidation(accessor, schema.anyOf, defaultValue, isRequired);
230
- }
231
- if (hasAllOf(schema) && schema.allOf.length > 0) {
232
- const allChecks = [];
233
- for (const subSchema of schema.allOf) {
234
- const subChecks = generateSchemaChecks(accessor, subSchema);
235
- allChecks.push(...subChecks);
236
- }
237
- if (allChecks.length > 0) {
238
- let combinedCheck2 = allChecks[0];
239
- for (let i = 1; i < allChecks.length; i++) {
240
- combinedCheck2 += " && " + allChecks[i];
241
- }
242
- return `${combinedCheck2} ? ${accessor} : ${isRequired ? defaultValue : "undefined"}`;
243
- }
244
- return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
245
- }
246
- if ("not" in schema && schema.not) {
247
- const notChecks = generateSchemaChecks(accessor, schema.not);
248
- if (notChecks.length > 0) {
249
- let combinedNotCheck = notChecks[0];
250
- for (let i = 1; i < notChecks.length; i++) {
251
- combinedNotCheck += " && " + notChecks[i];
252
- }
253
- return `!(${combinedNotCheck}) ? ${accessor} : ${isRequired ? defaultValue : "undefined"}`;
254
- }
255
- }
256
- if (hasConst(schema)) {
257
- const constLiteral = JSON.stringify(schema.const);
258
- return isRequired ? `${accessor} === ${constLiteral} ? ${accessor} : ${constLiteral}` : `${accessor} === ${constLiteral} ? ${accessor} : (${accessor} !== undefined ? ${constLiteral} : undefined)`;
259
- }
260
- const typeArray = getTypeArray(schema);
261
- if (typeArray && !hasEnum(schema)) {
262
- const checks2 = typeArray.map((t) => singleTypeCheck(accessor, t)).filter((c) => c !== null);
263
- if (checks2.length > 0) {
264
- const combined = checks2.map((c) => `(${c})`).join(" || ");
265
- return isRequired ? `${combined} ? ${accessor} : ${defaultValue}` : `${combined} ? ${accessor} : undefined`;
266
- }
267
- }
268
- if (!hasType(schema) && !hasEnum(schema)) {
269
- return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
270
- }
271
- const prefixItems = getPrefixItems(schema);
272
- if (hasType(schema) && schema.type === "array" && prefixItems) {
273
- const mapped = generatePrefixItemsMap(accessor, prefixItems, prefixItemsCapsLength(schema), rootSchema, visitedRefs, caseInsensitive);
274
- const built = `(Array.isArray(${accessor}) ? ${mapped} : ${defaultValue})`;
275
- return isRequired ? built : `(${accessor} !== undefined ? ${built} : undefined)`;
276
- }
277
- if (hasType(schema) && schema.type === "array" && hasItems(schema) && !Array.isArray(schema.items) && isCoercibleItemSchema(schema.items)) {
278
- const itemSchema = schema.items;
279
- const itemExpr = generateValidationExpression("", itemSchema, getDefaultValue(itemSchema), true, rootSchema, visitedRefs, "_it", true, caseInsensitive);
280
- const mapped = `(Array.isArray(${accessor}) ? (${accessor} as unknown[]).map((_it) => ${itemExpr}) : ${defaultValue})`;
281
- return isRequired ? mapped : `(${accessor} !== undefined ? ${mapped} : undefined)`;
282
- }
283
- if (hasType(schema)) {
284
- switch (schema.type) {
285
- case "string": {
286
- checks.push(`typeof ${accessor} === "string"`);
287
- if (hasPattern(schema)) {
288
- checks.push(`${regexLiteral(schema.pattern)}.test(${accessor})`);
289
- }
290
- if (hasMinLength(schema)) {
291
- checks.push(minLengthPassExpr(accessor, schema.minLength));
292
- }
293
- if (hasMaxLength(schema)) {
294
- checks.push(maxLengthPassExpr(accessor, schema.maxLength));
295
- }
296
- break;
297
- }
298
- case "number":
299
- case "integer": {
300
- checks.push(`typeof ${accessor} === "number"`);
301
- if (schema.type === "integer") {
302
- checks.push(`Number.isInteger(${accessor})`);
303
- }
304
- if (hasMinimum(schema)) {
305
- checks.push(`${accessor} >= ${schema.minimum}`);
306
- }
307
- if (hasMaximum(schema)) {
308
- checks.push(`${accessor} <= ${schema.maximum}`);
309
- }
310
- if (hasExclusiveMinimum(schema)) {
311
- checks.push(`${accessor} > ${schema.exclusiveMinimum}`);
312
- }
313
- if (hasExclusiveMaximum(schema)) {
314
- checks.push(`${accessor} < ${schema.exclusiveMaximum}`);
315
- }
316
- if (hasMultipleOf(schema)) {
317
- checks.push(multipleOfPassExpr(accessor, schema.multipleOf));
318
- }
319
- break;
320
- }
321
- case "boolean":
322
- checks.push(`typeof ${accessor} === "boolean"`);
323
- break;
324
- case "null":
325
- checks.push(`${accessor} === null`);
326
- break;
327
- case "array": {
328
- checks.push(`Array.isArray(${accessor})`);
329
- if (hasMinItems(schema)) {
330
- checks.push(`${accessor}.length >= ${schema.minItems}`);
331
- }
332
- if (hasMaxItems(schema)) {
333
- checks.push(`${accessor}.length <= ${schema.maxItems}`);
334
- }
335
- if (hasUniqueItems(schema) && schema.uniqueItems === true) {
336
- checks.push(generateUniqueItemsCheck(accessor, schema));
337
- }
338
- break;
339
- }
340
- case "object": {
341
- checks.push(`isObject(${accessor})`);
342
- if (hasRequired(schema) && schema.required.length > 0) {
343
- for (const requiredKey of schema.required) {
344
- checks.push(hasOwnCheck(accessor, requiredKey));
345
- }
346
- }
347
- if (hasMinProperties(schema)) {
348
- checks.push(`Object.keys(${accessor}).length >= ${schema.minProperties}`);
349
- }
350
- if (hasMaxProperties(schema)) {
351
- checks.push(`Object.keys(${accessor}).length <= ${schema.maxProperties}`);
352
- }
353
- if (hasAdditionalProperties(schema) && schema.additionalProperties === false && hasProperties(schema)) {
354
- const allowedKeys = Object.keys(schema.properties);
355
- const allowedKeysStr = JSON.stringify(allowedKeys);
356
- checks.push(`Object.keys(${accessor}).every(k => ${allowedKeysStr}.includes(k))`);
357
- }
358
- break;
359
- }
360
- }
361
- }
362
- if (hasEnum(schema) && schema.enum.length > 0) {
363
- checks.push(generateEnumCheck(accessor, schema.enum));
364
- }
365
- if (checks.length === 0) {
366
- return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
367
- }
368
- let combinedCheck = checks[0];
369
- for (let i = 1; i < checks.length; i++) {
370
- combinedCheck += " && " + checks[i];
371
- }
372
- const typeCoercion = hasEnum(schema) ? null : getTypeCoercion(accessor, schema, defaultValue);
373
- const enumFallback = (fallback) => (caseInsensitive && hasEnum(schema) && schema.enum.length > 0 ? generateEnumCaseInsensitiveCoercion(accessor, schema.enum, fallback) : null) ?? fallback;
374
- if (isRequired) {
375
- if (typeCoercion) {
376
- if (knownNotUndefined) {
377
- return `${combinedCheck} ? ${accessor} : ${typeCoercion}`;
378
- }
379
- return `${combinedCheck} ? ${accessor} : (${accessor} !== undefined ? ${typeCoercion} : ${defaultValue})`;
380
- }
381
- return `${combinedCheck} ? ${accessor} : ${enumFallback(defaultValue)}`;
382
- } else {
383
- if (typeCoercion) {
384
- if (knownNotUndefined) {
385
- return `${combinedCheck} ? ${accessor} : ${typeCoercion}`;
386
- }
387
- return `${combinedCheck} ? ${accessor} : (${accessor} !== undefined ? ${typeCoercion} : undefined)`;
388
- }
389
- return `${combinedCheck} ? ${accessor} : ${enumFallback("undefined")}`;
390
- }
391
- };
392
- const generatePrefixItemsMap = (accessor, prefix, capLength, rootSchema, visitedRefs, caseInsensitive) => {
393
- const branches = prefix.map((rawPos, i) => {
394
- let pos = rawPos;
395
- if (isSchemaObject(pos) && hasRef(pos) && rootSchema) {
396
- const resolved = resolveRef(pos.$ref, rootSchema);
397
- if (resolved)
398
- pos = resolved;
399
- }
400
- const posExpr = generateValidationExpression("", pos, getDefaultValue(pos), true, rootSchema, visitedRefs, "_el", true, caseInsensitive);
401
- return `_i === ${i} ? ${posExpr}`;
402
- });
403
- const mapped = `(${accessor} as unknown[]).map((_el, _i) => ${[...branches, "_el"].join(" : ")})`;
404
- return capLength ? `${mapped}.slice(0, ${prefix.length})` : mapped;
405
- };
406
- export {
407
- everyTailItem,
408
- generatePrefixItemsMap,
409
- generateValidationExpression,
410
- getPrefixItems,
411
- isCoercibleItemSchema,
412
- plainScalarItemCheck,
413
- prefixItemsCapsLength,
414
- scalarItemTypeCheck,
415
- singleTypeCheck
416
- };
@@ -1,48 +0,0 @@
1
- import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
2
- /**
3
- * What the matcher needs beyond the schema node itself: the root document (to
4
- * resolve `$ref`), the recursion depth (which seeds collision-free loop variable
5
- * names and caps expression growth), and the `$ref` pointers already open on this
6
- * path (a cycle cannot be inlined, so it bails rather than recursing forever).
7
- */
8
- export type MatchContext = {
9
- readonly rootSchema?: Record<string, unknown>;
10
- readonly depth?: number;
11
- readonly seen?: ReadonlySet<string>;
12
- };
13
- /**
14
- * Builds a boolean expression that is `true` *exactly* when `accessor` matches
15
- * `schema` — sound in **both** directions (true ⇒ valid, false ⇒ invalid) — or
16
- * `null` when the schema uses a form this generator cannot prove inline.
17
- *
18
- * This is deliberately self-contained: it never emits a `$ref` shape-validator
19
- * call (those can be conservative `=> false` stubs, which would break false-
20
- * soundness) and never touches combinators. That keeps the expression usable
21
- * both for *counting* matches (`contains`) and for *rejecting* non-matches
22
- * (`propertyNames`, `dependentSchemas`) in strict mode, where a wrong verdict
23
- * would silently accept or reject a document. Anything richer returns `null`,
24
- * and `assertNoUnsupportedKeywords` turns that into a generation-time error.
25
- *
26
- * Constraint keywords apply per JSON Schema semantics: a `pattern`/`maxLength`
27
- * with no `type` still constrains string instances (and is a no-op for other
28
- * types), so a type-less schema guards each family by its runtime type rather
29
- * than ignoring it — the subtlety `propertyNames: { maxLength: 3 }` depends on.
30
- *
31
- * `ctx.depth` seeds the loop variable names so nested walks never shadow one
32
- * another, and caps how far the expression may grow.
33
- */
34
- export declare const subschemaMatchExpr: (accessor: string, schema: JSONSchema, ctx?: MatchContext) => string | null;
35
- /**
36
- * True when a `$ref` node carries a keyword that constrains the value on its own.
37
- * 2020-12 applies a `$ref`'s siblings alongside the target, so delegating the
38
- * whole node to the referenced parser (which knows nothing about them) drops
39
- * every one of them — and a fast path built from the target's shape validator
40
- * skips them too.
41
- */
42
- export declare const hasConstrainingRefSibling: (schema: JSONSchema) => boolean;
43
- /**
44
- * Whether {@link subschemaMatchExpr} can build an exact matcher for `schema`.
45
- * The generation-time guard uses this to decide whether a `contains`,
46
- * `propertyNames`, or `dependentSchemas` subschema is enforceable.
47
- */
48
- export declare const canMatchSubschema: (schema: JSONSchema, rootSchema?: Record<string, unknown>) => boolean;