@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,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)) {
|
|
@@ -14,6 +14,74 @@ export type StrictAssertionContext = {
|
|
|
14
14
|
readonly rootSchema?: Record<string, unknown>;
|
|
15
15
|
readonly stripUnknown?: boolean;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Why a node needs the whole-schema backstop, or `null` when the specialized
|
|
19
|
+
* assertions above already cover it. Each reason is a keyword whose enforcement
|
|
20
|
+
* no per-property / per-type check can express:
|
|
21
|
+
*
|
|
22
|
+
* - `unevaluated*` depends on which *other* keywords evaluated which keys, so it
|
|
23
|
+
* can only be answered against the whole node at once.
|
|
24
|
+
* - a `$ref` is normally enforced by the parser generated for its target, but
|
|
25
|
+
* that delegation covers neither an inlined build (no ref imports) nor the
|
|
26
|
+
* 2020-12 rule that a `$ref`'s *siblings* still apply.
|
|
27
|
+
* - `items: false` (with no tuple positions) admits only the empty array, and
|
|
28
|
+
* the item-check builder reads `items` through a guard that a boolean fails.
|
|
29
|
+
* - a constraint keyword with no `type` never reaches the typed constraint pass.
|
|
30
|
+
*/
|
|
31
|
+
export declare const backstopReason: (schema: JSONSchema, context: StrictAssertionContext) => string | null;
|
|
32
|
+
/**
|
|
33
|
+
* The slice of a node the backstop actually has to prove.
|
|
34
|
+
*
|
|
35
|
+
* `unevaluated*` and `$ref` need the node whole — the first because its answer
|
|
36
|
+
* depends on what every sibling keyword evaluated, the second because a ref's
|
|
37
|
+
* siblings apply alongside its target. A bare constraint needs only itself, and
|
|
38
|
+
* proving it in isolation is what keeps an ordinary
|
|
39
|
+
* `{ allOf: [{…}, { $ref: … }], required: ['role'] }` — the OpenAPI idiom —
|
|
40
|
+
* generating: the `required` is provable even when the surrounding `allOf`
|
|
41
|
+
* member is too deep (or too unresolvable) to inline, and that member has its
|
|
42
|
+
* own enforcement anyway.
|
|
43
|
+
*
|
|
44
|
+
* `properties` rides along, flattened to `true` schemas, only because
|
|
45
|
+
* `additionalProperties` needs the declared key names to know which keys it
|
|
46
|
+
* owns. Their values are irrelevant to that question and are asserted
|
|
47
|
+
* per-property elsewhere.
|
|
48
|
+
*/
|
|
49
|
+
export declare const backstopSchema: (schema: JSONSchema, reason: string) => JSONSchema;
|
|
50
|
+
/**
|
|
51
|
+
* The strict-mode backstop: a single whole-node match, emitted only for the
|
|
52
|
+
* keywords {@link backstopReason} identifies as unreachable by the specialized
|
|
53
|
+
* assertions. It is the same exact matcher `contains` and `propertyNames` use, so
|
|
54
|
+
* a `true` verdict means valid and a `false` one means invalid — which is what
|
|
55
|
+
* lets it stand alone as the enforcement for those keywords.
|
|
56
|
+
*
|
|
57
|
+
* Emitting nothing here is only safe because `assertNoUnsupportedKeywords`
|
|
58
|
+
* refuses generation for any node that needs a backstop the matcher cannot
|
|
59
|
+
* build.
|
|
60
|
+
*/
|
|
61
|
+
export declare const generateBackstopAssertion: (acc: string, schema: JSONSchema, label: string, context: StrictAssertionContext, optional?: boolean) => string[];
|
|
62
|
+
/**
|
|
63
|
+
* Strict-mode `minProperties` / `maxProperties`. Both were read by the coercing
|
|
64
|
+
* expression builder and the subschema matcher but by nothing on the strict
|
|
65
|
+
* path, so a strict parser accepted `{}` against `{ minProperties: 2 }` — at the
|
|
66
|
+
* root, on a property, and inside the empty-object/record parsers alike. `guard`
|
|
67
|
+
* wraps the comparison in a runtime `isObject` for accessors whose type is
|
|
68
|
+
* reported separately; callers that have already proven an object pass `false`.
|
|
69
|
+
*/
|
|
70
|
+
export declare const generateObjectSizeChecks: (acc: string, schema: JSONSchema, label: string, guard: boolean) => string[];
|
|
71
|
+
/**
|
|
72
|
+
* Strict-mode value checks for keys the schema constrains by *pattern* rather
|
|
73
|
+
* than by name: `patternProperties` (every key matching the regex) and a
|
|
74
|
+
* schema-valued `additionalProperties` (every key matched by neither
|
|
75
|
+
* `properties` nor a pattern). Both were read only by the *building* code paths
|
|
76
|
+
* — which coerce or copy — so a strict parser accepted `{ s_a: 1 }` against
|
|
77
|
+
* `patternProperties: { '^s_': { type: 'string' } }`.
|
|
78
|
+
*
|
|
79
|
+
* One `Object.keys` walk covers both, and a term is emitted only when
|
|
80
|
+
* {@link subschemaMatchExpr} proves its subschema exactly. `additionalProperties:
|
|
81
|
+
* false` is not handled here — that is the unknown-key rejection the parsers
|
|
82
|
+
* already emit.
|
|
83
|
+
*/
|
|
84
|
+
export declare const generateKeyedValueChecks: (obj: string, schema: JSONSchema, label: string, context?: StrictAssertionContext) => string[];
|
|
17
85
|
/**
|
|
18
86
|
* Strict-mode `contains` / `minContains` / `maxContains`: at least `minContains`
|
|
19
87
|
* (default 1) and at most `maxContains` (default ∞) items of the array must
|
|
@@ -24,7 +92,15 @@ export type StrictAssertionContext = {
|
|
|
24
92
|
* `contains` or its subschema is not matchable inline (the generation-time guard
|
|
25
93
|
* rejects the latter, so a strict parser never silently drops the constraint).
|
|
26
94
|
*/
|
|
27
|
-
export declare const generateContainsCheck: (acc: string, schema: JSONSchema, label: string) => string[];
|
|
95
|
+
export declare const generateContainsCheck: (acc: string, schema: JSONSchema, label: string, context?: StrictAssertionContext) => string[];
|
|
96
|
+
/**
|
|
97
|
+
* The composition checks (`not`, `allOf`, `if`/`then`/`else`) for a value the
|
|
98
|
+
* caller has already proven present. Exported for the specialized object parsers
|
|
99
|
+
* (empty-object, pattern-properties, additionalProperties), which build their
|
|
100
|
+
* result outside {@link generateObjectStrictAssertion} and so would otherwise
|
|
101
|
+
* emit no composition check at all.
|
|
102
|
+
*/
|
|
103
|
+
export declare const generateCompositionChecks: (acc: string, schema: JSONSchema, label: string, context?: StrictAssertionContext) => string[];
|
|
28
104
|
/**
|
|
29
105
|
* Emits the object-level keyword checks (`dependentRequired`, `dependentSchemas`,
|
|
30
106
|
* `propertyNames`) for the object reached by `obj`. When `guard` is true the
|
|
@@ -34,7 +110,7 @@ export declare const generateContainsCheck: (acc: string, schema: JSONSchema, la
|
|
|
34
110
|
* `guard = false` because the enclosing parser has already proven the value is
|
|
35
111
|
* an object.
|
|
36
112
|
*/
|
|
37
|
-
export declare const generateObjectKeywordChecks: (obj: string, schema: JSONSchema, label: string, guard: boolean) => string[];
|
|
113
|
+
export declare const generateObjectKeywordChecks: (obj: string, schema: JSONSchema, label: string, guard: boolean, context?: StrictAssertionContext) => string[];
|
|
38
114
|
/**
|
|
39
115
|
* Generates strict-mode assertions for the body of an object parser.
|
|
40
116
|
* Throws on:
|
|
@@ -46,9 +122,17 @@ export declare const generateObjectKeywordChecks: (obj: string, schema: JSONSche
|
|
|
46
122
|
* Properties with a `$ref` are validated by the nested parser's own strict
|
|
47
123
|
* check when that parser is invoked downstream.
|
|
48
124
|
*/
|
|
125
|
+
/**
|
|
126
|
+
* The `allOf` members this assertion must enforce itself: plain object schemas
|
|
127
|
+
* written inline. A `$ref` member is enforced by the parser generated for its
|
|
128
|
+
* target, and a member that composes further (nested `allOf`/`oneOf`/`if`) is
|
|
129
|
+
* left alone rather than half-checked.
|
|
130
|
+
*/
|
|
131
|
+
export declare const inlineAllOfMembers: (schema: JSONSchema) => JSONSchema[];
|
|
49
132
|
export declare const generateObjectStrictAssertion: (schema: JSONSchema, typeName: string, context?: StrictAssertionContext) => string[];
|
|
50
133
|
/**
|
|
51
|
-
* Generates
|
|
52
|
-
* Returns null when the schema
|
|
134
|
+
* Generates the strict-mode assertion lines for a non-object (scalar, array,
|
|
135
|
+
* type-less) parser. Returns null when the schema constrains nothing this
|
|
136
|
+
* generator can assert on, which is the caller's cue to emit a bare cast.
|
|
53
137
|
*/
|
|
54
|
-
export declare const generateScalarStrictAssertion: (schema: JSONSchema, typeName: string,
|
|
138
|
+
export declare const generateScalarStrictAssertion: (schema: JSONSchema, typeName: string, context?: StrictAssertionContext) => string | null;
|