@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.
- package/dist/generators/build-schema.d.ts +9 -10
- package/dist/generators/build-schema.d.ts.map +1 -0
- package/dist/generators/build-schema.js +118 -0
- package/dist/generators/generate-default-from-pattern.d.ts +1 -0
- package/dist/generators/generate-default-from-pattern.d.ts.map +1 -0
- package/dist/generators/generate-default-from-pattern.js +33 -0
- package/dist/generators/generate-discriminated-union-validation.d.ts +1 -0
- package/dist/generators/generate-discriminated-union-validation.d.ts.map +1 -0
- package/dist/generators/generate-discriminated-union-validation.js +38 -0
- package/dist/generators/generate-enum-check.d.ts +1 -0
- package/dist/generators/generate-enum-check.d.ts.map +1 -0
- package/dist/generators/generate-enum-check.js +8 -0
- package/dist/generators/generate-files.d.ts +2 -1
- package/dist/generators/generate-files.d.ts.map +1 -0
- package/dist/generators/generate-files.js +74 -0
- package/dist/generators/generate-non-discriminated-union-validation.d.ts +1 -0
- package/dist/generators/generate-non-discriminated-union-validation.d.ts.map +1 -0
- package/dist/generators/generate-non-discriminated-union-validation.js +28 -0
- package/dist/generators/generate-parser-function.d.ts +1 -0
- package/dist/generators/generate-parser-function.d.ts.map +1 -0
- package/dist/generators/generate-parser-function.js +1070 -0
- package/dist/generators/generate-schema-checks.d.ts +1 -0
- package/dist/generators/generate-schema-checks.d.ts.map +1 -0
- package/dist/generators/generate-schema-checks.js +99 -0
- package/dist/generators/generate-strict-assertion.d.ts +1 -0
- package/dist/generators/generate-strict-assertion.d.ts.map +1 -0
- package/dist/generators/generate-strict-assertion.js +179 -0
- package/dist/generators/generate-validation-expression.d.ts +1 -0
- package/dist/generators/generate-validation-expression.d.ts.map +1 -0
- package/dist/generators/generate-validation-expression.js +267 -0
- package/dist/helpers/apply-schema-extensions.d.ts +2 -1
- package/dist/helpers/apply-schema-extensions.d.ts.map +1 -0
- package/dist/helpers/apply-schema-extensions.js +51 -0
- package/dist/helpers/collect-helpers.d.ts +1 -0
- package/dist/helpers/collect-helpers.d.ts.map +1 -0
- package/dist/helpers/collect-helpers.js +48 -0
- package/dist/helpers/collect-imports.d.ts +1 -0
- package/dist/helpers/collect-imports.d.ts.map +1 -0
- package/dist/helpers/collect-imports.js +216 -0
- package/dist/helpers/find-discriminator.d.ts +1 -0
- package/dist/helpers/find-discriminator.d.ts.map +1 -0
- package/dist/helpers/find-discriminator.js +33 -0
- package/dist/helpers/get-default-value.d.ts +1 -0
- package/dist/helpers/get-default-value.d.ts.map +1 -0
- package/dist/helpers/get-default-value.js +74 -0
- package/dist/helpers/get-discriminator-value.d.ts +1 -0
- package/dist/helpers/get-discriminator-value.d.ts.map +1 -0
- package/dist/helpers/get-discriminator-value.js +19 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -2783
- package/dist/types/schema-extensions.d.ts +1 -0
- package/dist/types/schema-extensions.d.ts.map +1 -0
- package/dist/types/schema-extensions.js +0 -0
- package/package.json +7 -7
- package/src/generators/build-schema.ts +22 -173
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const PACKAGE_IMPORTS = {
|
|
2
|
+
'is-object': "import { isObject } from '@amritk/helpers/is-object';",
|
|
3
|
+
'validate-array': "import { validateArray } from '@amritk/helpers/validate-array';",
|
|
4
|
+
'validate-record': "import { validateRecord } from '@amritk/helpers/validate-record';",
|
|
5
|
+
// hasRef has historically lived in the schema-guards subpath; preserve that for package mode.
|
|
6
|
+
'has-ref': "import { hasRef } from '@amritk/helpers/schema-guards';",
|
|
7
|
+
};
|
|
8
|
+
const EMBEDDED_NAMED_EXPORTS = {
|
|
9
|
+
'is-object': 'isObject',
|
|
10
|
+
'validate-array': 'validateArray',
|
|
11
|
+
'validate-record': 'validateRecord',
|
|
12
|
+
'has-ref': 'hasRef',
|
|
13
|
+
};
|
|
14
|
+
const embeddedImport = (helper, prefix) => `import { ${EMBEDDED_NAMED_EXPORTS[helper]} } from '${prefix}_helpers/${helper}';`;
|
|
15
|
+
/**
|
|
16
|
+
* Detects which runtime helpers a generated parser body references.
|
|
17
|
+
*
|
|
18
|
+
* @param helpersImportPrefix - Relative path prefix to the shared `_helpers/`
|
|
19
|
+
* directory in embedded mode. Defaults to `'./'`. The recursive multi-schema
|
|
20
|
+
* build passes `'../'`, `'../../'`, etc. so nested parsers can reach a single
|
|
21
|
+
* `_helpers/` directory at the output root.
|
|
22
|
+
*/
|
|
23
|
+
export const collectHelpers = (parserFunction, mode, helpersImportPrefix = './') => {
|
|
24
|
+
const imports = [];
|
|
25
|
+
const used = new Set();
|
|
26
|
+
const importFor = (helper) => mode === 'embedded' ? embeddedImport(helper, helpersImportPrefix) : PACKAGE_IMPORTS[helper];
|
|
27
|
+
if (parserFunction.includes('validateArray')) {
|
|
28
|
+
imports.push(importFor('validate-array'));
|
|
29
|
+
used.add('validate-array');
|
|
30
|
+
}
|
|
31
|
+
if (parserFunction.includes('validateRecord')) {
|
|
32
|
+
imports.push(importFor('validate-record'));
|
|
33
|
+
used.add('validate-record');
|
|
34
|
+
// The embedded validate-record.ts imports is-object, so it must be shipped too —
|
|
35
|
+
// but only the `validate-record` line is needed in the parser file unless the
|
|
36
|
+
// parser body itself references `isObject` (handled below).
|
|
37
|
+
used.add('is-object');
|
|
38
|
+
}
|
|
39
|
+
if (parserFunction.includes('isObject')) {
|
|
40
|
+
imports.push(importFor('is-object'));
|
|
41
|
+
used.add('is-object');
|
|
42
|
+
}
|
|
43
|
+
if (parserFunction.includes('hasRef(')) {
|
|
44
|
+
imports.push(importFor('has-ref'));
|
|
45
|
+
used.add('has-ref');
|
|
46
|
+
}
|
|
47
|
+
return { imports, used };
|
|
48
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-imports.d.ts","sourceRoot":"","sources":["../../src/helpers/collect-imports.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAIjE;;GAEG;AACH,KAAK,qBAAqB,GAAG;IAC3B;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACzD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,cAAc,WAAY,UAAU,YAAY,qBAAqB,KAAG,MAAM,EAsM1F,CAAA"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { refToFilename } from '@amritk/helpers/ref-to-filename';
|
|
2
|
+
import { refToName } from '@amritk/helpers/ref-to-name';
|
|
3
|
+
import { resolveRef } from '@amritk/helpers/resolve-ref';
|
|
4
|
+
import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasItems, hasOneOf, hasRef } from '@amritk/helpers/schema-guards';
|
|
5
|
+
const getImportPathForFilename = (filename) => `./${filename}`;
|
|
6
|
+
/**
|
|
7
|
+
* Collects all import statements needed for a schema by finding $ref references
|
|
8
|
+
* that are directly used in the type definition (from properties, items, etc.).
|
|
9
|
+
* Does not include refs from nested schema definitions.
|
|
10
|
+
*
|
|
11
|
+
* Generates both type imports and parser function imports for each $ref.
|
|
12
|
+
* The parser imports allow validators to delegate validation to the referenced
|
|
13
|
+
* schema's parser instead of inlining the validation logic.
|
|
14
|
+
*
|
|
15
|
+
* Also detects when helper functions (isObject) and validators (validateRecord,
|
|
16
|
+
* validateArray) are needed based on the schema structure.
|
|
17
|
+
*
|
|
18
|
+
* @param schema - The JSON Schema to collect imports from
|
|
19
|
+
* @param options - Optional settings to control import generation
|
|
20
|
+
* @returns An array of import statements
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const schema = {
|
|
25
|
+
* type: 'object',
|
|
26
|
+
* properties: {
|
|
27
|
+
* contact: { $ref: '#/$defs/contact' },
|
|
28
|
+
* server: { $ref: '#/$defs/server' }
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* const imports = collectImports(schema)
|
|
32
|
+
* // imports = [
|
|
33
|
+
* // "import { type Contact, parseContact } from './contact';",
|
|
34
|
+
* // "import { type Server, parseServer } from './server';"
|
|
35
|
+
* // ]
|
|
36
|
+
*
|
|
37
|
+
* // With typesOnly, only type imports are generated:
|
|
38
|
+
* const typeImports = collectImports(schema, { typesOnly: true })
|
|
39
|
+
* // typeImports = [
|
|
40
|
+
* // "import type { Contact } from './contact';",
|
|
41
|
+
* // "import type { Server } from './server';"
|
|
42
|
+
* // ]
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const collectImports = (schema, options) => {
|
|
46
|
+
const typesOnly = options?.typesOnly === true;
|
|
47
|
+
const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : undefined;
|
|
48
|
+
const rootSchema = options?.rootSchema;
|
|
49
|
+
const typeSuffix = options?.typeSuffix;
|
|
50
|
+
const refs = new Set();
|
|
51
|
+
const collectRefsFromValue = (value) => {
|
|
52
|
+
if (typeof value !== 'object' || value === null) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
for (const item of value) {
|
|
57
|
+
collectRefsFromValue(item);
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const record = value;
|
|
62
|
+
// If this is a $ref, add it — but skip:
|
|
63
|
+
// - Relative path refs (e.g. /components/messages/foo) which point into example data
|
|
64
|
+
// - URI refs with fragments pointing into `properties` (not standalone definitions)
|
|
65
|
+
if (hasRef(record)) {
|
|
66
|
+
const ref = record.$ref;
|
|
67
|
+
const isInternal = ref.startsWith('#');
|
|
68
|
+
const isUri = ref.startsWith('http://') || ref.startsWith('https://');
|
|
69
|
+
const isPropertyFragment = isUri && ref.includes('#/properties/');
|
|
70
|
+
if (isInternal || (isUri && !isPropertyFragment)) {
|
|
71
|
+
refs.add(ref);
|
|
72
|
+
}
|
|
73
|
+
return; // Don't traverse further into a $ref
|
|
74
|
+
}
|
|
75
|
+
// Check if this is an object type with additionalProperties that has a $ref
|
|
76
|
+
// This pattern requires validateRecord
|
|
77
|
+
if (hasAdditionalProperties(record) && hasRef(record.additionalProperties)) {
|
|
78
|
+
collectRefsFromValue(record.additionalProperties);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Check if this is an array type with items that has a $ref
|
|
82
|
+
// This pattern requires validateArray
|
|
83
|
+
if (record.type === 'array' && hasItems(record) && hasRef(record.items)) {
|
|
84
|
+
collectRefsFromValue(record.items);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Traverse nested properties regardless of whether `type` is explicitly set.
|
|
88
|
+
if ('properties' in record && typeof record.properties === 'object' && record.properties !== null) {
|
|
89
|
+
const props = record.properties;
|
|
90
|
+
for (const key in props) {
|
|
91
|
+
collectRefsFromValue(props[key]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Traverse into type composition keywords
|
|
95
|
+
if (hasOneOf(record)) {
|
|
96
|
+
for (const item of record.oneOf) {
|
|
97
|
+
collectRefsFromValue(item);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (hasAnyOf(record)) {
|
|
101
|
+
for (const item of record.anyOf) {
|
|
102
|
+
collectRefsFromValue(item);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (hasAllOf(record)) {
|
|
106
|
+
for (const item of record.allOf) {
|
|
107
|
+
collectRefsFromValue(item);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Traverse into array items
|
|
111
|
+
if (hasItems(record)) {
|
|
112
|
+
collectRefsFromValue(record.items);
|
|
113
|
+
}
|
|
114
|
+
// Traverse into additionalProperties
|
|
115
|
+
if (hasAdditionalProperties(record)) {
|
|
116
|
+
collectRefsFromValue(record.additionalProperties);
|
|
117
|
+
}
|
|
118
|
+
// Traverse all patternProperties
|
|
119
|
+
if ('patternProperties' in record &&
|
|
120
|
+
typeof record.patternProperties === 'object' &&
|
|
121
|
+
record.patternProperties !== null) {
|
|
122
|
+
const patternProps = record.patternProperties;
|
|
123
|
+
for (const key in patternProps) {
|
|
124
|
+
collectRefsFromValue(patternProps[key]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Traverse into if/then/else branches
|
|
128
|
+
if ('then' in record) {
|
|
129
|
+
collectRefsFromValue(record.then);
|
|
130
|
+
}
|
|
131
|
+
if ('else' in record) {
|
|
132
|
+
collectRefsFromValue(record.else);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
// Collect refs from root-level $ref
|
|
136
|
+
if (typeof schema === 'object' && schema !== null && hasRef(schema)) {
|
|
137
|
+
const ref = schema.$ref;
|
|
138
|
+
const isInternal = ref.startsWith('#');
|
|
139
|
+
const isUri = ref.startsWith('http://') || ref.startsWith('https://');
|
|
140
|
+
const isPropertyFragment = isUri && ref.includes('#/properties/');
|
|
141
|
+
if (isInternal || (isUri && !isPropertyFragment)) {
|
|
142
|
+
refs.add(ref);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Collect refs from properties
|
|
146
|
+
if (typeof schema === 'object' && schema !== null && 'properties' in schema) {
|
|
147
|
+
const properties = schema.properties;
|
|
148
|
+
for (const key in properties) {
|
|
149
|
+
collectRefsFromValue(properties[key]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Collect refs from root-level additionalProperties
|
|
153
|
+
if (typeof schema === 'object' && schema !== null && 'additionalProperties' in schema) {
|
|
154
|
+
collectRefsFromValue(schema.additionalProperties);
|
|
155
|
+
}
|
|
156
|
+
// Collect refs from root-level patternProperties
|
|
157
|
+
if (typeof schema === 'object' && schema !== null && 'patternProperties' in schema) {
|
|
158
|
+
const patternProps = schema.patternProperties;
|
|
159
|
+
for (const key in patternProps) {
|
|
160
|
+
collectRefsFromValue(patternProps[key]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Collect refs from root-level items (when the schema itself is an array type)
|
|
164
|
+
if (typeof schema === 'object' && schema !== null && hasItems(schema)) {
|
|
165
|
+
collectRefsFromValue(schema.items);
|
|
166
|
+
}
|
|
167
|
+
// Collect refs from root-level composition keywords.
|
|
168
|
+
if (typeof schema === 'object' && schema !== null && hasOneOf(schema)) {
|
|
169
|
+
for (const item of schema.oneOf) {
|
|
170
|
+
collectRefsFromValue(item);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (typeof schema === 'object' && schema !== null && hasAnyOf(schema)) {
|
|
174
|
+
for (const item of schema.anyOf) {
|
|
175
|
+
collectRefsFromValue(item);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (typeof schema === 'object' && schema !== null && hasAllOf(schema)) {
|
|
179
|
+
for (const item of schema.allOf) {
|
|
180
|
+
collectRefsFromValue(item);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Collect refs from root-level conditional branches.
|
|
184
|
+
if (typeof schema === 'object' && schema !== null && 'if' in schema) {
|
|
185
|
+
collectRefsFromValue(schema.if);
|
|
186
|
+
}
|
|
187
|
+
if (typeof schema === 'object' && schema !== null && 'then' in schema) {
|
|
188
|
+
collectRefsFromValue(schema.then);
|
|
189
|
+
}
|
|
190
|
+
if (typeof schema === 'object' && schema !== null && 'else' in schema) {
|
|
191
|
+
collectRefsFromValue(schema.else);
|
|
192
|
+
}
|
|
193
|
+
// Convert refs to combined import statements, deduplicating by filename
|
|
194
|
+
const importMap = new Map();
|
|
195
|
+
for (const ref of refs) {
|
|
196
|
+
// For URI refs, skip if the base URI is not resolvable in the root schema.
|
|
197
|
+
// This prevents generating imports for external schemas that were never generated as files.
|
|
198
|
+
if (rootSchema !== undefined && (ref.startsWith('http://') || ref.startsWith('https://'))) {
|
|
199
|
+
if (!resolveRef(ref, rootSchema))
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const typeName = refToName(ref, typeSuffix);
|
|
203
|
+
const filename = refToFilename(ref);
|
|
204
|
+
// Skip self-referential imports — a file must not import from itself
|
|
205
|
+
if (selfFilename !== undefined && filename === selfFilename) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const importPath = getImportPathForFilename(filename);
|
|
209
|
+
// In types-only mode, omit the parser function import since there is no parser to call
|
|
210
|
+
const importStatement = typesOnly
|
|
211
|
+
? `import type { ${typeName} } from '${importPath}';`
|
|
212
|
+
: `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
|
|
213
|
+
importMap.set(filename, importStatement);
|
|
214
|
+
}
|
|
215
|
+
return Array.from(importMap.values()).sort();
|
|
216
|
+
};
|
|
@@ -4,3 +4,4 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
|
4
4
|
* Returns the property name if a suitable discriminator is found, otherwise null.
|
|
5
5
|
*/
|
|
6
6
|
export declare const findDiscriminator: (schemas: readonly JSONSchema[]) => string | null;
|
|
7
|
+
//# sourceMappingURL=find-discriminator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-discriminator.d.ts","sourceRoot":"","sources":["../../src/helpers/find-discriminator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAEjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YAAa,SAAS,UAAU,EAAE,KAAG,MAAM,GAAG,IA+B3E,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { hasConst, hasEnum, hasProperties, isObjectSchema, isSchemaObject } from '@amritk/helpers/schema-guards';
|
|
2
|
+
/**
|
|
3
|
+
* Attempts to find a discriminator property that can be used to distinguish between union types.
|
|
4
|
+
* Returns the property name if a suitable discriminator is found, otherwise null.
|
|
5
|
+
*/
|
|
6
|
+
export const findDiscriminator = (schemas) => {
|
|
7
|
+
// Look for a property that has different constant/enum values in each schema
|
|
8
|
+
const propertyValues = new Map();
|
|
9
|
+
for (const schema of schemas) {
|
|
10
|
+
if (isObjectSchema(schema) && hasProperties(schema)) {
|
|
11
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
12
|
+
if (!propertyValues.has(key)) {
|
|
13
|
+
propertyValues.set(key, new Set());
|
|
14
|
+
}
|
|
15
|
+
// Check if property has a const value
|
|
16
|
+
if (isSchemaObject(propSchema) && hasConst(propSchema)) {
|
|
17
|
+
propertyValues.get(key)?.add(propSchema.const);
|
|
18
|
+
}
|
|
19
|
+
// Check if property has a single enum value
|
|
20
|
+
else if (isSchemaObject(propSchema) && hasEnum(propSchema) && propSchema.enum.length === 1) {
|
|
21
|
+
propertyValues.get(key)?.add(propSchema.enum[0]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Find a property where each schema has a unique value
|
|
27
|
+
for (const [key, values] of propertyValues.entries()) {
|
|
28
|
+
if (values.size === schemas.length) {
|
|
29
|
+
return key;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-default-value.d.ts","sourceRoot":"","sources":["../../src/helpers/get-default-value.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAGjE;;;;GAIG;AACH,eAAO,MAAM,eAAe,WAAY,UAAU,KAAG,MA0EpD,CAAA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { getMjstPrimitive } from '@amritk/helpers/mjst-extension';
|
|
2
|
+
import { hasAllOf, hasAnyOf, hasDefault, hasEnum, hasExamples, hasOneOf, hasPattern, hasType, isSchemaObject, } from '@amritk/helpers/schema-guards';
|
|
3
|
+
import { generateDefaultFromPattern } from '../generators/generate-default-from-pattern.js';
|
|
4
|
+
/**
|
|
5
|
+
* Returns the default value for a JSON Schema property.
|
|
6
|
+
* Priority order: explicit default > first enum value > first example > union first schema > pattern-based > type-based.
|
|
7
|
+
* These defaults ensure that parsing never fails, even with missing data.
|
|
8
|
+
*/
|
|
9
|
+
export const getDefaultValue = (schema) => {
|
|
10
|
+
if (!isSchemaObject(schema)) {
|
|
11
|
+
return 'undefined';
|
|
12
|
+
}
|
|
13
|
+
// Explicit default takes highest priority
|
|
14
|
+
if (hasDefault(schema)) {
|
|
15
|
+
return JSON.stringify(schema.default);
|
|
16
|
+
}
|
|
17
|
+
// Use first enum value if available
|
|
18
|
+
if (hasEnum(schema) && schema.enum.length > 0) {
|
|
19
|
+
return JSON.stringify(schema.enum[0]);
|
|
20
|
+
}
|
|
21
|
+
// Use first example if available
|
|
22
|
+
if (hasExamples(schema) && schema.examples.length > 0) {
|
|
23
|
+
return JSON.stringify(schema.examples[0]);
|
|
24
|
+
}
|
|
25
|
+
// Handle union types - use first schema's default
|
|
26
|
+
if (hasOneOf(schema) && schema.oneOf.length > 0) {
|
|
27
|
+
const firstSchema = schema.oneOf[0];
|
|
28
|
+
if (firstSchema !== undefined) {
|
|
29
|
+
return getDefaultValue(firstSchema);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (hasAnyOf(schema) && schema.anyOf.length > 0) {
|
|
33
|
+
const firstSchema = schema.anyOf[0];
|
|
34
|
+
if (firstSchema !== undefined) {
|
|
35
|
+
return getDefaultValue(firstSchema);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Handle allOf - use first schema's default
|
|
39
|
+
if (hasAllOf(schema) && schema.allOf.length > 0) {
|
|
40
|
+
const firstSchema = schema.allOf[0];
|
|
41
|
+
if (firstSchema !== undefined) {
|
|
42
|
+
return getDefaultValue(firstSchema);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// x-mjst bigint has no native JSON type but a sensible zero default.
|
|
46
|
+
if (getMjstPrimitive(schema) === 'bigint') {
|
|
47
|
+
return '0n';
|
|
48
|
+
}
|
|
49
|
+
if (!hasType(schema)) {
|
|
50
|
+
return 'undefined';
|
|
51
|
+
}
|
|
52
|
+
switch (schema.type) {
|
|
53
|
+
case 'string': {
|
|
54
|
+
if (hasPattern(schema)) {
|
|
55
|
+
const patternDefault = generateDefaultFromPattern(schema.pattern);
|
|
56
|
+
if (patternDefault) {
|
|
57
|
+
return patternDefault;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return '""';
|
|
61
|
+
}
|
|
62
|
+
case 'number':
|
|
63
|
+
case 'integer':
|
|
64
|
+
return '0';
|
|
65
|
+
case 'boolean':
|
|
66
|
+
return 'false';
|
|
67
|
+
case 'array':
|
|
68
|
+
return '[]';
|
|
69
|
+
case 'object':
|
|
70
|
+
return '{}';
|
|
71
|
+
default:
|
|
72
|
+
return 'undefined';
|
|
73
|
+
}
|
|
74
|
+
};
|
|
@@ -4,3 +4,4 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
|
|
4
4
|
* Returns the const value or single enum value if available.
|
|
5
5
|
*/
|
|
6
6
|
export declare const getDiscriminatorValue: (schema: JSONSchema, discriminatorKey: string) => unknown | null;
|
|
7
|
+
//# sourceMappingURL=get-discriminator-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-discriminator-value.d.ts","sourceRoot":"","sources":["../../src/helpers/get-discriminator-value.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAEjE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,WAAY,UAAU,oBAAoB,MAAM,KAAG,OAAO,GAAG,IAa9F,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { hasConst, hasEnum, hasProperties, isObjectSchema, isSchemaObject } from '@amritk/helpers/schema-guards';
|
|
2
|
+
/**
|
|
3
|
+
* Gets the discriminator value for a specific schema.
|
|
4
|
+
* Returns the const value or single enum value if available.
|
|
5
|
+
*/
|
|
6
|
+
export const getDiscriminatorValue = (schema, discriminatorKey) => {
|
|
7
|
+
if (isObjectSchema(schema) && hasProperties(schema)) {
|
|
8
|
+
const propSchema = schema.properties[discriminatorKey];
|
|
9
|
+
if (propSchema && isSchemaObject(propSchema)) {
|
|
10
|
+
if (hasConst(propSchema)) {
|
|
11
|
+
return propSchema.const;
|
|
12
|
+
}
|
|
13
|
+
if (hasEnum(propSchema) && propSchema.enum.length === 1) {
|
|
14
|
+
return propSchema.enum[0];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { buildSchema, type GeneratedFile } from './generators/build-schema';
|
|
1
|
+
export { buildSchema, type GeneratedFile } from './generators/build-schema.js';
|
|
2
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,2BAA2B,CAAA"}
|