@amritk/generate-parsers 0.6.3 → 0.7.1

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 (56) hide show
  1. package/dist/generators/build-schema.d.ts +9 -10
  2. package/dist/generators/build-schema.d.ts.map +1 -0
  3. package/dist/generators/build-schema.js +118 -0
  4. package/dist/generators/generate-default-from-pattern.d.ts +1 -0
  5. package/dist/generators/generate-default-from-pattern.d.ts.map +1 -0
  6. package/dist/generators/generate-default-from-pattern.js +33 -0
  7. package/dist/generators/generate-discriminated-union-validation.d.ts +1 -0
  8. package/dist/generators/generate-discriminated-union-validation.d.ts.map +1 -0
  9. package/dist/generators/generate-discriminated-union-validation.js +38 -0
  10. package/dist/generators/generate-enum-check.d.ts +1 -0
  11. package/dist/generators/generate-enum-check.d.ts.map +1 -0
  12. package/dist/generators/generate-enum-check.js +8 -0
  13. package/dist/generators/generate-files.d.ts +2 -1
  14. package/dist/generators/generate-files.d.ts.map +1 -0
  15. package/dist/generators/generate-files.js +74 -0
  16. package/dist/generators/generate-non-discriminated-union-validation.d.ts +1 -0
  17. package/dist/generators/generate-non-discriminated-union-validation.d.ts.map +1 -0
  18. package/dist/generators/generate-non-discriminated-union-validation.js +28 -0
  19. package/dist/generators/generate-parser-function.d.ts +1 -0
  20. package/dist/generators/generate-parser-function.d.ts.map +1 -0
  21. package/dist/generators/generate-parser-function.js +1070 -0
  22. package/dist/generators/generate-schema-checks.d.ts +1 -0
  23. package/dist/generators/generate-schema-checks.d.ts.map +1 -0
  24. package/dist/generators/generate-schema-checks.js +99 -0
  25. package/dist/generators/generate-strict-assertion.d.ts +1 -0
  26. package/dist/generators/generate-strict-assertion.d.ts.map +1 -0
  27. package/dist/generators/generate-strict-assertion.js +179 -0
  28. package/dist/generators/generate-validation-expression.d.ts +1 -0
  29. package/dist/generators/generate-validation-expression.d.ts.map +1 -0
  30. package/dist/generators/generate-validation-expression.js +267 -0
  31. package/dist/helpers/apply-schema-extensions.d.ts +2 -1
  32. package/dist/helpers/apply-schema-extensions.d.ts.map +1 -0
  33. package/dist/helpers/apply-schema-extensions.js +51 -0
  34. package/dist/helpers/collect-helpers.d.ts +1 -0
  35. package/dist/helpers/collect-helpers.d.ts.map +1 -0
  36. package/dist/helpers/collect-helpers.js +48 -0
  37. package/dist/helpers/collect-imports.d.ts +1 -0
  38. package/dist/helpers/collect-imports.d.ts.map +1 -0
  39. package/dist/helpers/collect-imports.js +216 -0
  40. package/dist/helpers/find-discriminator.d.ts +1 -0
  41. package/dist/helpers/find-discriminator.d.ts.map +1 -0
  42. package/dist/helpers/find-discriminator.js +33 -0
  43. package/dist/helpers/get-default-value.d.ts +1 -0
  44. package/dist/helpers/get-default-value.d.ts.map +1 -0
  45. package/dist/helpers/get-default-value.js +74 -0
  46. package/dist/helpers/get-discriminator-value.d.ts +1 -0
  47. package/dist/helpers/get-discriminator-value.d.ts.map +1 -0
  48. package/dist/helpers/get-discriminator-value.js +19 -0
  49. package/dist/index.d.ts +2 -1
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +1 -2783
  52. package/dist/types/schema-extensions.d.ts +1 -0
  53. package/dist/types/schema-extensions.d.ts.map +1 -0
  54. package/dist/types/schema-extensions.js +0 -0
  55. package/package.json +7 -7
  56. package/src/generators/build-schema.ts +22 -173
@@ -3,3 +3,4 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
3
3
  * Generates validation checks for a single schema (used in union validation).
4
4
  */
5
5
  export declare const generateSchemaChecks: (accessor: string, schema: JSONSchema) => string[];
