@amritk/generate-parsers 0.17.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI.md +10 -0
- package/README.md +147 -19
- package/dist/generators/assert-supported-keywords.d.ts +13 -9
- package/dist/generators/assert-supported-keywords.js +58 -18
- package/dist/generators/build-schema.d.ts +9 -1
- package/dist/generators/build-schema.js +5 -2
- package/dist/generators/generate-deep-equal-check.d.ts +24 -0
- package/dist/generators/generate-deep-equal-check.js +35 -0
- package/dist/generators/generate-discriminated-union-validation.js +1 -2
- package/dist/generators/generate-enum-check.d.ts +8 -3
- package/dist/generators/generate-enum-check.js +8 -6
- package/dist/generators/generate-files.d.ts +7 -0
- package/dist/generators/generate-files.js +23 -4
- package/dist/generators/generate-parser-function.d.ts +1 -1
- package/dist/generators/generate-parser-function.js +212 -65
- package/dist/generators/generate-schema-checks.js +10 -8
- package/dist/generators/generate-strict-assertion.d.ts +89 -5
- package/dist/generators/generate-strict-assertion.js +429 -58
- package/dist/generators/generate-type-checks.d.ts +46 -10
- package/dist/generators/generate-type-checks.js +112 -16
- package/dist/generators/generate-unique-items-check.d.ts +16 -0
- package/dist/generators/generate-unique-items-check.js +32 -0
- package/dist/generators/generate-validation-expression.d.ts +29 -4
- package/dist/generators/generate-validation-expression.js +15 -6
- package/dist/generators/subschema-match.d.ts +23 -4
- package/dist/generators/subschema-match.js +312 -48
- package/dist/generators/unevaluated-match.d.ts +19 -0
- package/dist/generators/unevaluated-match.js +206 -0
- package/dist/helpers/collect-imports.d.ts +8 -0
- package/dist/helpers/collect-imports.js +1 -1
- package/dist/helpers/get-default-value.js +45 -2
- package/package.json +9 -3
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { regexLiteral } from "@amritk/helpers/escape-regex-pattern";
|
|
2
2
|
import { multipleOfPassExpr } from "@amritk/helpers/multiple-of-check";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { resolveRef } from "@amritk/helpers/resolve-ref";
|
|
4
|
+
import { hasOwnCheck, missingCheck, safeAccessor } from "@amritk/helpers/safe-accessor";
|
|
5
|
+
import { hasConst, hasEnum, hasExclusiveMaximum, hasExclusiveMinimum, hasItems, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasPattern, hasProperties, hasRequired, hasType, hasUniqueItems, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
6
|
+
import { maxLengthPassExpr, minLengthPassExpr } from "@amritk/helpers/string-length-check";
|
|
7
|
+
import { generateDeepEqualCheck } from "./generate-deep-equal-check.js";
|
|
5
8
|
import { generateEnumCheck } from "./generate-enum-check.js";
|
|
9
|
+
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
10
|
+
import { unevaluatedItemsExpr, unevaluatedPropertiesExpr } from "./unevaluated-match.js";
|
|
6
11
|
const HANDLED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
7
12
|
"type",
|
|
8
13
|
"enum",
|
|
@@ -19,11 +24,30 @@ const HANDLED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
19
24
|
"maxItems",
|
|
20
25
|
"uniqueItems",
|
|
21
26
|
"items",
|
|
27
|
+
"prefixItems",
|
|
28
|
+
"additionalItems",
|
|
29
|
+
"contains",
|
|
30
|
+
"minContains",
|
|
31
|
+
"maxContains",
|
|
22
32
|
"required",
|
|
23
33
|
"properties",
|
|
34
|
+
"patternProperties",
|
|
35
|
+
"propertyNames",
|
|
36
|
+
"dependentRequired",
|
|
37
|
+
"dependentSchemas",
|
|
24
38
|
"minProperties",
|
|
25
39
|
"maxProperties",
|
|
26
|
-
"additionalProperties"
|
|
40
|
+
"additionalProperties",
|
|
41
|
+
"unevaluatedProperties",
|
|
42
|
+
"unevaluatedItems",
|
|
43
|
+
"$ref",
|
|
44
|
+
"allOf",
|
|
45
|
+
"anyOf",
|
|
46
|
+
"oneOf",
|
|
47
|
+
"not",
|
|
48
|
+
"if",
|
|
49
|
+
"then",
|
|
50
|
+
"else"
|
|
27
51
|
]);
|
|
28
52
|
const ANNOTATION_KEYWORDS = /* @__PURE__ */ new Set([
|
|
29
53
|
"title",
|
|
@@ -32,6 +56,7 @@ const ANNOTATION_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
32
56
|
"$id",
|
|
33
57
|
"$schema",
|
|
34
58
|
"$anchor",
|
|
59
|
+
"$dynamicAnchor",
|
|
35
60
|
"$vocabulary",
|
|
36
61
|
"$defs",
|
|
37
62
|
"definitions",
|
|
@@ -45,16 +70,24 @@ const ANNOTATION_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
45
70
|
"contentEncoding",
|
|
46
71
|
"contentSchema"
|
|
47
72
|
]);
|
|
73
|
+
const depthOf = (ctx) => ctx.depth ?? 0;
|
|
74
|
+
const nest = (ctx) => ({ ...ctx, depth: depthOf(ctx) + 1 });
|
|
48
75
|
const isObjectExpr = (acc) => `typeof ${acc} === "object" && ${acc} !== null && !Array.isArray(${acc})`;
|
|
49
|
-
const
|
|
76
|
+
const recordOf = (acc) => `(${acc} as Record<string, unknown>)`;
|
|
77
|
+
const patternEntriesOf = (schema) => {
|
|
78
|
+
const source = schema["patternProperties"];
|
|
79
|
+
if (typeof source !== "object" || source === null || Array.isArray(source))
|
|
80
|
+
return null;
|
|
81
|
+
return Object.entries(source);
|
|
82
|
+
};
|
|
50
83
|
const stringConstraints = (acc, schema) => {
|
|
51
84
|
const c = [];
|
|
52
85
|
if (hasPattern(schema))
|
|
53
|
-
c.push(
|
|
86
|
+
c.push(`${regexLiteral(schema.pattern)}.test(${acc})`);
|
|
54
87
|
if (hasMinLength(schema))
|
|
55
|
-
c.push(
|
|
88
|
+
c.push(minLengthPassExpr(acc, schema.minLength));
|
|
56
89
|
if (hasMaxLength(schema))
|
|
57
|
-
c.push(
|
|
90
|
+
c.push(maxLengthPassExpr(acc, schema.maxLength));
|
|
58
91
|
return c;
|
|
59
92
|
};
|
|
60
93
|
const numberConstraints = (acc, schema, integer) => {
|
|
@@ -73,40 +106,103 @@ const numberConstraints = (acc, schema, integer) => {
|
|
|
73
106
|
c.push(multipleOfPassExpr(acc, schema.multipleOf));
|
|
74
107
|
return c;
|
|
75
108
|
};
|
|
76
|
-
const
|
|
109
|
+
const tupleShape = (schema) => {
|
|
110
|
+
const s = schema;
|
|
111
|
+
const declared = s["prefixItems"];
|
|
112
|
+
if (Array.isArray(declared))
|
|
113
|
+
return { prefix: declared, tail: s["items"] };
|
|
114
|
+
if (Array.isArray(s["items"]))
|
|
115
|
+
return { prefix: s["items"], tail: s["additionalItems"] };
|
|
116
|
+
return { prefix: null, tail: void 0 };
|
|
117
|
+
};
|
|
118
|
+
const arrayConstraints = (acc, schema, ctx) => {
|
|
77
119
|
const c = [];
|
|
120
|
+
const d = depthOf(ctx);
|
|
121
|
+
const elements = `(${acc} as unknown[])`;
|
|
78
122
|
if (hasMinItems(schema))
|
|
79
123
|
c.push(`${acc}.length >= ${schema.minItems}`);
|
|
80
124
|
if (hasMaxItems(schema))
|
|
81
125
|
c.push(`${acc}.length <= ${schema.maxItems}`);
|
|
82
126
|
if (hasUniqueItems(schema) && schema.uniqueItems === true) {
|
|
83
|
-
c.push(
|
|
127
|
+
c.push(generateUniqueItemsCheck(acc, schema));
|
|
84
128
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
c.push(`(${acc}
|
|
129
|
+
const { prefix, tail } = tupleShape(schema);
|
|
130
|
+
if (prefix) {
|
|
131
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
132
|
+
const positional = subschemaMatchExpr(`${elements}[${i}]`, prefix[i], nest(ctx));
|
|
133
|
+
if (positional === null)
|
|
134
|
+
return null;
|
|
135
|
+
if (positional === "true")
|
|
136
|
+
continue;
|
|
137
|
+
c.push(`(${acc}.length <= ${i} || ${positional})`);
|
|
138
|
+
}
|
|
139
|
+
if (tail === false) {
|
|
140
|
+
c.push(`${acc}.length <= ${prefix.length}`);
|
|
141
|
+
} else if (tail !== void 0 && tail !== true) {
|
|
142
|
+
const el = `_i${d}`;
|
|
143
|
+
const tailMatch = subschemaMatchExpr(el, tail, nest(ctx));
|
|
144
|
+
if (tailMatch === null)
|
|
145
|
+
return null;
|
|
146
|
+
if (tailMatch !== "true")
|
|
147
|
+
c.push(`${elements}.every((${el}, _x${d}) => _x${d} < ${prefix.length} || ${tailMatch})`);
|
|
148
|
+
}
|
|
149
|
+
} else if ("items" in schema) {
|
|
150
|
+
if (schema["items"] === false) {
|
|
151
|
+
c.push(`${acc}.length === 0`);
|
|
152
|
+
} else if (hasItems(schema)) {
|
|
153
|
+
const el = `_i${d}`;
|
|
154
|
+
const itemMatch = subschemaMatchExpr(el, schema.items, nest(ctx));
|
|
155
|
+
if (itemMatch === null)
|
|
156
|
+
return null;
|
|
157
|
+
if (itemMatch !== "true")
|
|
158
|
+
c.push(`${elements}.every((${el}) => ${itemMatch})`);
|
|
159
|
+
}
|
|
94
160
|
}
|
|
161
|
+
const contains = containsConstraint(acc, schema, ctx);
|
|
162
|
+
if (contains === null)
|
|
163
|
+
return null;
|
|
164
|
+
c.push(...contains);
|
|
165
|
+
const unevaluated = unevaluatedItemsExpr(acc, schema, ctx, subschemaMatchExpr);
|
|
166
|
+
if (unevaluated === null)
|
|
167
|
+
return null;
|
|
168
|
+
if (unevaluated !== void 0)
|
|
169
|
+
c.push(unevaluated);
|
|
95
170
|
return c;
|
|
96
171
|
};
|
|
97
|
-
const
|
|
98
|
-
|
|
172
|
+
const containsConstraint = (acc, schema, ctx) => {
|
|
173
|
+
const s = schema;
|
|
174
|
+
if (!("contains" in s))
|
|
175
|
+
return [];
|
|
176
|
+
const d = depthOf(ctx);
|
|
177
|
+
const min = typeof s["minContains"] === "number" ? s["minContains"] : 1;
|
|
178
|
+
const max = typeof s["maxContains"] === "number" ? s["maxContains"] : void 0;
|
|
179
|
+
if (min === 0 && max === void 0)
|
|
180
|
+
return [];
|
|
181
|
+
const el = `_c${d}`;
|
|
182
|
+
const match = subschemaMatchExpr(el, s["contains"], nest(ctx));
|
|
183
|
+
if (match === null)
|
|
99
184
|
return null;
|
|
185
|
+
const count = `(${acc} as unknown[]).filter((${el}) => ${match}).length`;
|
|
186
|
+
const bounds = [];
|
|
187
|
+
if (min > 0)
|
|
188
|
+
bounds.push(`${count} >= ${min}`);
|
|
189
|
+
if (max !== void 0)
|
|
190
|
+
bounds.push(`${count} <= ${max}`);
|
|
191
|
+
return bounds;
|
|
192
|
+
};
|
|
193
|
+
const objectConstraints = (acc, schema, ctx) => {
|
|
100
194
|
const c = [];
|
|
195
|
+
const d = depthOf(ctx);
|
|
101
196
|
const required = new Set(hasRequired(schema) ? schema.required : []);
|
|
197
|
+
const rec = recordOf(acc);
|
|
102
198
|
if (hasProperties(schema)) {
|
|
103
199
|
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
104
|
-
const propAcc = safeAccessor(
|
|
105
|
-
const propMatch = subschemaMatchExpr(propAcc, propSchema,
|
|
200
|
+
const propAcc = safeAccessor(rec, key);
|
|
201
|
+
const propMatch = subschemaMatchExpr(propAcc, propSchema, nest(ctx));
|
|
106
202
|
if (propMatch === null)
|
|
107
203
|
return null;
|
|
108
204
|
if (required.has(key)) {
|
|
109
|
-
c.push(propMatch === "true" ?
|
|
205
|
+
c.push(propMatch === "true" ? hasOwnCheck(rec, key) : `(${hasOwnCheck(rec, key)} && ${propMatch})`);
|
|
110
206
|
} else if (propMatch !== "true") {
|
|
111
207
|
c.push(`(${propAcc} === undefined || ${propMatch})`);
|
|
112
208
|
}
|
|
@@ -114,18 +210,100 @@ const objectConstraints = (acc, schema, depth) => {
|
|
|
114
210
|
}
|
|
115
211
|
}
|
|
116
212
|
for (const key of required)
|
|
117
|
-
c.push(
|
|
213
|
+
c.push(hasOwnCheck(rec, key));
|
|
118
214
|
if (hasMinProperties(schema))
|
|
119
|
-
c.push(`Object.keys(${
|
|
215
|
+
c.push(`Object.keys(${rec}).length >= ${schema.minProperties}`);
|
|
120
216
|
if (hasMaxProperties(schema))
|
|
121
|
-
c.push(`Object.keys(${
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
217
|
+
c.push(`Object.keys(${rec}).length <= ${schema.maxProperties}`);
|
|
218
|
+
const keyTerms = keyWalkTerms(`_k${d}`, `_v${d}`, schema, ctx);
|
|
219
|
+
if (keyTerms === null)
|
|
220
|
+
return null;
|
|
221
|
+
if (keyTerms.length > 0) {
|
|
222
|
+
const needsValue = keyTerms.some((term) => term.usesValue);
|
|
223
|
+
const body = keyTerms.map((term) => term.expr).join(" && ");
|
|
224
|
+
c.push(needsValue ? `Object.keys(${rec}).every((_k${d}) => { const _v${d} = ${rec}[_k${d}]; return ${body}; })` : `Object.keys(${rec}).every((_k${d}) => ${body})`);
|
|
125
225
|
}
|
|
226
|
+
const dependent = dependentTerms(rec, acc, schema, ctx);
|
|
227
|
+
if (dependent === null)
|
|
228
|
+
return null;
|
|
229
|
+
c.push(...dependent);
|
|
230
|
+
const unevaluated = unevaluatedPropertiesExpr(acc, schema, ctx, subschemaMatchExpr);
|
|
231
|
+
if (unevaluated === null)
|
|
232
|
+
return null;
|
|
233
|
+
if (unevaluated !== void 0)
|
|
234
|
+
c.push(unevaluated);
|
|
126
235
|
return c;
|
|
127
236
|
};
|
|
128
|
-
const
|
|
237
|
+
const keyWalkTerms = (keyVar, valueVar, schema, ctx) => {
|
|
238
|
+
const terms = [];
|
|
239
|
+
const patterns = patternEntriesOf(schema);
|
|
240
|
+
const declared = hasProperties(schema) ? Object.keys(schema.properties) : [];
|
|
241
|
+
if (patterns) {
|
|
242
|
+
for (const [pattern, sub] of patterns) {
|
|
243
|
+
const match = subschemaMatchExpr(valueVar, sub, nest(ctx));
|
|
244
|
+
if (match === null)
|
|
245
|
+
return null;
|
|
246
|
+
if (match === "true")
|
|
247
|
+
continue;
|
|
248
|
+
terms.push({ expr: `(!${regexLiteral(pattern)}.test(${keyVar}) || ${match})`, usesValue: true });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const additional = schema["additionalProperties"];
|
|
252
|
+
if (additional !== void 0 && additional !== true) {
|
|
253
|
+
const guards = [];
|
|
254
|
+
if (declared.length > 0)
|
|
255
|
+
guards.push(`!${JSON.stringify(declared)}.includes(${keyVar})`);
|
|
256
|
+
if (patterns)
|
|
257
|
+
for (const [pattern] of patterns)
|
|
258
|
+
guards.push(`!${regexLiteral(pattern)}.test(${keyVar})`);
|
|
259
|
+
const unclaimed = guards.length > 0 ? guards.join(" && ") : null;
|
|
260
|
+
if (additional === false) {
|
|
261
|
+
terms.push({ expr: unclaimed === null ? "false" : `!(${unclaimed})`, usesValue: false });
|
|
262
|
+
} else {
|
|
263
|
+
const match = subschemaMatchExpr(valueVar, additional, nest(ctx));
|
|
264
|
+
if (match === null)
|
|
265
|
+
return null;
|
|
266
|
+
if (match !== "true") {
|
|
267
|
+
terms.push({ expr: unclaimed === null ? match : `(!(${unclaimed}) || ${match})`, usesValue: true });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const names = schema["propertyNames"];
|
|
272
|
+
if (names !== void 0) {
|
|
273
|
+
const match = subschemaMatchExpr(keyVar, names, nest(ctx));
|
|
274
|
+
if (match === null)
|
|
275
|
+
return null;
|
|
276
|
+
if (match !== "true")
|
|
277
|
+
terms.push({ expr: match, usesValue: false });
|
|
278
|
+
}
|
|
279
|
+
return terms;
|
|
280
|
+
};
|
|
281
|
+
const dependentTerms = (rec, acc, schema, ctx) => {
|
|
282
|
+
const s = schema;
|
|
283
|
+
const terms = [];
|
|
284
|
+
const dependentRequired = s["dependentRequired"];
|
|
285
|
+
if (typeof dependentRequired === "object" && dependentRequired !== null && !Array.isArray(dependentRequired)) {
|
|
286
|
+
for (const [trigger, deps] of Object.entries(dependentRequired)) {
|
|
287
|
+
if (!Array.isArray(deps) || deps.length === 0)
|
|
288
|
+
continue;
|
|
289
|
+
const present = deps.map((dep) => hasOwnCheck(rec, dep)).join(" && ");
|
|
290
|
+
terms.push(`(${missingCheck(rec, trigger)} || ${deps.length === 1 ? present : `(${present})`})`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const dependentSchemas = s["dependentSchemas"];
|
|
294
|
+
if (typeof dependentSchemas === "object" && dependentSchemas !== null && !Array.isArray(dependentSchemas)) {
|
|
295
|
+
for (const [trigger, sub] of Object.entries(dependentSchemas)) {
|
|
296
|
+
const match = subschemaMatchExpr(acc, sub, nest(ctx));
|
|
297
|
+
if (match === null)
|
|
298
|
+
return null;
|
|
299
|
+
if (match === "true")
|
|
300
|
+
continue;
|
|
301
|
+
terms.push(`(${missingCheck(rec, trigger)} || ${match})`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return terms;
|
|
305
|
+
};
|
|
306
|
+
const subschemaMatchExpr = (accessor, schema, ctx = {}) => {
|
|
129
307
|
if (schema === true)
|
|
130
308
|
return "true";
|
|
131
309
|
if (schema === false)
|
|
@@ -136,27 +314,41 @@ const subschemaMatchExpr = (accessor, schema, depth = 0) => {
|
|
|
136
314
|
if (!HANDLED_KEYWORDS.has(key) && !ANNOTATION_KEYWORDS.has(key))
|
|
137
315
|
return null;
|
|
138
316
|
}
|
|
139
|
-
if (
|
|
317
|
+
if (depthOf(ctx) > 8)
|
|
140
318
|
return null;
|
|
141
319
|
const checks = [];
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
checks.push(
|
|
147
|
-
|
|
320
|
+
const refCheck = refMatch(accessor, schema, ctx);
|
|
321
|
+
if (refCheck === null)
|
|
322
|
+
return null;
|
|
323
|
+
if (refCheck !== "true")
|
|
324
|
+
checks.push(refCheck);
|
|
325
|
+
const combinators = combinatorChecks(accessor, schema, ctx);
|
|
326
|
+
if (combinators === null)
|
|
327
|
+
return null;
|
|
328
|
+
checks.push(...combinators);
|
|
329
|
+
if (hasConst(schema))
|
|
330
|
+
checks.push(`(${generateDeepEqualCheck(accessor, schema.const)})`);
|
|
148
331
|
if (hasEnum(schema)) {
|
|
149
332
|
if (schema.enum.length === 0)
|
|
150
|
-
return
|
|
333
|
+
return "false";
|
|
151
334
|
checks.push(generateEnumCheck(accessor, schema.enum));
|
|
152
335
|
}
|
|
153
336
|
if (hasType(schema)) {
|
|
154
|
-
const typeChecks = buildTypedChecks(accessor, schema,
|
|
337
|
+
const typeChecks = buildTypedChecks(accessor, schema, ctx);
|
|
155
338
|
if (typeChecks === null)
|
|
156
339
|
return null;
|
|
157
340
|
checks.push(...typeChecks);
|
|
341
|
+
} else if (Array.isArray(schema.type)) {
|
|
342
|
+
const branches = [];
|
|
343
|
+
for (const type of schema.type) {
|
|
344
|
+
const branch = buildTypedChecks(accessor, { ...schema, type }, ctx);
|
|
345
|
+
if (branch === null)
|
|
346
|
+
return null;
|
|
347
|
+
branches.push(branch.length === 1 ? branch[0] : `(${branch.join(" && ")})`);
|
|
348
|
+
}
|
|
349
|
+
checks.push(branches.length === 0 ? "false" : branches.length === 1 ? branches[0] : `(${branches.join(" || ")})`);
|
|
158
350
|
} else {
|
|
159
|
-
const typeless = buildTypelessChecks(accessor, schema,
|
|
351
|
+
const typeless = buildTypelessChecks(accessor, schema, ctx);
|
|
160
352
|
if (typeless === null)
|
|
161
353
|
return null;
|
|
162
354
|
checks.push(...typeless);
|
|
@@ -165,7 +357,73 @@ const subschemaMatchExpr = (accessor, schema, depth = 0) => {
|
|
|
165
357
|
return "true";
|
|
166
358
|
return checks.length === 1 ? checks[0] : `(${checks.join(" && ")})`;
|
|
167
359
|
};
|
|
168
|
-
const
|
|
360
|
+
const refMatch = (accessor, schema, ctx) => {
|
|
361
|
+
const ref = schema["$ref"];
|
|
362
|
+
if (typeof ref !== "string")
|
|
363
|
+
return "true";
|
|
364
|
+
if (ctx.rootSchema === void 0)
|
|
365
|
+
return null;
|
|
366
|
+
if (ctx.seen?.has(ref))
|
|
367
|
+
return null;
|
|
368
|
+
const target = resolveRef(ref, ctx.rootSchema);
|
|
369
|
+
if (target === void 0)
|
|
370
|
+
return null;
|
|
371
|
+
const seen = new Set(ctx.seen ?? []);
|
|
372
|
+
seen.add(ref);
|
|
373
|
+
return subschemaMatchExpr(accessor, target, { ...nest(ctx), seen });
|
|
374
|
+
};
|
|
375
|
+
const combinatorChecks = (accessor, schema, ctx) => {
|
|
376
|
+
const record = schema;
|
|
377
|
+
const checks = [];
|
|
378
|
+
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
379
|
+
const members = record[keyword];
|
|
380
|
+
if (members === void 0)
|
|
381
|
+
continue;
|
|
382
|
+
if (!Array.isArray(members) || members.length === 0)
|
|
383
|
+
return null;
|
|
384
|
+
const matches = [];
|
|
385
|
+
for (const member of members) {
|
|
386
|
+
const match = subschemaMatchExpr(accessor, member, nest(ctx));
|
|
387
|
+
if (match === null)
|
|
388
|
+
return null;
|
|
389
|
+
matches.push(match);
|
|
390
|
+
}
|
|
391
|
+
if (keyword === "allOf") {
|
|
392
|
+
const constraining = matches.filter((match) => match !== "true");
|
|
393
|
+
if (constraining.length > 0)
|
|
394
|
+
checks.push(joinAnd(constraining));
|
|
395
|
+
} else if (keyword === "anyOf") {
|
|
396
|
+
if (matches.some((match) => match === "true"))
|
|
397
|
+
continue;
|
|
398
|
+
checks.push(matches.length === 1 ? matches[0] : `(${matches.join(" || ")})`);
|
|
399
|
+
} else {
|
|
400
|
+
checks.push(matches.length === 1 ? matches[0] : `((${matches.map((match) => `(${match} ? 1 : 0)`).join(" + ")}) === 1)`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if ("not" in record) {
|
|
404
|
+
const match = subschemaMatchExpr(accessor, record["not"], nest(ctx));
|
|
405
|
+
if (match === null)
|
|
406
|
+
return null;
|
|
407
|
+
if (match !== "false")
|
|
408
|
+
checks.push(`!(${match})`);
|
|
409
|
+
}
|
|
410
|
+
if ("if" in record) {
|
|
411
|
+
const ifMatch = subschemaMatchExpr(accessor, record["if"], nest(ctx));
|
|
412
|
+
if (ifMatch === null)
|
|
413
|
+
return null;
|
|
414
|
+
const branch = (keyword) => keyword in record ? subschemaMatchExpr(accessor, record[keyword], nest(ctx)) : void 0;
|
|
415
|
+
const thenMatch = branch("then");
|
|
416
|
+
const elseMatch = branch("else");
|
|
417
|
+
if (thenMatch === null || elseMatch === null)
|
|
418
|
+
return null;
|
|
419
|
+
if (thenMatch !== void 0 && thenMatch !== "true")
|
|
420
|
+
checks.push(`(!(${ifMatch}) || ${thenMatch})`);
|
|
421
|
+
if (elseMatch !== void 0 && elseMatch !== "true")
|
|
422
|
+
checks.push(`((${ifMatch}) || ${elseMatch})`);
|
|
423
|
+
}
|
|
424
|
+
return checks;
|
|
425
|
+
};
|
|
426
|
+
const buildTypedChecks = (accessor, schema, ctx) => {
|
|
169
427
|
switch (schema.type) {
|
|
170
428
|
case "string":
|
|
171
429
|
return [`typeof ${accessor} === "string"`, ...stringConstraints(accessor, schema)];
|
|
@@ -178,13 +436,13 @@ const buildTypedChecks = (accessor, schema, depth) => {
|
|
|
178
436
|
case "null":
|
|
179
437
|
return [`${accessor} === null`];
|
|
180
438
|
case "array": {
|
|
181
|
-
const constraints = arrayConstraints(accessor, schema,
|
|
439
|
+
const constraints = arrayConstraints(accessor, schema, ctx);
|
|
182
440
|
if (constraints === null)
|
|
183
441
|
return null;
|
|
184
442
|
return [`Array.isArray(${accessor})`, ...constraints];
|
|
185
443
|
}
|
|
186
444
|
case "object": {
|
|
187
|
-
const constraints = objectConstraints(accessor, schema,
|
|
445
|
+
const constraints = objectConstraints(accessor, schema, ctx);
|
|
188
446
|
if (constraints === null)
|
|
189
447
|
return null;
|
|
190
448
|
return [isObjectExpr(accessor), ...constraints];
|
|
@@ -193,7 +451,7 @@ const buildTypedChecks = (accessor, schema, depth) => {
|
|
|
193
451
|
return null;
|
|
194
452
|
}
|
|
195
453
|
};
|
|
196
|
-
const buildTypelessChecks = (accessor, schema,
|
|
454
|
+
const buildTypelessChecks = (accessor, schema, ctx) => {
|
|
197
455
|
const checks = [];
|
|
198
456
|
const strChecks = stringConstraints(accessor, schema);
|
|
199
457
|
if (strChecks.length > 0)
|
|
@@ -201,12 +459,12 @@ const buildTypelessChecks = (accessor, schema, depth) => {
|
|
|
201
459
|
const numChecks = numberConstraints(accessor, schema, false);
|
|
202
460
|
if (numChecks.length > 0)
|
|
203
461
|
checks.push(`(typeof ${accessor} !== "number" || ${joinAnd(numChecks)})`);
|
|
204
|
-
const arrChecks = arrayConstraints(accessor, schema,
|
|
462
|
+
const arrChecks = arrayConstraints(accessor, schema, ctx);
|
|
205
463
|
if (arrChecks === null)
|
|
206
464
|
return null;
|
|
207
465
|
if (arrChecks.length > 0)
|
|
208
466
|
checks.push(`(!Array.isArray(${accessor}) || ${joinAnd(arrChecks)})`);
|
|
209
|
-
const objChecks = objectConstraints(accessor, schema,
|
|
467
|
+
const objChecks = objectConstraints(accessor, schema, ctx);
|
|
210
468
|
if (objChecks === null)
|
|
211
469
|
return null;
|
|
212
470
|
if (objChecks.length > 0)
|
|
@@ -214,8 +472,14 @@ const buildTypelessChecks = (accessor, schema, depth) => {
|
|
|
214
472
|
return checks;
|
|
215
473
|
};
|
|
216
474
|
const joinAnd = (checks) => checks.length === 1 ? checks[0] : `(${checks.join(" && ")})`;
|
|
217
|
-
const
|
|
475
|
+
const hasConstrainingRefSibling = (schema) => {
|
|
476
|
+
if (!isSchemaObject(schema) || !("$ref" in schema))
|
|
477
|
+
return false;
|
|
478
|
+
return Object.keys(schema).some((key) => key !== "$ref" && !ANNOTATION_KEYWORDS.has(key));
|
|
479
|
+
};
|
|
480
|
+
const canMatchSubschema = (schema, rootSchema) => subschemaMatchExpr("_x", schema, rootSchema ? { rootSchema } : {}) !== null;
|
|
218
481
|
export {
|
|
219
482
|
canMatchSubschema,
|
|
483
|
+
hasConstrainingRefSibling,
|
|
220
484
|
subschemaMatchExpr
|
|
221
485
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
2
|
+
import type { MatchContext } from './subschema-match.js';
|
|
3
|
+
type MatchFn = (accessor: string, schema: JSONSchema, ctx: MatchContext) => string | null;
|
|
4
|
+
/**
|
|
5
|
+
* The `unevaluatedProperties` conjunct for an object accessor: every own key the
|
|
6
|
+
* schema's other keywords did not evaluate must satisfy the unevaluated
|
|
7
|
+
* subschema (and `false` means there must be no such key).
|
|
8
|
+
*
|
|
9
|
+
* Returns `undefined` when the keyword is absent or constrains nothing (a `true`
|
|
10
|
+
* schema, or a node whose other keywords already sweep every key), and `null`
|
|
11
|
+
* when it cannot be proven inline.
|
|
12
|
+
*/
|
|
13
|
+
export declare const unevaluatedPropertiesExpr: (acc: string, schema: JSONSchema, ctx: MatchContext, match: MatchFn) => string | null | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* The `unevaluatedItems` conjunct for an array accessor — the index counterpart
|
|
16
|
+
* of {@link unevaluatedPropertiesExpr}.
|
|
17
|
+
*/
|
|
18
|
+
export declare const unevaluatedItemsExpr: (acc: string, schema: JSONSchema, ctx: MatchContext, match: MatchFn) => string | null | undefined;
|
|
19
|
+
export {};
|