@amritk/generate-parsers 0.19.3 → 0.19.5
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 +5 -1
- package/README.md +9 -9
- package/dist/generators/build-schema.d.ts +4 -1
- package/dist/generators/build-schema.js +7 -0
- package/dist/generators/generate-parser-function.js +38 -39
- package/dist/generators/generate-schema-checks.js +30 -4
- package/dist/generators/generate-strict-assertion.d.ts +11 -0
- package/dist/generators/generate-strict-assertion.js +33 -3
- package/dist/generators/generate-type-checks.js +14 -5
- package/dist/generators/generate-validation-expression.d.ts +10 -0
- package/dist/generators/generate-validation-expression.js +83 -17
- package/dist/generators/subschema-match.js +3 -3
- package/dist/generators/unevaluated-match.js +2 -2
- package/dist/helpers/collect-imports.js +61 -30
- package/dist/helpers/get-default-value.js +39 -12
- package/package.json +3 -3
package/AI.md
CHANGED
|
@@ -26,7 +26,11 @@ const files = await buildSchema(schema, 'Document')
|
|
|
26
26
|
To set a later flag you must pass every intervening positional. (The README's
|
|
27
27
|
short form omits the trailing seven.)
|
|
28
28
|
2. **It returns files in memory — it does NOT write to disk.** You write the
|
|
29
|
-
`GeneratedFile[]` (`{ filename, content }`) yourself.
|
|
29
|
+
`GeneratedFile[]` (`{ filename, content }`) yourself. `rootTypeName` becomes
|
|
30
|
+
that filename (lowercased) as well as the `export type` name, so it must be a
|
|
31
|
+
TypeScript identifier — `buildSchema` throws otherwise rather than hand you a
|
|
32
|
+
path that escapes your output directory. Derive it with `@amritk/helpers`'
|
|
33
|
+
`deriveRootTypeName`, which always produces one.
|
|
30
34
|
3. **Output is more than one file per `$def`:** always an `index.ts` barrel, and
|
|
31
35
|
(unless `typesOnly`) runtime helper files.
|
|
32
36
|
4. **Default parsers COERCE invalid input to defaults** rather than throwing;
|
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ Every option after the first two is **positional** — pass them in this order:
|
|
|
82
82
|
| # | Parameter | Type | Default | Description |
|
|
83
83
|
|--:|:---|:---|:---|:---|
|
|
84
84
|
| 1 | `rootSchema` | `JSONSchema` | — | The root schema to traverse. `$ref` and `$dynamicRef` are resolved recursively. |
|
|
85
|
-
| 2 | `rootTypeName` | `string` | — | Name used for the root type (e.g. `"Document"`). |
|
|
85
|
+
| 2 | `rootTypeName` | `string` | — | Name used for the root type (e.g. `"Document"`). Must be a TypeScript identifier: it is emitted verbatim as `export type <name>` and, lowercased, becomes the output filename, so a name derived from an untrusted document is refused rather than written outside your output directory. `@amritk/helpers`' `deriveRootTypeName` always produces one. |
|
|
86
86
|
| 3 | `extensions` | `SchemaExtensions` | — | Map of definition name → extra optional properties to merge in before generation. |
|
|
87
87
|
| 4 | `typesOnly` | `boolean` | — | Only emit `.ts` type definitions — skip parser functions and runtime helpers. |
|
|
88
88
|
| 5 | `logWarnings` | `boolean` | — | Generated parsers emit a `console.warn` for every input key not declared in the schema's properties. |
|
|
@@ -296,23 +296,23 @@ keys) and `parseStrict` (assert + reject undeclared keys) halves of
|
|
|
296
296
|
[`moltar/typescript-runtime-type-benchmarks`](https://github.com/moltar/typescript-runtime-type-benchmarks)
|
|
297
297
|
against the other *pure* parsers — the ones that return a new typed value rather
|
|
298
298
|
than mutating in place — [zod](https://zod.dev) and
|
|
299
|
-
[TypeBox](https://github.com/sinclairzx81/typebox). Measured on Bun 1.
|
|
299
|
+
[TypeBox](https://github.com/sinclairzx81/typebox). Measured on Bun 1.4 (Linux
|
|
300
300
|
x64), parsing valid input at steady state:
|
|
301
301
|
|
|
302
302
|
| case | mode | mjst (generated) | zod | typebox |
|
|
303
303
|
|:--|:--|--:|--:|--:|
|
|
304
|
-
| user (4 fields) | parseSafe | **~18M** ops/s | ~
|
|
305
|
-
| order (nested + array) | parseSafe | **~
|
|
306
|
-
| user (4 fields) | parseStrict | **~
|
|
307
|
-
| order (nested + array) | parseStrict | **~
|
|
304
|
+
| user (4 fields) | parseSafe | **~18M** ops/s | ~3.7M ops/s | ~1.6M ops/s |
|
|
305
|
+
| order (nested + array) | parseSafe | **~6.9M** ops/s | ~0.71M ops/s | ~0.24M ops/s |
|
|
306
|
+
| user (4 fields) | parseStrict | **~14M** ops/s | ~2.3M ops/s | ~2.04M ops/s |
|
|
307
|
+
| order (nested + array) | parseStrict | **~7M** ops/s | ~0.43M ops/s | ~0.36M ops/s |
|
|
308
308
|
|
|
309
309
|
The upstream `assert` case (seven scalar roots plus a nested object) runs faster
|
|
310
|
-
still — ~
|
|
310
|
+
still — ~51M ops/s strict, ~127M safe — but at that size the numbers swing enough
|
|
311
311
|
run-to-run that the ratio, not the absolute, is the honest signal: mjst lands
|
|
312
|
-
~
|
|
312
|
+
~5–29× ahead of zod across every case above.
|
|
313
313
|
|
|
314
314
|
The trade is a one-shot **prepare** cost that only mjst pays — generating the
|
|
315
|
-
parser source — which measures **~0.
|
|
315
|
+
parser source — which measures **~0.2–0.8 ms** per schema here (zod and TypeBox
|
|
316
316
|
author or interpret their parsers with no separate build step, so there is
|
|
317
317
|
nothing to time). That is trivially amortized: you generate once at build time
|
|
318
318
|
and run the emitted code forever.
|
|
@@ -19,7 +19,10 @@ export type GeneratedFile = {
|
|
|
19
19
|
* TypeScript file, then emits an `index.ts` barrel re-exporting them all.
|
|
20
20
|
*
|
|
21
21
|
* @param rootSchema - The root JSON Schema to build from
|
|
22
|
-
* @param rootTypeName - The name for the root type (e.g., "Document")
|
|
22
|
+
* @param rootTypeName - The name for the root type (e.g., "Document"). Must be a TypeScript
|
|
23
|
+
* identifier: it is emitted verbatim as `export type <name>` and, lowercased, becomes the
|
|
24
|
+
* output filename, so a name derived from an untrusted document is refused rather than
|
|
25
|
+
* written outside the output directory.
|
|
23
26
|
* @param extensions - Optional map of custom extension properties to add to specific definitions.
|
|
24
27
|
* Keys are definition names (matching $defs keys), values are records of extension property
|
|
25
28
|
* names to their JSON Schema definitions. Extensions are merged as optional properties before
|
|
@@ -20,7 +20,14 @@ const readHelperSource = async (helper, importExt) => {
|
|
|
20
20
|
return { ext: "js", content: compiled };
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
+
const ROOT_TYPE_NAME = /^[\p{ID_Start}_$][\p{ID_Continue}$]*$/u;
|
|
24
|
+
const assertRootTypeName = (rootTypeName) => {
|
|
25
|
+
if (ROOT_TYPE_NAME.test(rootTypeName))
|
|
26
|
+
return;
|
|
27
|
+
throw new Error(`Invalid root type name ${JSON.stringify(rootTypeName)}: expected a TypeScript identifier. The name is emitted as \`export type <name>\` and, lowercased, used as the output filename, so anything else either fails to compile or escapes the output directory. Derive it with @amritk/helpers' \`deriveRootTypeName\`, which always produces one.`);
|
|
28
|
+
};
|
|
23
29
|
const buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict, helpersMode = "package", helpersImportPrefix = "./", readonly = false, stripUnknown = false, typeSuffix = "", importExt = "js", caseInsensitive = false, schemas) => {
|
|
30
|
+
assertRootTypeName(rootTypeName);
|
|
24
31
|
const files = [];
|
|
25
32
|
const usedHelpers = /* @__PURE__ */ new Set();
|
|
26
33
|
const foldedSchema = foldNullable(rootSchema);
|
|
@@ -151,7 +151,25 @@ const generateFallbackValue = (_, propSchema, useRefImports, suffix) => {
|
|
|
151
151
|
return getDefaultValue(propSchema);
|
|
152
152
|
};
|
|
153
153
|
const PROTOTYPE_MEMBER_NAMES = new Set(Object.getOwnPropertyNames(Object.prototype));
|
|
154
|
-
const declaresPrototypeMemberProperty = (schema
|
|
154
|
+
const declaresPrototypeMemberProperty = (schema, depth = 4) => {
|
|
155
|
+
if (!isSchemaObject(schema) || depth < 0)
|
|
156
|
+
return false;
|
|
157
|
+
if (hasProperties(schema) && Object.keys(schema.properties).some((key) => PROTOTYPE_MEMBER_NAMES.has(key)))
|
|
158
|
+
return true;
|
|
159
|
+
const record = schema;
|
|
160
|
+
const children = [];
|
|
161
|
+
if (hasProperties(schema))
|
|
162
|
+
children.push(...Object.values(schema.properties));
|
|
163
|
+
const items = record["items"];
|
|
164
|
+
if (Array.isArray(items))
|
|
165
|
+
children.push(...items);
|
|
166
|
+
else if (items !== void 0)
|
|
167
|
+
children.push(items);
|
|
168
|
+
const prefixItems = record["prefixItems"];
|
|
169
|
+
if (Array.isArray(prefixItems))
|
|
170
|
+
children.push(...prefixItems);
|
|
171
|
+
return children.some((child) => declaresPrototypeMemberProperty(child, depth - 1));
|
|
172
|
+
};
|
|
155
173
|
const generateFallbackObject = (schema, useRefImports, typeName, suffix, subTypeNames) => {
|
|
156
174
|
if (!hasProperties(schema)) {
|
|
157
175
|
return `{} as ${typeName}`;
|
|
@@ -183,33 +201,11 @@ const generateFallbackObject = (schema, useRefImports, typeName, suffix, subType
|
|
|
183
201
|
}
|
|
184
202
|
}
|
|
185
203
|
result += "\n }";
|
|
186
|
-
return declaresPrototypeMemberProperty(schema) ? `${result} as ${typeName}` : result;
|
|
204
|
+
return declaresPrototypeMemberProperty(schema) ? `${result} as unknown as ${typeName}` : result;
|
|
187
205
|
};
|
|
188
206
|
const scalarDefaultLiteral = (schema) => {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (hasConst(schema))
|
|
192
|
-
return JSON.stringify(schema.const);
|
|
193
|
-
if (hasEnum(schema) && schema.enum.length > 0)
|
|
194
|
-
return JSON.stringify(schema.enum[0]);
|
|
195
|
-
if (hasType(schema)) {
|
|
196
|
-
switch (schema.type) {
|
|
197
|
-
case "string":
|
|
198
|
-
return '""';
|
|
199
|
-
case "number":
|
|
200
|
-
case "integer":
|
|
201
|
-
return "0";
|
|
202
|
-
case "boolean":
|
|
203
|
-
return "false";
|
|
204
|
-
case "null":
|
|
205
|
-
return "null";
|
|
206
|
-
case "array":
|
|
207
|
-
return "[]";
|
|
208
|
-
case "object":
|
|
209
|
-
return "{}";
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return "{}";
|
|
207
|
+
const value = getDefaultValue(schema);
|
|
208
|
+
return value === "undefined" ? "{}" : value;
|
|
213
209
|
};
|
|
214
210
|
const generateRefUnionDispatch = (functionName, typeName, schema, branches, strict, ctx) => {
|
|
215
211
|
const rootSchema = ctx.rootSchema;
|
|
@@ -552,7 +548,7 @@ const exactKeyCountOf = (schema) => {
|
|
|
552
548
|
return null;
|
|
553
549
|
return declaredCount;
|
|
554
550
|
};
|
|
555
|
-
const shouldCacheVariable = (propSchema
|
|
551
|
+
const shouldCacheVariable = (propSchema) => {
|
|
556
552
|
return isSchemaObject(propSchema);
|
|
557
553
|
};
|
|
558
554
|
const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict, exported = true, stripUnknown = false, rootSchema, reservedNames = NO_RESERVED_NAMES, caseInsensitive = false, shapeValidatorSource) => {
|
|
@@ -592,9 +588,11 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
592
588
|
}
|
|
593
589
|
const strictKeys = hasStrictKeys(schema);
|
|
594
590
|
const stripKeys = strictKeys || stripUnknown;
|
|
595
|
-
const
|
|
591
|
+
const rejectsUnknownKeys = strict && isSchemaObject(schema) && hasAdditionalProperties(schema) && schema.additionalProperties === false && !("patternProperties" in schema) || false;
|
|
592
|
+
const guardKeys = stripKeys || rejectsUnknownKeys;
|
|
593
|
+
const exactKeyCount = guardKeys ? exactKeyCountOf(schema) : null;
|
|
596
594
|
const strictKeyCheck = unknownKeyCheck(propertyKeys, `_knownKeys${typeName}`);
|
|
597
|
-
if (
|
|
595
|
+
if (guardKeys && exactKeyCount === null) {
|
|
598
596
|
for (const declaration of strictKeyCheck.declarations) {
|
|
599
597
|
preamble.push(`${declaration};`);
|
|
600
598
|
}
|
|
@@ -602,7 +600,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
602
600
|
for (const _k in input) if (${strictKeyCheck.isUnknown("_k")}) return false;
|
|
603
601
|
return true;
|
|
604
602
|
};`);
|
|
605
|
-
} else if (
|
|
603
|
+
} else if (rejectsUnknownKeys) {
|
|
606
604
|
for (const declaration of strictKeyCheck.declarations) {
|
|
607
605
|
preamble.push(`${declaration};`);
|
|
608
606
|
}
|
|
@@ -661,7 +659,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
661
659
|
fastPathAccessorChecks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
|
|
662
660
|
}
|
|
663
661
|
}
|
|
664
|
-
if (
|
|
662
|
+
if (guardKeys) {
|
|
665
663
|
fastPathChecks.push(exactKeyCount !== null ? `Object.getPrototypeOf(input) === Object.prototype && Object.keys(input).length === ${exactKeyCount}` : `_hasOnlyKnownKeys${typeName}(input)`);
|
|
666
664
|
}
|
|
667
665
|
const deepGuard = canFastPath && fastPathChecks.length > 0 ? fastPathChecks.join(" && ") : null;
|
|
@@ -710,7 +708,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
710
708
|
const shallowGuard = canShallowGuard && shallowChecks.length > 0 ? shallowChecks.join(" && ") : null;
|
|
711
709
|
const varDeclLines = [];
|
|
712
710
|
for (const { key, varName, propSchema } of propInfo) {
|
|
713
|
-
if (shouldCacheVariable(propSchema
|
|
711
|
+
if (shouldCacheVariable(propSchema)) {
|
|
714
712
|
varDeclLines.push(` const ${varName} = ${safeAccessor("input", key)};`);
|
|
715
713
|
}
|
|
716
714
|
}
|
|
@@ -740,7 +738,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
740
738
|
}
|
|
741
739
|
}
|
|
742
740
|
for (const { key, varName, isRequired, propSchema } of propInfo) {
|
|
743
|
-
const shouldCache = shouldCacheVariable(propSchema
|
|
741
|
+
const shouldCache = shouldCacheVariable(propSchema);
|
|
744
742
|
const accessor = shouldCache ? varName : safeAccessor("input", key);
|
|
745
743
|
const subName = subTypeNames.get(key);
|
|
746
744
|
if (subName) {
|
|
@@ -848,12 +846,12 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
848
846
|
}
|
|
849
847
|
continue;
|
|
850
848
|
}
|
|
851
|
-
const accessor = shouldCacheVariable(propSchema
|
|
849
|
+
const accessor = shouldCacheVariable(propSchema) ? varName : safeAccessor("input", key);
|
|
852
850
|
fields.push(isRequired ? ` ${safeLiteralKey(key)}: ${accessor},` : ` ...(${accessor} !== undefined && { ${safeLiteralKey(key)}: ${accessor} }),`);
|
|
853
851
|
}
|
|
854
852
|
lines2.push(` if (${deepGuardExpr}) return {`);
|
|
855
853
|
lines2.push(fields.join("\n"));
|
|
856
|
-
lines2.push(` } as ${typeName};`);
|
|
854
|
+
lines2.push(` } as unknown as ${typeName};`);
|
|
857
855
|
};
|
|
858
856
|
const lines = [];
|
|
859
857
|
lines.push(`${exportPrefix}const ${functionName} = (input: unknown): ${typeName} => {`);
|
|
@@ -903,7 +901,7 @@ const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnin
|
|
|
903
901
|
for (const assertionLine of assertionLines) {
|
|
904
902
|
lines.push(assertionLine);
|
|
905
903
|
}
|
|
906
|
-
if (
|
|
904
|
+
if (rejectsUnknownKeys) {
|
|
907
905
|
lines.push(` for (const _k in input) {`);
|
|
908
906
|
lines.push(` if (${strictKeyCheck.isUnknown("_k")}) throw new Error(\`[${typeName}] unknown property "\${_k}"\`);`);
|
|
909
907
|
lines.push(` }`);
|
|
@@ -1280,6 +1278,7 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1280
1278
|
stripUnknown,
|
|
1281
1279
|
...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {}
|
|
1282
1280
|
};
|
|
1281
|
+
const copyLoopContext = { ...context, subParsers: false };
|
|
1283
1282
|
if (!options?.isRoot && typeName === `Schema${suffix}`) {
|
|
1284
1283
|
return generateSchemaObjectParser(typeName);
|
|
1285
1284
|
}
|
|
@@ -1296,7 +1295,7 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1296
1295
|
});
|
|
1297
1296
|
}
|
|
1298
1297
|
if (hasProperties(schema) && isSchemaObject(schema) && "patternProperties" in schema) {
|
|
1299
|
-
return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown, caseInsensitive,
|
|
1298
|
+
return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown, caseInsensitive, copyLoopContext);
|
|
1300
1299
|
}
|
|
1301
1300
|
if (strict && isSchemaObject(schema) && Array.isArray(schema.type) && schema.type.includes("null")) {
|
|
1302
1301
|
return generateNonObjectParser(typeName, schema, strict, {
|
|
@@ -1342,14 +1341,14 @@ const selectParserStrategy = (schema, typeName, options) => {
|
|
|
1342
1341
|
});
|
|
1343
1342
|
}
|
|
1344
1343
|
if ("patternProperties" in schema) {
|
|
1345
|
-
return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict,
|
|
1344
|
+
return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict, copyLoopContext);
|
|
1346
1345
|
}
|
|
1347
1346
|
if ("additionalProperties" in schema && !hasProperties(schema)) {
|
|
1348
1347
|
const additionalProps = schema.additionalProperties;
|
|
1349
1348
|
if (additionalProps === true || additionalProps === false) {
|
|
1350
1349
|
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
1351
1350
|
}
|
|
1352
|
-
return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict,
|
|
1351
|
+
return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict, copyLoopContext);
|
|
1353
1352
|
}
|
|
1354
1353
|
if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
|
|
1355
1354
|
return generateEmptyObjectParser(typeName, strict, schema, context);
|
|
@@ -3,6 +3,8 @@ import { getMjstInstanceOf, getMjstPrimitive } from "@amritk/helpers/mjst-extens
|
|
|
3
3
|
import { multipleOfPassExpr } from "@amritk/helpers/multiple-of-check";
|
|
4
4
|
import { hasOwnCheck } from "@amritk/helpers/safe-accessor";
|
|
5
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";
|
|
6
|
+
import { maxLengthPassExpr, minLengthPassExpr } from "@amritk/helpers/string-length-check";
|
|
7
|
+
import { generateDeepEqualCheck } from "./generate-deep-equal-check.js";
|
|
6
8
|
import { generateEnumCheck } from "./generate-enum-check.js";
|
|
7
9
|
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
8
10
|
const OBJECT_KEYWORDS = [
|
|
@@ -95,10 +97,10 @@ const generateInferredChecks = (accessor, schema, type) => {
|
|
|
95
97
|
checks.push(`${regexLiteral(schema.pattern)}.test(${accessor})`);
|
|
96
98
|
}
|
|
97
99
|
if (hasMinLength(schema)) {
|
|
98
|
-
checks.push(
|
|
100
|
+
checks.push(minLengthPassExpr(accessor, schema.minLength));
|
|
99
101
|
}
|
|
100
102
|
if (hasMaxLength(schema)) {
|
|
101
|
-
checks.push(
|
|
103
|
+
checks.push(maxLengthPassExpr(accessor, schema.maxLength));
|
|
102
104
|
}
|
|
103
105
|
break;
|
|
104
106
|
case "number":
|
|
@@ -140,6 +142,23 @@ const generateSchemaChecks = (accessor, schema) => {
|
|
|
140
142
|
if (primitive) {
|
|
141
143
|
return [`typeof ${accessor} === "${primitive}"`];
|
|
142
144
|
}
|
|
145
|
+
if (hasConst(schema)) {
|
|
146
|
+
return [generateDeepEqualCheck(accessor, schema.const)];
|
|
147
|
+
}
|
|
148
|
+
if (isSchemaObject(schema) && Array.isArray(schema.type)) {
|
|
149
|
+
const branches = [];
|
|
150
|
+
for (const type of schema.type) {
|
|
151
|
+
if (typeof type !== "string")
|
|
152
|
+
continue;
|
|
153
|
+
const branch = generateSchemaChecks(accessor, { ...schema, type });
|
|
154
|
+
if (branch.length === 0)
|
|
155
|
+
continue;
|
|
156
|
+
branches.push(branch.length === 1 ? branch[0] : `(${branch.join(" && ")})`);
|
|
157
|
+
}
|
|
158
|
+
if (branches.length === 0)
|
|
159
|
+
return checks;
|
|
160
|
+
return [branches.length === 1 ? branches[0] : `(${branches.join(" || ")})`];
|
|
161
|
+
}
|
|
143
162
|
if (!hasType(schema)) {
|
|
144
163
|
const inferred = inferSchemaType(schema);
|
|
145
164
|
if (!inferred) {
|
|
@@ -158,10 +177,10 @@ const generateSchemaChecks = (accessor, schema) => {
|
|
|
158
177
|
checks.push(`${regexLiteral(schema.pattern)}.test(${accessor})`);
|
|
159
178
|
}
|
|
160
179
|
if (hasMinLength(schema)) {
|
|
161
|
-
checks.push(
|
|
180
|
+
checks.push(minLengthPassExpr(accessor, schema.minLength));
|
|
162
181
|
}
|
|
163
182
|
if (hasMaxLength(schema)) {
|
|
164
|
-
checks.push(
|
|
183
|
+
checks.push(maxLengthPassExpr(accessor, schema.maxLength));
|
|
165
184
|
}
|
|
166
185
|
break;
|
|
167
186
|
}
|
|
@@ -190,6 +209,13 @@ const generateSchemaChecks = (accessor, schema) => {
|
|
|
190
209
|
case "boolean":
|
|
191
210
|
checks.push(`typeof ${accessor} === "boolean"`);
|
|
192
211
|
break;
|
|
212
|
+
// Without this the `null` branch of a union produced NO check, which reads
|
|
213
|
+
// as "this branch matches nothing": `{ anyOf: [{ type: 'string' },
|
|
214
|
+
// { type: 'null' }] }` rejected `null` — the one value the branch exists to
|
|
215
|
+
// admit — and the coercing build dropped the property instead of keeping it.
|
|
216
|
+
case "null":
|
|
217
|
+
checks.push(`${accessor} === null`);
|
|
218
|
+
break;
|
|
193
219
|
case "array":
|
|
194
220
|
checks.push(`Array.isArray(${accessor})`);
|
|
195
221
|
if (hasMinItems(schema)) {
|
|
@@ -13,6 +13,17 @@ export type StrictAssertionContext = {
|
|
|
13
13
|
readonly suffix?: string;
|
|
14
14
|
readonly rootSchema?: Record<string, unknown>;
|
|
15
15
|
readonly stripUnknown?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the parser these assertions run inside also emits the private
|
|
18
|
+
* sub-parsers for its inline object properties (and inline-object array
|
|
19
|
+
* items). `generateObjectParser` does, so the shape of such a property is
|
|
20
|
+
* proven by the call it delegates to and asserting it here would be
|
|
21
|
+
* redundant. The pattern-property parsers do NOT — they build their result
|
|
22
|
+
* with a copy loop — so a nested object property there is proven by nobody
|
|
23
|
+
* unless these assertions prove it. Defaults to true, the historical
|
|
24
|
+
* assumption.
|
|
25
|
+
*/
|
|
26
|
+
readonly subParsers?: boolean;
|
|
16
27
|
};
|
|
17
28
|
/**
|
|
18
29
|
* Why a node needs the whole-schema backstop, or `null` when the specialized
|
|
@@ -10,8 +10,9 @@ import { generateDeepEqualCheck } from "./generate-deep-equal-check.js";
|
|
|
10
10
|
import { generateEnumCheck } from "./generate-enum-check.js";
|
|
11
11
|
import { canEnforceUnion, generateUnionCheck, getUnionBranches, isExclusiveUnion, isInlineObjectProperty, multiTypeCheck } from "./generate-type-checks.js";
|
|
12
12
|
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
13
|
-
import { everyTailItem, getPrefixItems,
|
|
13
|
+
import { everyTailItem, getPrefixItems, plainScalarItemCheck, prefixItemsCapsLength } from "./generate-validation-expression.js";
|
|
14
14
|
import { hasConstrainingRefSibling, subschemaMatchExpr } from "./subschema-match.js";
|
|
15
|
+
const shapeIsDelegated = (schema, context) => (context.subParsers ?? true) && isInlineObjectProperty(schema);
|
|
15
16
|
const matchContext = (context) => context.rootSchema ? { rootSchema: context.rootSchema } : {};
|
|
16
17
|
const TYPELESS_CONSTRAINT_KEYWORDS = [
|
|
17
18
|
"minimum",
|
|
@@ -212,11 +213,11 @@ const generateItemCheck = (schema, context) => {
|
|
|
212
213
|
const label = items.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
213
214
|
return { check: generateEnumCheck("_it", items.enum), message: `items must be one of: ${label}` };
|
|
214
215
|
}
|
|
215
|
-
const scalarCheck =
|
|
216
|
+
const scalarCheck = plainScalarItemCheck(items, "_it");
|
|
216
217
|
if (scalarCheck !== null && isSchemaObject(items) && hasType(items)) {
|
|
217
218
|
return { check: scalarCheck, message: `items expected ${typeLabel(items.type)}` };
|
|
218
219
|
}
|
|
219
|
-
if (hasRef(items) && context.useRefImports === true ||
|
|
220
|
+
if (hasRef(items) && context.useRefImports === true || shapeIsDelegated(items, context))
|
|
220
221
|
return null;
|
|
221
222
|
const match = subschemaMatchExpr("_it", items, matchContext(context));
|
|
222
223
|
if (match === null || match === "true")
|
|
@@ -232,6 +233,34 @@ const nullableObjectShapeChecks = (acc, schema, type, label, optional, context)
|
|
|
232
233
|
const presence = optional ? `${acc} !== undefined && ${acc} !== null` : `${acc} !== null`;
|
|
233
234
|
return [` if (${presence} && !(${match})) ${throwError(`${label} does not match the declared object shape`)};`];
|
|
234
235
|
};
|
|
236
|
+
const objectShapeResidual = (schema, context) => {
|
|
237
|
+
if (!isSchemaObject(schema) || !hasType(schema) || schema.type !== "object")
|
|
238
|
+
return null;
|
|
239
|
+
if (shapeIsDelegated(schema, context))
|
|
240
|
+
return null;
|
|
241
|
+
const record = schema;
|
|
242
|
+
const residual = { type: "object" };
|
|
243
|
+
if (hasProperties(schema))
|
|
244
|
+
residual["properties"] = record["properties"];
|
|
245
|
+
if (hasRequired(schema))
|
|
246
|
+
residual["required"] = record["required"];
|
|
247
|
+
if (record["additionalProperties"] === false) {
|
|
248
|
+
residual["additionalProperties"] = false;
|
|
249
|
+
if ("patternProperties" in record)
|
|
250
|
+
residual["patternProperties"] = record["patternProperties"];
|
|
251
|
+
}
|
|
252
|
+
return "properties" in residual || "required" in residual || "additionalProperties" in residual ? residual : null;
|
|
253
|
+
};
|
|
254
|
+
const declaredObjectShapeChecks = (acc, schema, label, optional, context) => {
|
|
255
|
+
const residual = objectShapeResidual(schema, context);
|
|
256
|
+
if (residual === null)
|
|
257
|
+
return [];
|
|
258
|
+
const match = subschemaMatchExpr(acc, residual, matchContext(context));
|
|
259
|
+
if (match === null || match === "true")
|
|
260
|
+
return [];
|
|
261
|
+
const presence = optional ? `${acc} !== undefined && ` : "";
|
|
262
|
+
return [` if (${presence}!(${match})) ${throwError(`${label} does not match the declared object shape`)};`];
|
|
263
|
+
};
|
|
235
264
|
const generateNotCheck = (acc, schema, label, optional, context) => {
|
|
236
265
|
if (!isSchemaObject(schema) || !("not" in schema))
|
|
237
266
|
return [];
|
|
@@ -518,6 +547,7 @@ const generatePropertyAssertion = (key, propSchema, isRequired, typeName, contex
|
|
|
518
547
|
lines.push(...generateAllOfChecks(acc, propSchema, field, !isRequired, context));
|
|
519
548
|
lines.push(...generateUnionMatchChecks(acc, propSchema, field, !isRequired, context));
|
|
520
549
|
lines.push(...generateConditionalChecks(acc, propSchema, field, context));
|
|
550
|
+
lines.push(...declaredObjectShapeChecks(acc, propSchema, field, !isRequired, context));
|
|
521
551
|
const unionBranches = getUnionBranches(propSchema);
|
|
522
552
|
if (unionBranches) {
|
|
523
553
|
if (!context.stripUnknown && canEnforceUnion(unionBranches, context.rootSchema)) {
|
|
@@ -3,10 +3,11 @@ import { multipleOfPassExpr } from "@amritk/helpers/multiple-of-check";
|
|
|
3
3
|
import { refToName } from "@amritk/helpers/ref-to-name";
|
|
4
4
|
import { resolveRef } from "@amritk/helpers/resolve-ref";
|
|
5
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, hasType, hasUniqueItems, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
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, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
|
|
7
|
+
import { maxLengthPassExpr, minLengthPassExpr } from "@amritk/helpers/string-length-check";
|
|
7
8
|
import { generateEnumCheck } from "./generate-enum-check.js";
|
|
8
9
|
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
9
|
-
import { everyTailItem, getPrefixItems,
|
|
10
|
+
import { everyTailItem, getPrefixItems, plainScalarItemCheck, prefixItemsCapsLength, singleTypeCheck } from "./generate-validation-expression.js";
|
|
10
11
|
import { hasConstrainingRefSibling, subschemaMatchExpr } from "./subschema-match.js";
|
|
11
12
|
const shapeValidatorName = (typeName) => `validate${typeName}Shape`;
|
|
12
13
|
const isInlineObjectProperty = (propSchema) => {
|
|
@@ -92,6 +93,10 @@ const CONSTRAINT_KEYWORDS = [
|
|
|
92
93
|
"minProperties",
|
|
93
94
|
"maxProperties"
|
|
94
95
|
];
|
|
96
|
+
const hasBranchSiblingConstraint = (schema) => {
|
|
97
|
+
const record = schema;
|
|
98
|
+
return "type" in record || CONSTRAINT_KEYWORDS.some((keyword) => keyword in record);
|
|
99
|
+
};
|
|
95
100
|
const multiTypeCheck = (varName, schema, options = {}) => {
|
|
96
101
|
if (!isSchemaObject(schema) || !Array.isArray(schema.type))
|
|
97
102
|
return void 0;
|
|
@@ -125,6 +130,8 @@ const generatePropertyTypeCheck = (varName, schema, useRefImports, suffix) => {
|
|
|
125
130
|
}
|
|
126
131
|
const branches = getUnionBranches(schema);
|
|
127
132
|
if (branches) {
|
|
133
|
+
if (hasBranchSiblingConstraint(schema))
|
|
134
|
+
return null;
|
|
128
135
|
return generateUnionCheck(varName, branches, useRefImports, suffix, isExclusiveUnion(schema));
|
|
129
136
|
}
|
|
130
137
|
if (hasOneOf(schema) || hasAnyOf(schema) || hasAllOf(schema) || "not" in schema) {
|
|
@@ -150,9 +157,9 @@ const generatePropertyTypeCheck = (varName, schema, useRefImports, suffix) => {
|
|
|
150
157
|
if (hasPattern(schema))
|
|
151
158
|
checks.push(`${regexLiteral(schema.pattern)}.test(${varName})`);
|
|
152
159
|
if (hasMinLength(schema))
|
|
153
|
-
checks.push(
|
|
160
|
+
checks.push(minLengthPassExpr(varName, schema.minLength));
|
|
154
161
|
if (hasMaxLength(schema))
|
|
155
|
-
checks.push(
|
|
162
|
+
checks.push(maxLengthPassExpr(varName, schema.maxLength));
|
|
156
163
|
break;
|
|
157
164
|
}
|
|
158
165
|
case "number":
|
|
@@ -186,7 +193,7 @@ const generatePropertyTypeCheck = (varName, schema, useRefImports, suffix) => {
|
|
|
186
193
|
}
|
|
187
194
|
if (hasItems(schema) && !Array.isArray(schema.items)) {
|
|
188
195
|
const items = schema.items;
|
|
189
|
-
const itemCheck =
|
|
196
|
+
const itemCheck = plainScalarItemCheck(items, "_it") ?? (isSchemaObject(items) && hasEnum(items) && items.enum.length > 0 ? generateEnumCheck("_it", items.enum) : null);
|
|
190
197
|
if (itemCheck) {
|
|
191
198
|
checks.push(everyTailItem(varName, itemCheck, schema));
|
|
192
199
|
} else if (!(isSchemaObject(items) && (hasRef(items) || isInlineObjectProperty(items)))) {
|
|
@@ -211,6 +218,8 @@ const generatePropertyTypeCheck = (varName, schema, useRefImports, suffix) => {
|
|
|
211
218
|
break;
|
|
212
219
|
}
|
|
213
220
|
case "object":
|
|
221
|
+
if (hasProperties(schema) || hasRequired(schema))
|
|
222
|
+
return null;
|
|
214
223
|
checks.push(`isObject(${varName})`);
|
|
215
224
|
if (hasMinProperties(schema))
|
|
216
225
|
checks.push(`Object.keys(${varName}).length >= ${schema.minProperties}`);
|
|
@@ -6,6 +6,16 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
|
6
6
|
* objects, arrays, unions, `$ref`s, and anything richer.
|
|
7
7
|
*/
|
|
8
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;
|
|
9
19
|
/**
|
|
10
20
|
* True when an array's `items` schema can be coerced element-by-element by the
|
|
11
21
|
* mapping slow path: single scalar types (via {@link scalarItemTypeCheck}) and
|
|
@@ -4,12 +4,14 @@ import { multipleOfPassExpr } from "@amritk/helpers/multiple-of-check";
|
|
|
4
4
|
import { resolveRef } from "@amritk/helpers/resolve-ref";
|
|
5
5
|
import { hasOwnCheck, safeAccessor } from "@amritk/helpers/safe-accessor";
|
|
6
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";
|
|
7
8
|
import { findDiscriminator } from "../helpers/find-discriminator.js";
|
|
8
9
|
import { getDefaultValue } from "../helpers/get-default-value.js";
|
|
9
10
|
import { generateDiscriminatedUnionValidation } from "./generate-discriminated-union-validation.js";
|
|
10
11
|
import { generateEnumCaseInsensitiveCoercion, generateEnumCheck } from "./generate-enum-check.js";
|
|
11
12
|
import { generateNonDiscriminatedUnionValidation } from "./generate-non-discriminated-union-validation.js";
|
|
12
13
|
import { generateSchemaChecks } from "./generate-schema-checks.js";
|
|
14
|
+
import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
|
|
13
15
|
const getInstanceCoercion = (accessor, instanceOf, defaultValue) => {
|
|
14
16
|
if (instanceOf === "Date")
|
|
15
17
|
return `((_d) => Number.isNaN(_d.getTime()) ? ${defaultValue} : _d)(new Date(${accessor} as string | number | Date))`;
|
|
@@ -33,10 +35,43 @@ const scalarItemTypeCheck = (itemSchema, accessor) => {
|
|
|
33
35
|
return null;
|
|
34
36
|
}
|
|
35
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
|
+
};
|
|
36
63
|
const isCoercibleItemSchema = (itemSchema) => {
|
|
37
64
|
if (scalarItemTypeCheck(itemSchema, "_it") !== null)
|
|
38
65
|
return true;
|
|
39
|
-
|
|
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;
|
|
40
75
|
};
|
|
41
76
|
const getPrefixItems = (schema) => {
|
|
42
77
|
if (!isSchemaObject(schema))
|
|
@@ -49,13 +84,13 @@ const getPrefixItems = (schema) => {
|
|
|
49
84
|
const everyTailItem = (accessor, itemCheck, schema) => {
|
|
50
85
|
const prefix = isSchemaObject(schema) ? schema["prefixItems"] : void 0;
|
|
51
86
|
const tailStart = Array.isArray(prefix) ? prefix.length : 0;
|
|
52
|
-
return tailStart === 0 ? `${accessor}.every((_it) => ${itemCheck})` : `${accessor}.every((_it, _ix) => _ix < ${tailStart} || (${itemCheck}))`;
|
|
87
|
+
return tailStart === 0 ? `${accessor}.every((_it: unknown) => ${itemCheck})` : `${accessor}.every((_it: unknown, _ix: number) => _ix < ${tailStart} || (${itemCheck}))`;
|
|
53
88
|
};
|
|
54
89
|
const prefixItemsCapsLength = (schema) => {
|
|
55
90
|
if (!isSchemaObject(schema))
|
|
56
91
|
return false;
|
|
57
92
|
const s = schema;
|
|
58
|
-
return s["items"] === false || s["additionalItems"] === false;
|
|
93
|
+
return (Object.hasOwn(s, "items") ? s["items"] : void 0) === false || (Object.hasOwn(s, "additionalItems") ? s["additionalItems"] : void 0) === false;
|
|
59
94
|
};
|
|
60
95
|
const singleTypeCheck = (accessor, type) => {
|
|
61
96
|
switch (type) {
|
|
@@ -82,25 +117,55 @@ const getTypeArray = (schema) => {
|
|
|
82
117
|
return null;
|
|
83
118
|
return schema.type;
|
|
84
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
|
+
};
|
|
85
144
|
const getTypeCoercion = (accessor, schema, defaultValue) => {
|
|
86
145
|
if (!hasType(schema)) {
|
|
87
146
|
return null;
|
|
88
147
|
}
|
|
89
148
|
switch (schema.type) {
|
|
90
|
-
case "string":
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
149
|
+
case "string": {
|
|
150
|
+
const coerced = `String(${accessor})`;
|
|
151
|
+
const bounds = stringBoundChecks("_s", schema);
|
|
152
|
+
if (bounds.length === 0)
|
|
153
|
+
return coerced;
|
|
154
|
+
return `((_s) => ${bounds.join(" && ")} ? _s : ${defaultValue})(${coerced})`;
|
|
95
155
|
}
|
|
156
|
+
case "number":
|
|
96
157
|
case "integer": {
|
|
158
|
+
const wellFormed = schema.type === "integer" ? "Number.isInteger" : "Number.isFinite";
|
|
159
|
+
const bounds = numberBoundChecks("_n", schema);
|
|
97
160
|
const n = `Number(${accessor})`;
|
|
98
|
-
|
|
161
|
+
if (bounds.length === 0)
|
|
162
|
+
return `(${wellFormed}(${n}) ? ${n} : ${defaultValue})`;
|
|
163
|
+
return `((_n) => ${[`${wellFormed}(_n)`, ...bounds].join(" && ")} ? _n : ${defaultValue})(${n})`;
|
|
99
164
|
}
|
|
100
165
|
case "boolean":
|
|
101
166
|
return `Boolean(${accessor})`;
|
|
102
167
|
case "array":
|
|
103
|
-
return
|
|
168
|
+
return defaultValue;
|
|
104
169
|
case "object":
|
|
105
170
|
return `typeof ${accessor} === "object" && ${accessor} !== null ? ${accessor} : ${defaultValue}`;
|
|
106
171
|
default:
|
|
@@ -165,9 +230,9 @@ const generateValidationExpression = (key, schema, defaultValue, isRequired, roo
|
|
|
165
230
|
for (let i = 1; i < allChecks.length; i++) {
|
|
166
231
|
combinedCheck2 += " && " + allChecks[i];
|
|
167
232
|
}
|
|
168
|
-
return `${combinedCheck2} ? ${accessor} : ${defaultValue}`;
|
|
233
|
+
return `${combinedCheck2} ? ${accessor} : ${isRequired ? defaultValue : "undefined"}`;
|
|
169
234
|
}
|
|
170
|
-
return `${accessor} ?? ${defaultValue}
|
|
235
|
+
return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
|
|
171
236
|
}
|
|
172
237
|
if ("not" in schema && schema.not) {
|
|
173
238
|
const notChecks = generateSchemaChecks(accessor, schema.not);
|
|
@@ -176,7 +241,7 @@ const generateValidationExpression = (key, schema, defaultValue, isRequired, roo
|
|
|
176
241
|
for (let i = 1; i < notChecks.length; i++) {
|
|
177
242
|
combinedNotCheck += " && " + notChecks[i];
|
|
178
243
|
}
|
|
179
|
-
return `!(${combinedNotCheck}) ? ${accessor} : ${defaultValue}`;
|
|
244
|
+
return `!(${combinedNotCheck}) ? ${accessor} : ${isRequired ? defaultValue : "undefined"}`;
|
|
180
245
|
}
|
|
181
246
|
}
|
|
182
247
|
if (hasConst(schema)) {
|
|
@@ -192,7 +257,7 @@ const generateValidationExpression = (key, schema, defaultValue, isRequired, roo
|
|
|
192
257
|
}
|
|
193
258
|
}
|
|
194
259
|
if (!hasType(schema) && !hasEnum(schema)) {
|
|
195
|
-
return `${accessor} ?? ${defaultValue}
|
|
260
|
+
return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
|
|
196
261
|
}
|
|
197
262
|
const prefixItems = getPrefixItems(schema);
|
|
198
263
|
if (hasType(schema) && schema.type === "array" && prefixItems) {
|
|
@@ -214,10 +279,10 @@ const generateValidationExpression = (key, schema, defaultValue, isRequired, roo
|
|
|
214
279
|
checks.push(`${regexLiteral(schema.pattern)}.test(${accessor})`);
|
|
215
280
|
}
|
|
216
281
|
if (hasMinLength(schema)) {
|
|
217
|
-
checks.push(
|
|
282
|
+
checks.push(minLengthPassExpr(accessor, schema.minLength));
|
|
218
283
|
}
|
|
219
284
|
if (hasMaxLength(schema)) {
|
|
220
|
-
checks.push(
|
|
285
|
+
checks.push(maxLengthPassExpr(accessor, schema.maxLength));
|
|
221
286
|
}
|
|
222
287
|
break;
|
|
223
288
|
}
|
|
@@ -259,7 +324,7 @@ const generateValidationExpression = (key, schema, defaultValue, isRequired, roo
|
|
|
259
324
|
checks.push(`${accessor}.length <= ${schema.maxItems}`);
|
|
260
325
|
}
|
|
261
326
|
if (hasUniqueItems(schema) && schema.uniqueItems === true) {
|
|
262
|
-
checks.push(
|
|
327
|
+
checks.push(generateUniqueItemsCheck(accessor, schema));
|
|
263
328
|
}
|
|
264
329
|
break;
|
|
265
330
|
}
|
|
@@ -335,6 +400,7 @@ export {
|
|
|
335
400
|
generateValidationExpression,
|
|
336
401
|
getPrefixItems,
|
|
337
402
|
isCoercibleItemSchema,
|
|
403
|
+
plainScalarItemCheck,
|
|
338
404
|
prefixItemsCapsLength,
|
|
339
405
|
scalarItemTypeCheck,
|
|
340
406
|
singleTypeCheck
|
|
@@ -73,7 +73,7 @@ const ANNOTATION_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
73
73
|
const depthOf = (ctx) => ctx.depth ?? 0;
|
|
74
74
|
const nest = (ctx) => ({ ...ctx, depth: depthOf(ctx) + 1 });
|
|
75
75
|
const isObjectExpr = (acc) => `typeof ${acc} === "object" && ${acc} !== null && !Array.isArray(${acc})`;
|
|
76
|
-
const recordOf = (acc) => `(${acc} as Record<string,
|
|
76
|
+
const recordOf = (acc) => `(${acc} as Record<string, any>)`;
|
|
77
77
|
const patternEntriesOf = (schema) => {
|
|
78
78
|
const source = schema["patternProperties"];
|
|
79
79
|
if (typeof source !== "object" || source === null || Array.isArray(source))
|
|
@@ -118,7 +118,7 @@ const tupleShape = (schema) => {
|
|
|
118
118
|
const arrayConstraints = (acc, schema, ctx) => {
|
|
119
119
|
const c = [];
|
|
120
120
|
const d = depthOf(ctx);
|
|
121
|
-
const elements = `(${acc} as
|
|
121
|
+
const elements = `(${acc} as any[])`;
|
|
122
122
|
if (hasMinItems(schema))
|
|
123
123
|
c.push(`${acc}.length >= ${schema.minItems}`);
|
|
124
124
|
if (hasMaxItems(schema))
|
|
@@ -182,7 +182,7 @@ const containsConstraint = (acc, schema, ctx) => {
|
|
|
182
182
|
const match = subschemaMatchExpr(el, s["contains"], nest(ctx));
|
|
183
183
|
if (match === null)
|
|
184
184
|
return null;
|
|
185
|
-
const count = `(${acc} as
|
|
185
|
+
const count = `(${acc} as any[]).filter((${el}) => ${match}).length`;
|
|
186
186
|
const bounds = [];
|
|
187
187
|
if (min > 0)
|
|
188
188
|
bounds.push(`${count} >= ${min}`);
|
|
@@ -52,7 +52,7 @@ const ownItemCoverage = (schema, indexVar, ignoreOwnUnevaluated) => {
|
|
|
52
52
|
const containsCoverage = (schema, indexVar, acc, ctx, match) => {
|
|
53
53
|
if (!("contains" in schema))
|
|
54
54
|
return NONE;
|
|
55
|
-
const itemMatch = match(`(${acc} as
|
|
55
|
+
const itemMatch = match(`(${acc} as any[])[${indexVar}]`, schema["contains"], nest(ctx));
|
|
56
56
|
if (itemMatch === null)
|
|
57
57
|
return null;
|
|
58
58
|
if (itemMatch === "true")
|
|
@@ -186,7 +186,7 @@ const unevaluatedItemsExpr = (acc, schema, ctx, match) => {
|
|
|
186
186
|
return null;
|
|
187
187
|
if (coverage.all)
|
|
188
188
|
return void 0;
|
|
189
|
-
const elements = `(${acc} as
|
|
189
|
+
const elements = `(${acc} as any[])`;
|
|
190
190
|
const covered = coverage.terms.length > 0 ? orJoin(coverage.terms) : null;
|
|
191
191
|
if (unevaluated === false) {
|
|
192
192
|
if (covered === null)
|
|
@@ -7,17 +7,17 @@ const collectImports = (schema, options) => {
|
|
|
7
7
|
const typesOnly = options?.typesOnly === true;
|
|
8
8
|
const importExt = options?.importExt ?? "js";
|
|
9
9
|
const importMap = /* @__PURE__ */ new Map();
|
|
10
|
-
for (const [filename, typeName] of collectImportTargets(schema, options)) {
|
|
10
|
+
for (const [filename, { typeName, typeOnly }] of collectImportTargets(schema, options)) {
|
|
11
11
|
const importPath = getImportPathForFilename(filename, importExt);
|
|
12
|
-
const importStatement = typesOnly ? `import type { ${typeName} } from '${importPath}';` : `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
|
|
12
|
+
const importStatement = typesOnly || typeOnly ? `import type { ${typeName} } from '${importPath}';` : `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
|
|
13
13
|
importMap.set(filename, importStatement);
|
|
14
14
|
}
|
|
15
15
|
return Array.from(importMap.values()).sort();
|
|
16
16
|
};
|
|
17
17
|
const collectImportTypeNames = (schema, options) => {
|
|
18
18
|
const names = /* @__PURE__ */ new Set();
|
|
19
|
-
for (const [,
|
|
20
|
-
names.add(typeName);
|
|
19
|
+
for (const [, target] of collectImportTargets(schema, options))
|
|
20
|
+
names.add(target.typeName);
|
|
21
21
|
return names;
|
|
22
22
|
};
|
|
23
23
|
const collectImportTargets = (schema, options) => {
|
|
@@ -25,6 +25,8 @@ const collectImportTargets = (schema, options) => {
|
|
|
25
25
|
const rootSchema = options?.rootSchema;
|
|
26
26
|
const typeSuffix = options?.typeSuffix;
|
|
27
27
|
const refs = /* @__PURE__ */ new Set();
|
|
28
|
+
const valueRefs = /* @__PURE__ */ new Set();
|
|
29
|
+
let typeOnlyDepth = 0;
|
|
28
30
|
const collectRefsFromValue = (value) => {
|
|
29
31
|
if (typeof value !== "object" || value === null) {
|
|
30
32
|
return;
|
|
@@ -43,21 +45,34 @@ const collectImportTargets = (schema, options) => {
|
|
|
43
45
|
const isPropertyFragment = isUri && ref.includes("#/properties/");
|
|
44
46
|
if (isInternal || isUri && !isPropertyFragment) {
|
|
45
47
|
refs.add(ref);
|
|
48
|
+
if (typeOnlyDepth === 0)
|
|
49
|
+
valueRefs.add(ref);
|
|
46
50
|
}
|
|
47
51
|
return;
|
|
48
52
|
}
|
|
53
|
+
for (const keyword of ["prefixItems", "items"]) {
|
|
54
|
+
const positions = Object.hasOwn(record, keyword) ? record[keyword] : void 0;
|
|
55
|
+
if (!Array.isArray(positions))
|
|
56
|
+
continue;
|
|
57
|
+
typeOnlyDepth++;
|
|
58
|
+
for (const item of positions) {
|
|
59
|
+
collectRefsFromValue(item);
|
|
60
|
+
}
|
|
61
|
+
if (Object.hasOwn(record, "additionalItems"))
|
|
62
|
+
collectRefsFromValue(record["additionalItems"]);
|
|
63
|
+
typeOnlyDepth--;
|
|
64
|
+
}
|
|
49
65
|
if (hasAdditionalProperties(record) && hasRef(record.additionalProperties)) {
|
|
50
66
|
collectRefsFromValue(record.additionalProperties);
|
|
51
67
|
return;
|
|
52
68
|
}
|
|
53
|
-
if (record.type === "array" && hasItems(record) && hasRef(record.items)) {
|
|
69
|
+
if (record.type === "array" && hasItems(record) && !Array.isArray(record.items) && hasRef(record.items)) {
|
|
54
70
|
collectRefsFromValue(record.items);
|
|
55
71
|
return;
|
|
56
72
|
}
|
|
57
|
-
if ("properties"
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
collectRefsFromValue(props[key]);
|
|
73
|
+
if (Object.hasOwn(record, "properties") && typeof record.properties === "object" && record.properties !== null) {
|
|
74
|
+
for (const value2 of Object.values(record.properties)) {
|
|
75
|
+
collectRefsFromValue(value2);
|
|
61
76
|
}
|
|
62
77
|
}
|
|
63
78
|
if (hasOneOf(record)) {
|
|
@@ -75,22 +90,24 @@ const collectImportTargets = (schema, options) => {
|
|
|
75
90
|
collectRefsFromValue(item);
|
|
76
91
|
}
|
|
77
92
|
}
|
|
78
|
-
if (hasItems(record)) {
|
|
93
|
+
if (hasItems(record) && !Array.isArray(record.items)) {
|
|
79
94
|
collectRefsFromValue(record.items);
|
|
80
95
|
}
|
|
81
96
|
if (hasAdditionalProperties(record)) {
|
|
82
97
|
collectRefsFromValue(record.additionalProperties);
|
|
83
98
|
}
|
|
84
|
-
if ("patternProperties"
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
collectRefsFromValue(patternProps[key]);
|
|
99
|
+
if (Object.hasOwn(record, "patternProperties") && typeof record.patternProperties === "object" && record.patternProperties !== null) {
|
|
100
|
+
for (const value2 of Object.values(record.patternProperties)) {
|
|
101
|
+
collectRefsFromValue(value2);
|
|
88
102
|
}
|
|
89
103
|
}
|
|
90
|
-
if ("
|
|
104
|
+
if (Object.hasOwn(record, "if")) {
|
|
105
|
+
collectRefsFromValue(record.if);
|
|
106
|
+
}
|
|
107
|
+
if (Object.hasOwn(record, "then")) {
|
|
91
108
|
collectRefsFromValue(record.then);
|
|
92
109
|
}
|
|
93
|
-
if ("else"
|
|
110
|
+
if (Object.hasOwn(record, "else")) {
|
|
94
111
|
collectRefsFromValue(record.else);
|
|
95
112
|
}
|
|
96
113
|
};
|
|
@@ -101,26 +118,39 @@ const collectImportTargets = (schema, options) => {
|
|
|
101
118
|
const isPropertyFragment = isUri && ref.includes("#/properties/");
|
|
102
119
|
if (isInternal || isUri && !isPropertyFragment) {
|
|
103
120
|
refs.add(ref);
|
|
121
|
+
valueRefs.add(ref);
|
|
104
122
|
}
|
|
105
123
|
}
|
|
106
|
-
if (typeof schema === "object" && schema !== null && "properties"
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
collectRefsFromValue(properties[key]);
|
|
124
|
+
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "properties") && typeof schema.properties === "object" && schema.properties !== null) {
|
|
125
|
+
for (const value of Object.values(schema.properties)) {
|
|
126
|
+
collectRefsFromValue(value);
|
|
110
127
|
}
|
|
111
128
|
}
|
|
112
|
-
if (typeof schema === "object" && schema !== null && "additionalProperties"
|
|
129
|
+
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "additionalProperties")) {
|
|
113
130
|
collectRefsFromValue(schema.additionalProperties);
|
|
114
131
|
}
|
|
115
|
-
if (typeof schema === "object" && schema !== null && "patternProperties"
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
collectRefsFromValue(patternProps[key]);
|
|
132
|
+
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "patternProperties") && typeof schema.patternProperties === "object" && schema.patternProperties !== null) {
|
|
133
|
+
for (const value of Object.values(schema.patternProperties)) {
|
|
134
|
+
collectRefsFromValue(value);
|
|
119
135
|
}
|
|
120
136
|
}
|
|
121
|
-
if (typeof schema === "object" && schema !== null && hasItems(schema)) {
|
|
137
|
+
if (typeof schema === "object" && schema !== null && hasItems(schema) && !Array.isArray(schema.items)) {
|
|
122
138
|
collectRefsFromValue(schema.items);
|
|
123
139
|
}
|
|
140
|
+
if (typeof schema === "object" && schema !== null) {
|
|
141
|
+
const record = schema;
|
|
142
|
+
for (const keyword of ["prefixItems", "items"]) {
|
|
143
|
+
const positions = Object.hasOwn(record, keyword) ? record[keyword] : void 0;
|
|
144
|
+
if (!Array.isArray(positions))
|
|
145
|
+
continue;
|
|
146
|
+
typeOnlyDepth++;
|
|
147
|
+
for (const item of positions)
|
|
148
|
+
collectRefsFromValue(item);
|
|
149
|
+
if (Object.hasOwn(record, "additionalItems"))
|
|
150
|
+
collectRefsFromValue(record["additionalItems"]);
|
|
151
|
+
typeOnlyDepth--;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
124
154
|
if (typeof schema === "object" && schema !== null && hasOneOf(schema)) {
|
|
125
155
|
for (const item of schema.oneOf) {
|
|
126
156
|
collectRefsFromValue(item);
|
|
@@ -136,13 +166,13 @@ const collectImportTargets = (schema, options) => {
|
|
|
136
166
|
collectRefsFromValue(item);
|
|
137
167
|
}
|
|
138
168
|
}
|
|
139
|
-
if (typeof schema === "object" && schema !== null && "if"
|
|
169
|
+
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "if")) {
|
|
140
170
|
collectRefsFromValue(schema.if);
|
|
141
171
|
}
|
|
142
|
-
if (typeof schema === "object" && schema !== null && "then"
|
|
172
|
+
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "then")) {
|
|
143
173
|
collectRefsFromValue(schema.then);
|
|
144
174
|
}
|
|
145
|
-
if (typeof schema === "object" && schema !== null && "else"
|
|
175
|
+
if (typeof schema === "object" && schema !== null && Object.hasOwn(schema, "else")) {
|
|
146
176
|
collectRefsFromValue(schema.else);
|
|
147
177
|
}
|
|
148
178
|
const targets = /* @__PURE__ */ new Map();
|
|
@@ -156,7 +186,8 @@ const collectImportTargets = (schema, options) => {
|
|
|
156
186
|
if (selfFilename !== void 0 && filename === selfFilename) {
|
|
157
187
|
continue;
|
|
158
188
|
}
|
|
159
|
-
targets.
|
|
189
|
+
const typeOnly = !valueRefs.has(ref) && (targets.get(filename)?.typeOnly ?? true);
|
|
190
|
+
targets.set(filename, { typeName, typeOnly });
|
|
160
191
|
}
|
|
161
192
|
return targets;
|
|
162
193
|
};
|
|
@@ -26,8 +26,8 @@ const defaultMatchesType = (value, schema) => {
|
|
|
26
26
|
const MAX_FALLBACK_ITEMS = 32;
|
|
27
27
|
const arrayFallback = (schema) => {
|
|
28
28
|
const record = schema;
|
|
29
|
-
const positions = Array.isArray(record["prefixItems"]) ? record["prefixItems"] : Array.isArray(record["items"]) ? record["items"] : [];
|
|
30
|
-
const tail = Array.isArray(record["items"]) ? record["additionalItems"] : record["items"];
|
|
29
|
+
const positions = Array.isArray(Object.hasOwn(record, "prefixItems") ? record["prefixItems"] : void 0) ? Object.hasOwn(record, "prefixItems") ? record["prefixItems"] : void 0 : Array.isArray(Object.hasOwn(record, "items") ? record["items"] : void 0) ? Object.hasOwn(record, "items") ? record["items"] : void 0 : [];
|
|
30
|
+
const tail = Array.isArray(Object.hasOwn(record, "items") ? record["items"] : void 0) ? Object.hasOwn(record, "additionalItems") ? record["additionalItems"] : void 0 : Object.hasOwn(record, "items") ? record["items"] : void 0;
|
|
31
31
|
const minItems = typeof record["minItems"] === "number" ? record["minItems"] : 0;
|
|
32
32
|
const wanted = minItems > MAX_FALLBACK_ITEMS ? Math.min(minItems, positions.length) : minItems;
|
|
33
33
|
if (wanted === 0)
|
|
@@ -44,6 +44,40 @@ const arrayFallback = (schema) => {
|
|
|
44
44
|
}
|
|
45
45
|
return `[${elements.join(", ")}]`;
|
|
46
46
|
};
|
|
47
|
+
const MAX_FALLBACK_LENGTH = 256;
|
|
48
|
+
const stringFallback = (schema) => {
|
|
49
|
+
if (hasPattern(schema)) {
|
|
50
|
+
const patternDefault = generateDefaultFromPattern(schema.pattern);
|
|
51
|
+
if (patternDefault)
|
|
52
|
+
return patternDefault;
|
|
53
|
+
return '""';
|
|
54
|
+
}
|
|
55
|
+
const minLength = schema["minLength"];
|
|
56
|
+
if (typeof minLength !== "number" || !(minLength > 0))
|
|
57
|
+
return '""';
|
|
58
|
+
return JSON.stringify("x".repeat(Math.min(minLength, MAX_FALLBACK_LENGTH)));
|
|
59
|
+
};
|
|
60
|
+
const finiteKeyword = (schema, keyword) => {
|
|
61
|
+
const value = schema[keyword];
|
|
62
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
63
|
+
};
|
|
64
|
+
const numberFallback = (schema, integer) => {
|
|
65
|
+
const minimum = finiteKeyword(schema, "minimum");
|
|
66
|
+
const maximum = finiteKeyword(schema, "maximum");
|
|
67
|
+
const exclusiveMinimum = finiteKeyword(schema, "exclusiveMinimum");
|
|
68
|
+
const exclusiveMaximum = finiteKeyword(schema, "exclusiveMaximum");
|
|
69
|
+
const multipleOf = finiteKeyword(schema, "multipleOf");
|
|
70
|
+
const low = Math.max(minimum ?? -Infinity, exclusiveMinimum !== void 0 ? exclusiveMinimum + 1 : -Infinity);
|
|
71
|
+
const high = Math.min(maximum ?? Infinity, exclusiveMaximum !== void 0 ? exclusiveMaximum - 1 : Infinity);
|
|
72
|
+
let candidate = Math.min(Math.max(0, low), high);
|
|
73
|
+
if (integer)
|
|
74
|
+
candidate = Math.ceil(candidate);
|
|
75
|
+
if (multipleOf !== void 0 && multipleOf > 0 && candidate !== 0) {
|
|
76
|
+
candidate = Math.ceil(candidate / multipleOf) * multipleOf;
|
|
77
|
+
}
|
|
78
|
+
const satisfies = Number.isFinite(candidate) && (minimum === void 0 || candidate >= minimum) && (maximum === void 0 || candidate <= maximum) && (exclusiveMinimum === void 0 || candidate > exclusiveMinimum) && (exclusiveMaximum === void 0 || candidate < exclusiveMaximum) && (!integer || Number.isInteger(candidate)) && (multipleOf === void 0 || multipleOf <= 0 || Number.isInteger(candidate / multipleOf));
|
|
79
|
+
return satisfies ? String(candidate) : "0";
|
|
80
|
+
};
|
|
47
81
|
const getDefaultValue = (schema) => {
|
|
48
82
|
if (!isSchemaObject(schema)) {
|
|
49
83
|
return "undefined";
|
|
@@ -89,18 +123,11 @@ const getDefaultValue = (schema) => {
|
|
|
89
123
|
return "undefined";
|
|
90
124
|
}
|
|
91
125
|
switch (schema.type) {
|
|
92
|
-
case "string":
|
|
93
|
-
|
|
94
|
-
const patternDefault = generateDefaultFromPattern(schema.pattern);
|
|
95
|
-
if (patternDefault) {
|
|
96
|
-
return patternDefault;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return '""';
|
|
100
|
-
}
|
|
126
|
+
case "string":
|
|
127
|
+
return stringFallback(schema);
|
|
101
128
|
case "number":
|
|
102
129
|
case "integer":
|
|
103
|
-
return "
|
|
130
|
+
return numberFallback(schema, schema.type === "integer");
|
|
104
131
|
case "boolean":
|
|
105
132
|
return "false";
|
|
106
133
|
case "null":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/generate-parsers",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.5",
|
|
4
4
|
"description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"json-schema-typed": "^8.0.1",
|
|
59
|
-
"@amritk/helpers": "^0.
|
|
59
|
+
"@amritk/helpers": "^0.16.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@amritk/generate-markdown": "^0.
|
|
62
|
+
"@amritk/generate-markdown": "^0.5.0",
|
|
63
63
|
"@sinclair/typebox": "^0.34.49",
|
|
64
64
|
"ajv": "^8.17.1",
|
|
65
65
|
"zod": "^4.4.3"
|