6
+ //# sourceMappingURL=generate-schema-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-schema-checks.d.ts","sourceRoot":"","sources":["../../src/generators/generate-schema-checks.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAIjE;;GAEG;AACH,eAAO,MAAM,oBAAoB,aAAc,MAAM,UAAU,UAAU,KAAG,MAAM,EAkGjF,CAAA"}
@@ -0,0 +1,99 @@
1
+ import { getMjstInstanceOf, getMjstPrimitive } from '@amritk/helpers/mjst-extension';
2
+ import { hasAdditionalProperties, hasEnum, hasExclusiveMaximum, hasExclusiveMinimum, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasPattern, hasProperties, hasRequired, hasType, hasUniqueItems, } from '@amritk/helpers/schema-guards';
3
+ import { generateEnumCheck } from './generate-enum-check.js';
4
+ /**
5
+ * Generates validation checks for a single schema (used in union validation).
6
+ */
7
+ export const generateSchemaChecks = (accessor, schema) => {
8
+ const checks = [];
9
+ // x-mjst instanceOf nodes validate purely by instance check (no native type).
10
+ const instanceOf = getMjstInstanceOf(schema);
11
+ if (instanceOf) {
12
+ return [`${accessor} instanceof ${instanceOf}`];
13
+ }
14
+ // x-mjst primitive nodes (e.g. bigint) validate purely by typeof check.
15
+ const primitive = getMjstPrimitive(schema);
16
+ if (primitive) {
17
+ return [`typeof ${accessor} === "${primitive}"`];
18
+ }
19
+ if (!hasType(schema)) {
20
+ return checks;
21
+ }
22
+ switch (schema.type) {
23
+ case 'string': {
24
+ checks.push(`typeof ${accessor} === "string"`);
25
+ if (hasPattern(schema)) {
26
+ checks.push(`/${schema.pattern.replace(/\//g, '\\/')}/.test(${accessor})`);
27
+ }
28
+ if (hasMinLength(schema)) {
29
+ checks.push(`${accessor}.length >= ${schema.minLength}`);
30
+ }
31
+ if (hasMaxLength(schema)) {
32
+ checks.push(`${accessor}.length <= ${schema.maxLength}`);
33
+ }
34
+ break;
35
+ }
36
+ case 'number':
37
+ case 'integer':
38
+ checks.push(`typeof ${accessor} === "number"`);
39
+ if (hasMinimum(schema)) {
40
+ checks.push(`${accessor} >= ${schema.minimum}`);
41
+ }
42
+ if (hasMaximum(schema)) {
43
+ checks.push(`${accessor} <= ${schema.maximum}`);
44
+ }
45
+ if (hasExclusiveMinimum(schema)) {
46
+ checks.push(`${accessor} > ${schema.exclusiveMinimum}`);
47
+ }
48
+ if (hasExclusiveMaximum(schema)) {
49
+ checks.push(`${accessor} < ${schema.exclusiveMaximum}`);
50
+ }
51
+ if (hasMultipleOf(schema)) {
52
+ checks.push(`${accessor} % ${schema.multipleOf} === 0`);
53
+ }
54
+ break;
55
+ case 'boolean':
56
+ checks.push(`typeof ${accessor} === "boolean"`);
57
+ break;
58
+ case 'array':
59
+ checks.push(`Array.isArray(${accessor})`);
60
+ if (hasMinItems(schema)) {
61
+ checks.push(`${accessor}.length >= ${schema.minItems}`);
62
+ }
63
+ if (hasMaxItems(schema)) {
64
+ checks.push(`${accessor}.length <= ${schema.maxItems}`);
65
+ }
66
+ if (hasUniqueItems(schema) && schema.uniqueItems === true) {
67
+ checks.push(`new Set(${accessor}).size === ${accessor}.length`);
68
+ }
69
+ break;
70
+ case 'object': {
71
+ checks.push(`typeof ${accessor} === "object" && ${accessor} !== null && !Array.isArray(${accessor})`);
72
+ // Check required properties exist
73
+ if (hasRequired(schema) && schema.required.length > 0) {
74
+ for (const requiredKey of schema.required) {
75
+ checks.push(`"${requiredKey}" in ${accessor}`);
76
+ }
77
+ }
78
+ // Check minProperties
79
+ if (hasMinProperties(schema)) {
80
+ checks.push(`Object.keys(${accessor}).length >= ${schema.minProperties}`);
81
+ }
82
+ // Check maxProperties
83
+ if (hasMaxProperties(schema)) {
84
+ checks.push(`Object.keys(${accessor}).length <= ${schema.maxProperties}`);
85
+ }
86
+ // Check additionalProperties
87
+ if (hasAdditionalProperties(schema) && schema.additionalProperties === false && hasProperties(schema)) {
88
+ const allowedKeys = Object.keys(schema.properties);
89
+ const allowedKeysStr = JSON.stringify(allowedKeys);
90
+ checks.push(`Object.keys(${accessor}).every(k => ${allowedKeysStr}.includes(k))`);
91
+ }
92
+ break;
93
+ }
94
+ }
95
+ if (hasEnum(schema) && schema.enum.length > 0) {
96
+ checks.push(generateEnumCheck(accessor, schema.enum));
97
+ }
98
+ return checks;
99
+ };
@@ -16,3 +16,4 @@ export declare const generateObjectStrictAssertion: (schema: JSONSchema, typeNam
16
16
  * Returns null when the schema has no type information to assert on.
17
17
  */
18
18
  export declare const generateScalarStrictAssertion: (schema: JSONSchema, typeName: string) => string | null;
19
+ //# sourceMappingURL=generate-strict-assertion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-strict-assertion.d.ts","sourceRoot":"","sources":["../../src/generators/generate-strict-assertion.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAsLjE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,WAAY,UAAU,YAAY,MAAM,KAAG,MAAM,EAe1F,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,WAAY,UAAU,YAAY,MAAM,KAAG,MAAM,GAAG,IAgB7F,CAAA"}
@@ -0,0 +1,179 @@
1
+ import { getMjstInstanceOf, getMjstPrimitive } from '@amritk/helpers/mjst-extension';
2
+ import { safeAccessor } from '@amritk/helpers/safe-accessor';
3
+ import { hasEnum, hasExclusiveMaximum, hasExclusiveMinimum, hasMaximum, hasMaxLength, hasMinimum, hasMinLength, hasMultipleOf, hasPattern, hasProperties, hasRef, hasRequired, hasType, isSchemaObject, } from '@amritk/helpers/schema-guards';
4
+ /**
5
+ * Returns the inline condition that is true when `accessor` is the wrong type
6
+ * for the given JSON Schema primitive type.
7
+ */
8
+ const wrongTypeCondition = (accessor, type) => {
9
+ switch (type) {
10
+ case 'string':
11
+ return `typeof ${accessor} !== "string"`;
12
+ case 'number':
13
+ case 'integer':
14
+ return `typeof ${accessor} !== "number"`;
15
+ case 'boolean':
16
+ return `typeof ${accessor} !== "boolean"`;
17
+ case 'array':
18
+ return `!Array.isArray(${accessor})`;
19
+ case 'object':
20
+ return `!isObject(${accessor})`;
21
+ default:
22
+ return null;
23
+ }
24
+ };
25
+ /**
26
+ * Maps a JSON Schema type to the label used in error messages.
27
+ * `integer` collapses to `number` since both are validated via `typeof === "number"`.
28
+ */
29
+ const typeLabel = (type) => (type === 'integer' ? 'number' : type);
30
+ /**
31
+ * Escapes special regex characters in a pattern string for use in a regex literal.
32
+ */
33
+ const escapeRegexPattern = (pattern) => {
34
+ return pattern.replace(/\\/g, '\\\\').replace(/\//g, '\\/');
35
+ };
36
+ /**
37
+ * Generates strict-mode constraint checks for a typed property
38
+ * (pattern, length, min/max, multipleOf).
39
+ */
40
+ const generateConstraintChecks = (acc, propSchema, typeName, key) => {
41
+ if (!isSchemaObject(propSchema) || !hasType(propSchema))
42
+ return [];
43
+ const t = propSchema.type;
44
+ const lines = [];
45
+ if (t === 'string') {
46
+ if (hasPattern(propSchema)) {
47
+ const pattern = escapeRegexPattern(propSchema.pattern);
48
+ lines.push(` if (typeof ${acc} === "string" && !/${pattern}/.test(${acc})) throw new Error('[${typeName}] field "${key}" must match pattern ${propSchema.pattern}');`);
49
+ }
50
+ if (hasMinLength(propSchema)) {
51
+ lines.push(` if (typeof ${acc} === "string" && ${acc}.length < ${propSchema.minLength}) throw new Error('[${typeName}] field "${key}" must have at least ${propSchema.minLength} characters');`);
52
+ }
53
+ if (hasMaxLength(propSchema)) {
54
+ lines.push(` if (typeof ${acc} === "string" && ${acc}.length > ${propSchema.maxLength}) throw new Error('[${typeName}] field "${key}" must have at most ${propSchema.maxLength} characters');`);
55
+ }
56
+ }
57
+ if (t === 'number' || t === 'integer') {
58
+ if (hasMinimum(propSchema)) {
59
+ lines.push(` if (typeof ${acc} === "number" && ${acc} < ${propSchema.minimum}) throw new Error('[${typeName}] field "${key}" must be >= ${propSchema.minimum}');`);
60
+ }
61
+ if (hasMaximum(propSchema)) {
62
+ lines.push(` if (typeof ${acc} === "number" && ${acc} > ${propSchema.maximum}) throw new Error('[${typeName}] field "${key}" must be <= ${propSchema.maximum}');`);
63
+ }
64
+ if (hasExclusiveMinimum(propSchema)) {
65
+ lines.push(` if (typeof ${acc} === "number" && ${acc} <= ${propSchema.exclusiveMinimum}) throw new Error('[${typeName}] field "${key}" must be > ${propSchema.exclusiveMinimum}');`);
66
+ }
67
+ if (hasExclusiveMaximum(propSchema)) {
68
+ lines.push(` if (typeof ${acc} === "number" && ${acc} >= ${propSchema.exclusiveMaximum}) throw new Error('[${typeName}] field "${key}" must be < ${propSchema.exclusiveMaximum}');`);
69
+ }
70
+ if (hasMultipleOf(propSchema)) {
71
+ lines.push(` if (typeof ${acc} === "number" && ${acc} % ${propSchema.multipleOf} !== 0) throw new Error('[${typeName}] field "${key}" must be a multiple of ${propSchema.multipleOf}');`);
72
+ }
73
+ }
74
+ return lines;
75
+ };
76
+ /**
77
+ * Generates strict-mode lines for a single property of an object schema.
78
+ * Properties with a `$ref` are skipped here — the nested parser handles its own
79
+ * strict check when called from the parent's slow path.
80
+ */
81
+ const generatePropertyAssertion = (key, propSchema, isRequired, typeName) => {
82
+ const acc = safeAccessor('input', key);
83
+ const lines = [];
84
+ if (isRequired) {
85
+ lines.push(` if (!(${JSON.stringify(key)} in input)) throw new Error('[${typeName}] missing required property "${key}"');`);
86
+ }
87
+ if (!isSchemaObject(propSchema))
88
+ return lines;
89
+ if (hasRef(propSchema))
90
+ return lines;
91
+ const instanceOf = getMjstInstanceOf(propSchema);
92
+ if (instanceOf) {
93
+ if (isRequired) {
94
+ lines.push(` if (!(${acc} instanceof ${instanceOf})) throw new Error('[${typeName}] field "${key}" must be ${instanceOf}');`);
95
+ }
96
+ else {
97
+ lines.push(` if (${acc} !== undefined && !(${acc} instanceof ${instanceOf})) throw new Error('[${typeName}] field "${key}" must be ${instanceOf}');`);
98
+ }
99
+ return lines;
100
+ }
101
+ const primitive = getMjstPrimitive(propSchema);
102
+ if (primitive) {
103
+ if (isRequired) {
104
+ lines.push(` if (typeof ${acc} !== "${primitive}") throw new Error('[${typeName}] field "${key}" must be ${primitive}');`);
105
+ }
106
+ else {
107
+ lines.push(` if (${acc} !== undefined && typeof ${acc} !== "${primitive}") throw new Error('[${typeName}] field "${key}" must be ${primitive}');`);
108
+ }
109
+ return lines;
110
+ }
111
+ if (hasEnum(propSchema)) {
112
+ const allowed = JSON.stringify(propSchema.enum);
113
+ const label = propSchema.enum.map((v) => JSON.stringify(v)).join(', ');
114
+ if (isRequired) {
115
+ lines.push(` if (!(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
116
+ }
117
+ else {
118
+ lines.push(` if (${acc} !== undefined && !(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
119
+ }
120
+ return lines;
121
+ }
122
+ if (hasType(propSchema)) {
123
+ const t = propSchema.type;
124
+ const wrongType = wrongTypeCondition(acc, t);
125
+ if (wrongType) {
126
+ if (isRequired) {
127
+ lines.push(` if (${wrongType}) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
128
+ }
129
+ else {
130
+ lines.push(` if (${acc} !== undefined && (${wrongType})) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
131
+ }
132
+ }
133
+ lines.push(...generateConstraintChecks(acc, propSchema, typeName, key));
134
+ }
135
+ return lines;
136
+ };
137
+ /**
138
+ * Generates strict-mode assertions for the body of an object parser.
139
+ * Throws on:
140
+ * - non-object input
141
+ * - missing required property
142
+ * - property of the wrong primitive type
143
+ * - enum / pattern / length / min / max / multipleOf violations
144
+ *
145
+ * Properties with a `$ref` are validated by the nested parser's own strict
146
+ * check when that parser is invoked downstream.
147
+ */
148
+ export const generateObjectStrictAssertion = (schema, typeName) => {
149
+ const lines = [];
150
+ lines.push(` if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);`);
151
+ if (!hasProperties(schema) || !isSchemaObject(schema))
152
+ return lines;
153
+ const required = new Set(hasRequired(schema) ? schema.required : []);
154
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
155
+ lines.push(...generatePropertyAssertion(key, propSchema, required.has(key), typeName));
156
+ }
157
+ return lines;
158
+ };
159
+ /**
160
+ * Generates a single strict-mode assertion line for a non-object scalar parser.
161
+ * Returns null when the schema has no type information to assert on.
162
+ */
163
+ export const generateScalarStrictAssertion = (schema, typeName) => {
164
+ const instanceOf = getMjstInstanceOf(schema);
165
+ if (instanceOf) {
166
+ return ` if (!(input instanceof ${instanceOf})) throw new Error(\`[${typeName}] expected ${instanceOf}, got \${input === null ? "null" : typeof input}\`);`;
167
+ }
168
+ const primitive = getMjstPrimitive(schema);
169
+ if (primitive) {
170
+ return ` if (typeof input !== "${primitive}") throw new Error(\`[${typeName}] expected ${primitive}, got \${input === null ? "null" : typeof input}\`);`;
171
+ }
172
+ if (!isSchemaObject(schema) || !hasType(schema))
173
+ return null;
174
+ const t = schema.type;
175
+ const wrongType = wrongTypeCondition('input', t);
176
+ if (!wrongType)
177
+ return null;
178
+ return ` if (${wrongType}) throw new Error(\`[${typeName}] expected ${typeLabel(t)}, got \${input === null ? "null" : typeof input}\`);`;
179
+ };
@@ -5,3 +5,4 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
5
5
  * Supports type checking, pattern validation, enum validation, union types, and $ref resolution.
6
6
  */
7
7
  export declare const generateValidationExpression: (key: string, schema: JSONSchema, defaultValue: string, isRequired: boolean, rootSchema?: Record<string, unknown>, visitedRefs?: Set<string>, accessorOverride?: string, knownNotUndefined?: boolean) => string;
8
+ //# sourceMappingURL=generate-validation-expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-validation-expression.d.ts","sourceRoot":"","sources":["../../src/generators/generate-validation-expression.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAgDjE;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,QAClC,MAAM,UACH,UAAU,gBACJ,MAAM,cACR,OAAO,eACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACtB,GAAG,CAAC,MAAM,CAAC,qBACN,MAAM,sBACL,OAAO,KAC1B,MA0OF,CAAA"}
@@ -0,0 +1,267 @@
1
+ import { getMjstInstanceOf, getMjstPrimitive } from '@amritk/helpers/mjst-extension';
2
+ import { resolveRef } from '@amritk/helpers/resolve-ref';
3
+ import { safeAccessor } from '@amritk/helpers/safe-accessor';
4
+ import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasEnum, hasExclusiveMaximum, hasExclusiveMinimum, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasOneOf, hasPattern, hasProperties, hasRef, hasRequired, hasType, hasUniqueItems, isSchemaObject, } from '@amritk/helpers/schema-guards';
5
+ import { findDiscriminator } from '../helpers/find-discriminator.js';
6
+ import { generateDiscriminatedUnionValidation } from './generate-discriminated-union-validation.js';
7
+ import { generateEnumCheck } from './generate-enum-check.js';
8
+ import { generateNonDiscriminatedUnionValidation } from './generate-non-discriminated-union-validation.js';
9
+ import { generateSchemaChecks } from './generate-schema-checks.js';
10
+ /**
11
+ * Coercion expression for an `x-mjst` instanceOf value, when one is known.
12
+ * `Date` can be reconstructed from the common JSON encodings (ISO string or
13
+ * epoch number); other classes have no safe generic coercion, so we return null
14
+ * and the caller falls back to the default.
15
+ */
16
+ const getInstanceCoercion = (accessor, instanceOf) => {
17
+ if (instanceOf === 'Date')
18
+ return `new Date(${accessor} as string | number | Date)`;
19
+ return null;
20
+ };
21
+ /**
22
+ * Generates a type coercion expression for converting a value to the expected type.
23
+ */
24
+ const getTypeCoercion = (accessor, schema, defaultValue) => {
25
+ if (!hasType(schema)) {
26
+ return null;
27
+ }
28
+ switch (schema.type) {
29
+ case 'string':
30
+ return `String(${accessor})`;
31
+ case 'number':
32
+ case 'integer': {
33
+ // Number() of a non-numeric string produces NaN, which silently poisons
34
+ // arithmetic. Guard with Number.isFinite and fall back to the default.
35
+ const n = `Number(${accessor})`;
36
+ return `(Number.isFinite(${n}) ? ${n} : ${defaultValue})`;
37
+ }
38
+ case 'boolean':
39
+ return `Boolean(${accessor})`;
40
+ case 'array':
41
+ return `[]`;
42
+ case 'object':
43
+ return `typeof ${accessor} === "object" && ${accessor} !== null ? ${accessor} : {}`;
44
+ default:
45
+ return null;
46
+ }
47
+ };
48
+ /**
49
+ * Generates a validation expression that checks if a value matches the expected type.
50
+ * Returns the value if valid, coerces to the correct type if invalid, or uses default if required and missing.
51
+ * Supports type checking, pattern validation, enum validation, union types, and $ref resolution.
52
+ */
53
+ export const generateValidationExpression = (key, schema, defaultValue, isRequired, rootSchema, visitedRefs, accessorOverride, knownNotUndefined) => {
54
+ const accessor = accessorOverride ?? safeAccessor('input?', key);
55
+ const checks = [];
56
+ if (!isSchemaObject(schema)) {
57
+ return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
58
+ }
59
+ // Handle x-mjst instanceOf (e.g. Date): the value must be an instance of the
60
+ // named class. Invalid values are coerced when a coercer is known, otherwise
61
+ // they fall back to the default (required) or undefined (optional).
62
+ const instanceOf = getMjstInstanceOf(schema);
63
+ if (instanceOf) {
64
+ const valid = `${accessor} instanceof ${instanceOf}`;
65
+ const coercion = getInstanceCoercion(accessor, instanceOf);
66
+ if (isRequired) {
67
+ return coercion
68
+ ? `${valid} ? ${accessor} : (${accessor} !== undefined ? ${coercion} : ${defaultValue})`
69
+ : `${valid} ? ${accessor} : ${defaultValue}`;
70
+ }
71
+ return coercion
72
+ ? `${valid} ? ${accessor} : (${accessor} !== undefined ? ${coercion} : undefined)`
73
+ : `${valid} ? ${accessor} : undefined`;
74
+ }
75
+ // Handle x-mjst primitive (e.g. bigint): validate with typeof. We do not
76
+ // coerce — BigInt() throws on invalid input, which a pure coercion expression
77
+ // cannot guard — so invalid values fall back to the default (required) or
78
+ // undefined (optional).
79
+ const primitive = getMjstPrimitive(schema);
80
+ if (primitive) {
81
+ const valid = `typeof ${accessor} === "${primitive}"`;
82
+ return isRequired ? `${valid} ? ${accessor} : ${defaultValue}` : `${valid} ? ${accessor} : undefined`;
83
+ }
84
+ // Handle $ref - resolve and validate against the referenced schema
85
+ if (hasRef(schema) && rootSchema) {
86
+ const ref = schema.$ref;
87
+ // If we have already visited this ref, we have a circular reference - return accessor to break cycle
88
+ if (visitedRefs?.has(ref)) {
89
+ return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
90
+ }
91
+ const resolvedSchema = resolveRef(ref, rootSchema);
92
+ if (resolvedSchema) {
93
+ const visited = new Set(visitedRefs);
94
+ visited.add(ref);
95
+ return generateValidationExpression(key, resolvedSchema, defaultValue, isRequired, rootSchema, visited);
96
+ }
97
+ }
98
+ // Handle union types (oneOf, anyOf)
99
+ if (hasOneOf(schema) && schema.oneOf.length > 0) {
100
+ const discriminator = findDiscriminator(schema.oneOf);
101
+ if (discriminator) {
102
+ return generateDiscriminatedUnionValidation(accessor, schema.oneOf, discriminator, defaultValue, isRequired);
103
+ }
104
+ return generateNonDiscriminatedUnionValidation(accessor, schema.oneOf, defaultValue, isRequired);
105
+ }
106
+ if (hasAnyOf(schema) && schema.anyOf.length > 0) {
107
+ const discriminator = findDiscriminator(schema.anyOf);
108
+ if (discriminator) {
109
+ return generateDiscriminatedUnionValidation(accessor, schema.anyOf, discriminator, defaultValue, isRequired);
110
+ }
111
+ return generateNonDiscriminatedUnionValidation(accessor, schema.anyOf, defaultValue, isRequired);
112
+ }
113
+ // Handle allOf (intersection - all schemas must match)
114
+ if (hasAllOf(schema) && schema.allOf.length > 0) {
115
+ const allChecks = [];
116
+ for (const subSchema of schema.allOf) {
117
+ const subChecks = generateSchemaChecks(accessor, subSchema);
118
+ allChecks.push(...subChecks);
119
+ }
120
+ if (allChecks.length > 0) {
121
+ let combinedCheck = allChecks[0];
122
+ for (let i = 1; i < allChecks.length; i++) {
123
+ combinedCheck += ' && ' + allChecks[i];
124
+ }
125
+ return `${combinedCheck} ? ${accessor} : ${defaultValue}`;
126
+ }
127
+ return `${accessor} ?? ${defaultValue}`;
128
+ }
129
+ // Handle not (negation - value must NOT match this schema)
130
+ if ('not' in schema && schema.not) {
131
+ const notChecks = generateSchemaChecks(accessor, schema.not);
132
+ if (notChecks.length > 0) {
133
+ let combinedNotCheck = notChecks[0];
134
+ for (let i = 1; i < notChecks.length; i++) {
135
+ combinedNotCheck += ' && ' + notChecks[i];
136
+ }
137
+ return `!(${combinedNotCheck}) ? ${accessor} : ${defaultValue}`;
138
+ }
139
+ }
140
+ // Handle const — single exact value that must match
141
+ if (hasConst(schema)) {
142
+ const constLiteral = JSON.stringify(schema.const);
143
+ return isRequired
144
+ ? `${accessor} === ${constLiteral} ? ${accessor} : ${constLiteral}`
145
+ : `${accessor} === ${constLiteral} ? ${accessor} : (${accessor} !== undefined ? ${constLiteral} : undefined)`;
146
+ }
147
+ if (!hasType(schema) && !hasEnum(schema)) {
148
+ return `${accessor} ?? ${defaultValue}`;
149
+ }
150
+ // Build type-specific checks
151
+ if (hasType(schema)) {
152
+ switch (schema.type) {
153
+ case 'string': {
154
+ checks.push(`typeof ${accessor} === "string"`);
155
+ if (hasPattern(schema)) {
156
+ checks.push(`/${schema.pattern.replace(/\//g, '\\/')}/.test(${accessor})`);
157
+ }
158
+ if (hasMinLength(schema)) {
159
+ checks.push(`${accessor}.length >= ${schema.minLength}`);
160
+ }
161
+ if (hasMaxLength(schema)) {
162
+ checks.push(`${accessor}.length <= ${schema.maxLength}`);
163
+ }
164
+ break;
165
+ }
166
+ case 'number':
167
+ case 'integer': {
168
+ checks.push(`typeof ${accessor} === "number"`);
169
+ if (hasMinimum(schema)) {
170
+ checks.push(`${accessor} >= ${schema.minimum}`);
171
+ }
172
+ if (hasMaximum(schema)) {
173
+ checks.push(`${accessor} <= ${schema.maximum}`);
174
+ }
175
+ if (hasExclusiveMinimum(schema)) {
176
+ checks.push(`${accessor} > ${schema.exclusiveMinimum}`);
177
+ }
178
+ if (hasExclusiveMaximum(schema)) {
179
+ checks.push(`${accessor} < ${schema.exclusiveMaximum}`);
180
+ }
181
+ if (hasMultipleOf(schema)) {
182
+ checks.push(`${accessor} % ${schema.multipleOf} === 0`);
183
+ }
184
+ break;
185
+ }
186
+ case 'boolean':
187
+ checks.push(`typeof ${accessor} === "boolean"`);
188
+ break;
189
+ case 'array': {
190
+ checks.push(`Array.isArray(${accessor})`);
191
+ if (hasMinItems(schema)) {
192
+ checks.push(`${accessor}.length >= ${schema.minItems}`);
193
+ }
194
+ if (hasMaxItems(schema)) {
195
+ checks.push(`${accessor}.length <= ${schema.maxItems}`);
196
+ }
197
+ if (hasUniqueItems(schema) && schema.uniqueItems === true) {
198
+ checks.push(`new Set(${accessor}).size === ${accessor}.length`);
199
+ }
200
+ break;
201
+ }
202
+ case 'object': {
203
+ checks.push(`isObject(${accessor})`);
204
+ // Check required properties exist
205
+ if (hasRequired(schema) && schema.required.length > 0) {
206
+ for (const requiredKey of schema.required) {
207
+ checks.push(`"${requiredKey}" in ${accessor}`);
208
+ }
209
+ }
210
+ // Check minProperties
211
+ if (hasMinProperties(schema)) {
212
+ checks.push(`Object.keys(${accessor}).length >= ${schema.minProperties}`);
213
+ }
214
+ // Check maxProperties
215
+ if (hasMaxProperties(schema)) {
216
+ checks.push(`Object.keys(${accessor}).length <= ${schema.maxProperties}`);
217
+ }
218
+ // Check additionalProperties
219
+ if (hasAdditionalProperties(schema) && schema.additionalProperties === false && hasProperties(schema)) {
220
+ const allowedKeys = Object.keys(schema.properties);
221
+ const allowedKeysStr = JSON.stringify(allowedKeys);
222
+ checks.push(`Object.keys(${accessor}).every(k => ${allowedKeysStr}.includes(k))`);
223
+ }
224
+ break;
225
+ }
226
+ }
227
+ }
228
+ // Add enum check if present
229
+ if (hasEnum(schema) && schema.enum.length > 0) {
230
+ checks.push(generateEnumCheck(accessor, schema.enum));
231
+ }
232
+ // Combine all checks
233
+ if (checks.length === 0) {
234
+ return isRequired ? `${accessor} ?? ${defaultValue}` : accessor;
235
+ }
236
+ let combinedCheck = checks[0];
237
+ for (let i = 1; i < checks.length; i++) {
238
+ combinedCheck += ' && ' + checks[i];
239
+ }
240
+ // Generate the fallback value
241
+ // If the value exists but fails validation, try to coerce it
242
+ // If the value is missing and required, use the default
243
+ const typeCoercion = getTypeCoercion(accessor, schema, defaultValue);
244
+ if (isRequired) {
245
+ // For required fields: valid ? use_value : (exists ? coerce : default)
246
+ if (typeCoercion) {
247
+ // If we know the value is not undefined (e.g., we're inside an undefined check),
248
+ // we can skip the redundant undefined check
249
+ if (knownNotUndefined) {
250
+ return `${combinedCheck} ? ${accessor} : ${typeCoercion}`;
251
+ }
252
+ return `${combinedCheck} ? ${accessor} : (${accessor} !== undefined ? ${typeCoercion} : ${defaultValue})`;
253
+ }
254
+ return `${combinedCheck} ? ${accessor} : ${defaultValue}`;
255
+ }
256
+ else {
257
+ // For optional fields: valid ? use_value : (exists ? coerce : undefined)
258
+ if (typeCoercion) {
259
+ // If we know the value is not undefined, we can skip the check and just coerce
260
+ if (knownNotUndefined) {
261
+ return `${combinedCheck} ? ${accessor} : ${typeCoercion}`;
262
+ }
263
+ return `${combinedCheck} ? ${accessor} : (${accessor} !== undefined ? ${typeCoercion} : undefined)`;
264
+ }
265
+ return `${combinedCheck} ? ${accessor} : undefined`;
266
+ }
267
+ };
@@ -1,5 +1,5 @@
1
1
  import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
2
- import type { SchemaExtensions } from '#types/schema-extensions';
2
+ import type { SchemaExtensions } from '../types/schema-extensions.js';
3
3
  /**
4
4
  * Merges custom extension properties into a schema's properties based on the
5
5
  * definition name. Extension properties are added as optional (not included
@@ -33,3 +33,4 @@ import type { SchemaExtensions } from '#types/schema-extensions';
33
33
  * ```
34
34
  */
35
35
  export declare const applySchemaExtensions: (schema: JSONSchema, definitionName: string, extensions: SchemaExtensions) => JSONSchema;
36
+ //# sourceMappingURL=apply-schema-extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-schema-extensions.d.ts","sourceRoot":"","sources":["../../src/helpers/apply-schema-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,qBAAqB,WACxB,UAAU,kBACF,MAAM,cACV,gBAAgB,KAC3B,UAuBF,CAAA"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Merges custom extension properties into a schema's properties based on the
3
+ * definition name. Extension properties are added as optional (not included
4
+ * in the required array) since they are custom additions that may not always
5
+ * be present.
6
+ *
7
+ * If the schema is not an object, has no matching extensions, or the
8
+ * extensions record is empty, the original schema is returned unchanged.
9
+ *
10
+ * @param schema - The JSON Schema to extend
11
+ * @param definitionName - The name of the definition (e.g., "parameter")
12
+ * @param extensions - The extensions configuration mapping definition names to properties
13
+ * @returns A new schema with extension properties merged in, or the original if unchanged
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const schema = {
18
+ * type: 'object',
19
+ * properties: { name: { type: 'string' } },
20
+ * required: ['name'],
21
+ * }
22
+ *
23
+ * const extensions = {
24
+ * parameter: {
25
+ * 'x-enabled': { type: 'boolean' },
26
+ * },
27
+ * }
28
+ *
29
+ * const extended = applySchemaExtensions(schema, 'parameter', extensions)
30
+ * // extended.properties now includes 'x-enabled' alongside 'name'
31
+ * ```
32
+ */
33
+ export const applySchemaExtensions = (schema, definitionName, extensions) => {
34
+ const extensionProperties = extensions[definitionName];
35
+ if (!extensionProperties || Object.keys(extensionProperties).length === 0) {
36
+ return schema;
37
+ }
38
+ if (typeof schema !== 'object' || schema === null) {
39
+ return schema;
40
+ }
41
+ const existingProperties = 'properties' in schema && typeof schema.properties === 'object' && schema.properties !== null
42
+ ? schema.properties
43
+ : {};
44
+ return {
45
+ ...schema,
46
+ properties: {
47
+ ...existingProperties,
48
+ ...extensionProperties,
49
+ },
50
+ };
51
+ };
@@ -21,3 +21,4 @@ export type CollectedHelpers = {
21
21
  * `_helpers/` directory at the output root.
22
22
  */
23
23
  export declare const collectHelpers: (parserFunction: string, mode: HelpersMode, helpersImportPrefix?: string) => CollectedHelpers;
24
+ //# sourceMappingURL=collect-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-helpers.d.ts","sourceRoot":"","sources":["../../src/helpers/collect-helpers.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,SAAS,CAAA;AAE9F,sEAAsE;AACtE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,CAAA;AAEhD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAA;CACtC,CAAA;AAoBD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,mBACT,MAAM,QAChB,WAAW,mCAEhB,gBA+BF,CAAA"}