@amritk/generate-parsers 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +37 -463
  2. package/dist/index.d.ts +22 -2
  3. package/dist/index.js +18 -1
  4. package/package.json +15 -40
  5. package/AI.md +0 -52
  6. package/dist/generators/assert-supported-keywords.d.ts +0 -30
  7. package/dist/generators/assert-supported-keywords.js +0 -87
  8. package/dist/generators/build-schema.d.ts +0 -111
  9. package/dist/generators/build-schema.js +0 -74
  10. package/dist/generators/generate-deep-equal-check.d.ts +0 -24
  11. package/dist/generators/generate-deep-equal-check.js +0 -35
  12. package/dist/generators/generate-default-from-pattern.d.ts +0 -26
  13. package/dist/generators/generate-default-from-pattern.js +0 -223
  14. package/dist/generators/generate-discriminated-union-validation.d.ts +0 -15
  15. package/dist/generators/generate-discriminated-union-validation.js +0 -28
  16. package/dist/generators/generate-enum-check.d.ts +0 -35
  17. package/dist/generators/generate-enum-check.js +0 -31
  18. package/dist/generators/generate-files.d.ts +0 -138
  19. package/dist/generators/generate-files.js +0 -89
  20. package/dist/generators/generate-non-discriminated-union-validation.d.ts +0 -6
  21. package/dist/generators/generate-non-discriminated-union-validation.js +0 -26
  22. package/dist/generators/generate-parser-function.d.ts +0 -120
  23. package/dist/generators/generate-parser-function.js +0 -1973
  24. package/dist/generators/generate-schema-checks.d.ts +0 -5
  25. package/dist/generators/generate-schema-checks.js +0 -260
  26. package/dist/generators/generate-strict-assertion.d.ts +0 -155
  27. package/dist/generators/generate-strict-assertion.js +0 -759
  28. package/dist/generators/generate-type-checks.d.ts +0 -148
  29. package/dist/generators/generate-type-checks.js +0 -454
  30. package/dist/generators/generate-unique-items-check.d.ts +0 -16
  31. package/dist/generators/generate-unique-items-check.js +0 -32
  32. package/dist/generators/generate-validation-expression.d.ts +0 -79
  33. package/dist/generators/generate-validation-expression.js +0 -416
  34. package/dist/generators/subschema-match.d.ts +0 -48
  35. package/dist/generators/subschema-match.js +0 -485
  36. package/dist/generators/unevaluated-match.d.ts +0 -19
  37. package/dist/generators/unevaluated-match.js +0 -206
  38. package/dist/helpers/apply-schema-extensions.d.ts +0 -35
  39. package/dist/helpers/apply-schema-extensions.js +0 -20
  40. package/dist/helpers/collect-helpers.d.ts +0 -15
  41. package/dist/helpers/collect-helpers.js +0 -64
  42. package/dist/helpers/collect-imports.d.ts +0 -104
  43. package/dist/helpers/collect-imports.js +0 -227
  44. package/dist/helpers/find-discriminator.d.ts +0 -6
  45. package/dist/helpers/find-discriminator.js +0 -27
  46. package/dist/helpers/get-default-value.d.ts +0 -7
  47. package/dist/helpers/get-default-value.js +0 -155
  48. package/dist/helpers/get-discriminator-value.d.ts +0 -6
  49. package/dist/helpers/get-discriminator-value.js +0 -18
  50. package/dist/types/schema-extensions.d.ts +0 -34
  51. package/dist/types/schema-extensions.js +0 -0
