@takeshape/json-schema 11.52.0 → 11.55.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 (50) hide show
  1. package/dist/__tests__/schema-validator.test.d.ts +1 -0
  2. package/dist/__tests__/schema-validator.test.js +295 -0
  3. package/dist/converters/__tests__/schema-converter.test.d.ts +1 -0
  4. package/dist/converters/__tests__/schema-converter.test.js +1134 -0
  5. package/dist/converters/__tests__/search-shape-schema.json +495 -0
  6. package/dist/converters/index.d.ts +1 -2
  7. package/dist/converters/index.js +1 -16
  8. package/dist/converters/schema-converter.d.ts +0 -1
  9. package/dist/converters/schema-converter.js +540 -643
  10. package/dist/index.d.ts +1 -2
  11. package/dist/index.js +1 -16
  12. package/dist/schema-validator.d.ts +1 -2
  13. package/dist/schema-validator.js +163 -189
  14. package/dist/utils/__tests__/references.test.d.ts +1 -0
  15. package/dist/utils/__tests__/references.test.js +121 -0
  16. package/dist/utils/__tests__/type-utils.test.d.ts +1 -0
  17. package/dist/utils/__tests__/type-utils.test.js +143 -0
  18. package/dist/utils/constants.d.ts +0 -1
  19. package/dist/utils/constants.js +49 -7
  20. package/dist/utils/index.d.ts +4 -5
  21. package/dist/utils/index.js +4 -49
  22. package/dist/utils/keys.d.ts +0 -1
  23. package/dist/utils/keys.js +5 -12
  24. package/dist/utils/references.d.ts +0 -1
  25. package/dist/utils/references.js +56 -57
  26. package/dist/utils/type-utils.d.ts +1 -2
  27. package/dist/utils/type-utils.js +36 -53
  28. package/dist/utils/types.d.ts +0 -1
  29. package/dist/utils/types.js +1 -5
  30. package/package.json +18 -25
  31. package/dist/converters/index.d.ts.map +0 -1
  32. package/dist/converters/schema-converter.d.ts.map +0 -1
  33. package/dist/index.d.ts.map +0 -1
  34. package/dist/schema-validator.d.ts.map +0 -1
  35. package/dist/utils/constants.d.ts.map +0 -1
  36. package/dist/utils/index.d.ts.map +0 -1
  37. package/dist/utils/keys.d.ts.map +0 -1
  38. package/dist/utils/references.d.ts.map +0 -1
  39. package/dist/utils/type-utils.d.ts.map +0 -1
  40. package/dist/utils/types.d.ts.map +0 -1
  41. package/es/converters/index.js +0 -1
  42. package/es/converters/schema-converter.js +0 -654
  43. package/es/index.js +0 -1
  44. package/es/schema-validator.js +0 -207
  45. package/es/utils/constants.js +0 -1
  46. package/es/utils/index.js +0 -4
  47. package/es/utils/keys.js +0 -9
  48. package/es/utils/references.js +0 -66
  49. package/es/utils/type-utils.js +0 -36
  50. package/es/utils/types.js +0 -1
