@amritk/generate-parsers 0.18.0 → 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 +120 -8
- 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 +2 -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-enum-check.d.ts +8 -3
- package/dist/generators/generate-enum-check.js +8 -6
- package/dist/generators/generate-parser-function.d.ts +1 -1
- package/dist/generators/generate-parser-function.js +201 -57
- package/dist/generators/generate-schema-checks.js +10 -8
- package/dist/generators/generate-strict-assertion.d.ts +82 -5
- package/dist/generators/generate-strict-assertion.js +381 -53
- package/dist/generators/generate-type-checks.d.ts +31 -10
- package/dist/generators/generate-type-checks.js +65 -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 +16 -0
- package/dist/generators/generate-validation-expression.js +10 -4
- 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/get-default-value.js +22 -1
- package/package.json +5 -3
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { regexLiteral } from "@amritk/helpers/escape-regex-pattern";
|
|
2
2
|
import { quoteJsString } from "@amritk/helpers/quote-js-string";
|
|
3
3
|
import { refToName } from "@amritk/helpers/ref-to-name";
|
|
4
4
|
import { resolveRef } from "@amritk/helpers/resolve-ref";
|
|
5
|
-
import { safeAccessor, safeKey } from "@amritk/helpers/safe-accessor";
|
|
6
|
-
import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasEnum, hasItems, hasOneOf, hasProperties, hasRef, hasRequired, hasType, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
5
|
+
import { missingCheck, safeAccessor, safeKey } from "@amritk/helpers/safe-accessor";
|
|
6
|
+
import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasEnum, hasItems, hasMaxItems, hasMaxProperties, hasMinItems, hasMinProperties, hasOneOf, hasProperties, hasRef, hasRequired, hasType, hasUniqueItems, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
7
7
|
import { unknownKeyCheck } from "@amritk/helpers/unknown-key-check";
|
|
8
8
|
import { findDiscriminator } from "../helpers/find-discriminator.js";
|
|
9
9
|
import { getDefaultValue } from "../helpers/get-default-value.js";
|
|
10
10
|
import { getDiscriminatorValue } from "../helpers/get-discriminator-value.js";
|
|
11
11
|
import { assertNoUnsupportedKeywords } from "./assert-supported-keywords.js";
|
|
12
12
|
import { generateEnumCaseInsensitiveCoercion } from "./generate-enum-check.js";
|
|
13
|
-
import { generateContainsCheck, generateObjectKeywordChecks, generateObjectStrictAssertion, generateScalarStrictAssertion, inlineAllOfMembers } from "./generate-strict-assertion.js";
|
|
14
|
-
import { canEnforceUnion, generatePropertyTypeCheck, generateUnionCheck, getUnionBranches, hasFastPathBlockingKeyword, isInlineObjectArrayProperty, isInlineObjectProperty, shapeValidatorName } from "./generate-type-checks.js";
|
|
13
|
+
import { generateBackstopAssertion, generateCompositionChecks, generateContainsCheck, generateKeyedValueChecks, generateObjectKeywordChecks, generateObjectSizeChecks, generateObjectStrictAssertion, generateScalarStrictAssertion, inlineAllOfMembers } from "./generate-strict-assertion.js";
|
|
14
|
+
import { canEnforceUnion, generatePropertyTypeCheck, generateUnionCheck, getUnionBranches, hasFastPathBlockingKeyword, isExclusiveUnion, isInlineObjectArrayProperty, isInlineObjectProperty, shapeValidatorName } from "./generate-type-checks.js";
|
|
15
|
+
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
15
16
|
import { generatePrefixItemsMap, generateValidationExpression, getPrefixItems, isCoercibleItemSchema, prefixItemsCapsLength } from "./generate-validation-expression.js";
|
|
16
17
|
const generateParserName = (typeName) => {
|
|
17
18
|
return `parse${typeName}`;
|
|
@@ -149,6 +150,8 @@ const generateFallbackValue = (_, propSchema, useRefImports, suffix) => {
|
|
|
149
150
|
}
|
|
150
151
|
return getDefaultValue(propSchema);
|
|
151
152
|
};
|
|
153
|
+
const PROTOTYPE_MEMBER_NAMES = new Set(Object.getOwnPropertyNames(Object.prototype));
|
|
154
|
+
const declaresPrototypeMemberProperty = (schema) => hasProperties(schema) && Object.keys(schema.properties).some((key) => PROTOTYPE_MEMBER_NAMES.has(key));
|
|
152
155
|
const generateFallbackObject = (schema, useRefImports, typeName, suffix, subTypeNames) => {
|
|
153
156
|
if (!hasProperties(schema)) {
|
|
154
157
|
return `{} as ${typeName}`;
|
|
@@ -180,7 +183,7 @@ const generateFallbackObject = (schema, useRefImports, typeName, suffix, subType
|
|
|
180
183
|
}
|
|
181
184
|
}
|
|
182
185
|
result += "\n }";
|
|
183
|
-
return result;
|
|
186
|
+
return declaresPrototypeMemberProperty(schema) ? `${result} as ${typeName}` : result;
|
|
184
187
|
};
|
|
185
188
|
const scalarDefaultLiteral = (schema) => {
|
|
186
189
|
if (!isSchemaObject(schema))
|
|
@@ -208,7 +211,7 @@ const scalarDefaultLiteral = (schema) => {
|
|
|
208
211
|
}
|
|
209
212
|
return "{}";
|
|
210
213
|
};
|
|
211
|
-
const generateRefUnionDispatch = (functionName, typeName, branches, strict, ctx) => {
|
|
214
|
+
const generateRefUnionDispatch = (functionName, typeName, schema, branches, strict, ctx) => {
|
|
212
215
|
const rootSchema = ctx.rootSchema;
|
|
213
216
|
if (!ctx.useRefImports || rootSchema === void 0)
|
|
214
217
|
return null;
|
|
@@ -239,17 +242,26 @@ const generateRefUnionDispatch = (functionName, typeName, branches, strict, ctx)
|
|
|
239
242
|
if (c)
|
|
240
243
|
expr = `_disc === ${JSON.stringify(c.value)} ? ${c.parser}(input) : ${expr}`;
|
|
241
244
|
}
|
|
245
|
+
const backstop = strict ? generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(ctx)) : [];
|
|
246
|
+
const prelude = backstop.length > 0 ? `${backstop.join("\n")}
|
|
247
|
+
` : "";
|
|
242
248
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
243
|
-
const _disc = ${discExpr};
|
|
249
|
+
${prelude} const _disc = ${discExpr};
|
|
244
250
|
return ${expr};
|
|
245
251
|
};`;
|
|
246
252
|
};
|
|
253
|
+
const strictContext = (ctx) => ({
|
|
254
|
+
useRefImports: ctx?.useRefImports ?? false,
|
|
255
|
+
suffix: ctx?.suffix ?? "",
|
|
256
|
+
...ctx?.rootSchema !== void 0 ? { rootSchema: ctx.rootSchema } : {},
|
|
257
|
+
...ctx?.stripUnknown !== void 0 ? { stripUnknown: ctx.stripUnknown } : {}
|
|
258
|
+
});
|
|
247
259
|
const generateNonObjectParser = (typeName, schema, strict, unionCtx) => {
|
|
248
260
|
const functionName = generateParserName(typeName);
|
|
249
261
|
if (unionCtx && isSchemaObject(schema) && (hasOneOf(schema) || hasAnyOf(schema))) {
|
|
250
262
|
const branches = hasOneOf(schema) ? schema.oneOf : hasAnyOf(schema) ? schema.anyOf : [];
|
|
251
263
|
if (branches.length > 0 && branches.every((b) => isSchemaObject(b) && hasRef(b))) {
|
|
252
|
-
const dispatch = generateRefUnionDispatch(functionName, typeName, branches, strict, unionCtx);
|
|
264
|
+
const dispatch = generateRefUnionDispatch(functionName, typeName, schema, branches, strict, unionCtx);
|
|
253
265
|
if (dispatch !== null)
|
|
254
266
|
return dispatch;
|
|
255
267
|
}
|
|
@@ -257,16 +269,28 @@ const generateNonObjectParser = (typeName, schema, strict, unionCtx) => {
|
|
|
257
269
|
if (unionCtx?.useRefImports && isSchemaObject(schema) && hasRef(schema) && !hasProperties(schema)) {
|
|
258
270
|
const refName = refToName(schema.$ref, unionCtx.suffix);
|
|
259
271
|
if (refName !== typeName) {
|
|
260
|
-
|
|
272
|
+
const assertion = strict ? generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx)) : [];
|
|
273
|
+
if (assertion.length === 0) {
|
|
274
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => ${generateParserName(refName)}(input) as ${typeName};`;
|
|
275
|
+
}
|
|
276
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
277
|
+
${assertion.join("\n")}
|
|
278
|
+
return ${generateParserName(refName)}(input) as ${typeName};
|
|
279
|
+
};`;
|
|
261
280
|
}
|
|
262
281
|
}
|
|
263
282
|
if (strict && unionCtx && isSchemaObject(schema)) {
|
|
264
283
|
const branches = getUnionBranches(schema);
|
|
265
284
|
if (branches && !unionCtx.stripUnknown && canEnforceUnion(branches, unionCtx.rootSchema)) {
|
|
266
|
-
const check = generateUnionCheck("input", branches, unionCtx.useRefImports, unionCtx.suffix);
|
|
285
|
+
const check = generateUnionCheck("input", branches, unionCtx.useRefImports, unionCtx.suffix, isExclusiveUnion(schema));
|
|
267
286
|
if (check !== null) {
|
|
287
|
+
const backstop = generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx));
|
|
288
|
+
const assertions = [
|
|
289
|
+
` if (!(${check})) throw new Error(${quoteJsString(`[${typeName}] value does not match any union branch`)});`,
|
|
290
|
+
...backstop
|
|
291
|
+
].join("\n");
|
|
268
292
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
269
|
-
|
|
293
|
+
${assertions}
|
|
270
294
|
return input as ${typeName};
|
|
271
295
|
};`;
|
|
272
296
|
}
|
|
@@ -296,8 +320,8 @@ const generateNonObjectParser = (typeName, schema, strict, unionCtx) => {
|
|
|
296
320
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
297
321
|
}
|
|
298
322
|
}
|
|
299
|
-
if (strict && isSchemaObject(schema) && !hasType(schema) && (hasConst(schema) || hasEnum(schema))) {
|
|
300
|
-
const assertion = generateScalarStrictAssertion(schema, typeName, unionCtx
|
|
323
|
+
if (strict && isSchemaObject(schema) && !hasType(schema) && (hasConst(schema) || hasEnum(schema) || hasAllOf(schema) || hasOneOf(schema) || hasAnyOf(schema) || "not" in schema || "if" in schema)) {
|
|
324
|
+
const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
|
|
301
325
|
if (assertion !== null) {
|
|
302
326
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
303
327
|
${assertion}
|
|
@@ -308,7 +332,26 @@ ${assertion}
|
|
|
308
332
|
if (isSchemaObject(schema) && hasType(schema) && schema.type === "array" && hasItems(schema) && !Array.isArray(schema.items)) {
|
|
309
333
|
const items = schema.items;
|
|
310
334
|
const notArrayThrow = `if (!Array.isArray(input)) throw new Error(\`[${typeName}] expected array, got \${input === null ? "null" : typeof input}\`);`;
|
|
311
|
-
const strictArrayChecks = strict ?
|
|
335
|
+
const strictArrayChecks = strict ? [
|
|
336
|
+
// `minItems`/`maxItems` were dropped on this path entirely: a root
|
|
337
|
+
// array of objects or `$ref`s bypasses generateScalarStrictAssertion,
|
|
338
|
+
// so `{ type: 'array', minItems: 2, items: { type: 'object' } }`
|
|
339
|
+
// accepted a one-element array.
|
|
340
|
+
...hasMinItems(schema) ? [
|
|
341
|
+
` if (input.length < ${schema.minItems}) throw new Error(${quoteJsString(`[${typeName}] must have at least ${schema.minItems} items`)});`
|
|
342
|
+
] : [],
|
|
343
|
+
...hasMaxItems(schema) ? [
|
|
344
|
+
` if (input.length > ${schema.maxItems}) throw new Error(${quoteJsString(`[${typeName}] must have at most ${schema.maxItems} items`)});`
|
|
345
|
+
] : [],
|
|
346
|
+
...generateContainsCheck("input", schema, `[${typeName}]`),
|
|
347
|
+
...hasUniqueItems(schema) && schema.uniqueItems === true ? [
|
|
348
|
+
` if (!(${generateUniqueItemsCheck("input", schema)})) throw new Error(${quoteJsString(`[${typeName}] must NOT have duplicate items`)});`
|
|
349
|
+
] : [],
|
|
350
|
+
// This path delegates elements to another parser and so never reaches
|
|
351
|
+
// generateScalarStrictAssertion — `unevaluatedItems` and friends would
|
|
352
|
+
// be lost with it.
|
|
353
|
+
...generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx))
|
|
354
|
+
].join("\n") : "";
|
|
312
355
|
const delegatedBody = (itemParserName) => ` const _parsed = validateArray(input, ${itemParserName});
|
|
313
356
|
return (_parsed === input ? [..._parsed] : _parsed) as ${typeName};`;
|
|
314
357
|
const delegated = (itemParserName) => strict ? `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
@@ -342,13 +385,22 @@ ${delegated(generateParserName(itemName))}`;
|
|
|
342
385
|
}
|
|
343
386
|
const isMultiType = isSchemaObject(schema) && Array.isArray(schema.type);
|
|
344
387
|
if (!isSchemaObject(schema) || !hasType(schema) && !isMultiType) {
|
|
388
|
+
if (strict) {
|
|
389
|
+
const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
|
|
390
|
+
if (assertion !== null) {
|
|
391
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
392
|
+
${assertion}
|
|
393
|
+
return input as ${typeName};
|
|
394
|
+
};`;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
345
397
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
346
398
|
}
|
|
347
399
|
if (!strict && schema.type === "null") {
|
|
348
400
|
return `export const ${functionName} = (input: unknown): ${typeName} => null as ${typeName};`;
|
|
349
401
|
}
|
|
350
402
|
if (strict) {
|
|
351
|
-
const assertion = generateScalarStrictAssertion(schema, typeName, unionCtx
|
|
403
|
+
const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
|
|
352
404
|
if (assertion === null) {
|
|
353
405
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
354
406
|
}
|
|
@@ -385,17 +437,50 @@ ${assertion}
|
|
|
385
437
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
386
438
|
}
|
|
387
439
|
};
|
|
388
|
-
const strictObjectKeywordPrelude = (schema, typeName) => {
|
|
440
|
+
const strictObjectKeywordPrelude = (schema, typeName, context = {}) => {
|
|
389
441
|
if (!isSchemaObject(schema))
|
|
390
442
|
return "";
|
|
391
|
-
const lines =
|
|
443
|
+
const lines = [
|
|
444
|
+
...generateObjectKeywordChecks("input", schema, `[${typeName}]`, false, context),
|
|
445
|
+
// `required` and the size bounds are the object-level constraints these
|
|
446
|
+
// specialized parsers never had a place for: `{ type: 'object', required:
|
|
447
|
+
// ['a'] }` and `{ type: 'object', minProperties: 2 }` carry no `properties`,
|
|
448
|
+
// so they land on the empty-object / record paths, which asserted only
|
|
449
|
+
// `isObject`. Keys that *are* declared are skipped — the per-property
|
|
450
|
+
// assertions of the combined parser already demand them.
|
|
451
|
+
...requiredWithoutPropertyLines(schema, typeName),
|
|
452
|
+
...generateObjectSizeChecks("input", schema, `[${typeName}]`, false),
|
|
453
|
+
// Values behind `patternProperties` / a schema-valued `additionalProperties`:
|
|
454
|
+
// these parsers build their result by copying or coercing, so nothing
|
|
455
|
+
// rejected a wrongly-typed pattern-matched value before this.
|
|
456
|
+
...generateKeyedValueChecks("input", schema, `[${typeName}]`, context),
|
|
457
|
+
// Composition on a property-less object: these parsers delegate nothing, so
|
|
458
|
+
// `{ type: 'object', allOf: [{ $ref: … }] }` had no check at all.
|
|
459
|
+
...generateCompositionChecks("input", schema, `[${typeName}]`, context),
|
|
460
|
+
// Whatever no specialized check above reaches (`unevaluated*`, a `$ref` with
|
|
461
|
+
// constraining siblings): these parsers never run generateObjectStrictAssertion,
|
|
462
|
+
// so the backstop has to be spliced in here too.
|
|
463
|
+
...generateBackstopAssertion("input", schema, `[${typeName}]`, context)
|
|
464
|
+
];
|
|
392
465
|
return lines.length > 0 ? `
|
|
393
466
|
${lines.join("\n")}` : "";
|
|
394
467
|
};
|
|
395
|
-
const
|
|
468
|
+
const requiredWithoutPropertyLines = (schema, typeName) => {
|
|
469
|
+
if (!isSchemaObject(schema) || !hasRequired(schema))
|
|
470
|
+
return [];
|
|
471
|
+
const declared = hasProperties(schema) ? schema.properties : {};
|
|
472
|
+
const lines = [];
|
|
473
|
+
for (const key of new Set(schema.required)) {
|
|
474
|
+
if (key in declared)
|
|
475
|
+
continue;
|
|
476
|
+
lines.push(` if (${missingCheck("input", key)}) throw new Error(${quoteJsString(`[${typeName}] missing required property '${key}'`)});`);
|
|
477
|
+
}
|
|
478
|
+
return lines;
|
|
479
|
+
};
|
|
480
|
+
const generateEmptyObjectParser = (typeName, strict, schema, context = {}) => {
|
|
396
481
|
const functionName = generateParserName(typeName);
|
|
397
482
|
if (strict) {
|
|
398
|
-
const prelude = schema !== void 0 ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
483
|
+
const prelude = schema !== void 0 ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
399
484
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
400
485
|
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${prelude}
|
|
401
486
|
return { ...input } as ${typeName};
|
|
@@ -476,7 +561,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
476
561
|
if (!hasProperties(schema)) {
|
|
477
562
|
if (strict) {
|
|
478
563
|
return `${exportPrefix}const ${functionName} = (input: unknown): ${typeName} => {
|
|
479
|
-
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${strictObjectKeywordPrelude(schema, typeName)}
|
|
564
|
+
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${strictObjectKeywordPrelude(schema, typeName, { useRefImports, suffix, stripUnknown, ...rootSchema !== void 0 ? { rootSchema } : {} })}
|
|
480
565
|
return input as ${typeName};
|
|
481
566
|
};`;
|
|
482
567
|
}
|
|
@@ -566,6 +651,16 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
566
651
|
fastPathAccessorChecks.push(`(${accessor} === undefined || ${accessorCheck})`);
|
|
567
652
|
}
|
|
568
653
|
}
|
|
654
|
+
if (canFastPath) {
|
|
655
|
+
if (hasMinProperties(schema)) {
|
|
656
|
+
fastPathChecks.push(`Object.keys(input).length >= ${schema.minProperties}`);
|
|
657
|
+
fastPathAccessorChecks.push(`Object.keys(input).length >= ${schema.minProperties}`);
|
|
658
|
+
}
|
|
659
|
+
if (hasMaxProperties(schema)) {
|
|
660
|
+
fastPathChecks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
|
|
661
|
+
fastPathAccessorChecks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
569
664
|
if (stripKeys) {
|
|
570
665
|
fastPathChecks.push(exactKeyCount !== null ? `Object.getPrototypeOf(input) === Object.prototype && Object.keys(input).length === ${exactKeyCount}` : `_hasOnlyKnownKeys${typeName}(input)`);
|
|
571
666
|
}
|
|
@@ -700,7 +795,13 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
700
795
|
continue;
|
|
701
796
|
}
|
|
702
797
|
if (!isSchemaObject(propSchema)) {
|
|
703
|
-
if (
|
|
798
|
+
if (propSchema === true) {
|
|
799
|
+
if (isRequired) {
|
|
800
|
+
objectLines.push(` ${safeLiteralKey(key)}: ${accessor},`);
|
|
801
|
+
} else if (stripKeys) {
|
|
802
|
+
objectLines.push(` ...(${accessor} !== undefined && { ${safeLiteralKey(key)}: ${accessor} }),`);
|
|
803
|
+
}
|
|
804
|
+
} else if (isRequired) {
|
|
704
805
|
objectLines.push(` ${safeLiteralKey(key)}: undefined,`);
|
|
705
806
|
}
|
|
706
807
|
continue;
|
|
@@ -739,8 +840,12 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
739
840
|
const fields = [];
|
|
740
841
|
for (const { key, varName, isRequired, propSchema } of propInfo) {
|
|
741
842
|
if (!isSchemaObject(propSchema)) {
|
|
742
|
-
if (
|
|
843
|
+
if (propSchema === true) {
|
|
844
|
+
const acc = safeAccessor("input", key);
|
|
845
|
+
fields.push(isRequired ? ` ${safeLiteralKey(key)}: ${acc},` : ` ...(${acc} !== undefined && { ${safeLiteralKey(key)}: ${acc} }),`);
|
|
846
|
+
} else if (isRequired) {
|
|
743
847
|
fields.push(` ${safeLiteralKey(key)}: undefined,`);
|
|
848
|
+
}
|
|
744
849
|
continue;
|
|
745
850
|
}
|
|
746
851
|
const accessor = shouldCacheVariable(propSchema, canFastPath, useRefImports) ? varName : safeAccessor("input", key);
|
|
@@ -826,19 +931,19 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
826
931
|
${fn}`;
|
|
827
932
|
};
|
|
828
933
|
const safeResultAssign = (keyVar, valueExpr) => `if (${keyVar} === "__proto__") { Object.defineProperty(result, ${keyVar}, { value: ${valueExpr}, writable: true, enumerable: true, configurable: true }); } else { (result as Record<string, unknown>)[${keyVar}] = ${valueExpr}; }`;
|
|
829
|
-
const generateStrictCombinedParser = (schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict) => {
|
|
934
|
+
const generateStrictCombinedParser = (schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict, context = {}) => {
|
|
830
935
|
const declaredKeys = hasProperties(schema) ? Object.keys(schema.properties) : [];
|
|
831
936
|
const knownKeyCheck = unknownKeyCheck(declaredKeys, "_knownKeys");
|
|
832
937
|
const refPattern = useRefImports ? patterns.find(([, ps]) => isSchemaObject(ps) && hasRef(ps)) : void 0;
|
|
833
938
|
const loopLines = [` if (${knownKeyCheck.isKnown("key")}) continue;`];
|
|
834
939
|
if (refPattern) {
|
|
835
940
|
const parserName = generateParserName(refToName(refPattern[1].$ref, suffix));
|
|
836
|
-
loopLines.push(` if (
|
|
941
|
+
loopLines.push(` if (${regexLiteral(refPattern[0])}.test(key)) {`);
|
|
837
942
|
loopLines.push(` ${safeResultAssign("key", `${parserName}(input[key])`)}`);
|
|
838
943
|
loopLines.push(` continue;`);
|
|
839
944
|
loopLines.push(` }`);
|
|
840
945
|
}
|
|
841
|
-
const keepConditions = patterns.filter(([p]) => !(refPattern && p === refPattern[0])).map(([p]) =>
|
|
946
|
+
const keepConditions = patterns.filter(([p]) => !(refPattern && p === refPattern[0])).map(([p]) => `${regexLiteral(p)}.test(key)`);
|
|
842
947
|
if (keepConditions.length > 0) {
|
|
843
948
|
loopLines.push(` if (${keepConditions.join(" || ")}) {`);
|
|
844
949
|
loopLines.push(` ${safeResultAssign("key", "input[key]")}`);
|
|
@@ -853,7 +958,7 @@ ${propertyLines.join("\n")}
|
|
|
853
958
|
}` : "{}";
|
|
854
959
|
const knownKeysDeclaration = knownKeyCheck.declarations.map((decl) => ` ${decl};
|
|
855
960
|
`).join("");
|
|
856
|
-
const assertionPrelude = strict ? generateObjectStrictAssertion(schema, typeName,
|
|
961
|
+
const assertionPrelude = strict ? generateObjectStrictAssertion(schema, typeName, context).slice(1) : [];
|
|
857
962
|
const keywordPrelude = assertionPrelude.length > 0 ? `
|
|
858
963
|
${assertionPrelude.join("\n")}` : "";
|
|
859
964
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
@@ -867,7 +972,7 @@ ${loopLines.join("\n")}
|
|
|
867
972
|
return result;
|
|
868
973
|
};`;
|
|
869
974
|
};
|
|
870
|
-
const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown = false, caseInsensitive = false) => {
|
|
975
|
+
const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown = false, caseInsensitive = false, context = {}) => {
|
|
871
976
|
const functionName = generateParserName(typeName);
|
|
872
977
|
const entries = generatePropertyEntries(schema, useRefImports, suffix, caseInsensitive);
|
|
873
978
|
const propertyLines = entries.map((entry) => {
|
|
@@ -877,26 +982,26 @@ const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, l
|
|
|
877
982
|
return ` ${safeLiteralKey(entry.key)}: ${entry.value},`;
|
|
878
983
|
});
|
|
879
984
|
if (!isSchemaObject(schema) || !("patternProperties" in schema)) {
|
|
880
|
-
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown,
|
|
985
|
+
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive);
|
|
881
986
|
}
|
|
882
987
|
const patternProps = schema.patternProperties;
|
|
883
988
|
const patterns = Object.entries(patternProps);
|
|
884
989
|
const refPattern = patterns.find(([, ps]) => isSchemaObject(ps) && hasRef(ps));
|
|
885
990
|
const strictKeys = hasAdditionalProperties(schema) && schema.additionalProperties === false;
|
|
886
991
|
if (strictKeys) {
|
|
887
|
-
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict);
|
|
992
|
+
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict, context);
|
|
888
993
|
}
|
|
889
994
|
if (stripUnknown && !strict) {
|
|
890
|
-
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, false);
|
|
995
|
+
return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, false, context);
|
|
891
996
|
}
|
|
892
997
|
if (!refPattern || !useRefImports) {
|
|
893
|
-
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown,
|
|
998
|
+
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive);
|
|
894
999
|
}
|
|
895
1000
|
const [pattern, patternSchema] = refPattern;
|
|
896
1001
|
const ref = patternSchema.$ref;
|
|
897
1002
|
const parserName = generateParserName(refToName(ref, suffix));
|
|
898
1003
|
const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
|
|
899
|
-
const
|
|
1004
|
+
const patternLiteral = regexLiteral(pattern);
|
|
900
1005
|
const inputSpread = " ...input,";
|
|
901
1006
|
let objectProperties = inputSpread;
|
|
902
1007
|
if (propertyLines.length > 0) {
|
|
@@ -905,7 +1010,7 @@ const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, l
|
|
|
905
1010
|
}
|
|
906
1011
|
}
|
|
907
1012
|
const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
|
|
908
|
-
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
1013
|
+
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
909
1014
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
910
1015
|
if (!isObject(input)) {
|
|
911
1016
|
${notObjectBranch}
|
|
@@ -914,7 +1019,7 @@ ${notObjectBranch}
|
|
|
914
1019
|
${objectProperties}
|
|
915
1020
|
} as unknown as ${typeName};
|
|
916
1021
|
for (const key in input) {
|
|
917
|
-
if (
|
|
1022
|
+
if (${patternLiteral}.test(key)) {
|
|
918
1023
|
const value = input[key];
|
|
919
1024
|
${assignmentCode}
|
|
920
1025
|
}
|
|
@@ -922,13 +1027,13 @@ ${objectProperties}
|
|
|
922
1027
|
return result;
|
|
923
1028
|
};`;
|
|
924
1029
|
};
|
|
925
|
-
const generateAdditionalPropertiesParser = (schema, typeName, useRefImports, suffix, strict) => {
|
|
1030
|
+
const generateAdditionalPropertiesParser = (schema, typeName, useRefImports, suffix, strict, context = {}) => {
|
|
926
1031
|
const functionName = generateParserName(typeName);
|
|
927
1032
|
const additionalProps = schema.additionalProperties;
|
|
928
1033
|
if (!additionalProps) {
|
|
929
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1034
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
930
1035
|
}
|
|
931
|
-
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
1036
|
+
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
932
1037
|
if (useRefImports && isSchemaObject(additionalProps) && hasRef(additionalProps)) {
|
|
933
1038
|
const ref = additionalProps.$ref;
|
|
934
1039
|
const parserName = generateParserName(refToName(ref, suffix));
|
|
@@ -996,15 +1101,15 @@ const generateInlineValueParser = (schema, strict = false, label = "") => {
|
|
|
996
1101
|
return null;
|
|
997
1102
|
}
|
|
998
1103
|
};
|
|
999
|
-
const generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix, strict) => {
|
|
1104
|
+
const generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix, strict, context = {}) => {
|
|
1000
1105
|
const functionName = generateParserName(typeName);
|
|
1001
1106
|
if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
|
|
1002
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1107
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1003
1108
|
}
|
|
1004
1109
|
const patternProps = schema.patternProperties;
|
|
1005
1110
|
const patterns = Object.entries(patternProps);
|
|
1006
1111
|
if (patterns.length === 0) {
|
|
1007
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1112
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1008
1113
|
}
|
|
1009
1114
|
const [pattern, patternSchema] = patterns[0];
|
|
1010
1115
|
let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
|
|
@@ -1018,19 +1123,19 @@ const generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix
|
|
|
1018
1123
|
patternAssignment = "";
|
|
1019
1124
|
patternValueExpr = null;
|
|
1020
1125
|
}
|
|
1021
|
-
const
|
|
1126
|
+
const patternLiteral = regexLiteral(pattern);
|
|
1022
1127
|
const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
|
|
1023
|
-
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName) : "";
|
|
1128
|
+
const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
|
|
1024
1129
|
const strictKeys = hasAdditionalProperties(schema) && schema.additionalProperties === false;
|
|
1025
1130
|
if (strictKeys) {
|
|
1026
|
-
const loopLines = [` if (
|
|
1131
|
+
const loopLines = [` if (${patternLiteral}.test(key)) {`];
|
|
1027
1132
|
if (patternValueExpr) {
|
|
1028
1133
|
loopLines.push(` const value = input[key];`);
|
|
1029
1134
|
loopLines.push(` ${safeResultAssign("key", patternValueExpr)}`);
|
|
1030
1135
|
}
|
|
1031
1136
|
loopLines.push(` continue;`);
|
|
1032
1137
|
loopLines.push(` }`);
|
|
1033
|
-
const keepConditions = patterns.slice(1).map(([p]) =>
|
|
1138
|
+
const keepConditions = patterns.slice(1).map(([p]) => `${regexLiteral(p)}.test(key)`);
|
|
1034
1139
|
if (keepConditions.length > 0) {
|
|
1035
1140
|
loopLines.push(` if (${keepConditions.join(" || ")}) {`);
|
|
1036
1141
|
loopLines.push(` ${safeResultAssign("key", "input[key]")}`);
|
|
@@ -1058,7 +1163,7 @@ ${notObjectBranch}
|
|
|
1058
1163
|
...input,
|
|
1059
1164
|
} as unknown as ${typeName};
|
|
1060
1165
|
for (const key in input) {
|
|
1061
|
-
if (
|
|
1166
|
+
if (${patternLiteral}.test(key)) {
|
|
1062
1167
|
const value = input[key];
|
|
1063
1168
|
${patternAssignment}
|
|
1064
1169
|
}
|
|
@@ -1169,6 +1274,12 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1169
1274
|
const stripUnknown = options?.stripUnknown ?? false;
|
|
1170
1275
|
const caseInsensitive = options?.caseInsensitive ?? false;
|
|
1171
1276
|
const suffix = options?.typeSuffix ?? "";
|
|
1277
|
+
const context = {
|
|
1278
|
+
useRefImports,
|
|
1279
|
+
suffix,
|
|
1280
|
+
stripUnknown,
|
|
1281
|
+
...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {}
|
|
1282
|
+
};
|
|
1172
1283
|
if (!options?.isRoot && typeName === `Schema${suffix}`) {
|
|
1173
1284
|
return generateSchemaObjectParser(typeName);
|
|
1174
1285
|
}
|
|
@@ -1185,17 +1296,39 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1185
1296
|
});
|
|
1186
1297
|
}
|
|
1187
1298
|
if (hasProperties(schema) && isSchemaObject(schema) && "patternProperties" in schema) {
|
|
1188
|
-
return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown, caseInsensitive);
|
|
1299
|
+
return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown, caseInsensitive, context);
|
|
1300
|
+
}
|
|
1301
|
+
if (strict && isSchemaObject(schema) && Array.isArray(schema.type) && schema.type.includes("null")) {
|
|
1302
|
+
return generateNonObjectParser(typeName, schema, strict, {
|
|
1303
|
+
useRefImports,
|
|
1304
|
+
suffix,
|
|
1305
|
+
stripUnknown,
|
|
1306
|
+
caseInsensitive,
|
|
1307
|
+
logWarnings,
|
|
1308
|
+
...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
|
|
1309
|
+
...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
|
|
1310
|
+
});
|
|
1189
1311
|
}
|
|
1190
1312
|
if (hasProperties(schema)) {
|
|
1191
1313
|
return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, options?.rootSchema, options?.reservedNames, caseInsensitive, options?.shapeValidatorSource);
|
|
1192
1314
|
}
|
|
1315
|
+
if (strict && isSchemaObject(schema) && ("if" in schema || "then" in schema || "else" in schema)) {
|
|
1316
|
+
return generateNonObjectParser(typeName, schema, strict, {
|
|
1317
|
+
useRefImports,
|
|
1318
|
+
suffix,
|
|
1319
|
+
stripUnknown,
|
|
1320
|
+
caseInsensitive,
|
|
1321
|
+
logWarnings,
|
|
1322
|
+
...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
|
|
1323
|
+
...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1193
1326
|
if (isSchemaObject(schema) && "if" in schema && "then" in schema && "else" in schema) {
|
|
1194
1327
|
return generateConditionalParser(schema, typeName, suffix, stripUnknown, caseInsensitive);
|
|
1195
1328
|
}
|
|
1196
1329
|
const conditionalObjectSchema = getConditionalObjectSchema(schema);
|
|
1197
1330
|
if (conditionalObjectSchema) {
|
|
1198
|
-
return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown,
|
|
1331
|
+
return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive);
|
|
1199
1332
|
}
|
|
1200
1333
|
if (!isObjectLikeSchema) {
|
|
1201
1334
|
return generateNonObjectParser(typeName, schema, strict, {
|
|
@@ -1209,23 +1342,30 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1209
1342
|
});
|
|
1210
1343
|
}
|
|
1211
1344
|
if ("patternProperties" in schema) {
|
|
1212
|
-
return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict);
|
|
1345
|
+
return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict, context);
|
|
1213
1346
|
}
|
|
1214
1347
|
if ("additionalProperties" in schema && !hasProperties(schema)) {
|
|
1215
1348
|
const additionalProps = schema.additionalProperties;
|
|
1216
1349
|
if (additionalProps === true || additionalProps === false) {
|
|
1217
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1350
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1218
1351
|
}
|
|
1219
|
-
return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict);
|
|
1352
|
+
return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict, context);
|
|
1220
1353
|
}
|
|
1221
1354
|
if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
|
|
1222
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1355
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1223
1356
|
}
|
|
1224
|
-
return generateEmptyObjectParser(typeName, strict, schema);
|
|
1357
|
+
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1225
1358
|
};
|
|
1226
|
-
const generateParserFunction = (schema, typeName,
|
|
1227
|
-
|
|
1228
|
-
|
|
1359
|
+
const generateParserFunction = (schema, typeName, rawOptions) => {
|
|
1360
|
+
const options = rawOptions?.strict && rawOptions.rootSchema === void 0 && isSchemaObject(schema) ? { ...rawOptions, rootSchema: schema } : rawOptions;
|
|
1361
|
+
if (options?.strict) {
|
|
1362
|
+
assertNoUnsupportedKeywords(schema, typeName, {
|
|
1363
|
+
useRefImports: options.useRefImports ?? false,
|
|
1364
|
+
suffix: options.typeSuffix ?? "",
|
|
1365
|
+
...options.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
|
|
1366
|
+
...options.stripUnknown !== void 0 ? { stripUnknown: options.stripUnknown } : {}
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1229
1369
|
return selectParserStrategy(schema, typeName, options);
|
|
1230
1370
|
};
|
|
1231
1371
|
const shapeValidatorFunctionName = (typeName) => shapeValidatorName(typeName);
|
|
@@ -1247,7 +1387,7 @@ const generateShapeValidator = (schema, typeName, useRefImports, suffix = "", ex
|
|
|
1247
1387
|
if (!hasProperties(schema) && !("patternProperties" in schema) && !("if" in schema) && !stripUnknown) {
|
|
1248
1388
|
const branches = getUnionBranches(schema);
|
|
1249
1389
|
if (branches) {
|
|
1250
|
-
const check = generateUnionCheck("input", branches, useRefImports, suffix);
|
|
1390
|
+
const check = generateUnionCheck("input", branches, useRefImports, suffix, isExclusiveUnion(schema));
|
|
1251
1391
|
if (check !== null) {
|
|
1252
1392
|
return `${exportPrefix}const ${fnName} = (input: unknown): boolean => ${check};`;
|
|
1253
1393
|
}
|
|
@@ -1289,6 +1429,10 @@ const generateShapeValidator = (schema, typeName, useRefImports, suffix = "", ex
|
|
|
1289
1429
|
checks.push(`(${accessor} === undefined || ${check})`);
|
|
1290
1430
|
}
|
|
1291
1431
|
}
|
|
1432
|
+
if (hasMinProperties(schema))
|
|
1433
|
+
checks.push(`Object.keys(input).length >= ${schema.minProperties}`);
|
|
1434
|
+
if (hasMaxProperties(schema))
|
|
1435
|
+
checks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
|
|
1292
1436
|
if (validatorKeyCount !== null) {
|
|
1293
1437
|
checks.push(`Object.getPrototypeOf(input) === Object.prototype && Object.keys(input).length === ${validatorKeyCount}`);
|
|
1294
1438
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { regexLiteral } from "@amritk/helpers/escape-regex-pattern";
|
|
2
2
|
import { getMjstInstanceOf, getMjstPrimitive } from "@amritk/helpers/mjst-extension";
|
|
3
3
|
import { multipleOfPassExpr } from "@amritk/helpers/multiple-of-check";
|
|
4
|
+
import { hasOwnCheck } from "@amritk/helpers/safe-accessor";
|
|
4
5
|
import { hasAdditionalProperties, hasConst, hasEnum, hasExclusiveMaximum, hasExclusiveMinimum, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasPattern, hasProperties, hasRequired, hasType, hasUniqueItems, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
5
6
|
import { generateEnumCheck } from "./generate-enum-check.js";
|
|
7
|
+
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
6
8
|
const OBJECT_KEYWORDS = [
|
|
7
9
|
"properties",
|
|
8
10
|
"required",
|
|
@@ -61,7 +63,7 @@ const generateInferredChecks = (accessor, schema, type) => {
|
|
|
61
63
|
checks.push(`typeof ${accessor} === "object" && ${accessor} !== null && !Array.isArray(${accessor})`);
|
|
62
64
|
if (hasRequired(schema) && schema.required.length > 0) {
|
|
63
65
|
for (const requiredKey of schema.required) {
|
|
64
|
-
checks.push(
|
|
66
|
+
checks.push(hasOwnCheck(accessor, requiredKey));
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
if (hasMinProperties(schema)) {
|
|
@@ -84,13 +86,13 @@ const generateInferredChecks = (accessor, schema, type) => {
|
|
|
84
86
|
checks.push(`${accessor}.length <= ${schema.maxItems}`);
|
|
85
87
|
}
|
|
86
88
|
if (hasUniqueItems(schema) && schema.uniqueItems === true) {
|
|
87
|
-
checks.push(
|
|
89
|
+
checks.push(generateUniqueItemsCheck(accessor, schema));
|
|
88
90
|
}
|
|
89
91
|
break;
|
|
90
92
|
case "string":
|
|
91
93
|
checks.push(`typeof ${accessor} === "string"`);
|
|
92
94
|
if (hasPattern(schema)) {
|
|
93
|
-
checks.push(
|
|
95
|
+
checks.push(`${regexLiteral(schema.pattern)}.test(${accessor})`);
|
|
94
96
|
}
|
|
95
97
|
if (hasMinLength(schema)) {
|
|
96
98
|
checks.push(`${accessor}.length >= ${schema.minLength}`);
|
|
@@ -145,7 +147,7 @@ const generateSchemaChecks = (accessor, schema) => {
|
|
|
145
147
|
}
|
|
146
148
|
checks.push(...generateInferredChecks(accessor, schema, inferred));
|
|
147
149
|
if (hasEnum(schema) && schema.enum.length > 0) {
|
|
148
|
-
checks.push(
|
|
150
|
+
checks.push(generateEnumCheck(accessor, schema.enum));
|
|
149
151
|
}
|
|
150
152
|
return checks;
|
|
151
153
|
}
|
|
@@ -153,7 +155,7 @@ const generateSchemaChecks = (accessor, schema) => {
|
|
|
153
155
|
case "string": {
|
|
154
156
|
checks.push(`typeof ${accessor} === "string"`);
|
|
155
157
|
if (hasPattern(schema)) {
|
|
156
|
-
checks.push(
|
|
158
|
+
checks.push(`${regexLiteral(schema.pattern)}.test(${accessor})`);
|
|
157
159
|
}
|
|
158
160
|
if (hasMinLength(schema)) {
|
|
159
161
|
checks.push(`${accessor}.length >= ${schema.minLength}`);
|
|
@@ -197,14 +199,14 @@ const generateSchemaChecks = (accessor, schema) => {
|
|
|
197
199
|
checks.push(`${accessor}.length <= ${schema.maxItems}`);
|
|
198
200
|
}
|
|
199
201
|
if (hasUniqueItems(schema) && schema.uniqueItems === true) {
|
|
200
|
-
checks.push(
|
|
202
|
+
checks.push(generateUniqueItemsCheck(accessor, schema));
|
|
201
203
|
}
|
|
202
204
|
break;
|
|
203
205
|
case "object": {
|
|
204
206
|
checks.push(`typeof ${accessor} === "object" && ${accessor} !== null && !Array.isArray(${accessor})`);
|
|
205
207
|
if (hasRequired(schema) && schema.required.length > 0) {
|
|
206
208
|
for (const requiredKey of schema.required) {
|
|
207
|
-
checks.push(
|
|
209
|
+
checks.push(hasOwnCheck(accessor, requiredKey));
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
212
|
if (hasMinProperties(schema)) {
|