@amritk/generate-parsers 0.16.2 → 0.17.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.
@@ -1,77 +1,64 @@
1
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';",
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
7
  };
8
8
  const EMBEDDED_NAMED_EXPORTS = {
9
- 'is-object': 'isObject',
10
- 'validate-array': 'validateArray',
11
- 'validate-record': 'validateRecord',
12
- 'has-ref': 'hasRef',
9
+ "is-object": "isObject",
10
+ "validate-array": "validateArray",
11
+ "validate-record": "validateRecord",
12
+ "has-ref": "hasRef"
13
13
  };
14
- const embeddedImport = (helper, prefix, ext) =>
15
- // An explicit extension so the embedded-helper import resolves under Node ESM, not only Bun.
16
- `import { ${EMBEDDED_NAMED_EXPORTS[helper]} } from '${prefix}_helpers/${helper}.${ext}';`;
17
- /**
18
- * Detects which runtime helpers a generated parser body references.
19
- *
20
- * @param helpersImportPrefix - Relative path prefix to the shared `_helpers/`
21
- * directory in embedded mode. Defaults to `'./'`. The recursive multi-schema
22
- * build passes `'../'`, `'../../'`, etc. so nested parsers can reach a single
23
- * `_helpers/` directory at the output root.
24
- * @param importExt - Extension used on embedded-helper import specifiers.
25
- * Defaults to `'js'` (the TS NodeNext form); `'ts'` makes the output runnable
26
- * under Node's type stripping.
27
- */
28
- // One alternation pass over the generated source instead of four full-text
29
- // `.includes` scans — an absent helper name used to cost a complete rescan
30
- // each, which showed up at several percent of total generation time.
14
+ const embeddedImport = (helper, prefix, ext) => (
15
+ // An explicit extension so the embedded-helper import resolves under Node ESM, not only Bun.
16
+ `import { ${EMBEDDED_NAMED_EXPORTS[helper]} } from '${prefix}_helpers/${helper}.${ext}';`
17
+ );
31
18
  const HELPER_USAGE = /validateArray|validateRecord|isObject|hasRef\(/g;
32
- export const collectHelpers = (parserFunction, mode, helpersImportPrefix = './', importExt = 'js') => {
33
- const imports = [];
34
- const used = new Set();
35
- const importFor = (helper) => mode === 'embedded' ? embeddedImport(helper, helpersImportPrefix, importExt) : PACKAGE_IMPORTS[helper];
36
- let sawValidateArray = false;
37
- let sawValidateRecord = false;
38
- let sawIsObject = false;
39
- let sawHasRef = false;
40
- HELPER_USAGE.lastIndex = 0;
41
- let match = HELPER_USAGE.exec(parserFunction);
42
- while (match !== null) {
43
- const token = match[0];
44
- if (token === 'validateArray')
45
- sawValidateArray = true;
46
- else if (token === 'validateRecord')
47
- sawValidateRecord = true;
48
- else if (token === 'isObject')
49
- sawIsObject = true;
50
- else
51
- sawHasRef = true;
52
- if (sawValidateArray && sawValidateRecord && sawIsObject && sawHasRef)
53
- break;
54
- match = HELPER_USAGE.exec(parserFunction);
55
- }
56
- if (sawValidateArray) {
57
- imports.push(importFor('validate-array'));
58
- used.add('validate-array');
59
- }
60
- if (sawValidateRecord) {
61
- imports.push(importFor('validate-record'));
62
- used.add('validate-record');
63
- // The embedded validate-record.ts imports is-object, so it must be shipped too —
64
- // but only the `validate-record` line is needed in the parser file unless the
65
- // parser body itself references `isObject` (handled below).
66
- used.add('is-object');
67
- }
68
- if (sawIsObject) {
69
- imports.push(importFor('is-object'));
70
- used.add('is-object');
71
- }
72
- if (sawHasRef) {
73
- imports.push(importFor('has-ref'));
74
- used.add('has-ref');
75
- }
76
- return { imports, used };
19
+ const collectHelpers = (parserFunction, mode, helpersImportPrefix = "./", importExt = "js") => {
20
+ const imports = [];
21
+ const used = /* @__PURE__ */ new Set();
22
+ const importFor = (helper) => mode === "embedded" ? embeddedImport(helper, helpersImportPrefix, importExt) : PACKAGE_IMPORTS[helper];
23
+ let sawValidateArray = false;
24
+ let sawValidateRecord = false;
25
+ let sawIsObject = false;
26
+ let sawHasRef = false;
27
+ HELPER_USAGE.lastIndex = 0;
28
+ let match = HELPER_USAGE.exec(parserFunction);
29
+ while (match !== null) {
30
+ const token = match[0];
31
+ if (token === "validateArray")
32
+ sawValidateArray = true;
33
+ else if (token === "validateRecord")
34
+ sawValidateRecord = true;
35
+ else if (token === "isObject")
36
+ sawIsObject = true;
37
+ else
38
+ sawHasRef = true;
39
+ if (sawValidateArray && sawValidateRecord && sawIsObject && sawHasRef)
40
+ break;
41
+ match = HELPER_USAGE.exec(parserFunction);
42
+ }
43
+ if (sawValidateArray) {
44
+ imports.push(importFor("validate-array"));
45
+ used.add("validate-array");
46
+ }
47
+ if (sawValidateRecord) {
48
+ imports.push(importFor("validate-record"));
49
+ used.add("validate-record");
50
+ used.add("is-object");
51
+ }
52
+ if (sawIsObject) {
53
+ imports.push(importFor("is-object"));
54
+ used.add("is-object");
55
+ }
56
+ if (sawHasRef) {
57
+ imports.push(importFor("has-ref"));
58
+ used.add("has-ref");
59
+ }
60
+ return { imports, used };
61
+ };
62
+ export {
63
+ collectHelpers
77
64
  };
@@ -1,242 +1,166 @@
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
- // Emit an explicit extension on the relative specifier. Node's ESM resolver
6
- // requires one (extensionless relative imports only work under a bundler or
7
- // Bun). `./x.js` pointing at a sibling `x.ts` is the standard TS NodeNext form
8
- // — accepted by Bun, esbuild, webpack, and tsc alike — while `./x.ts` is the
9
- // literal on-disk path Node's type stripping needs to run the sources directly.
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";
10
5
  const getImportPathForFilename = (filename, ext) => `./${filename}.${ext}`;
11
- /**
12
- * Collects all import statements needed for a schema by finding $ref references
13
- * that are directly used in the type definition (from properties, items, etc.).
14
- * Does not include refs from nested schema definitions.
15
- *
16
- * Generates both type imports and parser function imports for each $ref.
17
- * The parser imports allow validators to delegate validation to the referenced
18
- * schema's parser instead of inlining the validation logic.
19
- *
20
- * Also detects when helper functions (isObject) and validators (validateRecord,
21
- * validateArray) are needed based on the schema structure.
22
- *
23
- * @param schema - The JSON Schema to collect imports from
24
- * @param options - Optional settings to control import generation
25
- * @returns An array of import statements
26
- *
27
- * @example
28
- * ```ts
29
- * const schema = {
30
- * type: 'object',
31
- * properties: {
32
- * contact: { $ref: '#/$defs/contact' },
33
- * server: { $ref: '#/$defs/server' }
34
- * }
35
- * }
36
- * const imports = collectImports(schema)
37
- * // imports = [
38
- * // "import { type Contact, parseContact } from './contact';",
39
- * // "import { type Server, parseServer } from './server';"
40
- * // ]
41
- *
42
- * // With typesOnly, only type imports are generated:
43
- * const typeImports = collectImports(schema, { typesOnly: true })
44
- * // typeImports = [
45
- * // "import type { Contact } from './contact';",
46
- * // "import type { Server } from './server';"
47
- * // ]
48
- * ```
49
- */
50
- export const collectImports = (schema, options) => {
51
- const typesOnly = options?.typesOnly === true;
52
- const importExt = options?.importExt ?? 'js';
53
- const importMap = new Map();
54
- for (const [filename, typeName] of collectImportTargets(schema, options)) {
55
- const importPath = getImportPathForFilename(filename, importExt);
56
- // In types-only mode, omit the parser function import since there is no parser to call
57
- const importStatement = typesOnly
58
- ? `import type { ${typeName} } from '${importPath}';`
59
- : `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
60
- importMap.set(filename, importStatement);
61
- }
62
- return Array.from(importMap.values()).sort();
6
+ const collectImports = (schema, options) => {
7
+ const typesOnly = options?.typesOnly === true;
8
+ const importExt = options?.importExt ?? "js";
9
+ const importMap = /* @__PURE__ */ new Map();
10
+ for (const [filename, typeName] of collectImportTargets(schema, options)) {
11
+ const importPath = getImportPathForFilename(filename, importExt);
12
+ const importStatement = typesOnly ? `import type { ${typeName} } from '${importPath}';` : `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
13
+ importMap.set(filename, importStatement);
14
+ }
15
+ return Array.from(importMap.values()).sort();
63
16
  };
64
- /**
65
- * The type names a generated file will import for its `$ref`s — the same
66
- * dedup/skip rules as {@link collectImports}. Generators seed their private
67
- * sub-type naming with this set so a synthesized name (e.g. a root array's
68
- * `FooItem`) can never shadow an imported identifier.
69
- */
70
- export const collectImportTypeNames = (schema, options) => {
71
- const names = new Set();
72
- for (const [, typeName] of collectImportTargets(schema, options))
73
- names.add(typeName);
74
- return names;
17
+ const collectImportTypeNames = (schema, options) => {
18
+ const names = /* @__PURE__ */ new Set();
19
+ for (const [, typeName] of collectImportTargets(schema, options))
20
+ names.add(typeName);
21
+ return names;
75
22
  };
76
- /** Shared `$ref` walk: filename → derived type name for every import target. */
77
23
  const collectImportTargets = (schema, options) => {
78
- const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : undefined;
79
- const rootSchema = options?.rootSchema;
80
- const typeSuffix = options?.typeSuffix;
81
- const refs = new Set();
82
- const collectRefsFromValue = (value) => {
83
- if (typeof value !== 'object' || value === null) {
84
- return;
85
- }
86
- if (Array.isArray(value)) {
87
- for (const item of value) {
88
- collectRefsFromValue(item);
89
- }
90
- return;
91
- }
92
- const record = value;
93
- // If this is a $ref, add it — but skip:
94
- // - Relative path refs (e.g. /components/messages/foo) which point into example data
95
- // - URI refs with fragments pointing into `properties` (not standalone definitions)
96
- if (hasRef(record)) {
97
- const ref = record.$ref;
98
- const isInternal = ref.startsWith('#');
99
- const isUri = ref.startsWith('http://') || ref.startsWith('https://');
100
- const isPropertyFragment = isUri && ref.includes('#/properties/');
101
- if (isInternal || (isUri && !isPropertyFragment)) {
102
- refs.add(ref);
103
- }
104
- return; // Don't traverse further into a $ref
105
- }
106
- // Check if this is an object type with additionalProperties that has a $ref
107
- // This pattern requires validateRecord
108
- if (hasAdditionalProperties(record) && hasRef(record.additionalProperties)) {
109
- collectRefsFromValue(record.additionalProperties);
110
- return;
111
- }
112
- // Check if this is an array type with items that has a $ref
113
- // This pattern requires validateArray
114
- if (record.type === 'array' && hasItems(record) && hasRef(record.items)) {
115
- collectRefsFromValue(record.items);
116
- return;
117
- }
118
- // Traverse nested properties regardless of whether `type` is explicitly set.
119
- if ('properties' in record && typeof record.properties === 'object' && record.properties !== null) {
120
- const props = record.properties;
121
- for (const key in props) {
122
- collectRefsFromValue(props[key]);
123
- }
124
- }
125
- // Traverse into type composition keywords
126
- if (hasOneOf(record)) {
127
- for (const item of record.oneOf) {
128
- collectRefsFromValue(item);
129
- }
130
- }
131
- if (hasAnyOf(record)) {
132
- for (const item of record.anyOf) {
133
- collectRefsFromValue(item);
134
- }
135
- }
136
- if (hasAllOf(record)) {
137
- for (const item of record.allOf) {
138
- collectRefsFromValue(item);
139
- }
140
- }
141
- // Traverse into array items
142
- if (hasItems(record)) {
143
- collectRefsFromValue(record.items);
144
- }
145
- // Traverse into additionalProperties
146
- if (hasAdditionalProperties(record)) {
147
- collectRefsFromValue(record.additionalProperties);
148
- }
149
- // Traverse all patternProperties
150
- if ('patternProperties' in record &&
151
- typeof record.patternProperties === 'object' &&
152
- record.patternProperties !== null) {
153
- const patternProps = record.patternProperties;
154
- for (const key in patternProps) {
155
- collectRefsFromValue(patternProps[key]);
156
- }
157
- }
158
- // Traverse into if/then/else branches
159
- if ('then' in record) {
160
- collectRefsFromValue(record.then);
161
- }
162
- if ('else' in record) {
163
- collectRefsFromValue(record.else);
164
- }
165
- };
166
- // Collect refs from root-level $ref
167
- if (typeof schema === 'object' && schema !== null && hasRef(schema)) {
168
- const ref = schema.$ref;
169
- const isInternal = ref.startsWith('#');
170
- const isUri = ref.startsWith('http://') || ref.startsWith('https://');
171
- const isPropertyFragment = isUri && ref.includes('#/properties/');
172
- if (isInternal || (isUri && !isPropertyFragment)) {
173
- refs.add(ref);
174
- }
175
- }
176
- // Collect refs from properties
177
- if (typeof schema === 'object' && schema !== null && 'properties' in schema) {
178
- const properties = schema.properties;
179
- for (const key in properties) {
180
- collectRefsFromValue(properties[key]);
181
- }
182
- }
183
- // Collect refs from root-level additionalProperties
184
- if (typeof schema === 'object' && schema !== null && 'additionalProperties' in schema) {
185
- collectRefsFromValue(schema.additionalProperties);
186
- }
187
- // Collect refs from root-level patternProperties
188
- if (typeof schema === 'object' && schema !== null && 'patternProperties' in schema) {
189
- const patternProps = schema.patternProperties;
190
- for (const key in patternProps) {
191
- collectRefsFromValue(patternProps[key]);
192
- }
193
- }
194
- // Collect refs from root-level items (when the schema itself is an array type)
195
- if (typeof schema === 'object' && schema !== null && hasItems(schema)) {
196
- collectRefsFromValue(schema.items);
197
- }
198
- // Collect refs from root-level composition keywords.
199
- if (typeof schema === 'object' && schema !== null && hasOneOf(schema)) {
200
- for (const item of schema.oneOf) {
201
- collectRefsFromValue(item);
202
- }
203
- }
204
- if (typeof schema === 'object' && schema !== null && hasAnyOf(schema)) {
205
- for (const item of schema.anyOf) {
206
- collectRefsFromValue(item);
207
- }
208
- }
209
- if (typeof schema === 'object' && schema !== null && hasAllOf(schema)) {
210
- for (const item of schema.allOf) {
211
- collectRefsFromValue(item);
212
- }
213
- }
214
- // Collect refs from root-level conditional branches.
215
- if (typeof schema === 'object' && schema !== null && 'if' in schema) {
216
- collectRefsFromValue(schema.if);
217
- }
218
- if (typeof schema === 'object' && schema !== null && 'then' in schema) {
219
- collectRefsFromValue(schema.then);
220
- }
221
- if (typeof schema === 'object' && schema !== null && 'else' in schema) {
222
- collectRefsFromValue(schema.else);
223
- }
224
- // Convert refs to import targets, deduplicating by filename.
225
- const targets = new Map();
226
- for (const ref of refs) {
227
- // For URI refs, skip if the base URI is not resolvable in the root schema.
228
- // This prevents generating imports for external schemas that were never generated as files.
229
- if (rootSchema !== undefined && (ref.startsWith('http://') || ref.startsWith('https://'))) {
230
- if (!resolveRef(ref, rootSchema))
231
- continue;
232
- }
233
- const typeName = refToName(ref, typeSuffix);
234
- const filename = refToFilename(ref);
235
- // Skip self-referential imports — a file must not import from itself
236
- if (selfFilename !== undefined && filename === selfFilename) {
237
- continue;
238
- }
239
- targets.set(filename, typeName);
240
- }
241
- return targets;
24
+ const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : void 0;
25
+ const rootSchema = options?.rootSchema;
26
+ const typeSuffix = options?.typeSuffix;
27
+ const refs = /* @__PURE__ */ new Set();
28
+ const collectRefsFromValue = (value) => {
29
+ if (typeof value !== "object" || value === null) {
30
+ return;
31
+ }
32
+ if (Array.isArray(value)) {
33
+ for (const item of value) {
34
+ collectRefsFromValue(item);
35
+ }
36
+ return;
37
+ }
38
+ const record = value;
39
+ if (hasRef(record)) {
40
+ const ref = record.$ref;
41
+ const isInternal = ref.startsWith("#");
42
+ const isUri = ref.startsWith("http://") || ref.startsWith("https://");
43
+ const isPropertyFragment = isUri && ref.includes("#/properties/");
44
+ if (isInternal || isUri && !isPropertyFragment) {
45
+ refs.add(ref);
46
+ }
47
+ return;
48
+ }
49
+ if (hasAdditionalProperties(record) && hasRef(record.additionalProperties)) {
50
+ collectRefsFromValue(record.additionalProperties);
51
+ return;
52
+ }
53
+ if (record.type === "array" && hasItems(record) && hasRef(record.items)) {
54
+ collectRefsFromValue(record.items);
55
+ return;
56
+ }
57
+ if ("properties" in record && typeof record.properties === "object" && record.properties !== null) {
58
+ const props = record.properties;
59
+ for (const key in props) {
60
+ collectRefsFromValue(props[key]);
61
+ }
62
+ }
63
+ if (hasOneOf(record)) {
64
+ for (const item of record.oneOf) {
65
+ collectRefsFromValue(item);
66
+ }
67
+ }
68
+ if (hasAnyOf(record)) {
69
+ for (const item of record.anyOf) {
70
+ collectRefsFromValue(item);
71
+ }
72
+ }
73
+ if (hasAllOf(record)) {
74
+ for (const item of record.allOf) {
75
+ collectRefsFromValue(item);
76
+ }
77
+ }
78
+ if (hasItems(record)) {
79
+ collectRefsFromValue(record.items);
80
+ }
81
+ if (hasAdditionalProperties(record)) {
82
+ collectRefsFromValue(record.additionalProperties);
83
+ }
84
+ if ("patternProperties" in record && typeof record.patternProperties === "object" && record.patternProperties !== null) {
85
+ const patternProps = record.patternProperties;
86
+ for (const key in patternProps) {
87
+ collectRefsFromValue(patternProps[key]);
88
+ }
89
+ }
90
+ if ("then" in record) {
91
+ collectRefsFromValue(record.then);
92
+ }
93
+ if ("else" in record) {
94
+ collectRefsFromValue(record.else);
95
+ }
96
+ };
97
+ if (typeof schema === "object" && schema !== null && hasRef(schema)) {
98
+ const ref = schema.$ref;
99
+ const isInternal = ref.startsWith("#");
100
+ const isUri = ref.startsWith("http://") || ref.startsWith("https://");
101
+ const isPropertyFragment = isUri && ref.includes("#/properties/");
102
+ if (isInternal || isUri && !isPropertyFragment) {
103
+ refs.add(ref);
104
+ }
105
+ }
106
+ if (typeof schema === "object" && schema !== null && "properties" in schema) {
107
+ const properties = schema.properties;
108
+ for (const key in properties) {
109
+ collectRefsFromValue(properties[key]);
110
+ }
111
+ }
112
+ if (typeof schema === "object" && schema !== null && "additionalProperties" in schema) {
113
+ collectRefsFromValue(schema.additionalProperties);
114
+ }
115
+ if (typeof schema === "object" && schema !== null && "patternProperties" in schema) {
116
+ const patternProps = schema.patternProperties;
117
+ for (const key in patternProps) {
118
+ collectRefsFromValue(patternProps[key]);
119
+ }
120
+ }
121
+ if (typeof schema === "object" && schema !== null && hasItems(schema)) {
122
+ collectRefsFromValue(schema.items);
123
+ }
124
+ if (typeof schema === "object" && schema !== null && hasOneOf(schema)) {
125
+ for (const item of schema.oneOf) {
126
+ collectRefsFromValue(item);
127
+ }
128
+ }
129
+ if (typeof schema === "object" && schema !== null && hasAnyOf(schema)) {
130
+ for (const item of schema.anyOf) {
131
+ collectRefsFromValue(item);
132
+ }
133
+ }
134
+ if (typeof schema === "object" && schema !== null && hasAllOf(schema)) {
135
+ for (const item of schema.allOf) {
136
+ collectRefsFromValue(item);
137
+ }
138
+ }
139
+ if (typeof schema === "object" && schema !== null && "if" in schema) {
140
+ collectRefsFromValue(schema.if);
141
+ }
142
+ if (typeof schema === "object" && schema !== null && "then" in schema) {
143
+ collectRefsFromValue(schema.then);
144
+ }
145
+ if (typeof schema === "object" && schema !== null && "else" in schema) {
146
+ collectRefsFromValue(schema.else);
147
+ }
148
+ const targets = /* @__PURE__ */ new Map();
149
+ for (const ref of refs) {
150
+ if (rootSchema !== void 0 && (ref.startsWith("http://") || ref.startsWith("https://"))) {
151
+ if (!resolveRef(ref, rootSchema))
152
+ continue;
153
+ }
154
+ const typeName = refToName(ref, typeSuffix);
155
+ const filename = refToFilename(ref);
156
+ if (selfFilename !== void 0 && filename === selfFilename) {
157
+ continue;
158
+ }
159
+ targets.set(filename, typeName);
160
+ }
161
+ return targets;
162
+ };
163
+ export {
164
+ collectImportTypeNames,
165
+ collectImports
242
166
  };
@@ -1,33 +1,27 @@
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
- }
1
+ import { hasConst, hasEnum, hasProperties, isObjectSchema, isSchemaObject } from "@amritk/helpers/schema-guards";
2
+ const findDiscriminator = (schemas) => {
3
+ const propertyValues = /* @__PURE__ */ new Map();
4
+ for (const schema of schemas) {
5
+ if (isObjectSchema(schema) && hasProperties(schema)) {
6
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
7
+ if (!propertyValues.has(key)) {
8
+ propertyValues.set(key, /* @__PURE__ */ new Set());
24
9
  }
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;
10
+ if (isSchemaObject(propSchema) && hasConst(propSchema)) {
11
+ propertyValues.get(key)?.add(propSchema.const);
12
+ } else if (isSchemaObject(propSchema) && hasEnum(propSchema) && propSchema.enum.length === 1) {
13
+ propertyValues.get(key)?.add(propSchema.enum[0]);
30
14
  }
15
+ }
16
+ }
17
+ }
18
+ for (const [key, values] of propertyValues.entries()) {
19
+ if (values.size === schemas.length) {
20
+ return key;
31
21
  }
32
- return null;
22
+ }
23
+ return null;
24
+ };
25
+ export {
26
+ findDiscriminator
33
27
  };