@@ -1,662 +1,559 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.SchemaConverter = exports.SchemaConversionTarget = void 0;
7
- var _util = require("@takeshape/util");
8
- var _intersection = _interopRequireDefault(require("lodash/intersection"));
9
- var _uniq = _interopRequireDefault(require("lodash/uniq"));
10
- var _minimatch = require("minimatch");
11
- var _utils = require("../utils");
12
- var _keys = require("../utils/keys");
13
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
- let SchemaConversionTarget = exports.SchemaConversionTarget = /*#__PURE__*/function (SchemaConversionTarget) {
15
- SchemaConversionTarget["OpenAI"] = "OPENAI";
16
- SchemaConversionTarget["JSONSchema"] = "JSON_SCHEMA";
17
- return SchemaConversionTarget;
18
- }({});
19
- var SchemaProcessingMode = /*#__PURE__*/function (SchemaProcessingMode) {
20
- SchemaProcessingMode["Schema"] = "SCHEMA";
21
- SchemaProcessingMode["Definition"] = "DEFINITION";
22
- SchemaProcessingMode["Count"] = "COUNT";
23
- return SchemaProcessingMode;
24
- }(SchemaProcessingMode || {});
25
- class SchemaConverter {
26
- warnings = [];
27
- stats = {
28
- propertiesCount: 0,
29
- stringLength: 0,
30
- enumValuesCount: 0
31
- };
32
- #schema;
33
- #config;
34
- #usedDefinitions = new Set();
35
- #definitions = {};
36
- #definitionsReferences = new Map();
37
- static convert(schema, options = {}) {
38
- return new SchemaConverter(schema, options).run();
39
- }
40
- constructor(originalSchema, options = {}) {
41
- const {
42
- removePropertyKeyPatterns,
43
- target = SchemaConversionTarget.JSONSchema,
44
- inlineDefinitions = false,
45
- maxDepth = 5,
46
- allowUnknownKeys = false
47
- } = options;
48
- this.#config = {
49
- target,
50
- maxDepth,
51
- allowUnknownKeys,
52
- removePropertyKeyPatterns: removePropertyKeyPatterns?.map(pattern => new _minimatch.Minimatch(pattern)) ?? [],
53
- inlineDefinitions
1
+ import { assert, ensureArray } from '@takeshape/util';
2
+ import intersection from 'lodash/intersection.js';
3
+ import uniq from 'lodash/uniq.js';
4
+ import { Minimatch } from 'minimatch';
5
+ import { getReferenceMap, isAllOfSchema, isAnyOfSchema, isArraySchema, isEnumSchema, isListSchema, isObjectSchema, isOneOfSchema, isPropertySchema, isRefSchema, isTupleSchema, pickJSONSchema7 } from "../utils/index.js";
6
+ export var SchemaConversionTarget;
7
+ (function (SchemaConversionTarget) {
8
+ SchemaConversionTarget["OpenAI"] = "OPENAI";
9
+ SchemaConversionTarget["JSONSchema"] = "JSON_SCHEMA";
10
+ })(SchemaConversionTarget || (SchemaConversionTarget = {}));
11
+ var SchemaProcessingMode;
12
+ (function (SchemaProcessingMode) {
13
+ SchemaProcessingMode["Schema"] = "SCHEMA";
14
+ SchemaProcessingMode["Definition"] = "DEFINITION";
15
+ SchemaProcessingMode["Count"] = "COUNT";
16
+ })(SchemaProcessingMode || (SchemaProcessingMode = {}));
17
+ export class SchemaConverter {
18
+ warnings = [];
19
+ stats = {
20
+ propertiesCount: 0,
21
+ stringLength: 0,
22
+ enumValuesCount: 0
54
23
  };
55
- const {
56
- $defs,
57
- definitions,
58
- ...schema
59
- } = originalSchema;
60
- this.#schema = schema;
61
- if ($defs || definitions) {
62
- const defs = {
63
- ...$defs,
64
- ...definitions
65
- };
66
- this.#definitionsReferences = (0, _utils.getReferenceMap)(defs);
67
- this.#definitions = this.processDefinitions(defs);
68
- }
69
- }
70
- run() {
71
- const {
72
- target,
73
- inlineDefinitions
74
- } = this.#config;
75
-
76
- // Process the schema
77
- const result = this.processSchema({
78
- processingMode: SchemaProcessingMode.Schema,
79
- isRequired: true,
80
- path: [],
81
- depth: 0
82
- }, this.#schema);
83
- if (!inlineDefinitions && this.#usedDefinitions.size) {
84
- const defs = this.getUsedDefinitions();
85
- if (Object.keys(defs).length) {
86
- result.$defs = defs;
87
- this.countDefinitions(defs);
88
- }
89
- }
90
-
91
- // Check the total property count
92
- if (target === SchemaConversionTarget.OpenAI && this.stats.propertiesCount > 100) {
93
- this.warnings.push(`Schema has ${this.stats.propertiesCount} total object properties, exceeding the 100 limit`);
94
- }
95
-
96
- // Check string length total
97
- if (target === SchemaConversionTarget.OpenAI && this.stats.stringLength > 15000) {
98
- this.warnings.push(`Total string length of property names, definition names, enum values exceeds the 15,000 limit (${this.stats.stringLength})`);
99
- }
100
-
101
- // Check enum values count
102
- if (target === SchemaConversionTarget.OpenAI && this.stats.enumValuesCount > 500) {
103
- this.warnings.push(`Schema has ${this.stats.enumValuesCount} enum values across all properties, exceeding the 500 limit`);
104
- }
105
-
106
- // Check if root is an object
107
- if (target === SchemaConversionTarget.OpenAI && !(0, _utils.isPropertySchema)(result)) {
108
- this.warnings.push('Root level must be an object type, wrapping in an object');
109
-
110
- // Attempt to fix by wrapping in an object if possible
111
- return {
112
- schema: {
113
- type: 'object',
114
- properties: {
115
- root: result
116
- },
117
- required: ['root'],
118
- additionalProperties: false
119
- },
120
- warnings: this.warnings,
121
- stats: this.stats
122
- };
123
- }
124
- return {
125
- schema: result,
126
- warnings: this.warnings,
127
- stats: this.stats
128
- };
129
- }
130
- processSchema(context, schema) {
131
- (0, _util.assert)(typeof schema === 'object', 'Schema must be an object');
132
- const {
133
- depth,
134
- path,
135
- isRequired,
136
- processingMode
137
- } = context;
138
- const {
139
- target,
140
- maxDepth
141
- } = this.#config;
142
- // Clone the schema to avoid modifying the original
143
- const schemaCopy = {
144
- ...schema
145
- };
146
- const currentPath = path.join('.') || 'root';
147
-
148
- // Check for unsupported keywords
149
- this.checkUnsupportedKeywords(schemaCopy, currentPath);
150
-
151
- // Check depth limit
152
- if (depth > maxDepth) {
153
- this.warnings.push(`Nesting depth exceeds limit (max 5) at ${currentPath}`);
154
- // Simplify the schema at this level
155
- return this.addNullable({
156
- type: 'string',
157
- description: 'Simplified due to excessive nesting'
158
- }, isRequired);
159
- }
160
-
161
- // Handle $ref
162
- if ((0, _utils.isRefSchema)(schemaCopy)) {
163
- return this.processRefSchema(context, schemaCopy);
164
- }
165
-
166
- // Process specific types
167
- if ((0, _utils.isObjectSchema)(schemaCopy) || (0, _utils.isPropertySchema)(schemaCopy)) {
168
- return this.addNullable(this.processObjectSchema(context, schemaCopy), isRequired);
169
- }
170
- if ((0, _utils.isArraySchema)(schemaCopy)) {
171
- return this.addNullable(this.processArraySchema(context, schemaCopy), isRequired);
172
- }
173
- if ((0, _utils.isAnyOfSchema)(schemaCopy)) {
174
- return this.addNullable(this.processAnyOfSchema(context, schemaCopy), isRequired);
175
- }
176
- if ((0, _utils.isOneOfSchema)(schemaCopy)) {
177
- if (target === SchemaConversionTarget.OpenAI) {
178
- this.warnings.push(`oneOf at ${currentPath} converted to anyOf (OpenAI only supports anyOf)`);
179
- const {
180
- oneOf,
181
- ...rest
182
- } = schemaCopy;
183
- return this.addNullable(this.processAnyOfSchema(context, {
184
- ...rest,
185
- anyOf: oneOf
186
- }), isRequired);
187
- }
188
- return this.addNullable(this.processOneOfSchema(context, schemaCopy), isRequired);
189
- }
190
- if ((0, _utils.isAllOfSchema)(schemaCopy)) {
191
- if (target === SchemaConversionTarget.OpenAI) {
192
- this.warnings.push(`allOf at ${currentPath} is not directly supported, attempting to merge schemas`);
193
- return this.addNullable(this.mergeAllOfSchema(context, schemaCopy), isRequired);
194
- }
195
- return this.addNullable(this.processAllOfSchema(context, schemaCopy), isRequired);
196
- }
197
- if ((0, _utils.isEnumSchema)(schemaCopy)) {
198
- return this.addNullable(this.processEnumSchema(context, schemaCopy), isRequired);
199
- }
200
- if (target === SchemaConversionTarget.OpenAI && schemaCopy.default) {
201
- const defaultText = `Default value: ${typeof schemaCopy.default === 'object' ? JSON.stringify(schemaCopy.default) : schemaCopy.default}`;
202
- schemaCopy.description = schema.description ? `${schema.description} ${defaultText}` : defaultText;
203
- // biome-ignore lint/performance/noDelete: don't want to leave cruft
204
- delete schemaCopy.default;
205
- }
206
-
207
- // Update string length total for const values
208
- if (schemaCopy.const && typeof schemaCopy.const === 'string' && (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count)) {
209
- this.stats.stringLength += schemaCopy.const.length;
210
- }
211
- const newSchema = this.initializeNewSchema(schemaCopy, {
212
- type: schemaCopy.type
213
- });
214
- return this.addNullable(newSchema, isRequired);
215
- }
216
- processDefinitions(defs) {
217
- return Object.entries(defs).reduce((acc, [defName, def]) => {
218
- acc[defName] = this.processSchema({
219
- processingMode: SchemaProcessingMode.Definition,
220
- isRequired: true,
221
- path: [],
222
- depth: 0
223
- }, def);
224
- return acc;
225
- }, {});
226
- }
227
- countDefinitions(defs) {
228
- for (const def of Object.values(defs)) {
229
- this.processSchema({
230
- processingMode: SchemaProcessingMode.Count,
231
- isRequired: true,
232
- path: [],
233
- depth: 0
234
- }, def);
235
- }
236
- }
237
- getUsedDefinitions() {
238
- const defs = {};
239
- for (const usedDef of this.#usedDefinitions) {
240
- this.addDefinition(defs, usedDef);
241
-
242
- // Add any definitions that are referenced by the used definition
243
- const defRefs = this.#definitionsReferences.get(usedDef);
244
- if (defRefs) {
245
- for (const ref of defRefs) {
246
- this.addDefinition(defs, ref);
247
- }
248
- }
249
- }
250
- return defs;
251
- }
252
- addDefinition(defs, key) {
253
- const definition = this.#definitions[key];
254
- if (definition) {
255
- this.stats.stringLength += key.length;
256
- defs[key] = definition;
257
- }
258
- }
259
- addNullable(schema, isRequired) {
260
- if (this.#config.target === SchemaConversionTarget.JSONSchema) {
261
- return schema;
262
- }
263
- if (isRequired) {
264
- return schema;
24
+ #schema;
25
+ #config;
26
+ #usedDefinitions = new Set();
27
+ #definitions = {};
28
+ #definitionsReferences = new Map();
29
+ static convert(schema, options = {}) {
30
+ return new SchemaConverter(schema, options).run();
31
+ }
32
+ constructor(originalSchema, options = {}) {
33
+ const { removePropertyKeyPatterns, target = SchemaConversionTarget.JSONSchema, inlineDefinitions = false, maxDepth = 5, allowUnknownKeys = false } = options;
34
+ this.#config = {
35
+ target,
36
+ maxDepth,
37
+ allowUnknownKeys,
38
+ removePropertyKeyPatterns: removePropertyKeyPatterns?.map((pattern) => new Minimatch(pattern)) ?? [],
39
+ inlineDefinitions
40
+ };
41
+ const { $defs, definitions, ...schema } = originalSchema;
42
+ this.#schema = schema;
43
+ if ($defs || definitions) {
44
+ const defs = { ...$defs, ...definitions };
45
+ this.#definitionsReferences = getReferenceMap(defs);
46
+ this.#definitions = this.processDefinitions(defs);
47
+ }
265
48
  }
266
- if (schema.type && !(0, _utils.isObjectSchema)(schema) && !(0, _utils.isArraySchema)(schema)) {
267
- return {
268
- ...schema,
269
- type: (0, _uniq.default)([...(0, _util.ensureArray)(schema.type), 'null'])
270
- };
49
+ run() {
50
+ const { target, inlineDefinitions } = this.#config;
51
+ // Process the schema
52
+ const result = this.processSchema({
53
+ processingMode: SchemaProcessingMode.Schema,
54
+ isRequired: true,
55
+ path: [],
56
+ depth: 0
57
+ }, this.#schema);
58
+ if (!inlineDefinitions && this.#usedDefinitions.size) {
59
+ const defs = this.getUsedDefinitions();
60
+ if (Object.keys(defs).length) {
61
+ result.$defs = defs;
62
+ this.countDefinitions(defs);
63
+ }
64
+ }
65
+ // Check the total property count
66
+ if (target === SchemaConversionTarget.OpenAI && this.stats.propertiesCount > 100) {
67
+ this.warnings.push(`Schema has ${this.stats.propertiesCount} total object properties, exceeding the 100 limit`);
68
+ }
69
+ // Check string length total
70
+ if (target === SchemaConversionTarget.OpenAI && this.stats.stringLength > 15000) {
71
+ this.warnings.push(`Total string length of property names, definition names, enum values exceeds the 15,000 limit (${this.stats.stringLength})`);
72
+ }
73
+ // Check enum values count
74
+ if (target === SchemaConversionTarget.OpenAI && this.stats.enumValuesCount > 500) {
75
+ this.warnings.push(`Schema has ${this.stats.enumValuesCount} enum values across all properties, exceeding the 500 limit`);
76
+ }
77
+ // Check if root is an object
78
+ if (target === SchemaConversionTarget.OpenAI && !isPropertySchema(result)) {
79
+ this.warnings.push('Root level must be an object type, wrapping in an object');
80
+ // Attempt to fix by wrapping in an object if possible
81
+ return {
82
+ schema: {
83
+ type: 'object',
84
+ properties: {
85
+ root: result
86
+ },
87
+ required: ['root'],
88
+ additionalProperties: false
89
+ },
90
+ warnings: this.warnings,
91
+ stats: this.stats
92
+ };
93
+ }
94
+ return {
95
+ schema: result,
96
+ warnings: this.warnings,
97
+ stats: this.stats
98
+ };
271
99
  }
272
- return {
273
- anyOf: [...(schema.anyOf ? schema.anyOf : [schema]), {
274
- type: 'null'
275
- }]
276
- };
277
- }
278
- processRefSchema(context, schema) {
279
- const {
280
- isRequired,
281
- processingMode
282
- } = context;
283
- const newSchema = {
284
- $ref: schema.$ref
285
- };
286
-
287
- // Normalize definitions path
288
- if (newSchema.$ref.startsWith('#/definitions/')) {
289
- newSchema.$ref = newSchema.$ref.replace('#/definitions/', '#/$defs/');
100
+ processSchema(context, schema) {
101
+ assert(typeof schema === 'object', 'Schema must be an object');
102
+ const { depth, path, isRequired, processingMode } = context;
103
+ const { target, maxDepth } = this.#config;
104
+ // Clone the schema to avoid modifying the original
105
+ const schemaCopy = { ...schema };
106
+ const currentPath = path.join('.') || 'root';
107
+ // Check for unsupported keywords
108
+ this.checkUnsupportedKeywords(schemaCopy, currentPath);
109
+ // Check depth limit
110
+ if (depth > maxDepth) {
111
+ this.warnings.push(`Nesting depth exceeds limit (max 5) at ${currentPath}`);
112
+ // Simplify the schema at this level
113
+ return this.addNullable({ type: 'string', description: 'Simplified due to excessive nesting' }, isRequired);
114
+ }
115
+ // Handle $ref
116
+ if (isRefSchema(schemaCopy)) {
117
+ return this.processRefSchema(context, schemaCopy);
118
+ }
119
+ // Process specific types
120
+ if (isObjectSchema(schemaCopy) || isPropertySchema(schemaCopy)) {
121
+ return this.addNullable(this.processObjectSchema(context, schemaCopy), isRequired);
122
+ }
123
+ if (isArraySchema(schemaCopy)) {
124
+ return this.addNullable(this.processArraySchema(context, schemaCopy), isRequired);
125
+ }
126
+ if (isAnyOfSchema(schemaCopy)) {
127
+ return this.addNullable(this.processAnyOfSchema(context, schemaCopy), isRequired);
128
+ }
129
+ if (isOneOfSchema(schemaCopy)) {
130
+ if (target === SchemaConversionTarget.OpenAI) {
131
+ this.warnings.push(`oneOf at ${currentPath} converted to anyOf (OpenAI only supports anyOf)`);
132
+ const { oneOf, ...rest } = schemaCopy;
133
+ return this.addNullable(this.processAnyOfSchema(context, { ...rest, anyOf: oneOf }), isRequired);
134
+ }
135
+ return this.addNullable(this.processOneOfSchema(context, schemaCopy), isRequired);
136
+ }
137
+ if (isAllOfSchema(schemaCopy)) {
138
+ if (target === SchemaConversionTarget.OpenAI) {
139
+ this.warnings.push(`allOf at ${currentPath} is not directly supported, attempting to merge schemas`);
140
+ return this.addNullable(this.mergeAllOfSchema(context, schemaCopy), isRequired);
141
+ }
142
+ return this.addNullable(this.processAllOfSchema(context, schemaCopy), isRequired);
143
+ }
144
+ if (isEnumSchema(schemaCopy)) {
145
+ return this.addNullable(this.processEnumSchema(context, schemaCopy), isRequired);
146
+ }
147
+ if (target === SchemaConversionTarget.OpenAI && schemaCopy.default) {
148
+ const defaultText = `Default value: ${typeof schemaCopy.default === 'object' ? JSON.stringify(schemaCopy.default) : schemaCopy.default}`;
149
+ schemaCopy.description = schema.description ? `${schema.description} ${defaultText}` : defaultText;
150
+ // biome-ignore lint/performance/noDelete: don't want to leave cruft
151
+ delete schemaCopy.default;
152
+ }
153
+ // Update string length total for const values
154
+ if (schemaCopy.const &&
155
+ typeof schemaCopy.const === 'string' &&
156
+ (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count)) {
157
+ this.stats.stringLength += schemaCopy.const.length;
158
+ }
159
+ const newSchema = this.initializeNewSchema(schemaCopy, {
160
+ type: schemaCopy.type
161
+ });
162
+ return this.addNullable(newSchema, isRequired);
163
+ }
164
+ processDefinitions(defs) {
165
+ return Object.entries(defs).reduce((acc, [defName, def]) => {
166
+ acc[defName] = this.processSchema({
167
+ processingMode: SchemaProcessingMode.Definition,
168
+ isRequired: true,
169
+ path: [],
170
+ depth: 0
171
+ }, def);
172
+ return acc;
173
+ }, {});
174
+ }
175
+ countDefinitions(defs) {
176
+ for (const def of Object.values(defs)) {
177
+ this.processSchema({
178
+ processingMode: SchemaProcessingMode.Count,
179
+ isRequired: true,
180
+ path: [],
181
+ depth: 0
182
+ }, def);
183
+ }
290
184
  }
291
- const defName = newSchema.$ref.replace('#/$defs/', '');
292
- if (defName && this.#config.inlineDefinitions && this.#definitions[defName]) {
293
- return this.processSchema(context, this.#definitions[defName]);
185
+ getUsedDefinitions() {
186
+ const defs = {};
187
+ for (const usedDef of this.#usedDefinitions) {
188
+ this.addDefinition(defs, usedDef);
189
+ // Add any definitions that are referenced by the used definition
190
+ const defRefs = this.#definitionsReferences.get(usedDef);
191
+ if (defRefs) {
192
+ for (const ref of defRefs) {
193
+ this.addDefinition(defs, ref);
194
+ }
195
+ }
196
+ }
197
+ return defs;
294
198
  }
295
- if (processingMode === SchemaProcessingMode.Schema) {
296
- this.#usedDefinitions.add(defName);
199
+ addDefinition(defs, key) {
200
+ const definition = this.#definitions[key];
201
+ if (definition) {
202
+ this.stats.stringLength += key.length;
203
+ defs[key] = definition;
204
+ }
297
205
  }
298
- return this.addNullable(newSchema, isRequired);
299
- }
300
- initializeNewSchema(schema, newSchema) {
301
- // OpenAI will only allow some very specific properties, so start fairly clean
302
- if (this.#config.target === SchemaConversionTarget.OpenAI) {
303
- let base = {
304
- title: schema.title,
305
- description: schema.description
306
- };
307
- if (newSchema.type === 'object') {
308
- base = {
309
- ...base,
310
- required: Object.keys(newSchema.properties ?? schema.properties ?? {}),
311
- additionalProperties: false
206
+ addNullable(schema, isRequired) {
207
+ if (this.#config.target === SchemaConversionTarget.JSONSchema) {
208
+ return schema;
209
+ }
210
+ if (isRequired) {
211
+ return schema;
212
+ }
213
+ if (schema.type && !isObjectSchema(schema) && !isArraySchema(schema)) {
214
+ return {
215
+ ...schema,
216
+ type: uniq([...ensureArray(schema.type), 'null'])
217
+ };
218
+ }
219
+ return {
220
+ anyOf: [...(schema.anyOf ? schema.anyOf : [schema]), { type: 'null' }]
312
221
  };
313
- } else if (newSchema.type === 'array') {
314
- base = {
315
- ...base,
316
- items: []
222
+ }
223
+ processRefSchema(context, schema) {
224
+ const { isRequired, processingMode } = context;
225
+ const newSchema = {
226
+ $ref: schema.$ref
317
227
  };
318
- } else if (newSchema.type === 'number' || newSchema.type === 'integer' || newSchema.type === 'string' || newSchema.type === 'boolean') {
319
- base = {
320
- ...base,
321
- const: schema.const
228
+ // Normalize definitions path
229
+ if (newSchema.$ref.startsWith('#/definitions/')) {
230
+ newSchema.$ref = newSchema.$ref.replace('#/definitions/', '#/$defs/');
231
+ }
232
+ const defName = newSchema.$ref.replace('#/$defs/', '');
233
+ if (defName && this.#config.inlineDefinitions && this.#definitions[defName]) {
234
+ return this.processSchema(context, this.#definitions[defName]);
235
+ }
236
+ if (processingMode === SchemaProcessingMode.Schema) {
237
+ this.#usedDefinitions.add(defName);
238
+ }
239
+ return this.addNullable(newSchema, isRequired);
240
+ }
241
+ initializeNewSchema(schema, newSchema) {
242
+ // OpenAI will only allow some very specific properties, so start fairly clean
243
+ if (this.#config.target === SchemaConversionTarget.OpenAI) {
244
+ let base = {
245
+ title: schema.title,
246
+ description: schema.description
247
+ };
248
+ if (newSchema.type === 'object') {
249
+ base = {
250
+ ...base,
251
+ required: Object.keys(newSchema.properties ?? schema.properties ?? {}),
252
+ additionalProperties: false
253
+ };
254
+ }
255
+ else if (newSchema.type === 'array') {
256
+ base = {
257
+ ...base,
258
+ items: []
259
+ };
260
+ }
261
+ else if (newSchema.type === 'number' ||
262
+ newSchema.type === 'integer' ||
263
+ newSchema.type === 'string' ||
264
+ newSchema.type === 'boolean') {
265
+ base = {
266
+ ...base,
267
+ const: schema.const
268
+ };
269
+ }
270
+ return {
271
+ ...base,
272
+ ...newSchema
273
+ };
274
+ }
275
+ return {
276
+ // We may be converting a schema with non-spec properties
277
+ ...(this.#config.allowUnknownKeys ? schema : pickJSONSchema7(schema)),
278
+ ...newSchema
322
279
  };
323
- }
324
- return {
325
- ...base,
326
- ...newSchema
327
- };
328
280
  }
329
- return {
330
- // We may be converting a schema with non-spec properties
331
- ...(this.#config.allowUnknownKeys ? schema : (0, _keys.pickJSONSchema7)(schema)),
332
- ...newSchema
333
- };
334
- }
335
- processObjectSchema(context, schema) {
336
- const {
337
- depth,
338
- path,
339
- processingMode
340
- } = context;
341
- const {
342
- target,
343
- removePropertyKeyPatterns
344
- } = this.#config;
345
- const newSchema = this.initializeNewSchema(schema, {
346
- type: 'object'
347
- });
348
- const uniqueRequired = new Set(schema.required ?? []);
349
- const newSchemaProperties = {};
350
-
351
- // Process properties
352
- if (schema.properties) {
353
- // Process each property
354
- for (const [key, property] of Object.entries(schema.properties)) {
355
- // Skip properties that match removePropertyKeyPatterns
356
- if (removePropertyKeyPatterns.length && removePropertyKeyPatterns.some(pattern => pattern.match(key))) {
357
- continue;
358
- }
359
- newSchemaProperties[key] = this.processSchema({
360
- ...context,
361
- path: [...path, key],
362
- depth: depth + 1,
363
- isRequired: uniqueRequired.has(key)
364
- }, property);
365
-
366
- // Update string length total for property names
281
+ processObjectSchema(context, schema) {
282
+ const { depth, path, processingMode } = context;
283
+ const { target, removePropertyKeyPatterns } = this.#config;
284
+ const newSchema = this.initializeNewSchema(schema, {
285
+ type: 'object'
286
+ });
287
+ const uniqueRequired = new Set(schema.required ?? []);
288
+ const newSchemaProperties = {};
289
+ // Process properties
290
+ if (schema.properties) {
291
+ // Process each property
292
+ for (const [key, property] of Object.entries(schema.properties)) {
293
+ // Skip properties that match removePropertyKeyPatterns
294
+ if (removePropertyKeyPatterns.length && removePropertyKeyPatterns.some((pattern) => pattern.match(key))) {
295
+ continue;
296
+ }
297
+ newSchemaProperties[key] = this.processSchema({
298
+ ...context,
299
+ path: [...path, key],
300
+ depth: depth + 1,
301
+ isRequired: uniqueRequired.has(key)
302
+ }, property);
303
+ // Update string length total for property names
304
+ if (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count) {
305
+ this.stats.stringLength += key.length;
306
+ }
307
+ }
308
+ }
309
+ const newPropertyKeys = Object.keys(newSchemaProperties);
310
+ // Update total property count
367
311
  if (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count) {
368
- this.stats.stringLength += key.length;
312
+ this.stats.propertiesCount += newPropertyKeys.length;
369
313
  }
370
- }
371
- }
372
- const newPropertyKeys = Object.keys(newSchemaProperties);
373
-
374
- // Update total property count
375
- if (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count) {
376
- this.stats.propertiesCount += newPropertyKeys.length;
377
- }
378
- let required = schema.required;
379
- if (target === SchemaConversionTarget.OpenAI) {
380
- // OpenAI requires all properties to be required
381
- required = newPropertyKeys;
382
- } else if (required) {
383
- required = (0, _intersection.default)([...uniqueRequired], newPropertyKeys);
384
- }
385
- return {
386
- ...newSchema,
387
- properties: newSchemaProperties,
388
- // Make all properties required for openai target, for json-schema some may have been removed
389
- required
390
- };
391
- }
392
- processArraySchema(context, schema) {
393
- const {
394
- depth,
395
- path
396
- } = context;
397
- const newSchema = this.initializeNewSchema(schema, {
398
- type: 'array'
399
- });
400
-
401
- // Process items schema
402
- if (schema.items) {
403
- if ((0, _utils.isTupleSchema)(schema)) {
404
- newSchema.items = schema.items.map((item, index) => this.processSchema({
405
- ...context,
406
- path: [...path, 'items', index],
407
- depth: depth + 1
408
- }, item));
409
- }
410
- if ((0, _utils.isListSchema)(schema)) {
411
- newSchema.items = this.processSchema({
412
- ...context,
413
- path: [...path, 'items'],
414
- depth: depth + 1
415
- }, schema.items);
416
- }
417
- } else {
418
- this.warnings.push(`Array missing items definition at ${path.join('.') || 'root'}, defaulting to string items`);
419
- newSchema.items = {
420
- type: 'string'
421
- };
422
- }
423
- for (const keyword of this.getUnsupportedArrayKeywords()) {
424
- if (schema[keyword] !== undefined) {
425
- this.warnings.push(`Unsupported array keyword "${keyword}" at ${path.join('.') || 'root'} will be ignored`);
426
- }
427
- }
428
- return newSchema;
429
- }
430
- getUnsupportedArrayKeywords() {
431
- if (this.#config.target === SchemaConversionTarget.OpenAI) {
432
- return [
433
- // 'unevaluatedItems',
434
- 'contains',
435
- // 'minContains',
436
- // 'maxContains',
437
- 'minItems', 'maxItems', 'uniqueItems'];
438
- }
439
- return [];
440
- }
441
- processOneOfSchema(context, schema) {
442
- const {
443
- depth,
444
- path
445
- } = context;
446
- const newSchema = this.initializeNewSchema(schema, {
447
- oneOf: []
448
- });
449
-
450
- // Process each oneOf schema
451
- newSchema.oneOf = schema.oneOf.map((subSchema, index) => {
452
- return this.processSchema({
453
- ...context,
454
- path: [...path, 'oneOf', index],
455
- depth: depth + 1
456
- }, subSchema);
457
- });
458
- return newSchema;
459
- }
460
- processAnyOfSchema(context, schema) {
461
- const {
462
- target
463
- } = this.#config;
464
- const {
465
- depth,
466
- path
467
- } = context;
468
- const newSchema = this.initializeNewSchema(schema, {
469
- anyOf: []
470
- });
471
-
472
- // Check if this is at the root level
473
- if (target === SchemaConversionTarget.OpenAI && path.length === 0) {
474
- this.warnings.push('anyOf at root level is not supported by OpenAI Structured Outputs');
475
- }
476
-
477
- // Process each anyOf schema
478
- newSchema.anyOf = schema.anyOf.map((subSchema, index) => {
479
- return this.processSchema({
480
- ...context,
481
- path: [...path, 'anyOf', index],
482
- depth: depth + 1
483
- }, subSchema);
484
- });
485
- return newSchema;
486
- }
487
- processAllOfSchema(context, schema) {
488
- const {
489
- depth,
490
- path
491
- } = context;
492
- const newSchema = this.initializeNewSchema(schema, {
493
- allOf: []
494
- });
495
-
496
- // Process each anyOf schema
497
- newSchema.allOf = schema.allOf.map((subSchema, index) => {
498
- return this.processSchema({
499
- ...context,
500
- path: [...path, 'allOf', index],
501
- depth: depth + 1
502
- }, subSchema);
503
- });
504
- return newSchema;
505
- }
506
- mergeAllOfSchema(context, schema) {
507
- const {
508
- depth,
509
- path
510
- } = context;
511
- const newSchema = this.initializeNewSchema(schema, {
512
- type: 'object'
513
- });
514
- const allOfSchemas = schema.allOf ?? [];
515
-
516
- // Try to merge properties from all schemas
517
- for (const [index, value] of allOfSchemas.entries()) {
518
- const subSchema = this.processSchema({
519
- ...context,
520
- path: [...path, 'allOf', index]
521
- }, value);
522
- if ((0, _utils.isObjectSchema)(subSchema) && (0, _utils.isPropertySchema)(subSchema)) {
523
- // Merge properties
524
- newSchema.properties = {
525
- ...newSchema.properties,
526
- ...subSchema.properties
314
+ let required = schema.required;
315
+ if (target === SchemaConversionTarget.OpenAI) {
316
+ // OpenAI requires all properties to be required
317
+ required = newPropertyKeys;
318
+ }
319
+ else if (required) {
320
+ required = intersection([...uniqueRequired], newPropertyKeys);
321
+ }
322
+ return {
323
+ ...newSchema,
324
+ properties: newSchemaProperties,
325
+ // Make all properties required for openai target, for json-schema some may have been removed
326
+ required
527
327
  };
528
-
529
- // Merge required fields
530
- if (Array.isArray(subSchema.required)) {
531
- newSchema.required = [...(newSchema.required ?? []), ...subSchema.required];
532
- }
533
-
534
- // Preserve description if we don't have one yet
535
- if (!newSchema.description && subSchema.description) {
536
- newSchema.description = subSchema.description;
537
- }
538
-
539
- // Preserve title if we don't have one yet
540
- if (!newSchema.title && subSchema.title) {
541
- newSchema.title = subSchema.title;
542
- }
543
- } else {
544
- this.warnings.push(`Non-object schema in allOf at ${path.join('.') || 'root'} cannot be merged properly`);
545
- }
546
328
  }
547
-
548
- // Remove duplicate required fields
549
- newSchema.required = [...new Set(newSchema.required)];
550
- return this.processObjectSchema({
551
- ...context,
552
- depth: depth + 1
553
- }, newSchema);
554
- }
555
- processEnumSchema(context, schema) {
556
- const {
557
- target
558
- } = this.#config;
559
- const {
560
- path,
561
- processingMode
562
- } = context;
563
- const newSchema = this.initializeNewSchema(schema, {
564
- enum: schema.enum,
565
- type: schema.type
566
- });
567
- if (Array.isArray(newSchema.enum)) {
568
- // Update enum values count
569
- if (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count) {
570
- this.stats.enumValuesCount += newSchema.enum.length;
571
- }
572
-
573
- // Check enum size
574
- if (target === SchemaConversionTarget.OpenAI && newSchema.enum.length > 500) {
575
- this.warnings.push(`Enum at ${path.join('.') || 'root'} has ${newSchema.enum.length} values, exceeding the 500 limit`);
576
- newSchema.enum = newSchema.enum.slice(0, 500);
577
- }
578
-
579
- // Check string length for large enums
580
- if (newSchema.type === 'string') {
581
- let totalLength = 0;
582
- for (const val of newSchema.enum) {
583
- if (typeof val === 'string') {
584
- totalLength += val.length;
585
- }
586
- }
587
- if (target === SchemaConversionTarget.OpenAI && totalLength > 7500) {
588
- this.warnings.push(`Enum strings at ${path.join('.') || 'root'} exceed 7500 characters (${totalLength}) with ${newSchema.enum.length} values`);
589
-
590
- // Truncate enum values to fit within limits
591
- let currentLength = 0;
592
- const truncatedEnum = [];
593
- for (const val of newSchema.enum) {
594
- if (typeof val === 'string') {
595
- if (currentLength + val.length <= 7500) {
596
- truncatedEnum.push(val);
597
- currentLength += val.length;
598
- } else {
599
- break;
600
- }
601
- } else {
602
- truncatedEnum.push(val);
329
+ processArraySchema(context, schema) {
330
+ const { depth, path } = context;
331
+ const newSchema = this.initializeNewSchema(schema, {
332
+ type: 'array'
333
+ });
334
+ // Process items schema
335
+ if (schema.items) {
336
+ if (isTupleSchema(schema)) {
337
+ newSchema.items = schema.items.map((item, index) => this.processSchema({
338
+ ...context,
339
+ path: [...path, 'items', index],
340
+ depth: depth + 1
341
+ }, item));
342
+ }
343
+ if (isListSchema(schema)) {
344
+ newSchema.items = this.processSchema({
345
+ ...context,
346
+ path: [...path, 'items'],
347
+ depth: depth + 1
348
+ }, schema.items);
603
349
  }
604
- }
605
- newSchema.enum = truncatedEnum;
606
350
  }
607
- if (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count) {
608
- this.stats.stringLength += totalLength;
351
+ else {
352
+ this.warnings.push(`Array missing items definition at ${path.join('.') || 'root'}, defaulting to string items`);
353
+ newSchema.items = { type: 'string' };
354
+ }
355
+ for (const keyword of this.getUnsupportedArrayKeywords()) {
356
+ if (schema[keyword] !== undefined) {
357
+ this.warnings.push(`Unsupported array keyword "${keyword}" at ${path.join('.') || 'root'} will be ignored`);
358
+ }
359
+ }
360
+ return newSchema;
361
+ }
362
+ getUnsupportedArrayKeywords() {
363
+ if (this.#config.target === SchemaConversionTarget.OpenAI) {
364
+ return [
365
+ // 'unevaluatedItems',
366
+ 'contains',
367
+ // 'minContains',
368
+ // 'maxContains',
369
+ 'minItems',
370
+ 'maxItems',
371
+ 'uniqueItems'
372
+ ];
373
+ }
374
+ return [];
375
+ }
376
+ processOneOfSchema(context, schema) {
377
+ const { depth, path } = context;
378
+ const newSchema = this.initializeNewSchema(schema, {
379
+ oneOf: []
380
+ });
381
+ // Process each oneOf schema
382
+ newSchema.oneOf = schema.oneOf.map((subSchema, index) => {
383
+ return this.processSchema({ ...context, path: [...path, 'oneOf', index], depth: depth + 1 }, subSchema);
384
+ });
385
+ return newSchema;
386
+ }
387
+ processAnyOfSchema(context, schema) {
388
+ const { target } = this.#config;
389
+ const { depth, path } = context;
390
+ const newSchema = this.initializeNewSchema(schema, {
391
+ anyOf: []
392
+ });
393
+ // Check if this is at the root level
394
+ if (target === SchemaConversionTarget.OpenAI && path.length === 0) {
395
+ this.warnings.push('anyOf at root level is not supported by OpenAI Structured Outputs');
396
+ }
397
+ // Process each anyOf schema
398
+ newSchema.anyOf = schema.anyOf.map((subSchema, index) => {
399
+ return this.processSchema({ ...context, path: [...path, 'anyOf', index], depth: depth + 1 }, subSchema);
400
+ });
401
+ return newSchema;
402
+ }
403
+ processAllOfSchema(context, schema) {
404
+ const { depth, path } = context;
405
+ const newSchema = this.initializeNewSchema(schema, {
406
+ allOf: []
407
+ });
408
+ // Process each anyOf schema
409
+ newSchema.allOf = schema.allOf.map((subSchema, index) => {
410
+ return this.processSchema({ ...context, path: [...path, 'allOf', index], depth: depth + 1 }, subSchema);
411
+ });
412
+ return newSchema;
413
+ }
414
+ mergeAllOfSchema(context, schema) {
415
+ const { depth, path } = context;
416
+ const newSchema = this.initializeNewSchema(schema, {
417
+ type: 'object'
418
+ });
419
+ const allOfSchemas = schema.allOf ?? [];
420
+ // Try to merge properties from all schemas
421
+ for (const [index, value] of allOfSchemas.entries()) {
422
+ const subSchema = this.processSchema({ ...context, path: [...path, 'allOf', index] }, value);
423
+ if (isObjectSchema(subSchema) && isPropertySchema(subSchema)) {
424
+ // Merge properties
425
+ newSchema.properties = {
426
+ ...newSchema.properties,
427
+ ...subSchema.properties
428
+ };
429
+ // Merge required fields
430
+ if (Array.isArray(subSchema.required)) {
431
+ newSchema.required = [...(newSchema.required ?? []), ...subSchema.required];
432
+ }
433
+ // Preserve description if we don't have one yet
434
+ if (!newSchema.description && subSchema.description) {
435
+ newSchema.description = subSchema.description;
436
+ }
437
+ // Preserve title if we don't have one yet
438
+ if (!newSchema.title && subSchema.title) {
439
+ newSchema.title = subSchema.title;
440
+ }
441
+ }
442
+ else {
443
+ this.warnings.push(`Non-object schema in allOf at ${path.join('.') || 'root'} cannot be merged properly`);
444
+ }
445
+ }
446
+ // Remove duplicate required fields
447
+ newSchema.required = [...new Set(newSchema.required)];
448
+ return this.processObjectSchema({ ...context, depth: depth + 1 }, newSchema);
449
+ }
450
+ processEnumSchema(context, schema) {
451
+ const { target } = this.#config;
452
+ const { path, processingMode } = context;
453
+ const newSchema = this.initializeNewSchema(schema, {
454
+ enum: schema.enum,
455
+ type: schema.type
456
+ });
457
+ if (Array.isArray(newSchema.enum)) {
458
+ // Update enum values count
459
+ if (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count) {
460
+ this.stats.enumValuesCount += newSchema.enum.length;
461
+ }
462
+ // Check enum size
463
+ if (target === SchemaConversionTarget.OpenAI && newSchema.enum.length > 500) {
464
+ this.warnings.push(`Enum at ${path.join('.') || 'root'} has ${newSchema.enum.length} values, exceeding the 500 limit`);
465
+ newSchema.enum = newSchema.enum.slice(0, 500);
466
+ }
467
+ // Check string length for large enums
468
+ if (newSchema.type === 'string') {
469
+ let totalLength = 0;
470
+ for (const val of newSchema.enum) {
471
+ if (typeof val === 'string') {
472
+ totalLength += val.length;
473
+ }
474
+ }
475
+ if (target === SchemaConversionTarget.OpenAI && totalLength > 7500) {
476
+ this.warnings.push(`Enum strings at ${path.join('.') || 'root'} exceed 7500 characters (${totalLength}) with ${newSchema.enum.length} values`);
477
+ // Truncate enum values to fit within limits
478
+ let currentLength = 0;
479
+ const truncatedEnum = [];
480
+ for (const val of newSchema.enum) {
481
+ if (typeof val === 'string') {
482
+ if (currentLength + val.length <= 7500) {
483
+ truncatedEnum.push(val);
484
+ currentLength += val.length;
485
+ }
486
+ else {
487
+ break;
488
+ }
489
+ }
490
+ else {
491
+ truncatedEnum.push(val);
492
+ }
493
+ }
494
+ newSchema.enum = truncatedEnum;
495
+ }
496
+ if (processingMode === SchemaProcessingMode.Schema || processingMode === SchemaProcessingMode.Count) {
497
+ this.stats.stringLength += totalLength;
498
+ }
499
+ }
500
+ }
501
+ return newSchema;
502
+ }
503
+ checkUnsupportedKeywords(schema, path) {
504
+ if (this.#config.target === SchemaConversionTarget.OpenAI) {
505
+ // String-specific unsupported keywords
506
+ if (schema.type === 'string' ||
507
+ (!schema.type &&
508
+ (schema.minLength !== undefined ||
509
+ schema.maxLength !== undefined ||
510
+ schema.pattern !== undefined ||
511
+ schema.format !== undefined))) {
512
+ const unsupportedStringKeywords = ['minLength', 'maxLength', 'pattern', 'format'];
513
+ for (const keyword of unsupportedStringKeywords) {
514
+ if (schema[keyword] !== undefined) {
515
+ this.warnings.push(`Unsupported string keyword "${keyword}" at ${path || 'root'} will be ignored`);
516
+ delete schema[keyword];
517
+ }
518
+ }
519
+ }
520
+ // Number-specific unsupported keywords
521
+ if (schema.type === 'number' ||
522
+ schema.type === 'integer' ||
523
+ (!schema.type &&
524
+ (schema.minimum !== undefined || schema.maximum !== undefined || schema.multipleOf !== undefined))) {
525
+ const unsupportedNumberKeywords = ['minimum', 'maximum', 'multipleOf'];
526
+ for (const keyword of unsupportedNumberKeywords) {
527
+ if (schema[keyword] !== undefined) {
528
+ this.warnings.push(`Unsupported number keyword "${keyword}" at ${path || 'root'} will be ignored`);
529
+ delete schema[keyword];
530
+ }
531
+ }
532
+ }
533
+ // Object-specific unsupported keywords
534
+ if (schema.type === 'object' || (!schema.type && schema.properties !== undefined)) {
535
+ const unsupportedObjectKeywords = [
536
+ 'patternProperties',
537
+ // 'unevaluatedProperties',
538
+ 'propertyNames',
539
+ 'minProperties',
540
+ 'maxProperties'
541
+ ];
542
+ for (const keyword of unsupportedObjectKeywords) {
543
+ if (schema[keyword] !== undefined) {
544
+ this.warnings.push(`Unsupported object keyword "${keyword}" at ${path || 'root'} will be ignored`);
545
+ delete schema[keyword];
546
+ }
547
+ }
548
+ }
549
+ // Check Draft 7 keywords that aren't supported in OpenAI schema
550
+ const draft7Keywords = ['if', 'then', 'else', 'not', 'dependencies'];
551
+ for (const keyword of draft7Keywords) {
552
+ if (schema[keyword] !== undefined) {
553
+ this.warnings.push(`JSON Schema Draft 7 keyword "${keyword}" at ${path || 'root'} is not supported and will be ignored`);
554
+ delete schema[keyword];
555
+ }
556
+ }
609
557
  }
610
- }
611
- }
612
- return newSchema;
613
- }
614
- checkUnsupportedKeywords(schema, path) {
615
- if (this.#config.target === SchemaConversionTarget.OpenAI) {
616
- // String-specific unsupported keywords
617
- if (schema.type === 'string' || !schema.type && (schema.minLength !== undefined || schema.maxLength !== undefined || schema.pattern !== undefined || schema.format !== undefined)) {
618
- const unsupportedStringKeywords = ['minLength', 'maxLength', 'pattern', 'format'];
619
- for (const keyword of unsupportedStringKeywords) {
620
- if (schema[keyword] !== undefined) {
621
- this.warnings.push(`Unsupported string keyword "${keyword}" at ${path || 'root'} will be ignored`);
622
- delete schema[keyword];
623
- }
624
- }
625
- }
626
-
627
- // Number-specific unsupported keywords
628
- if (schema.type === 'number' || schema.type === 'integer' || !schema.type && (schema.minimum !== undefined || schema.maximum !== undefined || schema.multipleOf !== undefined)) {
629
- const unsupportedNumberKeywords = ['minimum', 'maximum', 'multipleOf'];
630
- for (const keyword of unsupportedNumberKeywords) {
631
- if (schema[keyword] !== undefined) {
632
- this.warnings.push(`Unsupported number keyword "${keyword}" at ${path || 'root'} will be ignored`);
633
- delete schema[keyword];
634
- }
635
- }
636
- }
637
-
638
- // Object-specific unsupported keywords
639
- if (schema.type === 'object' || !schema.type && schema.properties !== undefined) {
640
- const unsupportedObjectKeywords = ['patternProperties',
641
- // 'unevaluatedProperties',
642
- 'propertyNames', 'minProperties', 'maxProperties'];
643
- for (const keyword of unsupportedObjectKeywords) {
644
- if (schema[keyword] !== undefined) {
645
- this.warnings.push(`Unsupported object keyword "${keyword}" at ${path || 'root'} will be ignored`);
646
- delete schema[keyword];
647
- }
648
- }
649
- }
650
-
651
- // Check Draft 7 keywords that aren't supported in OpenAI schema
652
- const draft7Keywords = ['if', 'then', 'else', 'not', 'dependencies'];
653
- for (const keyword of draft7Keywords) {
654
- if (schema[keyword] !== undefined) {
655
- this.warnings.push(`JSON Schema Draft 7 keyword "${keyword}" at ${path || 'root'} is not supported and will be ignored`);
656
- delete schema[keyword];
657
- }
658
- }
659
558
  }
660
- }
661
559
  }
662
- exports.SchemaConverter = SchemaConverter;