@@ -1,1973 +0,0 @@
1
- import { regexLiteral } from "@amritk/helpers/escape-regex-pattern";
2
- import { generateTypeDefinition } from "@amritk/helpers/generate-type-definition";
3
- import { quoteJsString } from "@amritk/helpers/quote-js-string";
4
- import { declaresKey, readKey } from "@amritk/helpers/read-key";
5
- import { refToName } from "@amritk/helpers/ref-to-name";
6
- import { resolveRef } from "@amritk/helpers/resolve-ref";
7
- import { hasOwnCheck, missingCheck, safeAccessor, safeKey } from "@amritk/helpers/safe-accessor";
8
- import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasEnum, hasItems, hasMaxItems, hasMaxProperties, hasMinItems, hasMinProperties, hasOneOf, hasProperties, hasRef, hasRequired, hasType, hasUniqueItems, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
9
- import { unknownKeyCheck } from "@amritk/helpers/unknown-key-check";
10
- import { DEFAULT_UNKNOWN_KEYS } from "@amritk/helpers/unknown-keys-strategy";
11
- import { findDiscriminator } from "../helpers/find-discriminator.js";
12
- import { getDefaultValue } from "../helpers/get-default-value.js";
13
- import { getDiscriminatorValue } from "../helpers/get-discriminator-value.js";
14
- import { assertNoUnsupportedKeywords } from "./assert-supported-keywords.js";
15
- import { generateEnumCaseInsensitiveCoercion } from "./generate-enum-check.js";
16
- import { generateBackstopAssertion, generateCompositionChecks, generateContainsCheck, generateKeyedValueChecks, generateObjectKeywordChecks, generateObjectSizeChecks, generateObjectStrictAssertion, generateScalarStrictAssertion, inlineAllOfMembers } from "./generate-strict-assertion.js";
17
- import { canEnforceUnion, generatePropertyTypeCheck, generateUnionCheck, getUnionBranches, hasFastPathBlockingKeyword, isExclusiveUnion, isInlineObjectArrayProperty, isInlineObjectProperty, isInlineUnionProperty, isUnionArrayProperty, shapeValidatorName } from "./generate-type-checks.js";
18
- import { generateUniqueItemsCheck } from "./generate-unique-items-check.js";
19
- import { generatePrefixItemsMap, generateValidationExpression, getPrefixItems, isCoercibleItemSchema, prefixItemsCapsLength } from "./generate-validation-expression.js";
20
- const generateParserName = (typeName) => {
21
- return `parse${typeName}`;
22
- };
23
- const safeLiteralKey = (key) => key === "__proto__" ? '["__proto__"]' : safeKey(key);
24
- const isPropertyRequired = (key, schema) => {
25
- if (!isSchemaObject(schema)) {
26
- return false;
27
- }
28
- if (!("required" in schema) || !Array.isArray(schema.required)) {
29
- return false;
30
- }
31
- return schema.required.includes(key);
32
- };
33
- const generateRequiredRefCall = (key, ref, suffix) => {
34
- const acc = safeAccessor("input", key);
35
- const parserName = generateParserName(refToName(ref, suffix));
36
- return `${parserName}(${acc})`;
37
- };
38
- const generateOptionalRefCall = (key, ref, suffix) => {
39
- const acc = safeAccessor("input", key);
40
- const parserName = generateParserName(refToName(ref, suffix));
41
- return `...(${acc} !== undefined && { ${safeLiteralKey(key)}: ${parserName}(${acc}) })`;
42
- };
43
- const generateRequiredArrayRefCall = (key, ref, suffix) => {
44
- const parserName = generateParserName(refToName(ref, suffix));
45
- return `validateArray(${safeAccessor("input", key)}, ${parserName})`;
46
- };
47
- const generateOptionalArrayRefCall = (key, ref, suffix) => {
48
- const parserName = generateParserName(refToName(ref, suffix));
49
- const acc = safeAccessor("input", key);
50
- return `...(${acc} !== undefined && { ${safeLiteralKey(key)}: validateArray(${acc}, ${parserName}) })`;
51
- };
52
- const generateRequiredRecordRefCall = (key, ref, suffix) => {
53
- const acc = safeAccessor("input", key);
54
- const parserName = generateParserName(refToName(ref, suffix));
55
- return `validateRecord(${acc}, ${parserName})`;
56
- };
57
- const generateOptionalRecordRefCall = (key, ref, suffix) => {
58
- const acc = safeAccessor("input", key);
59
- const parserName = generateParserName(refToName(ref, suffix));
60
- return `...(${acc} !== undefined && { ${safeLiteralKey(key)}: validateRecord(${acc}, ${parserName}) })`;
61
- };
62
- const generateOptionalInlineProperty = (key, valueExpression) => {
63
- return `...((value => value === undefined ? {} : { ${safeLiteralKey(key)}: value })(${valueExpression}))`;
64
- };
65
- const shouldUseRefImport = (propSchema, useRefImports) => {
66
- if (!useRefImports || !hasRef(propSchema)) {
67
- return false;
68
- }
69
- const ref = propSchema.$ref;
70
- const isUri = ref.startsWith("http://") || ref.startsWith("https://");
71
- if (isUri && (ref.includes("#/properties/") || ref.includes("#/definitions/"))) {
72
- return false;
73
- }
74
- return true;
75
- };
76
- const shouldUseArrayRefImport = (propSchema, useRefImports) => {
77
- if (!useRefImports) {
78
- return false;
79
- }
80
- if (!isSchemaObject(propSchema)) {
81
- return false;
82
- }
83
- if (!("type" in propSchema) || propSchema.type !== "array") {
84
- return false;
85
- }
86
- return hasItems(propSchema) && hasRef(propSchema.items);
87
- };
88
- const shouldUseRecordRefImport = (propSchema, useRefImports) => {
89
- if (!useRefImports) {
90
- return false;
91
- }
92
- if (!isSchemaObject(propSchema)) {
93
- return false;
94
- }
95
- if (!("type" in propSchema) || propSchema.type !== "object") {
96
- return false;
97
- }
98
- if (!("additionalProperties" in propSchema)) {
99
- return false;
100
- }
101
- const additionalProps = propSchema.additionalProperties;
102
- return isSchemaObject(additionalProps) && hasRef(additionalProps);
103
- };
104
- const generatePropertyValue = (key, propSchema, isRequired, useRefImports, suffix, caseInsensitive = false) => {
105
- if (shouldUseRefImport(propSchema, useRefImports)) {
106
- const ref = propSchema.$ref;
107
- return isRequired ? generateRequiredRefCall(key, ref, suffix) : generateOptionalRefCall(key, ref, suffix);
108
- }
109
- if (shouldUseArrayRefImport(propSchema, useRefImports)) {
110
- const items = propSchema.items;
111
- const ref = items.$ref;
112
- return isRequired ? generateRequiredArrayRefCall(key, ref, suffix) : generateOptionalArrayRefCall(key, ref, suffix);
113
- }
114
- if (shouldUseRecordRefImport(propSchema, useRefImports)) {
115
- const additionalProps = propSchema.additionalProperties;
116
- const ref = additionalProps.$ref;
117
- return isRequired ? generateRequiredRecordRefCall(key, ref, suffix) : generateOptionalRecordRefCall(key, ref, suffix);
118
- }
119
- if (!isSchemaObject(propSchema)) {
120
- return isRequired ? "undefined" : generateOptionalInlineProperty(key, "undefined");
121
- }
122
- const defaultValue = getDefaultValue(propSchema);
123
- const valueExpression = generateValidationExpression(key, propSchema, defaultValue, isRequired, void 0, void 0, void 0, void 0, caseInsensitive);
124
- return isRequired ? valueExpression : generateOptionalInlineProperty(key, valueExpression);
125
- };
126
- const generatePropertyEntries = (schema, useRefImports, suffix, caseInsensitive = false) => {
127
- if (!hasProperties(schema)) {
128
- return [];
129
- }
130
- const entries = [];
131
- for (const [key, propSchema] of Object.entries(schema.properties)) {
132
- const isRequired = isPropertyRequired(key, schema);
133
- const value = generatePropertyValue(key, propSchema, isRequired, useRefImports, suffix, caseInsensitive);
134
- const isOptional = value.startsWith("...");
135
- entries.push({ key, value, isOptional });
136
- }
137
- return entries;
138
- };
139
- const generateFallbackValue = (_, propSchema, useRefImports, suffix) => {
140
- if (useRefImports && hasRef(propSchema)) {
141
- const ref = propSchema.$ref;
142
- const parserName = generateParserName(refToName(ref, suffix));
143
- return `${parserName}(undefined)`;
144
- }
145
- if (useRefImports && isSchemaObject(propSchema) && propSchema.type === "array" && hasItems(propSchema) && hasRef(propSchema.items)) {
146
- return "[]";
147
- }
148
- if (useRefImports && isSchemaObject(propSchema) && propSchema.type === "object" && "additionalProperties" in propSchema) {
149
- const additionalProps = propSchema.additionalProperties;
150
- if (isSchemaObject(additionalProps) && hasRef(additionalProps)) {
151
- return "{}";
152
- }
153
- }
154
- return getDefaultValue(propSchema);
155
- };
156
- const PROTOTYPE_MEMBER_NAMES = new Set(Object.getOwnPropertyNames(Object.prototype));
157
- const declaresPrototypeMemberProperty = (schema, depth = 4) => {
158
- if (!isSchemaObject(schema) || depth < 0)
159
- return false;
160
- if (hasProperties(schema) && Object.keys(schema.properties).some((key) => PROTOTYPE_MEMBER_NAMES.has(key)))
161
- return true;
162
- const record = schema;
163
- const children = [];
164
- if (hasProperties(schema))
165
- children.push(...Object.values(schema.properties));
166
- const items = record["items"];
167
- if (Array.isArray(items))
168
- children.push(...items);
169
- else if (items !== void 0)
170
- children.push(items);
171
- const prefixItems = record["prefixItems"];
172
- if (Array.isArray(prefixItems))
173
- children.push(...prefixItems);
174
- return children.some((child) => declaresPrototypeMemberProperty(child, depth - 1));
175
- };
176
- const generateFallbackObject = (schema, useRefImports, typeName, suffix, subTypeNames) => {
177
- if (!hasProperties(schema)) {
178
- return `{} as ${typeName}`;
179
- }
180
- if (isSchemaObject(schema) && ("if" in schema || "then" in schema || "else" in schema)) {
181
- return `{} as ${typeName}`;
182
- }
183
- if (isSchemaObject(schema) && hasAllOf(schema)) {
184
- const composesConditional = schema.allOf.some((member) => isSchemaObject(member) && (hasRef(member) || "if" in member || "then" in member || "else" in member));
185
- if (composesConditional)
186
- return `{} as ${typeName}`;
187
- }
188
- const requiredProps = [];
189
- for (const [key, propSchema] of Object.entries(schema.properties)) {
190
- const isRequired = isPropertyRequired(key, schema);
191
- if (isRequired) {
192
- const subName = subTypeNames?.get(key);
193
- const fallbackValue = subName ? `${generateParserName(subName)}(undefined)` : generateFallbackValue(key, propSchema, useRefImports, suffix);
194
- if (fallbackValue === "undefined") {
195
- return `{} as ${typeName}`;
196
- }
197
- const partialFallback = fallbackValue === "{}" || isSchemaObject(propSchema) && (hasAllOf(propSchema) || hasOneOf(propSchema) || hasAnyOf(propSchema));
198
- requiredProps.push(` ${safeLiteralKey(key)}: ${partialFallback ? `${fallbackValue} as NonNullable<${typeName}>[${JSON.stringify(key)}]` : fallbackValue},`);
199
- }
200
- }
201
- if (requiredProps.length === 0) {
202
- return `{} as ${typeName}`;
203
- }
204
- let result = "{\n";
205
- for (let i = 0; i < requiredProps.length; i++) {
206
- result += requiredProps[i];
207
- if (i < requiredProps.length - 1) {
208
- result += "\n";
209
- }
210
- }
211
- result += "\n }";
212
- if (declaresPrototypeMemberProperty(schema))
213
- return `${result} as unknown as ${typeName}`;
214
- const composesUnion = isSchemaObject(schema) && (hasOneOf(schema) || hasAnyOf(schema));
215
- return composesUnion ? `${result} as ${typeName}` : result;
216
- };
217
- const SCALAR_ROOT_CONSTRAINTS = [
218
- "pattern",
219
- "minLength",
220
- "maxLength",
221
- "minimum",
222
- "maximum",
223
- "exclusiveMinimum",
224
- "exclusiveMaximum",
225
- "multipleOf"
226
- ];
227
- const isConstrainedScalarRoot = (schema) => {
228
- if (!isSchemaObject(schema) || !hasType(schema))
229
- return false;
230
- if (schema.type !== "string" && schema.type !== "number" && schema.type !== "integer")
231
- return false;
232
- const record = schema;
233
- return SCALAR_ROOT_CONSTRAINTS.some((keyword) => keyword in record);
234
- };
235
- const scalarDefaultLiteral = (schema) => {
236
- const value = getDefaultValue(schema);
237
- return value === "undefined" ? "{}" : value;
238
- };
239
- const generateRefUnionDispatch = (functionName, typeName, schema, branches, strict, ctx) => {
240
- const rootSchema = ctx.rootSchema;
241
- if (!ctx.useRefImports || rootSchema === void 0)
242
- return null;
243
- if (branches.length === 0 || !branches.every((b) => isSchemaObject(b) && hasRef(b)))
244
- return null;
245
- const resolved = branches.map((b) => {
246
- const ref = b.$ref;
247
- return { parser: generateParserName(refToName(ref, ctx.suffix)), schema: resolveRef(ref, rootSchema) };
248
- });
249
- if (resolved.some((r) => !isSchemaObject(r.schema)))
250
- return null;
251
- const discriminator = findDiscriminator(resolved.map((r) => r.schema));
252
- if (discriminator === null)
253
- return null;
254
- const cases = [];
255
- for (const r of resolved) {
256
- const value = getDiscriminatorValue(r.schema, discriminator);
257
- if (value === null)
258
- return null;
259
- cases.push({ value, parser: r.parser });
260
- }
261
- const access = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(discriminator) ? `.${discriminator}` : `[${JSON.stringify(discriminator)}]`;
262
- const discExpr = `input == null ? undefined : (input as Record<string, unknown>)${access}`;
263
- const fallback = strict ? `(() => { throw new Error(${quoteJsString(`[${typeName}] value does not match any union branch`)}); })()` : `${cases[0]?.parser}(input)`;
264
- let expr = fallback;
265
- for (let i = cases.length - 1; i >= 0; i--) {
266
- const c = cases[i];
267
- if (c)
268
- expr = `_disc === ${JSON.stringify(c.value)} ? ${c.parser}(input) : ${expr}`;
269
- }
270
- const backstop = strict ? generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(ctx)) : [];
271
- const prelude = backstop.length > 0 ? `${backstop.join("\n")}
272
- ` : "";
273
- return `export const ${functionName} = (input: unknown): ${typeName} => {
274
- ${prelude} const _disc = ${discExpr};
275
- return ${expr};
276
- };`;
277
- };
278
- const SCORE_TAG_MATCH = 100;
279
- const SCORE_TAG_MISMATCH = -100;
280
- const SCORE_REQUIRED_PRESENT = 4;
281
- const SCORE_REQUIRED_MISSING = -4;
282
- const SCORE_DECLARED_PRESENT = 1;
283
- const SCORE_DECLARED_WELL_TYPED = 2;
284
- const resolveForScoring = (schema, rootSchema) => {
285
- if (!isSchemaObject(schema) || !hasRef(schema) || rootSchema === void 0)
286
- return schema;
287
- const target = resolveRef(schema.$ref, rootSchema);
288
- return target !== void 0 && isSchemaObject(target) ? target : schema;
289
- };
290
- const tagValueOf = (schema) => {
291
- if (!isSchemaObject(schema))
292
- return void 0;
293
- if (hasConst(schema))
294
- return schema.const;
295
- if (hasEnum(schema) && schema.enum.length === 1)
296
- return schema.enum[0];
297
- return void 0;
298
- };
299
- const branchScoreExpression = (branch, ctx) => {
300
- const resolved = resolveForScoring(branch, ctx.rootSchema);
301
- if (!isSchemaObject(resolved) || !hasProperties(resolved))
302
- return "0";
303
- const properties = resolved.properties;
304
- const required = new Set(hasRequired(resolved) ? resolved.required : []);
305
- const terms = [];
306
- for (const key of Object.keys(properties)) {
307
- const propertySchema = resolveForScoring(properties[key], ctx.rootSchema);
308
- const accessor = safeAccessor("input", key);
309
- const present = hasOwnCheck("input", key);
310
- const tag = tagValueOf(propertySchema);
311
- if (tag !== void 0) {
312
- terms.push(`(${accessor} === ${JSON.stringify(tag)} ? ${SCORE_TAG_MATCH} : ${present} ? ${SCORE_TAG_MISMATCH} : 0)`);
313
- continue;
314
- }
315
- if (required.has(key)) {
316
- terms.push(`(${present} ? ${SCORE_REQUIRED_PRESENT} : ${SCORE_REQUIRED_MISSING})`);
317
- } else {
318
- terms.push(`(${present} ? ${SCORE_DECLARED_PRESENT} : 0)`);
319
- }
320
- const typeCheck = generatePropertyTypeCheck(accessor, properties[key], ctx.useRefImports, ctx.suffix);
321
- if (typeCheck !== null)
322
- terms.push(`(${typeCheck} ? ${SCORE_DECLARED_WELL_TYPED} : 0)`);
323
- }
324
- return terms.length === 0 ? "0" : terms.join(" + ");
325
- };
326
- const generateScoredUnionParser = (functionName, typeName, branches, ctx, exported = true) => {
327
- if (branches.length < 2)
328
- return null;
329
- const preamble = [];
330
- const dispatch = [];
331
- const reserved = new Set(ctx.reservedNames ?? []);
332
- const needsUnnarrowedAlias = branches.some((branch) => isSchemaObject(branch) && !(hasRef(branch) && ctx.useRefImports) && !(isObjectSchema(branch) && hasProperties(branch)));
333
- const valueRef = needsUnnarrowedAlias ? "_u" : "input";
334
- for (const [index, branch] of branches.entries()) {
335
- if (!isSchemaObject(branch))
336
- return null;
337
- if (hasRef(branch) && ctx.useRefImports) {
338
- const refName = refToName(branch.$ref, ctx.suffix);
339
- if (refName === typeName)
340
- return null;
341
- dispatch.push({
342
- predicate: `${shapeValidatorName(refName)}(input)`,
343
- result: `${generateParserName(refName)}(${valueRef}) as ${typeName}`
344
- });
345
- continue;
346
- }
347
- if (hasRef(branch))
348
- return null;
349
- if (hasOneOf(branch) || hasAnyOf(branch) || hasAllOf(branch) || "not" in branch)
350
- return null;
351
- if ("if" in branch || "then" in branch || "else" in branch || "patternProperties" in branch)
352
- return null;
353
- if (!isObjectSchema(branch) || !hasProperties(branch)) {
354
- const predicate = generatePropertyTypeCheck("input", branch, ctx.useRefImports, ctx.suffix);
355
- if (predicate === null)
356
- return null;
357
- const coerced = generateValidationExpression("", branch, scalarDefaultLiteral(branch), true, ctx.rootSchema, void 0, valueRef, false, ctx.caseInsensitive);
358
- dispatch.push({ predicate, result: `(${coerced}) as ${typeName}` });
359
- continue;
360
- }
361
- let subName = `${typeName}_B${index}`;
362
- while (reserved.has(subName))
363
- subName = `${subName}_`;
364
- reserved.add(subName);
365
- const shapeValidator = generateShapeValidator(branch, subName, ctx.useRefImports, ctx.suffix, false, ctx.stripUnknown ?? false, reserved, ctx.unknownKeys ?? DEFAULT_UNKNOWN_KEYS);
366
- if (shapeValidator.includes("(_input: unknown): boolean => false"))
367
- return null;
368
- preamble.push(generateTypeDefinition(branch, subName, {
369
- ...ctx.suffix ? { typeSuffix: ctx.suffix } : {},
370
- ...ctx.rootSchema ? { rootSchema: ctx.rootSchema } : {}
371
- }).replace(/^export /, ""));
372
- preamble.push(shapeValidator);
373
- preamble.push(generateObjectParser(
374
- branch,
375
- subName,
376
- ctx.useRefImports,
377
- ctx.suffix,
378
- ctx.logWarnings,
379
- // Always the coercing parser: this whole path exists to repair.
380
- false,
381
- false,
382
- ctx.stripUnknown ?? false,
383
- ctx.rootSchema,
384
- reserved,
385
- ctx.caseInsensitive ?? false,
386
- shapeValidator,
387
- ctx.unknownKeys ?? DEFAULT_UNKNOWN_KEYS
388
- ));
389
- dispatch.push({
390
- predicate: `${shapeValidatorName(subName)}(input)`,
391
- result: `${generateParserName(subName)}(${valueRef}) as ${typeName}`
392
- });
393
- }
394
- const first = dispatch[0];
395
- if (first === void 0)
396
- return null;
397
- const lines = [];
398
- if (needsUnnarrowedAlias)
399
- lines.push(" const _u: unknown = input;");
400
- for (const { predicate, result } of dispatch) {
401
- lines.push(` if (${predicate}) return ${result};`);
402
- }
403
- lines.push(` if (!isObject(input)) return ${first.result};`);
404
- const scores = branches.map((branch, index) => ` const _s${index} = ${branchScoreExpression(branch, ctx)};`);
405
- lines.push(...scores);
406
- const last = dispatch[dispatch.length - 1];
407
- let best = `${last?.result}`;
408
- for (let index = dispatch.length - 2; index >= 0; index--) {
409
- const atLeastAsGood = dispatch.slice(index + 1).map((_, offset) => `_s${index} >= _s${index + 1 + offset}`).join(" && ");
410
- best = `${atLeastAsGood} ? ${dispatch[index]?.result} : ${best}`;
411
- }
412
- lines.push(` return ${best};`);
413
- const head = preamble.length > 0 ? `${preamble.join("\n\n")}
414
-
415
- ` : "";
416
- const exportPrefix = exported ? "export " : "";
417
- return `${head}${exportPrefix}const ${functionName} = (input: unknown): ${typeName} => {
418
- ${lines.join("\n")}
419
- };`;
420
- };
421
- const strictContext = (ctx) => ({
422
- useRefImports: ctx?.useRefImports ?? false,
423
- suffix: ctx?.suffix ?? "",
424
- ...ctx?.rootSchema !== void 0 ? { rootSchema: ctx.rootSchema } : {},
425
- ...ctx?.stripUnknown !== void 0 ? { stripUnknown: ctx.stripUnknown } : {},
426
- ...ctx?.unknownKeys !== void 0 ? { unknownKeys: ctx.unknownKeys } : {}
427
- });
428
- const generateNonObjectParser = (typeName, schema, strict, unionCtx) => {
429
- const functionName = generateParserName(typeName);
430
- if (unionCtx && isSchemaObject(schema) && (hasOneOf(schema) || hasAnyOf(schema))) {
431
- const branches = hasOneOf(schema) ? schema.oneOf : hasAnyOf(schema) ? schema.anyOf : [];
432
- if (branches.length > 0 && branches.every((b) => isSchemaObject(b) && hasRef(b))) {
433
- const dispatch = generateRefUnionDispatch(functionName, typeName, schema, branches, strict, unionCtx);
434
- if (dispatch !== null)
435
- return dispatch;
436
- }
437
- }
438
- if (unionCtx?.useRefImports && isSchemaObject(schema) && hasRef(schema) && !hasProperties(schema)) {
439
- const refName = refToName(schema.$ref, unionCtx.suffix);
440
- if (refName !== typeName) {
441
- const assertion = strict ? generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx)) : [];
442
- if (assertion.length === 0) {
443
- return `export const ${functionName} = (input: unknown): ${typeName} => ${generateParserName(refName)}(input) as ${typeName};`;
444
- }
445
- return `export const ${functionName} = (input: unknown): ${typeName} => {
446
- ${assertion.join("\n")}
447
- return ${generateParserName(refName)}(input) as ${typeName};
448
- };`;
449
- }
450
- }
451
- if (strict && unionCtx && isSchemaObject(schema)) {
452
- const branches = getUnionBranches(schema);
453
- if (branches && !unionCtx.stripUnknown && canEnforceUnion(branches, unionCtx.rootSchema)) {
454
- const check = generateUnionCheck("input", branches, unionCtx.useRefImports, unionCtx.suffix, isExclusiveUnion(schema));
455
- if (check !== null) {
456
- const backstop = generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx));
457
- const assertions = [
458
- ` if (!(${check})) throw new Error(${quoteJsString(`[${typeName}] value does not match any union branch`)});`,
459
- ...backstop
460
- ].join("\n");
461
- return `export const ${functionName} = (input: unknown): ${typeName} => {
462
- ${assertions}
463
- return input as ${typeName};
464
- };`;
465
- }
466
- }
467
- }
468
- if (!strict && isSchemaObject(schema)) {
469
- if (hasConst(schema)) {
470
- const literal = JSON.stringify(schema.const);
471
- const isPrimitive = schema.const === null || typeof schema.const !== "object";
472
- return isPrimitive ? `export const ${functionName} = (input: unknown): ${typeName} => input === ${literal} ? input as ${typeName} : ${literal} as ${typeName};` : `export const ${functionName} = (input: unknown): ${typeName} => ${literal} as ${typeName};`;
473
- }
474
- if (hasEnum(schema) && schema.enum.length > 0) {
475
- const values = JSON.stringify(schema.enum);
476
- const fallback = JSON.stringify(schema.enum[0]);
477
- const ci = unionCtx?.caseInsensitive ? generateEnumCaseInsensitiveCoercion("input", schema.enum, fallback) : null;
478
- const coerced = ci ? `(${ci})` : fallback;
479
- return `export const ${functionName} = (input: unknown): ${typeName} => ${values}.includes(input as never) ? input as ${typeName} : ${coerced} as ${typeName};`;
480
- }
481
- if (hasOneOf(schema) || hasAnyOf(schema)) {
482
- const branches = hasOneOf(schema) ? schema.oneOf : hasAnyOf(schema) ? schema.anyOf : [];
483
- const scored = unionCtx ? generateScoredUnionParser(functionName, typeName, branches, unionCtx) : null;
484
- if (scored !== null)
485
- return scored;
486
- const hasRefBranch = branches.some((b) => isSchemaObject(b) && hasRef(b));
487
- if (branches.length > 0 && !hasRefBranch) {
488
- const fallback = scalarDefaultLiteral(branches[0]);
489
- const expr = generateValidationExpression("", schema, fallback, true, void 0, void 0, "input", true);
490
- return `export const ${functionName} = (input: unknown): ${typeName} => (${expr}) as ${typeName};`;
491
- }
492
- return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
493
- }
494
- }
495
- if (strict && isSchemaObject(schema) && !hasType(schema) && (hasConst(schema) || hasEnum(schema) || hasAllOf(schema) || hasOneOf(schema) || hasAnyOf(schema) || "not" in schema || "if" in schema)) {
496
- const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
497
- if (assertion !== null) {
498
- return `export const ${functionName} = (input: unknown): ${typeName} => {
499
- ${assertion}
500
- return input as ${typeName};
501
- };`;
502
- }
503
- }
504
- if (isSchemaObject(schema) && hasType(schema) && schema.type === "array" && hasItems(schema) && !Array.isArray(schema.items)) {
505
- const items = schema.items;
506
- const notArrayThrow = `if (!Array.isArray(input)) throw new Error(\`[${typeName}] expected array, got \${input === null ? "null" : typeof input}\`);`;
507
- const strictArrayChecks = strict ? [
508
- // `minItems`/`maxItems` were dropped on this path entirely: a root
509
- // array of objects or `$ref`s bypasses generateScalarStrictAssertion,
510
- // so `{ type: 'array', minItems: 2, items: { type: 'object' } }`
511
- // accepted a one-element array.
512
- ...hasMinItems(schema) ? [
513
- ` if (input.length < ${schema.minItems}) throw new Error(${quoteJsString(`[${typeName}] must have at least ${schema.minItems} items`)});`
514
- ] : [],
515
- ...hasMaxItems(schema) ? [
516
- ` if (input.length > ${schema.maxItems}) throw new Error(${quoteJsString(`[${typeName}] must have at most ${schema.maxItems} items`)});`
517
- ] : [],
518
- ...generateContainsCheck("input", schema, `[${typeName}]`),
519
- ...hasUniqueItems(schema) && schema.uniqueItems === true ? [
520
- ` if (!(${generateUniqueItemsCheck("input", schema)})) throw new Error(${quoteJsString(`[${typeName}] must NOT have duplicate items`)});`
521
- ] : [],
522
- // This path delegates elements to another parser and so never reaches
523
- // generateScalarStrictAssertion — `unevaluatedItems` and friends would
524
- // be lost with it.
525
- ...generateBackstopAssertion("input", schema, `[${typeName}]`, strictContext(unionCtx))
526
- ].join("\n") : "";
527
- const delegatedBody = (itemParserName) => ` const _parsed = validateArray(input, ${itemParserName});
528
- return (_parsed === input ? [..._parsed] : _parsed) as ${typeName};`;
529
- const delegated = (itemParserName) => strict ? `export const ${functionName} = (input: unknown): ${typeName} => {
530
- ${notArrayThrow}${strictArrayChecks ? `
531
- ${strictArrayChecks}` : ""}
532
- ${delegatedBody(itemParserName)}
533
- };` : `export const ${functionName} = (input: unknown): ${typeName} => {
534
- ${delegatedBody(itemParserName)}
535
- };`;
536
- if (unionCtx?.useRefImports && isSchemaObject(items) && hasRef(items)) {
537
- return delegated(generateParserName(refToName(items.$ref, unionCtx.suffix)));
538
- }
539
- if (isInlineObjectProperty(items)) {
540
- const reserved = unionCtx?.reservedNames ?? NO_RESERVED_NAMES;
541
- let itemName = `${typeName}_Item`;
542
- while (reserved.has(itemName))
543
- itemName = `${itemName}_`;
544
- const useRefImports = unionCtx?.useRefImports ?? false;
545
- const suffix = unionCtx?.suffix ?? "";
546
- const stripUnknown = unionCtx?.stripUnknown ?? false;
547
- const unknownKeys = unionCtx?.unknownKeys ?? DEFAULT_UNKNOWN_KEYS;
548
- const itemShapeValidator = generateShapeValidator(items, itemName, useRefImports, suffix, false, stripUnknown, reserved, unknownKeys);
549
- const preamble = [
550
- `type ${itemName} = NonNullable<${typeName}>[number];`,
551
- itemShapeValidator,
552
- generateObjectParser(items, itemName, useRefImports, suffix, unionCtx?.logWarnings ?? false, strict, false, stripUnknown, unionCtx?.rootSchema, reserved, unionCtx?.caseInsensitive ?? false, itemShapeValidator, unknownKeys)
553
- ].join("\n\n");
554
- return `${preamble}
555
-
556
- ${delegated(generateParserName(itemName))}`;
557
- }
558
- }
559
- const isMultiType = isSchemaObject(schema) && Array.isArray(schema.type);
560
- if (!isSchemaObject(schema) || !hasType(schema) && !isMultiType) {
561
- if (strict) {
562
- const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
563
- if (assertion !== null) {
564
- return `export const ${functionName} = (input: unknown): ${typeName} => {
565
- ${assertion}
566
- return input as ${typeName};
567
- };`;
568
- }
569
- }
570
- return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
571
- }
572
- if (!strict && schema.type === "null") {
573
- return `export const ${functionName} = (input: unknown): ${typeName} => null as ${typeName};`;
574
- }
575
- if (strict) {
576
- const assertion = generateScalarStrictAssertion(schema, typeName, strictContext(unionCtx));
577
- if (assertion === null) {
578
- return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
579
- }
580
- const arrayCast = getPrefixItems(schema) ? `[...(input as readonly unknown[])] as unknown as ${typeName}` : `[...(input as readonly unknown[])] as ${typeName}`;
581
- const returnExpr = !isMultiType && schema.type === "array" ? arrayCast : `input as ${typeName}`;
582
- return `export const ${functionName} = (input: unknown): ${typeName} => {
583
- ${assertion}
584
- return ${returnExpr};
585
- };`;
586
- }
587
- if (isConstrainedScalarRoot(schema)) {
588
- const expr = generateValidationExpression("", schema, getDefaultValue(schema), true, unionCtx?.rootSchema, void 0, "input", true, unionCtx?.caseInsensitive);
589
- return `export const ${functionName} = (input: unknown): ${typeName} => (${expr}) as ${typeName};`;
590
- }
591
- switch (schema.type) {
592
- case "string":
593
- return `export const ${functionName} = (input: unknown): ${typeName} => typeof input === "string" ? input as ${typeName} : "" as ${typeName};`;
594
- case "number":
595
- return `export const ${functionName} = (input: unknown): ${typeName} => typeof input === "number" ? input as ${typeName} : 0 as ${typeName};`;
596
- case "integer":
597
- return `export const ${functionName} = (input: unknown): ${typeName} => typeof input === "number" && Number.isInteger(input) ? input as ${typeName} : 0 as ${typeName};`;
598
- case "boolean":
599
- return `export const ${functionName} = (input: unknown): ${typeName} => typeof input === "boolean" ? input as ${typeName} : false as ${typeName};`;
600
- case "array": {
601
- const prefixItems = getPrefixItems(schema);
602
- if (prefixItems) {
603
- const mapped = generatePrefixItemsMap("input", prefixItems, prefixItemsCapsLength(schema), unionCtx?.rootSchema, void 0, unionCtx?.caseInsensitive);
604
- return `export const ${functionName} = (input: unknown): ${typeName} => Array.isArray(input) ? ${mapped} as unknown as ${typeName} : [] as unknown as ${typeName};`;
605
- }
606
- if (hasItems(schema) && !Array.isArray(schema.items) && isCoercibleItemSchema(schema.items)) {
607
- const item = schema.items;
608
- const itemExpr = generateValidationExpression("", item, getDefaultValue(item), true, void 0, void 0, "_it", true, unionCtx?.caseInsensitive);
609
- return `export const ${functionName} = (input: unknown): ${typeName} => Array.isArray(input) ? (input as unknown[]).map((_it) => ${itemExpr}) as ${typeName} : [] as ${typeName};`;
610
- }
611
- return `export const ${functionName} = (input: unknown): ${typeName} => Array.isArray(input) ? [...input] as ${typeName} : [] as ${typeName};`;
612
- }
613
- default:
614
- return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
615
- }
616
- };
617
- const strictObjectKeywordPrelude = (schema, typeName, context = {}) => {
618
- if (!isSchemaObject(schema))
619
- return "";
620
- const lines = [
621
- ...generateObjectKeywordChecks("input", schema, `[${typeName}]`, false, context),
622
- // `required` and the size bounds are the object-level constraints these
623
- // specialized parsers never had a place for: `{ type: 'object', required:
624
- // ['a'] }` and `{ type: 'object', minProperties: 2 }` carry no `properties`,
625
- // so they land on the empty-object / record paths, which asserted only
626
- // `isObject`. Keys that *are* declared are skipped — the per-property
627
- // assertions of the combined parser already demand them.
628
- ...requiredWithoutPropertyLines(schema, typeName),
629
- ...generateObjectSizeChecks("input", schema, `[${typeName}]`, false),
630
- // Values behind `patternProperties` / a schema-valued `additionalProperties`:
631
- // these parsers build their result by copying or coercing, so nothing
632
- // rejected a wrongly-typed pattern-matched value before this.
633
- ...generateKeyedValueChecks("input", schema, `[${typeName}]`, context),
634
- // Composition on a property-less object: these parsers delegate nothing, so
635
- // `{ type: 'object', allOf: [{ $ref: … }] }` had no check at all.
636
- ...generateCompositionChecks("input", schema, `[${typeName}]`, context),
637
- // Whatever no specialized check above reaches (`unevaluated*`, a `$ref` with
638
- // constraining siblings): these parsers never run generateObjectStrictAssertion,
639
- // so the backstop has to be spliced in here too.
640
- ...generateBackstopAssertion("input", schema, `[${typeName}]`, context)
641
- ];
642
- return lines.length > 0 ? `
643
- ${lines.join("\n")}` : "";
644
- };
645
- const requiredWithoutPropertyLines = (schema, typeName) => {
646
- if (!isSchemaObject(schema) || !hasRequired(schema))
647
- return [];
648
- const declared = hasProperties(schema) ? schema.properties : {};
649
- const lines = [];
650
- for (const key of new Set(schema.required)) {
651
- if (key in declared)
652
- continue;
653
- lines.push(` if (${missingCheck("input", key)}) throw new Error(${quoteJsString(`[${typeName}] missing required property '${key}'`)});`);
654
- }
655
- return lines;
656
- };
657
- const generateEmptyObjectParser = (typeName, strict, schema, context = {}) => {
658
- const functionName = generateParserName(typeName);
659
- if (strict) {
660
- const prelude = schema !== void 0 ? strictObjectKeywordPrelude(schema, typeName, context) : "";
661
- return `export const ${functionName} = (input: unknown): ${typeName} => {
662
- if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${prelude}
663
- return { ...input } as ${typeName};
664
- };`;
665
- }
666
- return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {} as ${typeName};`;
667
- };
668
- const toVarName = (key) => {
669
- const safe = key.replace(/[^a-zA-Z0-9_$]/g, "_");
670
- return `_${safe}`;
671
- };
672
- const pascalCaseKey = (key) => key.replace(/[^a-zA-Z0-9_$]/g, "_").split("_").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
673
- const NO_RESERVED_NAMES = /* @__PURE__ */ new Set();
674
- const EMPTY_SUB_TYPES = { objects: /* @__PURE__ */ new Map(), arrayItems: /* @__PURE__ */ new Map() };
675
- const collectInlineSubTypes = (schema, typeName, reservedNames = NO_RESERVED_NAMES) => {
676
- if (!hasProperties(schema))
677
- return EMPTY_SUB_TYPES;
678
- const props = schema.properties;
679
- let objects = null;
680
- let arrayItems = null;
681
- let used = null;
682
- const claim = (base) => {
683
- used ??= /* @__PURE__ */ new Set();
684
- let subName = base;
685
- while (used.has(subName) || reservedNames.has(subName))
686
- subName = `${subName}_`;
687
- used.add(subName);
688
- return subName;
689
- };
690
- for (const key in props) {
691
- const propSchema = props[key];
692
- if (isInlineObjectProperty(propSchema)) {
693
- objects ??= /* @__PURE__ */ new Map();
694
- objects.set(key, claim(`${typeName}_${pascalCaseKey(key) || "Value"}`));
695
- } else if (isInlineObjectArrayProperty(propSchema) || isUnionArrayProperty(propSchema)) {
696
- arrayItems ??= /* @__PURE__ */ new Map();
697
- arrayItems.set(key, claim(`${typeName}_${pascalCaseKey(key) || "Value"}Item`));
698
- }
699
- }
700
- if (objects === null && arrayItems === null)
701
- return EMPTY_SUB_TYPES;
702
- return { objects: objects ?? EMPTY_SUB_TYPES.objects, arrayItems: arrayItems ?? EMPTY_SUB_TYPES.arrayItems };
703
- };
704
- const hasStrictKeys = (schema) => {
705
- if (!isSchemaObject(schema) || !hasProperties(schema))
706
- return false;
707
- if (!hasAdditionalProperties(schema) || schema.additionalProperties !== false)
708
- return false;
709
- if ("patternProperties" in schema)
710
- return false;
711
- if (hasAllOf(schema) || hasOneOf(schema) || hasAnyOf(schema))
712
- return false;
713
- return true;
714
- };
715
- const exactKeyCountOf = (schema) => {
716
- if (!isSchemaObject(schema) || !hasProperties(schema) || !hasRequired(schema))
717
- return null;
718
- const props = schema.properties;
719
- const required = schema.required;
720
- const requiredLookup = required.length > 16 ? new Set(required) : null;
721
- let declaredCount = 0;
722
- for (const key in props) {
723
- declaredCount++;
724
- const isRequired = requiredLookup !== null ? requiredLookup.has(key) : required.includes(key);
725
- if (!isRequired || !isSchemaObject(props[key]))
726
- return null;
727
- }
728
- if (declaredCount === 0 || required.length !== declaredCount)
729
- return null;
730
- return declaredCount;
731
- };
732
- const KEY_COUNT_VAR = "_keyCount";
733
- const keyCountCheck = (unknownKeys, count, indent, obj = "input") => unknownKeys === "count-keys" ? {
734
- statements: [],
735
- verdict: `Object.getPrototypeOf(${obj}) === Object.prototype && Object.keys(${obj}).length === ${count}`
736
- } : {
737
- statements: [`${indent}let ${KEY_COUNT_VAR} = 0;`, `${indent}for (const _k in ${obj}) ${KEY_COUNT_VAR}++;`],
738
- verdict: `${KEY_COUNT_VAR} === ${count}`
739
- };
740
- const shouldCacheVariable = (propSchema) => {
741
- return isSchemaObject(propSchema);
742
- };
743
- const INLINE_NESTED_MAX_PROPERTIES = 12;
744
- const INLINE_NESTED_MAX_SITES = 4;
745
- const INLINE_NESTED_MAX_TOTAL_PROPERTIES = 24;
746
- const inlineNestedFastPath = (childSchema, childTypeName, objectExpr, useRefImports, suffix, stripUnknown, reservedNames, claimVarName) => {
747
- if (!isSchemaObject(childSchema) || !hasProperties(childSchema))
748
- return null;
749
- if (!stripUnknown)
750
- return null;
751
- if (hasAdditionalProperties(childSchema) && childSchema.additionalProperties === false)
752
- return null;
753
- if (hasAllOf(childSchema) || hasOneOf(childSchema) || hasAnyOf(childSchema))
754
- return null;
755
- if (hasFastPathBlockingKeyword(childSchema))
756
- return null;
757
- if (hasMinProperties(childSchema) || hasMaxProperties(childSchema))
758
- return null;
759
- if (exactKeyCountOf(childSchema) === null)
760
- return null;
761
- const { objects, arrayItems } = collectInlineSubTypes(childSchema, childTypeName, reservedNames);
762
- if (objects.size > 0 || arrayItems.size > 0)
763
- return null;
764
- const props = childSchema.properties;
765
- const keys = Object.keys(props);
766
- if (keys.length === 0 || keys.length > INLINE_NESTED_MAX_PROPERTIES)
767
- return null;
768
- const declarations = [];
769
- const checks = [];
770
- const fields = [];
771
- const readRoot = `(${objectExpr} as Record<string, any>)`;
772
- for (const key of keys) {
773
- const propSchema = props[key];
774
- if (shouldUseRefImport(propSchema, useRefImports) || shouldUseArrayRefImport(propSchema, useRefImports) || shouldUseRecordRefImport(propSchema, useRefImports)) {
775
- return null;
776
- }
777
- const accessor = claimVarName(`${objectExpr}_${toVarName(key).slice(1)}`);
778
- declarations.push(` const ${accessor} = ${safeAccessor(readRoot, key)};`);
779
- const check = generatePropertyTypeCheck(accessor, propSchema, useRefImports, suffix);
780
- if (check === null)
781
- return null;
782
- checks.push(check);
783
- fields.push(`${safeLiteralKey(key)}: ${accessor}`);
784
- }
785
- const condition = checks.join(" && ");
786
- return { declarations, condition, literal: `{ ${fields.join(", ")} }`, propertyCount: keys.length };
787
- };
788
- const RESULT_VARIABLE = "out";
789
- const PROTOTYPE_MEMBERS = new Set(Object.getOwnPropertyNames(Object.prototype));
790
- const resultMember = (key) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? `${RESULT_VARIABLE}.${key}` : `${RESULT_VARIABLE}[${JSON.stringify(key)}]`;
791
- const renderLiteralEntry = (entry, indent) => {
792
- if (entry.kind === "field")
793
- return `${indent}${safeLiteralKey(entry.key)}: ${entry.value},`;
794
- if (entry.kind === "spread")
795
- return `${indent}...${entry.expression},`;
796
- return `${indent}...(${entry.read} !== undefined && { ${safeLiteralKey(entry.key)}: ${entry.value} }),`;
797
- };
798
- const renderResultObject = (entries, typeName, guard) => {
799
- const firstOptional = entries.findIndex((entry) => entry.kind === "optional");
800
- const assignable = entries.every((entry, index) => index < firstOptional || entry.kind === "spread" || !PROTOTYPE_MEMBERS.has(entry.key));
801
- if (firstOptional === -1 || !assignable) {
802
- const open = guard === void 0 ? " return {" : ` if (${guard}) return {`;
803
- return [
804
- open,
805
- entries.map((entry) => renderLiteralEntry(entry, " ")).join("\n"),
806
- ` } as unknown as ${typeName};`
807
- ];
808
- }
809
- const indent = guard === void 0 ? " " : " ";
810
- const lines = [];
811
- if (guard !== void 0)
812
- lines.push(` if (${guard}) {`);
813
- const literal = entries.slice(0, firstOptional);
814
- if (literal.length === 0) {
815
- lines.push(`${indent}const ${RESULT_VARIABLE}: Record<string, unknown> = {};`);
816
- } else {
817
- lines.push(`${indent}const ${RESULT_VARIABLE}: Record<string, unknown> = {`);
818
- lines.push(literal.map((entry) => renderLiteralEntry(entry, `${indent} `)).join("\n"));
819
- lines.push(`${indent}};`);
820
- }
821
- for (const entry of entries.slice(firstOptional)) {
822
- if (entry.kind === "field")
823
- lines.push(`${indent}${resultMember(entry.key)} = ${entry.value};`);
824
- else if (entry.kind === "optional") {
825
- lines.push(`${indent}if (${entry.read} !== undefined) ${resultMember(entry.key)} = ${entry.value};`);
826
- } else
827
- lines.push(`${indent}Object.assign(${RESULT_VARIABLE}, ${entry.expression});`);
828
- }
829
- lines.push(`${indent}return ${RESULT_VARIABLE} as unknown as ${typeName};`);
830
- if (guard !== void 0)
831
- lines.push(" }");
832
- return lines;
833
- };
834
- const generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict, exported = true, stripUnknown = false, rootSchema, reservedNames = NO_RESERVED_NAMES, caseInsensitive = false, shapeValidatorSource, unknownKeys = DEFAULT_UNKNOWN_KEYS) => {
835
- const functionName = generateParserName(typeName);
836
- const exportPrefix = exported ? "export " : "";
837
- if (!hasProperties(schema)) {
838
- if (strict) {
839
- return `${exportPrefix}const ${functionName} = (input: unknown): ${typeName} => {
840
- 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 } : {} })}
841
- return input as ${typeName};
842
- };`;
843
- }
844
- return `${exportPrefix}const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? input as ${typeName} : {} as ${typeName};`;
845
- }
846
- const schemaProps = schema.properties;
847
- const propertyKeys = Object.keys(schemaProps);
848
- const { objects: subTypeNames, arrayItems: subItemNames } = collectInlineSubTypes(schema, typeName, reservedNames);
849
- const preamble = [];
850
- for (const [key, subName] of subTypeNames) {
851
- const propSchema = schemaProps[key];
852
- preamble.push(`type ${subName} = NonNullable<${typeName}>[${JSON.stringify(key)}];`);
853
- const subShapeValidator = generateShapeValidator(propSchema, subName, useRefImports, suffix, false, stripUnknown, reservedNames, unknownKeys);
854
- preamble.push(subShapeValidator);
855
- preamble.push(generateObjectParser(propSchema, subName, useRefImports, suffix, logWarnings, strict, false, stripUnknown, rootSchema, reservedNames, caseInsensitive, subShapeValidator, unknownKeys));
856
- }
857
- for (const [key, subName] of subItemNames) {
858
- const propSchema = schemaProps[key];
859
- const itemSchema = propSchema.items;
860
- preamble.push(`type ${subName} = NonNullable<NonNullable<${typeName}>[${JSON.stringify(key)}]>[number];`);
861
- const itemShapeValidator = generateShapeValidator(itemSchema, subName, useRefImports, suffix, false, stripUnknown, reservedNames, unknownKeys);
862
- preamble.push(itemShapeValidator);
863
- preamble.push(`const _every${subName} = (arr: readonly unknown[]): boolean => {
864
- for (let i = 0; i < arr.length; i++) if (!${shapeValidatorName(subName)}(arr[i])) return false;
865
- return true;
866
- };`);
867
- const unionBranches = strict ? null : getUnionBranches(itemSchema);
868
- const scoredItemParser = unionBranches === null ? null : generateScoredUnionParser(generateParserName(subName), subName, unionBranches, {
869
- useRefImports,
870
- suffix,
871
- ...rootSchema !== void 0 ? { rootSchema } : {},
872
- ...logWarnings !== void 0 ? { logWarnings } : {},
873
- reservedNames,
874
- stripUnknown,
875
- unknownKeys,
876
- caseInsensitive
877
- }, false);
878
- preamble.push(scoredItemParser ?? generateObjectParser(itemSchema, subName, useRefImports, suffix, logWarnings, strict, false, stripUnknown, rootSchema, reservedNames, caseInsensitive, itemShapeValidator, unknownKeys));
879
- }
880
- const unionSubNames = /* @__PURE__ */ new Map();
881
- if (!strict) {
882
- const claimedUnionNames = /* @__PURE__ */ new Set([...reservedNames, ...subTypeNames.values(), ...subItemNames.values()]);
883
- for (const key of Object.keys(schemaProps)) {
884
- if (subTypeNames.has(key) || subItemNames.has(key))
885
- continue;
886
- const propSchema = schemaProps[key];
887
- if (!isInlineUnionProperty(propSchema))
888
- continue;
889
- const branches = getUnionBranches(propSchema);
890
- if (branches === null)
891
- continue;
892
- let unionSubName = `${typeName}_${pascalCaseKey(key) || "Value"}Union`;
893
- while (claimedUnionNames.has(unionSubName))
894
- unionSubName = `${unionSubName}_`;
895
- claimedUnionNames.add(unionSubName);
896
- const unionParser = generateScoredUnionParser(generateParserName(unionSubName), unionSubName, branches, {
897
- useRefImports,
898
- suffix,
899
- ...rootSchema !== void 0 ? { rootSchema } : {},
900
- ...logWarnings !== void 0 ? { logWarnings } : {},
901
- reservedNames: claimedUnionNames,
902
- stripUnknown,
903
- unknownKeys,
904
- caseInsensitive
905
- }, false);
906
- if (unionParser === null)
907
- continue;
908
- preamble.push(`type ${unionSubName} = NonNullable<${typeName}>[${JSON.stringify(key)}];`);
909
- preamble.push(unionParser);
910
- unionSubNames.set(key, unionSubName);
911
- }
912
- }
913
- const strictKeys = hasStrictKeys(schema);
914
- const stripKeys = strictKeys || stripUnknown;
915
- const rejectsUnknownKeys = strict && isSchemaObject(schema) && hasAdditionalProperties(schema) && schema.additionalProperties === false && !("patternProperties" in schema) || false;
916
- const guardKeys = stripKeys || rejectsUnknownKeys;
917
- const fastPathBuildsDeclaredLiteral = stripKeys && !(isSchemaObject(schema) && hasAllOf(schema));
918
- const fastPathNeedsKnownKeys = guardKeys && (rejectsUnknownKeys || !fastPathBuildsDeclaredLiteral);
919
- const droppedKnownKeysTerm = guardKeys && !fastPathNeedsKnownKeys;
920
- const exactKeyCount = guardKeys ? exactKeyCountOf(schema) : null;
921
- const strictKeyCheck = unknownKeyCheck(propertyKeys, `_knownKeys${typeName}`);
922
- const shapeValidatorNeedsKnownKeys = shapeValidatorSource?.includes(`_hasOnlyKnownKeys${typeName}(`) ?? false;
923
- if ((fastPathNeedsKnownKeys || shapeValidatorNeedsKnownKeys) && exactKeyCount === null) {
924
- for (const declaration of strictKeyCheck.declarations) {
925
- preamble.push(`${declaration};`);
926
- }
927
- preamble.push(`const _hasOnlyKnownKeys${typeName} = (input: Record<string, unknown>): boolean => {
928
- for (const _k in input) if (${strictKeyCheck.isUnknown("_k")}) return false;
929
- return true;
930
- };`);
931
- } else if (rejectsUnknownKeys) {
932
- for (const declaration of strictKeyCheck.declarations) {
933
- preamble.push(`${declaration};`);
934
- }
935
- }
936
- const fallbackObject = generateFallbackObject(schema, useRefImports, typeName, suffix, subTypeNames);
937
- const propInfo = [];
938
- const hasAllOfRefParsers = useRefImports && isSchemaObject(schema) && hasAllOf(schema) && schema.allOf.some((entry) => isSchemaObject(entry) && hasRef(entry));
939
- let canFastPath = !hasAllOfRefParsers && !hasFastPathBlockingKeyword(schema) && !(strict && inlineAllOfMembers(schema).length > 0);
940
- const fastPathChecks = [];
941
- const fastPathAccessorChecks = [];
942
- const usedVarNames = /* @__PURE__ */ new Set([KEY_COUNT_VAR]);
943
- let inlinedGuardSites = 0;
944
- let inlinedGuardProperties = 0;
945
- for (const key of propertyKeys) {
946
- const propSchema = schemaProps[key];
947
- const isRequired = isPropertyRequired(key, schema);
948
- let varName = toVarName(key);
949
- while (usedVarNames.has(varName))
950
- varName = `${varName}_`;
951
- usedVarNames.add(varName);
952
- propInfo.push({ key, varName, isRequired, propSchema });
953
- if (!canFastPath)
954
- continue;
955
- if (shouldUseRecordRefImport(propSchema, useRefImports)) {
956
- canFastPath = false;
957
- continue;
958
- }
959
- const subName = subTypeNames.get(key);
960
- const inlined = subName !== void 0 && inlinedGuardSites < INLINE_NESTED_MAX_SITES ? inlineNestedShapeCheck(propSchema, subName, varName, useRefImports, suffix, stripUnknown, reservedNames, unknownKeys) : null;
961
- const withinBudget = inlined !== null && inlinedGuardProperties + inlined.propertyCount <= INLINE_NESTED_MAX_TOTAL_PROPERTIES;
962
- if (withinBudget) {
963
- inlinedGuardSites++;
964
- inlinedGuardProperties += inlined.propertyCount;
965
- }
966
- let check = withinBudget ? inlined.check : subName ? `${shapeValidatorName(subName)}(${varName})` : generatePropertyTypeCheck(varName, propSchema, useRefImports, suffix);
967
- const itemSubName = subItemNames.get(key);
968
- if (check !== null && itemSubName) {
969
- check = `${check} && _every${itemSubName}(${varName})`;
970
- }
971
- if (check === null) {
972
- canFastPath = false;
973
- continue;
974
- }
975
- const accessor = safeAccessor("input", key);
976
- let accessorCheck = subName ? `${shapeValidatorName(subName)}(${accessor})` : generatePropertyTypeCheck(accessor, propSchema, useRefImports, suffix);
977
- if (itemSubName) {
978
- accessorCheck = `${accessorCheck} && _every${itemSubName}(${accessor})`;
979
- }
980
- if (isRequired) {
981
- fastPathChecks.push(check);
982
- fastPathAccessorChecks.push(accessorCheck);
983
- } else {
984
- fastPathChecks.push(`(${varName} === undefined || ${check})`);
985
- fastPathAccessorChecks.push(`(${accessor} === undefined || ${accessorCheck})`);
986
- }
987
- }
988
- if (canFastPath) {
989
- if (hasMinProperties(schema)) {
990
- fastPathChecks.push(`Object.keys(input).length >= ${schema.minProperties}`);
991
- fastPathAccessorChecks.push(`Object.keys(input).length >= ${schema.minProperties}`);
992
- }
993
- if (hasMaxProperties(schema)) {
994
- fastPathChecks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
995
- fastPathAccessorChecks.push(`Object.keys(input).length <= ${schema.maxProperties}`);
996
- }
997
- }
998
- const fastPathKeyCount = fastPathNeedsKnownKeys ? exactKeyCount : null;
999
- if (fastPathNeedsKnownKeys && exactKeyCount === null) {
1000
- fastPathChecks.push(`_hasOnlyKnownKeys${typeName}(input)`);
1001
- }
1002
- const deepGuard = canFastPath && (fastPathChecks.length > 0 || fastPathKeyCount !== null) ? fastPathChecks.join(" && ") : null;
1003
- const literalReturnUsesVars = stripKeys && !(isSchemaObject(schema) && hasAllOf(schema)) && exported;
1004
- let deepGuardExpr = deepGuard;
1005
- if (deepGuard !== null && shapeValidatorSource !== void 0 && !literalReturnUsesVars && !droppedKnownKeysTerm) {
1006
- const shapeFnName = shapeValidatorName(typeName);
1007
- const shapeStrictKeysGuard = stripKeys && exactKeyCount === null ? `
1008
- if (!_hasOnlyKnownKeys${typeName}(input)) return false;` : "";
1009
- const expectedShapeValidator = renderShapeValidator(exportPrefix, shapeFnName, fastPathAccessorChecks, stripKeys ? exactKeyCount : null, shapeStrictKeysGuard, unknownKeys);
1010
- if (shapeValidatorSource === expectedShapeValidator) {
1011
- deepGuardExpr = `${shapeFnName}(input)`;
1012
- }
1013
- }
1014
- const deepGuardCallsShape = deepGuardExpr !== deepGuard;
1015
- const inlineKeyCount = deepGuardCallsShape ? null : fastPathKeyCount;
1016
- let canShallowGuard = strict && stripUnknown && !strictKeys && !hasFastPathBlockingKeyword(schema);
1017
- const shallowChecks = [];
1018
- if (canShallowGuard) {
1019
- for (const { key, varName, isRequired, propSchema } of propInfo) {
1020
- const subName = subTypeNames.get(key);
1021
- let check;
1022
- if (subName) {
1023
- check = `isObject(${varName})`;
1024
- } else if (isSchemaObject(propSchema) && !hasEnum(propSchema) && !hasRef(propSchema) && !hasOneOf(propSchema) && !hasAnyOf(propSchema) && !hasAllOf(propSchema) && !("not" in propSchema) && !shouldUseRefImport(propSchema, useRefImports) && !shouldUseArrayRefImport(propSchema, useRefImports) && !shouldUseRecordRefImport(propSchema, useRefImports)) {
1025
- check = generatePropertyTypeCheck(varName, propSchema, useRefImports, suffix);
1026
- } else {
1027
- check = null;
1028
- }
1029
- if (check === null) {
1030
- canShallowGuard = false;
1031
- break;
1032
- }
1033
- shallowChecks.push(isRequired ? check : `(${varName} === undefined || ${check})`);
1034
- }
1035
- }
1036
- const shallowGuard = canShallowGuard && shallowChecks.length > 0 ? shallowChecks.join(" && ") : null;
1037
- const varDeclLines = [];
1038
- for (const { key, varName, propSchema } of propInfo) {
1039
- if (shouldCacheVariable(propSchema)) {
1040
- varDeclLines.push(` const ${varName} = ${safeAccessor("input", key)};`);
1041
- }
1042
- }
1043
- const warnLines = [];
1044
- if (logWarnings && propInfo.length > 0) {
1045
- const warnKeyCheck = unknownKeyCheck(propInfo.map(({ key }) => key), "_knownKeys");
1046
- for (const declaration of warnKeyCheck.declarations) {
1047
- warnLines.push(` ${declaration};`);
1048
- }
1049
- warnLines.push(` for (const _k in input) {`);
1050
- warnLines.push(` if (${warnKeyCheck.isUnknown("_k")}) {`);
1051
- warnLines.push(` console.warn(\`[${typeName}] Unknown property "\${_k}"\`);`);
1052
- warnLines.push(` }`);
1053
- warnLines.push(` }`);
1054
- }
1055
- const claimNestedVarName = (base) => {
1056
- let name = base;
1057
- while (usedVarNames.has(name))
1058
- name = `${name}_`;
1059
- usedVarNames.add(name);
1060
- return name;
1061
- };
1062
- const buildObjectLines = (directAssign, hoistLines) => {
1063
- const entries = [];
1064
- let inlinedSites = 0;
1065
- let inlinedProperties = 0;
1066
- if (!stripKeys) {
1067
- entries.push({ kind: "spread", expression: "input" });
1068
- }
1069
- if (useRefImports && isSchemaObject(schema) && hasAllOf(schema)) {
1070
- for (const entry of schema.allOf) {
1071
- if (isSchemaObject(entry) && hasRef(entry)) {
1072
- const parserName = generateParserName(refToName(entry.$ref, suffix));
1073
- entries.push({ kind: "spread", expression: `(${parserName}(input) as Record<string, unknown>)` });
1074
- }
1075
- }
1076
- }
1077
- for (const { key, varName, isRequired, propSchema } of propInfo) {
1078
- const shouldCache = shouldCacheVariable(propSchema);
1079
- const accessor = shouldCache ? varName : safeAccessor("input", key);
1080
- const unionSubName = unionSubNames.get(key);
1081
- if (unionSubName) {
1082
- const unionParserName = generateParserName(unionSubName);
1083
- if (isRequired) {
1084
- entries.push({ kind: "field", key, value: `${unionParserName}(${accessor})` });
1085
- } else {
1086
- entries.push({ kind: "optional", key, read: accessor, value: `${unionParserName}(${accessor})` });
1087
- }
1088
- continue;
1089
- }
1090
- const subName = subTypeNames.get(key);
1091
- if (subName) {
1092
- const subParserName = generateParserName(subName);
1093
- if (isRequired) {
1094
- const inlined = hoistLines !== void 0 && directAssign && !logWarnings && inlinedSites < INLINE_NESTED_MAX_SITES ? inlineNestedFastPath(propSchema, subName, accessor, useRefImports, suffix, stripUnknown, reservedNames, claimNestedVarName) : null;
1095
- if (inlined !== null && inlinedProperties + inlined.propertyCount <= INLINE_NESTED_MAX_TOTAL_PROPERTIES) {
1096
- inlinedSites++;
1097
- inlinedProperties += inlined.propertyCount;
1098
- hoistLines?.push(...inlined.declarations);
1099
- entries.push({
1100
- kind: "field",
1101
- key,
1102
- value: `(${inlined.condition})
1103
- ? ${inlined.literal}
1104
- : ${subParserName}(${accessor})`
1105
- });
1106
- continue;
1107
- }
1108
- entries.push({ kind: "field", key, value: `${subParserName}(${accessor})` });
1109
- } else {
1110
- entries.push({ kind: "optional", key, read: accessor, value: `${subParserName}(${accessor})` });
1111
- }
1112
- continue;
1113
- }
1114
- const itemSubName = subItemNames.get(key);
1115
- if (itemSubName) {
1116
- const itemParserName = generateParserName(itemSubName);
1117
- if (isRequired) {
1118
- entries.push({ kind: "field", key, value: `validateArray(${accessor}, ${itemParserName})` });
1119
- } else {
1120
- entries.push({
1121
- kind: "optional",
1122
- key,
1123
- read: accessor,
1124
- value: `validateArray(${accessor}, ${itemParserName})`
1125
- });
1126
- }
1127
- continue;
1128
- }
1129
- if (shouldUseRefImport(propSchema, useRefImports)) {
1130
- const ref = propSchema.$ref;
1131
- const parserName = generateParserName(refToName(ref, suffix));
1132
- if (isRequired) {
1133
- entries.push({ kind: "field", key, value: `${parserName}(${accessor})` });
1134
- } else {
1135
- entries.push({ kind: "optional", key, read: accessor, value: `${parserName}(${accessor})` });
1136
- }
1137
- continue;
1138
- }
1139
- if (shouldUseArrayRefImport(propSchema, useRefImports)) {
1140
- const items = propSchema.items;
1141
- const ref = items.$ref;
1142
- const parserName = generateParserName(refToName(ref, suffix));
1143
- if (isRequired) {
1144
- entries.push({ kind: "field", key, value: `validateArray(${accessor}, ${parserName})` });
1145
- } else {
1146
- entries.push({ kind: "optional", key, read: accessor, value: `validateArray(${accessor}, ${parserName})` });
1147
- }
1148
- continue;
1149
- }
1150
- if (shouldUseRecordRefImport(propSchema, useRefImports)) {
1151
- const additionalProps = propSchema.additionalProperties;
1152
- const ref = additionalProps.$ref;
1153
- const parserName = generateParserName(refToName(ref, suffix));
1154
- if (isRequired) {
1155
- entries.push({ kind: "field", key, value: `validateRecord(${accessor}, ${parserName})` });
1156
- } else {
1157
- entries.push({ kind: "optional", key, read: accessor, value: `validateRecord(${accessor}, ${parserName})` });
1158
- }
1159
- continue;
1160
- }
1161
- if (!isSchemaObject(propSchema)) {
1162
- if (propSchema === true) {
1163
- if (isRequired) {
1164
- entries.push({ kind: "field", key, value: accessor });
1165
- } else if (stripKeys) {
1166
- entries.push({ kind: "optional", key, read: accessor, value: accessor });
1167
- }
1168
- } else if (isRequired) {
1169
- entries.push({ kind: "field", key, value: "undefined" });
1170
- }
1171
- continue;
1172
- }
1173
- let valueExpr;
1174
- if (directAssign) {
1175
- valueExpr = accessor;
1176
- } else {
1177
- const defaultValue = getDefaultValue(propSchema);
1178
- const knownNotUndefined = !isRequired;
1179
- valueExpr = generateValidationExpression(key, propSchema, defaultValue, true, void 0, void 0, shouldCache ? varName : void 0, knownNotUndefined, caseInsensitive);
1180
- }
1181
- if (isRequired) {
1182
- entries.push({ kind: "field", key, value: valueExpr });
1183
- } else {
1184
- entries.push({ kind: "optional", key, read: accessor, value: valueExpr });
1185
- }
1186
- }
1187
- return entries;
1188
- };
1189
- const emitReturn = (lines2, entries) => {
1190
- lines2.push(...renderResultObject(entries, typeName));
1191
- };
1192
- const withKeyCount = (lines2, guarded) => {
1193
- if (inlineKeyCount === null) {
1194
- lines2.push(...guarded(deepGuardExpr));
1195
- return;
1196
- }
1197
- const count = keyCountCheck(unknownKeys, inlineKeyCount, " ");
1198
- if (count.statements.length === 0) {
1199
- lines2.push(...guarded(deepGuardExpr === "" ? count.verdict : `${deepGuardExpr} && ${count.verdict}`));
1200
- return;
1201
- }
1202
- const counted = guarded(count.verdict);
1203
- if (deepGuardExpr === "") {
1204
- lines2.push(...keyCountCheck(unknownKeys, inlineKeyCount, " ").statements, ...counted);
1205
- return;
1206
- }
1207
- const nested = counted.map((line) => line.split("\n").map((part) => ` ${part}`).join("\n"));
1208
- lines2.push(` if (${deepGuardExpr}) {`, ...count.statements, ...nested, ` }`);
1209
- };
1210
- const emitGuardedReturn = (lines2, statement) => {
1211
- withKeyCount(lines2, (guard) => [` if (${guard}) ${statement}`]);
1212
- };
1213
- const emitGuardedResult = (lines2, fields) => {
1214
- withKeyCount(lines2, (guard) => renderResultObject(fields, typeName, guard));
1215
- };
1216
- const emitDeepGuardReturn = (lines2) => {
1217
- if (!fastPathBuildsDeclaredLiteral) {
1218
- emitGuardedReturn(lines2, `return { ...input } as ${typeName};`);
1219
- return;
1220
- }
1221
- if (!exported && fastPathNeedsKnownKeys) {
1222
- emitGuardedReturn(lines2, `return input as ${typeName};`);
1223
- return;
1224
- }
1225
- const fields = [];
1226
- for (const { key, varName, isRequired, propSchema } of propInfo) {
1227
- if (!isSchemaObject(propSchema)) {
1228
- if (propSchema === true) {
1229
- const acc = safeAccessor("input", key);
1230
- fields.push(isRequired ? { kind: "field", key, value: acc } : { kind: "optional", key, read: acc, value: acc });
1231
- } else if (isRequired) {
1232
- fields.push({ kind: "field", key, value: "undefined" });
1233
- }
1234
- continue;
1235
- }
1236
- const accessor = shouldCacheVariable(propSchema) ? varName : safeAccessor("input", key);
1237
- fields.push(isRequired ? { kind: "field", key, value: accessor } : { kind: "optional", key, read: accessor, value: accessor });
1238
- }
1239
- emitGuardedResult(lines2, fields);
1240
- };
1241
- const assertionLines = strict ? generateObjectStrictAssertion(schema, typeName, {
1242
- useRefImports,
1243
- suffix,
1244
- stripUnknown,
1245
- ...rootSchema !== void 0 ? { rootSchema } : {}
1246
- }) : [];
1247
- const unknownKeyThrowLines = () => rejectsUnknownKeys ? [
1248
- ` for (const _k in input) {`,
1249
- ` if (${strictKeyCheck.isUnknown("_k")}) throw new Error(\`[${typeName}] unknown property "\${_k}"\`);`,
1250
- ` }`
1251
- ] : [];
1252
- const varsAfterGuard = deepGuardCallsShape && shallowGuard === null;
1253
- const assertBodyLines = [...assertionLines.slice(1), ...shallowGuard === null ? unknownKeyThrowLines() : []];
1254
- const canSplit = !logWarnings && strict === true && (deepGuard !== null || shallowGuard !== null) && assertBodyLines.length > 0;
1255
- const assertName = `_assert${typeName}`;
1256
- const assertObjectName = `_assert${typeName}Object`;
1257
- const emitAssertObjectFunction = () => `const ${assertObjectName}: (input: unknown) => asserts input is Record<string, unknown> = (input) => {
1258
- ${assertionLines[0]}
1259
- };`;
1260
- const emitAssertFunction = () => `const ${assertName} = (input: Record<string, unknown>): void => {
1261
- ${assertBodyLines.join("\n")}
1262
- };`;
1263
- const lines = [];
1264
- lines.push(`${exportPrefix}const ${functionName} = (input: unknown): ${typeName} => {`);
1265
- if (canSplit) {
1266
- preamble.push(emitAssertObjectFunction());
1267
- preamble.push(emitAssertFunction());
1268
- lines.push(` if (!isObject(input)) ${assertObjectName}(input);`);
1269
- if (!varsAfterGuard)
1270
- lines.push(...varDeclLines);
1271
- if (shallowGuard) {
1272
- lines.push(` if (!(${shallowGuard})) ${assertName}(input);`);
1273
- if (!exported && deepGuard !== null && fastPathNeedsKnownKeys) {
1274
- const residual = [];
1275
- for (let i = 0; i < fastPathChecks.length; i++) {
1276
- const deep = fastPathChecks[i];
1277
- const shallow = shallowChecks[i];
1278
- if (deep === shallow)
1279
- continue;
1280
- residual.push(shallow !== void 0 && deep.startsWith(`${shallow} && `) ? deep.slice(shallow.length + 4) : deep);
1281
- }
1282
- const returnInput = `return input as ${typeName};`;
1283
- if (fastPathKeyCount === null) {
1284
- lines.push(residual.length > 0 ? ` if (${residual.join(" && ")}) ${returnInput}` : ` ${returnInput}`);
1285
- } else {
1286
- const count = keyCountCheck(unknownKeys, fastPathKeyCount, residual.length === 0 ? " " : " ");
1287
- if (count.statements.length === 0) {
1288
- lines.push(` if (${[...residual, count.verdict].join(" && ")}) ${returnInput}`);
1289
- } else if (residual.length === 0) {
1290
- lines.push(...count.statements, ` if (${count.verdict}) ${returnInput}`);
1291
- } else {
1292
- lines.push(` if (${residual.join(" && ")}) {`);
1293
- lines.push(...count.statements, ` if (${count.verdict}) ${returnInput}`);
1294
- lines.push(` }`);
1295
- }
1296
- }
1297
- }
1298
- const hoisted = [];
1299
- const objectLines = buildObjectLines(true, hoisted);
1300
- lines.push(...hoisted);
1301
- emitReturn(lines, objectLines);
1302
- } else {
1303
- emitDeepGuardReturn(lines);
1304
- if (varsAfterGuard)
1305
- lines.push(...varDeclLines);
1306
- lines.push(` ${assertName}(input);`);
1307
- emitReturn(lines, buildObjectLines(true));
1308
- }
1309
- } else if (strict) {
1310
- lines.push(` if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);`);
1311
- if (!varsAfterGuard)
1312
- lines.push(...varDeclLines);
1313
- lines.push(...warnLines);
1314
- if (shallowGuard) {
1315
- if (!exported && deepGuard && fastPathNeedsKnownKeys) {
1316
- const residual = [];
1317
- for (let i = 0; i < fastPathChecks.length; i++) {
1318
- const deep = fastPathChecks[i];
1319
- const shallow = shallowChecks[i];
1320
- if (deep === shallow)
1321
- continue;
1322
- residual.push(shallow !== void 0 && deep.startsWith(`${shallow} && `) ? deep.slice(shallow.length + 4) : deep);
1323
- }
1324
- lines.push(` if (${shallowGuard}) {`);
1325
- const returnInput = `return input as ${typeName};`;
1326
- if (fastPathKeyCount === null) {
1327
- lines.push(residual.length > 0 ? ` if (${residual.join(" && ")}) ${returnInput}` : ` ${returnInput}`);
1328
- } else {
1329
- const count = keyCountCheck(unknownKeys, fastPathKeyCount, residual.length === 0 ? " " : " ");
1330
- if (count.statements.length === 0) {
1331
- lines.push(` if (${[...residual, count.verdict].join(" && ")}) ${returnInput}`);
1332
- } else if (residual.length === 0) {
1333
- lines.push(...count.statements, ` if (${count.verdict}) ${returnInput}`);
1334
- } else {
1335
- lines.push(` if (${residual.join(" && ")}) {`);
1336
- lines.push(...count.statements, ` if (${count.verdict}) ${returnInput}`);
1337
- lines.push(` }`);
1338
- }
1339
- }
1340
- lines.push(` } else {`);
1341
- for (const assertionLine of assertionLines.slice(1))
1342
- lines.push(` ${assertionLine}`);
1343
- lines.push(` }`);
1344
- } else {
1345
- lines.push(` if (!(${shallowGuard})) {`);
1346
- for (const assertionLine of assertionLines.slice(1))
1347
- lines.push(` ${assertionLine}`);
1348
- lines.push(` }`);
1349
- }
1350
- emitReturn(lines, buildObjectLines(true));
1351
- } else {
1352
- if (deepGuard !== null) {
1353
- emitDeepGuardReturn(lines);
1354
- }
1355
- if (varsAfterGuard)
1356
- lines.push(...varDeclLines);
1357
- for (const assertionLine of assertionLines.slice(1))
1358
- lines.push(assertionLine);
1359
- lines.push(...unknownKeyThrowLines());
1360
- emitReturn(lines, buildObjectLines(true));
1361
- }
1362
- } else {
1363
- lines.push(` if (!isObject(input)) return ${fallbackObject};`);
1364
- if (!varsAfterGuard)
1365
- lines.push(...varDeclLines);
1366
- lines.push(...warnLines);
1367
- if (deepGuard !== null) {
1368
- emitDeepGuardReturn(lines);
1369
- }
1370
- if (varsAfterGuard)
1371
- lines.push(...varDeclLines);
1372
- emitReturn(lines, buildObjectLines(false));
1373
- }
1374
- lines.push(`}`);
1375
- const fn = lines.join("\n");
1376
- if (preamble.length === 0)
1377
- return fn;
1378
- return `${preamble.join("\n\n")}
1379
-
1380
- ${fn}`;
1381
- };
1382
- 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}; }`;
1383
- const generateStrictCombinedParser = (schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict, context = {}) => {
1384
- const declaredKeys = hasProperties(schema) ? Object.keys(schema.properties) : [];
1385
- const knownKeyCheck = unknownKeyCheck(declaredKeys, "_knownKeys");
1386
- const refPattern = useRefImports ? patterns.find(([, ps]) => isSchemaObject(ps) && hasRef(ps)) : void 0;
1387
- const loopLines = [` if (${knownKeyCheck.isKnown("key")}) continue;`];
1388
- if (refPattern) {
1389
- const parserName = generateParserName(refToName(refPattern[1].$ref, suffix));
1390
- loopLines.push(` if (${regexLiteral(refPattern[0])}.test(key)) {`);
1391
- loopLines.push(` ${safeResultAssign("key", `${parserName}(input[key])`)}`);
1392
- loopLines.push(` continue;`);
1393
- loopLines.push(` }`);
1394
- }
1395
- const keepConditions = patterns.filter(([p]) => !(refPattern && p === refPattern[0])).map(([p]) => `${regexLiteral(p)}.test(key)`);
1396
- if (keepConditions.length > 0) {
1397
- loopLines.push(` if (${keepConditions.join(" || ")}) {`);
1398
- loopLines.push(` ${safeResultAssign("key", "input[key]")}`);
1399
- loopLines.push(` continue;`);
1400
- loopLines.push(` }`);
1401
- }
1402
- if (strict)
1403
- loopLines.push(` throw new Error(\`[${typeName}] unknown property "\${key}"\`);`);
1404
- const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
1405
- const resultBody = propertyLines.length > 0 ? `{
1406
- ${propertyLines.join("\n")}
1407
- }` : "{}";
1408
- const knownKeysDeclaration = knownKeyCheck.declarations.map((decl) => ` ${decl};
1409
- `).join("");
1410
- const assertionPrelude = strict ? generateObjectStrictAssertion(schema, typeName, context).slice(1) : [];
1411
- const keywordPrelude = assertionPrelude.length > 0 ? `
1412
- ${assertionPrelude.join("\n")}` : "";
1413
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1414
- if (!isObject(input)) {
1415
- ${notObjectBranch}
1416
- }${keywordPrelude}
1417
- ${knownKeysDeclaration} const result = ${resultBody} as unknown as ${typeName};
1418
- for (const key in input) {
1419
- ${loopLines.join("\n")}
1420
- }
1421
- return result;
1422
- };`;
1423
- };
1424
- const generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown = false, caseInsensitive = false, context = {}) => {
1425
- const functionName = generateParserName(typeName);
1426
- const unknownKeys = context.unknownKeys ?? DEFAULT_UNKNOWN_KEYS;
1427
- const entries = generatePropertyEntries(schema, useRefImports, suffix, caseInsensitive);
1428
- const propertyLines = entries.map((entry) => {
1429
- if (entry.isOptional) {
1430
- return ` ${entry.value},`;
1431
- }
1432
- return ` ${safeLiteralKey(entry.key)}: ${entry.value},`;
1433
- });
1434
- if (!isSchemaObject(schema) || !("patternProperties" in schema)) {
1435
- return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive, void 0, unknownKeys);
1436
- }
1437
- const patternProps = schema.patternProperties;
1438
- const patterns = Object.entries(patternProps);
1439
- const refPattern = patterns.find(([, ps]) => isSchemaObject(ps) && hasRef(ps));
1440
- const strictKeys = hasAdditionalProperties(schema) && schema.additionalProperties === false;
1441
- if (strictKeys) {
1442
- return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, strict, context);
1443
- }
1444
- if (stripUnknown && !strict) {
1445
- return generateStrictCombinedParser(schema, typeName, functionName, propertyLines, patterns, useRefImports, suffix, false, context);
1446
- }
1447
- if (!refPattern || !useRefImports) {
1448
- return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive, void 0, unknownKeys);
1449
- }
1450
- const [pattern, patternSchema] = refPattern;
1451
- const ref = patternSchema.$ref;
1452
- const parserName = generateParserName(refToName(ref, suffix));
1453
- const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
1454
- const patternLiteral = regexLiteral(pattern);
1455
- const inputSpread = " ...input,";
1456
- let objectProperties = inputSpread;
1457
- if (propertyLines.length > 0) {
1458
- for (const line of propertyLines) {
1459
- objectProperties += "\n" + line;
1460
- }
1461
- }
1462
- const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
1463
- const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
1464
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1465
- if (!isObject(input)) {
1466
- ${notObjectBranch}
1467
- }${keywordPrelude}
1468
- const result = {
1469
- ${objectProperties}
1470
- } as unknown as ${typeName};
1471
- for (const key in input) {
1472
- if (${patternLiteral}.test(key)) {
1473
- const value = input[key];
1474
- ${assignmentCode}
1475
- }
1476
- }
1477
- return result;
1478
- };`;
1479
- };
1480
- const generateAdditionalPropertiesParser = (schema, typeName, useRefImports, suffix, strict, context = {}) => {
1481
- const functionName = generateParserName(typeName);
1482
- const additionalProps = schema.additionalProperties;
1483
- if (!additionalProps) {
1484
- return generateEmptyObjectParser(typeName, strict, schema, context);
1485
- }
1486
- const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
1487
- if (useRefImports && isSchemaObject(additionalProps) && hasRef(additionalProps)) {
1488
- const ref = additionalProps.$ref;
1489
- const parserName = generateParserName(refToName(ref, suffix));
1490
- if (strict) {
1491
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1492
- if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${keywordPrelude}
1493
- return validateRecord(input, ${parserName}) as ${typeName};
1494
- };`;
1495
- }
1496
- return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${parserName}) as ${typeName};`;
1497
- }
1498
- if (isSchemaObject(additionalProps) && hasType(additionalProps)) {
1499
- const inlineParser = generateInlineValueParser(additionalProps, strict, `[${typeName}]`);
1500
- if (inlineParser) {
1501
- if (strict) {
1502
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1503
- if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${keywordPrelude}
1504
- return validateRecord(input, ${inlineParser}) as ${typeName};
1505
- };`;
1506
- }
1507
- return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${inlineParser}) as ${typeName};`;
1508
- }
1509
- }
1510
- if (strict) {
1511
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1512
- if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);${keywordPrelude}
1513
- return { ...input } as ${typeName};
1514
- };`;
1515
- }
1516
- return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {};`;
1517
- };
1518
- const generateInlineValueParser = (schema, strict = false, label = "") => {
1519
- if (!isSchemaObject(schema) || !hasType(schema)) {
1520
- return null;
1521
- }
1522
- if (strict) {
1523
- const err = (t) => `throw new Error(${JSON.stringify(`${label} record value must be ${t}`)})`;
1524
- switch (schema.type) {
1525
- case "string":
1526
- return `(value: unknown) => { if (typeof value !== "string") ${err("string")}; return value; }`;
1527
- case "number":
1528
- return `(value: unknown) => { if (typeof value !== "number") ${err("number")}; return value; }`;
1529
- case "integer":
1530
- return `(value: unknown) => { if (typeof value !== "number" || !Number.isInteger(value)) ${err("integer")}; return value; }`;
1531
- case "boolean":
1532
- return `(value: unknown) => { if (typeof value !== "boolean") ${err("boolean")}; return value; }`;
1533
- case "array":
1534
- return `(value: unknown) => { if (!Array.isArray(value)) ${err("array")}; return value; }`;
1535
- default:
1536
- return null;
1537
- }
1538
- }
1539
- switch (schema.type) {
1540
- case "string":
1541
- return '(value: unknown) => typeof value === "string" ? value : ""';
1542
- case "number":
1543
- return '(value: unknown) => typeof value === "number" ? value : 0';
1544
- case "integer":
1545
- return '(value: unknown) => typeof value === "number" && Number.isInteger(value) ? value : 0';
1546
- case "boolean":
1547
- return '(value: unknown) => typeof value === "boolean" ? value : false';
1548
- case "array":
1549
- return "(value: unknown) => Array.isArray(value) ? value : []";
1550
- default:
1551
- return null;
1552
- }
1553
- };
1554
- const generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix, strict, context = {}) => {
1555
- const functionName = generateParserName(typeName);
1556
- if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
1557
- return generateEmptyObjectParser(typeName, strict, schema, context);
1558
- }
1559
- const patternProps = schema.patternProperties;
1560
- const patterns = Object.entries(patternProps);
1561
- if (patterns.length === 0) {
1562
- return generateEmptyObjectParser(typeName, strict, schema, context);
1563
- }
1564
- const [pattern, patternSchema] = patterns[0];
1565
- let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
1566
- let patternValueExpr = "value";
1567
- if (useRefImports && isSchemaObject(patternSchema) && hasRef(patternSchema)) {
1568
- const ref = patternSchema.$ref;
1569
- const parserName = generateParserName(refToName(ref, suffix));
1570
- patternAssignment = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
1571
- patternValueExpr = `${parserName}(value)`;
1572
- } else if (patternSchema === false) {
1573
- patternAssignment = "";
1574
- patternValueExpr = null;
1575
- }
1576
- const patternLiteral = regexLiteral(pattern);
1577
- const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
1578
- const keywordPrelude = strict ? strictObjectKeywordPrelude(schema, typeName, context) : "";
1579
- const strictKeys = hasAdditionalProperties(schema) && schema.additionalProperties === false;
1580
- if (strictKeys) {
1581
- const loopLines = [` if (${patternLiteral}.test(key)) {`];
1582
- if (patternValueExpr) {
1583
- loopLines.push(` const value = input[key];`);
1584
- loopLines.push(` ${safeResultAssign("key", patternValueExpr)}`);
1585
- }
1586
- loopLines.push(` continue;`);
1587
- loopLines.push(` }`);
1588
- const keepConditions = patterns.slice(1).map(([p]) => `${regexLiteral(p)}.test(key)`);
1589
- if (keepConditions.length > 0) {
1590
- loopLines.push(` if (${keepConditions.join(" || ")}) {`);
1591
- loopLines.push(` ${safeResultAssign("key", "input[key]")}`);
1592
- loopLines.push(` continue;`);
1593
- loopLines.push(` }`);
1594
- }
1595
- if (strict)
1596
- loopLines.push(` throw new Error(\`[${typeName}] unknown property "\${key}"\`);`);
1597
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1598
- if (!isObject(input)) {
1599
- ${notObjectBranch}
1600
- }${keywordPrelude}
1601
- const result = {} as unknown as ${typeName};
1602
- for (const key in input) {
1603
- ${loopLines.join("\n")}
1604
- }
1605
- return result;
1606
- };`;
1607
- }
1608
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1609
- if (!isObject(input)) {
1610
- ${notObjectBranch}
1611
- }${keywordPrelude}
1612
- const result = {
1613
- ...input,
1614
- } as unknown as ${typeName};
1615
- for (const key in input) {
1616
- if (${patternLiteral}.test(key)) {
1617
- const value = input[key];
1618
- ${patternAssignment}
1619
- }
1620
- }
1621
- return result;
1622
- };`;
1623
- };
1624
- const generateConditionalParser = (schema, typeName, suffix, stripUnknown = false, caseInsensitive = false, unknownKeys = DEFAULT_UNKNOWN_KEYS) => {
1625
- const functionName = generateParserName(typeName);
1626
- const ifSchema = schema.if;
1627
- const thenSchema = schema.then;
1628
- const elseSchema = schema.else;
1629
- const thenHasRef = thenSchema && isSchemaObject(thenSchema) && hasRef(thenSchema);
1630
- const elseHasRef = elseSchema && isSchemaObject(elseSchema) && hasRef(elseSchema);
1631
- if (!thenHasRef || !elseHasRef) {
1632
- const mergedSchema = getConditionalObjectSchema(schema);
1633
- if (mergedSchema) {
1634
- return generateObjectParser(mergedSchema, typeName, false, suffix, false, false, true, stripUnknown, void 0, NO_RESERVED_NAMES, caseInsensitive, void 0, unknownKeys);
1635
- }
1636
- return generateEmptyObjectParser(typeName);
1637
- }
1638
- const isRefCondition = ifSchema && isSchemaObject(ifSchema) && "required" in ifSchema && Array.isArray(ifSchema.required) && ifSchema.required.includes("$ref");
1639
- if (!isRefCondition) {
1640
- const mergedSchema = getConditionalObjectSchema(schema);
1641
- if (mergedSchema) {
1642
- return generateObjectParser(mergedSchema, typeName, false, suffix, false, false, true, stripUnknown, void 0, NO_RESERVED_NAMES, caseInsensitive, void 0, unknownKeys);
1643
- }
1644
- return generateEmptyObjectParser(typeName);
1645
- }
1646
- const thenRef = thenSchema.$ref;
1647
- const elseRef = elseSchema.$ref;
1648
- const thenParserName = generateParserName(refToName(thenRef, suffix));
1649
- const elseParserName = generateParserName(refToName(elseRef, suffix));
1650
- const thenTypeName = refToName(thenRef, suffix);
1651
- return `export const ${functionName} = (input: unknown): ${typeName} | ${thenTypeName} =>
1652
- hasRef(input) ? ${thenParserName}(input) : ${elseParserName}(input)
1653
- `;
1654
- };
1655
- const getConditionalObjectSchema = (schema) => {
1656
- if (!isSchemaObject(schema)) {
1657
- return null;
1658
- }
1659
- if (!("if" in schema) || !("then" in schema)) {
1660
- return null;
1661
- }
1662
- const ifSchema = schema.if;
1663
- const thenSchema = schema.then;
1664
- const elseSchema = "else" in schema ? schema.else : void 0;
1665
- if (!isSchemaObject(ifSchema) || !isSchemaObject(thenSchema)) {
1666
- return null;
1667
- }
1668
- const ifProperties = ifSchema.properties;
1669
- const thenProperties = thenSchema.properties;
1670
- const elseProperties = elseSchema && isSchemaObject(elseSchema) ? elseSchema.properties : void 0;
1671
- const hasIfProperties = ifProperties && typeof ifProperties === "object";
1672
- const hasThenProperties = thenProperties && typeof thenProperties === "object";
1673
- const hasElseProperties = elseProperties && typeof elseProperties === "object";
1674
- if (!hasIfProperties && !hasThenProperties && !hasElseProperties) {
1675
- return null;
1676
- }
1677
- const required = /* @__PURE__ */ new Set();
1678
- if (Array.isArray(ifSchema.required)) {
1679
- for (const key of ifSchema.required) {
1680
- required.add(key);
1681
- }
1682
- }
1683
- if (hasIfProperties && ifProperties && typeof ifProperties === "object") {
1684
- for (const [key, propSchema] of Object.entries(ifProperties)) {
1685
- if (isSchemaObject(propSchema) && hasConst(propSchema)) {
1686
- required.add(key);
1687
- }
1688
- }
1689
- }
1690
- if (Array.isArray(thenSchema.required)) {
1691
- for (const key of thenSchema.required) {
1692
- required.add(key);
1693
- }
1694
- }
1695
- return {
1696
- type: "object",
1697
- properties: {
1698
- // else properties first so then properties take precedence on overlap
1699
- ...hasElseProperties ? elseProperties : {},
1700
- ...hasIfProperties ? ifProperties : {},
1701
- ...hasThenProperties ? thenProperties : {}
1702
- },
1703
- ...required.size > 0 ? { required: Array.from(required) } : {}
1704
- };
1705
- };
1706
- const OBJECT_SHAPE_KEYWORDS = ["properties", "patternProperties", "additionalProperties", "required"];
1707
- const DELEGATING_KEYWORDS = ["$ref", "$dynamicRef", "allOf", "anyOf", "oneOf", "if", "not"];
1708
- const admitsBooleanSchema = (schema) => {
1709
- if (typeof schema === "boolean")
1710
- return true;
1711
- if (!isSchemaObject(schema))
1712
- return false;
1713
- const record = schema;
1714
- const type = readKey(record, "type");
1715
- if (type !== void 0)
1716
- return Array.isArray(type) ? type.includes("boolean") : type === "boolean";
1717
- return ![...OBJECT_SHAPE_KEYWORDS, ...DELEGATING_KEYWORDS].some((keyword) => declaresKey(record, keyword));
1718
- };
1719
- const generateSchemaObjectParser = (typeName, schema) => {
1720
- const functionName = generateParserName(typeName);
1721
- const booleanBranch = admitsBooleanSchema(schema) ? ` if (typeof input === 'boolean') {
1722
- return input as ${typeName};
1723
- }
1724
-
1725
- ` : "";
1726
- return `export const ${functionName} = (input: unknown): ${typeName} => {
1727
- ${booleanBranch} if (!isObject(input)) {
1728
- return {} as ${typeName};
1729
- }
1730
-
1731
- return input as ${typeName};
1732
- };`;
1733
- };
1734
- const selectParserStrategy = (schema, typeName, options) => {
1735
- const useRefImports = options?.useRefImports ?? false;
1736
- const logWarnings = options?.logWarnings ?? false;
1737
- const strict = options?.strict ?? false;
1738
- const stripUnknown = options?.stripUnknown ?? false;
1739
- const caseInsensitive = options?.caseInsensitive ?? false;
1740
- const unknownKeys = options?.unknownKeys ?? DEFAULT_UNKNOWN_KEYS;
1741
- const suffix = options?.typeSuffix ?? "";
1742
- const context = {
1743
- useRefImports,
1744
- suffix,
1745
- stripUnknown,
1746
- unknownKeys,
1747
- ...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {}
1748
- };
1749
- const copyLoopContext = { ...context, subParsers: false };
1750
- if (!options?.isRoot && typeName === `Schema${suffix}`) {
1751
- return generateSchemaObjectParser(typeName, schema);
1752
- }
1753
- const isObjectLikeSchema = isObjectSchema(schema) || isSchemaObject(schema) && ("patternProperties" in schema || "additionalProperties" in schema);
1754
- if (!isObjectLikeSchema && !isSchemaObject(schema)) {
1755
- return generateNonObjectParser(typeName, schema, strict, {
1756
- useRefImports,
1757
- suffix,
1758
- stripUnknown,
1759
- caseInsensitive,
1760
- logWarnings,
1761
- unknownKeys,
1762
- ...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
1763
- ...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
1764
- });
1765
- }
1766
- if (hasProperties(schema) && isSchemaObject(schema) && "patternProperties" in schema) {
1767
- return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, stripUnknown, caseInsensitive, copyLoopContext);
1768
- }
1769
- if (strict && isSchemaObject(schema) && Array.isArray(schema.type) && schema.type.includes("null")) {
1770
- return generateNonObjectParser(typeName, schema, strict, {
1771
- useRefImports,
1772
- suffix,
1773
- stripUnknown,
1774
- caseInsensitive,
1775
- logWarnings,
1776
- unknownKeys,
1777
- ...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
1778
- ...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
1779
- });
1780
- }
1781
- if (hasProperties(schema)) {
1782
- return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, options?.rootSchema, options?.reservedNames, caseInsensitive, options?.shapeValidatorSource, unknownKeys);
1783
- }
1784
- if (strict && isSchemaObject(schema) && ("if" in schema || "then" in schema || "else" in schema)) {
1785
- return generateNonObjectParser(typeName, schema, strict, {
1786
- useRefImports,
1787
- suffix,
1788
- stripUnknown,
1789
- caseInsensitive,
1790
- logWarnings,
1791
- unknownKeys,
1792
- ...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
1793
- ...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
1794
- });
1795
- }
1796
- if (isSchemaObject(schema) && "if" in schema && "then" in schema && "else" in schema) {
1797
- return generateConditionalParser(schema, typeName, suffix, stripUnknown, caseInsensitive, unknownKeys);
1798
- }
1799
- const conditionalObjectSchema = getConditionalObjectSchema(schema);
1800
- if (conditionalObjectSchema) {
1801
- return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, suffix, logWarnings, strict, true, stripUnknown, context.rootSchema, NO_RESERVED_NAMES, caseInsensitive, void 0, unknownKeys);
1802
- }
1803
- if (!isObjectLikeSchema) {
1804
- return generateNonObjectParser(typeName, schema, strict, {
1805
- useRefImports,
1806
- suffix,
1807
- stripUnknown,
1808
- caseInsensitive,
1809
- logWarnings,
1810
- unknownKeys,
1811
- ...options?.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
1812
- ...options?.reservedNames !== void 0 ? { reservedNames: options.reservedNames } : {}
1813
- });
1814
- }
1815
- if ("patternProperties" in schema) {
1816
- return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict, copyLoopContext);
1817
- }
1818
- if ("additionalProperties" in schema && !hasProperties(schema)) {
1819
- const additionalProps = schema.additionalProperties;
1820
- if (additionalProps === true || additionalProps === false) {
1821
- return generateEmptyObjectParser(typeName, strict, schema, context);
1822
- }
1823
- return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict, copyLoopContext);
1824
- }
1825
- if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
1826
- return generateEmptyObjectParser(typeName, strict, schema, context);
1827
- }
1828
- return generateEmptyObjectParser(typeName, strict, schema, context);
1829
- };
1830
- const generateParserFunction = (schema, typeName, rawOptions) => {
1831
- const options = rawOptions?.strict && rawOptions.rootSchema === void 0 && isSchemaObject(schema) ? { ...rawOptions, rootSchema: schema } : rawOptions;
1832
- if (options?.strict) {
1833
- assertNoUnsupportedKeywords(schema, typeName, {
1834
- useRefImports: options.useRefImports ?? false,
1835
- suffix: options.typeSuffix ?? "",
1836
- ...options.rootSchema !== void 0 ? { rootSchema: options.rootSchema } : {},
1837
- ...options.stripUnknown !== void 0 ? { stripUnknown: options.stripUnknown } : {}
1838
- });
1839
- }
1840
- return selectParserStrategy(schema, typeName, options);
1841
- };
1842
- const shapeValidatorFunctionName = (typeName) => shapeValidatorName(typeName);
1843
- const generateShapeValidator = (schema, typeName, useRefImports, suffix = "", exported = true, stripUnknown = false, reservedNames = NO_RESERVED_NAMES, unknownKeys = DEFAULT_UNKNOWN_KEYS) => {
1844
- const fnName = shapeValidatorName(typeName);
1845
- const exportPrefix = exported ? "export " : "";
1846
- const stub = `${exportPrefix}const ${fnName} = (_input: unknown): boolean => false;`;
1847
- if (!isSchemaObject(schema))
1848
- return stub;
1849
- if (hasFastPathBlockingKeyword(schema))
1850
- return stub;
1851
- if (useRefImports && hasRef(schema) && !hasProperties(schema)) {
1852
- const refName = refToName(schema.$ref, suffix);
1853
- if (refName !== typeName) {
1854
- return `${exportPrefix}const ${fnName} = (input: unknown): boolean => ${shapeValidatorName(refName)}(input);`;
1855
- }
1856
- return stub;
1857
- }
1858
- if (!hasProperties(schema) && !("patternProperties" in schema) && !("if" in schema) && !stripUnknown) {
1859
- const branches = getUnionBranches(schema);
1860
- if (branches) {
1861
- const check = generateUnionCheck("input", branches, useRefImports, suffix, isExclusiveUnion(schema));
1862
- if (check !== null) {
1863
- return `${exportPrefix}const ${fnName} = (input: unknown): boolean => ${check};`;
1864
- }
1865
- return stub;
1866
- }
1867
- const direct = generatePropertyTypeCheck("input", schema, useRefImports, suffix);
1868
- if (direct !== null) {
1869
- return `${exportPrefix}const ${fnName} = (input: unknown): boolean => ${direct};`;
1870
- }
1871
- return stub;
1872
- }
1873
- const shape = shapeChecksOf(schema, typeName, "input", useRefImports, suffix, stripUnknown, reservedNames);
1874
- if (shape === null)
1875
- return stub;
1876
- const strictKeysGuard = shape.walksKeys ? `
1877
- if (!_hasOnlyKnownKeys${typeName}(input)) return false;` : "";
1878
- return renderShapeValidator(exportPrefix, fnName, shape.checks, shape.keyCount, strictKeysGuard, unknownKeys);
1879
- };
1880
- const shapeChecksOf = (schema, typeName, root, useRefImports, suffix, stripUnknown, reservedNames) => {
1881
- if (!isSchemaObject(schema))
1882
- return null;
1883
- if (hasFastPathBlockingKeyword(schema))
1884
- return null;
1885
- if (!hasProperties(schema))
1886
- return null;
1887
- if (hasOneOf(schema) || hasAnyOf(schema) || hasAllOf(schema) || "not" in schema || "patternProperties" in schema || "if" in schema || "then" in schema || "else" in schema) {
1888
- return null;
1889
- }
1890
- const schemaProps = schema.properties;
1891
- const propertyKeys = Object.keys(schemaProps);
1892
- const { objects: subTypeNames, arrayItems: subItemNames } = collectInlineSubTypes(schema, typeName, reservedNames);
1893
- const validatorStripKeys = hasStrictKeys(schema) || stripUnknown;
1894
- const keyCount = validatorStripKeys ? exactKeyCountOf(schema) : null;
1895
- const checks = [];
1896
- for (const key of propertyKeys) {
1897
- const propSchema = schemaProps[key];
1898
- if (shouldUseRecordRefImport(propSchema, useRefImports))
1899
- return null;
1900
- const accessor = safeAccessor(root, key);
1901
- const isRequired = isPropertyRequired(key, schema);
1902
- const subName = subTypeNames.get(key);
1903
- let check = subName ? `${shapeValidatorName(subName)}(${accessor})` : generatePropertyTypeCheck(accessor, propSchema, useRefImports, suffix);
1904
- const itemSubName = subItemNames.get(key);
1905
- if (check !== null && itemSubName) {
1906
- check = `${check} && _every${itemSubName}(${accessor})`;
1907
- }
1908
- if (check === null)
1909
- return null;
1910
- if (isRequired) {
1911
- checks.push(check);
1912
- } else {
1913
- checks.push(`(${accessor} === undefined || ${check})`);
1914
- }
1915
- }
1916
- if (hasMinProperties(schema))
1917
- checks.push(`Object.keys(${root}).length >= ${schema.minProperties}`);
1918
- if (hasMaxProperties(schema))
1919
- checks.push(`Object.keys(${root}).length <= ${schema.maxProperties}`);
1920
- return { checks, keyCount, walksKeys: validatorStripKeys && keyCount === null, propertyCount: propertyKeys.length };
1921
- };
1922
- const inlineNestedShapeCheck = (childSchema, childTypeName, local, useRefImports, suffix, stripUnknown, reservedNames, unknownKeys) => {
1923
- const readRoot = `(${local} as Record<string, any>)`;
1924
- const shape = shapeChecksOf(childSchema, childTypeName, readRoot, useRefImports, suffix, stripUnknown, reservedNames);
1925
- if (shape === null || shape.walksKeys)
1926
- return null;
1927
- if (shape.propertyCount === 0 || shape.propertyCount > INLINE_NESTED_MAX_PROPERTIES)
1928
- return null;
1929
- const count = shape.keyCount === null ? null : keyCountCheck(unknownKeys, shape.keyCount, "", local);
1930
- if (count !== null && count.statements.length > 0)
1931
- return null;
1932
- const parts = [
1933
- `typeof ${local} === "object"`,
1934
- `${local} !== null`,
1935
- `!Array.isArray(${local})`,
1936
- ...shape.checks,
1937
- ...count === null ? [] : [count.verdict]
1938
- ];
1939
- return { check: `(${parts.join(" && ")})`, propertyCount: shape.propertyCount };
1940
- };
1941
- const renderShapeValidator = (exportPrefix, fnName, checks, keyCount, strictKeysGuard, unknownKeys) => {
1942
- const head = `${exportPrefix}const ${fnName} = (input: unknown): boolean => {
1943
- if (!isObject(input)) return false;${strictKeysGuard}`;
1944
- const chain = checks.join("\n && ");
1945
- if (keyCount === null) {
1946
- if (checks.length === 0) {
1947
- return strictKeysGuard ? `${head}
1948
- return true;
1949
- };` : `${exportPrefix}const ${fnName} = (input: unknown): boolean => isObject(input);`;
1950
- }
1951
- return `${head}
1952
- return ${chain};
1953
- };`;
1954
- }
1955
- const count = keyCountCheck(unknownKeys, keyCount, " ");
1956
- if (count.statements.length === 0) {
1957
- return `${head}
1958
- return ${checks.length === 0 ? count.verdict : `${chain}
1959
- && ${count.verdict}`};
1960
- };`;
1961
- }
1962
- const gate = checks.length === 0 ? "" : `
1963
- if (!(${chain})) return false;`;
1964
- return `${head}${gate}
1965
- ${count.statements.join("\n")}
1966
- return ${count.verdict};
1967
- };`;
1968
- };
1969
- export {
1970
- generateParserFunction,
1971
- generateShapeValidator,
1972
- shapeValidatorFunctionName
1973
- };