@mintlify/validation 0.1.98 → 0.1.100

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.
@@ -0,0 +1,532 @@
1
+ import lcm from 'lcm';
2
+ import _ from 'lodash';
3
+ import { BaseConverter } from './BaseConverter.js';
4
+ import { ConversionError, ImpossibleSchemaError, InvalidSchemaError } from './errors.js';
5
+ import { addKeyIfDefined, copyKeyIfDefined } from './utils.js';
6
+ export class SchemaConverter extends BaseConverter {
7
+ constructor(schema, required, path = ['#'], location, safeParse = false) {
8
+ super(safeParse);
9
+ this.schema = schema;
10
+ this.required = required;
11
+ this.path = path;
12
+ this.location = location;
13
+ this.safeParse = safeParse;
14
+ }
15
+ /**
16
+ * This function converts the `schema` property into a `DataSchemaArray`. Due to
17
+ * the recursive nature of OpenAPI schemas, this conversion happens in two parts:
18
+ *
19
+ * 1. **Reduction**\*: The schema is transformed into its *reduced form*. In this form,
20
+ * the schema and any subschemas are represented by a schema with one property, `oneOf`,
21
+ * whose items are guaranteed NOT to have a `oneOf`, `anyOf`, or `allOf` property.
22
+ *
23
+ * 2. **Conversion**: In this step, we take a schema in its reduced form and convert it
24
+ * into a new data type. This is fairly straightforward, as we just need to convert
25
+ * each element of each `oneOf` schema to a `DataSchema`, and do this for all subschemas.
26
+ *
27
+ * \*We call this step a reduction rather than a conversion because the result is still
28
+ * of the `OpenAPIV3_1.SchemaObject` type.
29
+ *
30
+ * @returns An array of `DataSchema` objects representing all valid schemas
31
+ */
32
+ convert() {
33
+ if (this.schema === undefined) {
34
+ throw new InvalidSchemaError(this.path, 'schema undefined');
35
+ }
36
+ try {
37
+ // TODO(ronan): remove when fully migrated to endpoint type, or don't modify schema in evaluateCompositionsRecursive
38
+ let schema = _.cloneDeep(this.schema);
39
+ // REDUCTION
40
+ schema = this.reduceCompositionsRecursive(this.path, schema);
41
+ // CONVERSION
42
+ return this.convertSchemaRecursive(this.path, schema, this.required);
43
+ }
44
+ catch (error) {
45
+ this.handleExistingError(error, this.path, 'error converting top-level schema');
46
+ return [{ type: 'any' }];
47
+ }
48
+ }
49
+ /**
50
+ * This function should be used to reduce strictly `oneOf` and `anyOf` compositions.
51
+ *
52
+ * @param schemaArray `schema.allOf` or `schema.oneOf`
53
+ * @returns a schema array equivalent to the `schemaArray` argument, but in reduced form
54
+ */
55
+ reduceOptionsCompositions(path, schemaArray) {
56
+ const evaluatedArray = schemaArray.flatMap((subschema, i) => {
57
+ var _a;
58
+ try {
59
+ return (_a = this.reduceCompositionsRecursive([...path, i.toString()], subschema).oneOf) !== null && _a !== void 0 ? _a : [];
60
+ }
61
+ catch (error) {
62
+ if (error instanceof ImpossibleSchemaError) {
63
+ return [];
64
+ }
65
+ else {
66
+ throw error;
67
+ }
68
+ }
69
+ });
70
+ if (evaluatedArray.length === 0) {
71
+ throw new ImpossibleSchemaError(path, 'no valid options in schema:', JSON.stringify(schemaArray, undefined, 2));
72
+ }
73
+ return evaluatedArray;
74
+ }
75
+ reduceCompositionsRecursive(path, schema) {
76
+ // evaluate compositions first; we are currently ignoring `not`
77
+ if (schema.oneOf && schema.oneOf.length > 0) {
78
+ schema.oneOf = this.reduceOptionsCompositions([...path, 'oneOf'], schema.oneOf);
79
+ }
80
+ else {
81
+ schema.oneOf = [];
82
+ }
83
+ if (schema.anyOf && schema.anyOf.length > 0) {
84
+ schema.anyOf = this.reduceOptionsCompositions([...path, 'anyOf'], schema.anyOf);
85
+ }
86
+ if (schema.allOf && schema.allOf.length > 0) {
87
+ const totalAllOfObj = schema.allOf
88
+ .map((subschema, i) => this.reduceCompositionsRecursive([...path, 'allOf', i.toString()], subschema))
89
+ .reduce((schema1, schema2, i) => this.combineReducedSchemas([...path, 'allOf', i.toString()], schema1, schema2), {
90
+ oneOf: [],
91
+ });
92
+ schema.oneOf = this.multiplySchemaArrays(path, schema.oneOf, totalAllOfObj.oneOf);
93
+ }
94
+ // evaluate subschemas, if present
95
+ if (schema.properties) {
96
+ for (const key in schema.properties) {
97
+ const propertyPath = [...path, 'properties', key];
98
+ try {
99
+ schema.properties[key] = this.reduceCompositionsRecursive(propertyPath, schema.properties[key]);
100
+ }
101
+ catch (error) {
102
+ this.handleExistingError(error, propertyPath, 'error reducing property schema');
103
+ schema.properties[key] = { oneOf: [{}] }; // an "any" schema
104
+ }
105
+ }
106
+ }
107
+ if ('items' in schema && schema.items) {
108
+ const itemsPath = [...path, 'items'];
109
+ try {
110
+ schema.items = this.reduceCompositionsRecursive(itemsPath, schema.items);
111
+ }
112
+ catch (error) {
113
+ this.handleExistingError(error, itemsPath, 'error reducing items schema');
114
+ schema.items = { oneOf: [{}] }; // an "any" schema
115
+ }
116
+ }
117
+ if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
118
+ const addlPropsPath = [...path, 'additionalProperties'];
119
+ try {
120
+ schema.additionalProperties = this.reduceCompositionsRecursive(addlPropsPath, schema.additionalProperties);
121
+ }
122
+ catch (error) {
123
+ if (error instanceof ImpossibleSchemaError) {
124
+ // if additionalProperties schema is impossible, rather than error, just disallow additionalProperties
125
+ schema.additionalProperties = false;
126
+ }
127
+ else {
128
+ this.handleExistingError(error, addlPropsPath, 'error reducing additionalProperties schema');
129
+ schema.additionalProperties = true; // an "any" schema
130
+ }
131
+ }
132
+ }
133
+ if (schema.anyOf && schema.anyOf.length > 0) {
134
+ schema.oneOf = this.multiplySchemaArrays(path, schema.oneOf, schema.anyOf);
135
+ }
136
+ const topLevelSchemaArray = this.generateTopLevelSchemaArray(schema);
137
+ return { oneOf: this.multiplySchemaArrays(path, schema.oneOf, topLevelSchemaArray) };
138
+ }
139
+ generateTopLevelSchemaArray(schema) {
140
+ if (schema.nullable) {
141
+ const typedSchema = Object.assign({}, schema);
142
+ delete typedSchema.oneOf;
143
+ delete typedSchema.nullable;
144
+ const nullSchema = Object.assign({}, schema);
145
+ delete nullSchema.oneOf;
146
+ delete nullSchema.nullable;
147
+ nullSchema.type = 'null';
148
+ return [typedSchema, nullSchema];
149
+ }
150
+ if (Array.isArray(schema.type)) {
151
+ if (schema.type.length === 0) {
152
+ const topLevelSchema = Object.assign({}, schema);
153
+ delete topLevelSchema.oneOf;
154
+ delete topLevelSchema.type;
155
+ return [topLevelSchema];
156
+ }
157
+ return schema.type.map((typeString) => {
158
+ const topLevelSchema = Object.assign({}, schema);
159
+ delete topLevelSchema.oneOf;
160
+ topLevelSchema.type = typeString;
161
+ return topLevelSchema;
162
+ });
163
+ }
164
+ const topLevelSchema = Object.assign({}, schema);
165
+ delete topLevelSchema.oneOf;
166
+ return [topLevelSchema];
167
+ }
168
+ /**
169
+ * Given two arrays representing schema options, return an array representing schema options that satisfy one element in both arrays.
170
+ *
171
+ * It is helpful to think of each array as a union of all the schemas in the array. This function can then be thought of as taking
172
+ * the intersection of the two union types.
173
+ *
174
+ * Used in the Reduction step.
175
+ *
176
+ * @param a first array of schema options
177
+ * @param b second array of schema options
178
+ * @returns array of schemas that satisfy both arrays
179
+ */
180
+ multiplySchemaArrays(path, a, b) {
181
+ if (a.length === 0 && b.length === 0) {
182
+ return [{}];
183
+ }
184
+ if (a.length === 0) {
185
+ return b;
186
+ }
187
+ if (b.length === 0) {
188
+ return a;
189
+ }
190
+ const product = a.flatMap((schema1) => {
191
+ return b.flatMap((schema2) => {
192
+ try {
193
+ const combinedSchema = this.combineTopLevelSchemas(path, schema1, schema2);
194
+ return [combinedSchema];
195
+ }
196
+ catch (error) {
197
+ if (error instanceof ImpossibleSchemaError) {
198
+ return [];
199
+ }
200
+ else {
201
+ throw error;
202
+ }
203
+ }
204
+ });
205
+ });
206
+ if (product.length === 0) {
207
+ throw new ImpossibleSchemaError(path, 'impossible schema combination:', 'schema array 1:', JSON.stringify(a, undefined, 2), 'schema array 2:', JSON.stringify(b, undefined, 2));
208
+ }
209
+ return product;
210
+ }
211
+ combineReducedSchemas(path, schema1, schema2) {
212
+ var _a, _b;
213
+ return {
214
+ oneOf: this.multiplySchemaArrays(path, ((_a = schema1.oneOf) !== null && _a !== void 0 ? _a : []), ((_b = schema2.oneOf) !== null && _b !== void 0 ? _b : [])),
215
+ };
216
+ }
217
+ combineTopLevelSchemas(path, schema1, schema2) {
218
+ var _a, _b;
219
+ let type1 = schema1.type;
220
+ let type2 = schema2.type;
221
+ // don't throw an error if number type is being constricted
222
+ if (type1 === 'integer' && type2 === 'number') {
223
+ type2 = 'integer';
224
+ }
225
+ else if (type1 === 'number' && type2 === 'integer') {
226
+ type1 = 'integer';
227
+ }
228
+ if (type1 && type2 && type1 !== type2) {
229
+ throw new ImpossibleSchemaError(path, `mismatched type in composition: "${type1}" "${type2}"`);
230
+ }
231
+ for (const schema of [schema1, schema2]) {
232
+ if (typeof schema.exclusiveMaximum === 'number') {
233
+ if (schema.maximum === undefined || schema.maximum >= schema.exclusiveMaximum) {
234
+ schema.maximum = schema.exclusiveMaximum;
235
+ schema.exclusiveMaximum = true;
236
+ }
237
+ else {
238
+ schema.exclusiveMaximum = undefined;
239
+ }
240
+ }
241
+ if (typeof schema.exclusiveMinimum === 'number') {
242
+ if (schema.minimum === undefined || schema.minimum <= schema.exclusiveMinimum) {
243
+ schema.minimum = schema.exclusiveMinimum;
244
+ schema.exclusiveMinimum = true;
245
+ }
246
+ else {
247
+ schema.exclusiveMinimum = undefined;
248
+ }
249
+ }
250
+ }
251
+ const combinedSchema = {
252
+ title: takeLast(schema1, schema2, 'title'),
253
+ description: takeLast(schema1, schema2, 'description'),
254
+ format: takeLast(schema1, schema2, 'format'),
255
+ multipleOf: combine(schema1, schema2, 'multipleOf', lcm),
256
+ maximum: combine(schema1, schema2, 'maximum', Math.min),
257
+ minimum: combine(schema1, schema2, 'minimum', Math.max),
258
+ maxLength: combine(schema1, schema2, 'maxLength', Math.min),
259
+ minLength: combine(schema1, schema2, 'minLength', Math.max),
260
+ maxItems: combine(schema1, schema2, 'maxItems', Math.min),
261
+ minItems: combine(schema1, schema2, 'minItems', Math.max),
262
+ maxProperties: combine(schema1, schema2, 'maxProperties', Math.min),
263
+ minProperties: combine(schema1, schema2, 'minProperties', Math.max),
264
+ required: combine(schema1, schema2, 'required', (a, b) => b.concat(a.filter((value) => !b.includes(value)))),
265
+ enum: combine(schema1, schema2, 'enum', (a, b) => b.filter((value) => a.includes(value))),
266
+ readOnly: schema1.readOnly && schema2.readOnly,
267
+ writeOnly: schema1.writeOnly && schema2.writeOnly,
268
+ deprecated: schema1.deprecated && schema2.deprecated,
269
+ };
270
+ combinedSchema.exclusiveMaximum =
271
+ (schema1.maximum === combinedSchema.maximum ? schema1.exclusiveMaximum : undefined) ||
272
+ (schema2.maximum === combinedSchema.maximum ? schema2.exclusiveMaximum : undefined);
273
+ combinedSchema.exclusiveMinimum =
274
+ (schema1.minimum === combinedSchema.minimum ? schema1.exclusiveMinimum : undefined) ||
275
+ (schema2.minimum === combinedSchema.minimum ? schema2.exclusiveMinimum : undefined);
276
+ // don't use coalesce operator, since null is a valid example
277
+ const example1 = ((_a = schema1.examples) === null || _a === void 0 ? void 0 : _a[0]) !== undefined ? schema1.examples[0] : schema1.example;
278
+ const example2 = ((_b = schema2.examples) === null || _b === void 0 ? void 0 : _b[0]) !== undefined ? schema2.examples[0] : schema2.example;
279
+ if (example1 && example2 && typeof example1 === 'object' && typeof example2 === 'object') {
280
+ combinedSchema.example = Object.assign(Object.assign({}, example1), example2);
281
+ }
282
+ else {
283
+ // don't use coalesce operator, since null is a valid example
284
+ combinedSchema.example = example2 !== undefined ? example2 : example1;
285
+ }
286
+ const type = type1 !== null && type1 !== void 0 ? type1 : type2;
287
+ if (type === 'array') {
288
+ return Object.assign({ type, items: this.combineReducedSchemas([...path, 'items'], 'items' in schema1 && schema1.items ? schema1.items : {}, 'items' in schema2 && schema2.items ? schema2.items : {}) }, combinedSchema);
289
+ }
290
+ if (schema1.properties && schema2.properties) {
291
+ const combinedProperties = Object.assign({}, schema1.properties);
292
+ Object.entries(schema2.properties).forEach(([property, schema]) => {
293
+ const schema1Property = combinedProperties[property];
294
+ if (schema1Property) {
295
+ combinedProperties[property] = this.combineReducedSchemas([...path, 'properties', property], schema1Property, schema);
296
+ }
297
+ else {
298
+ combinedProperties[property] = schema;
299
+ }
300
+ });
301
+ combinedSchema.properties = combinedProperties;
302
+ }
303
+ else if (schema1.properties || schema2.properties) {
304
+ combinedSchema.properties = Object.assign(Object.assign({}, schema1.properties), schema2.properties);
305
+ }
306
+ if (schema1.additionalProperties === false || schema2.additionalProperties === false) {
307
+ combinedSchema.additionalProperties = false;
308
+ }
309
+ else if (schema1.additionalProperties &&
310
+ typeof schema1.additionalProperties === 'object' &&
311
+ schema2.additionalProperties &&
312
+ typeof schema2.additionalProperties === 'object') {
313
+ combinedSchema.additionalProperties = this.combineReducedSchemas([...path, 'additionalProperties'], schema1.additionalProperties, schema2.additionalProperties);
314
+ }
315
+ else if (schema1.additionalProperties && typeof schema1.additionalProperties === 'object') {
316
+ combinedSchema.additionalProperties = schema1.additionalProperties;
317
+ }
318
+ else if (schema2.additionalProperties && typeof schema2.additionalProperties === 'object') {
319
+ combinedSchema.additionalProperties = schema2.additionalProperties;
320
+ }
321
+ return Object.assign({ type }, combinedSchema);
322
+ }
323
+ convertSchemaRecursive(path, schema, required) {
324
+ if (schema.oneOf === undefined || schema.oneOf.length === 0) {
325
+ throw new ConversionError(path, 'missing schema definition');
326
+ }
327
+ const schemaArray = schema.oneOf.map((schema) => {
328
+ const sharedProps = {};
329
+ addKeyIfDefined('required', required, sharedProps);
330
+ copyKeyIfDefined('title', schema, sharedProps);
331
+ copyKeyIfDefined('description', schema, sharedProps);
332
+ copyKeyIfDefined('readOnly', schema, sharedProps);
333
+ copyKeyIfDefined('writeOnly', schema, sharedProps);
334
+ copyKeyIfDefined('deprecated', schema, sharedProps);
335
+ if (schema.type === undefined) {
336
+ const inferredType = inferType(schema);
337
+ if (inferredType === undefined) {
338
+ return Object.assign({ type: 'any' }, sharedProps);
339
+ }
340
+ schema.type = inferredType;
341
+ }
342
+ switch (schema.type) {
343
+ case 'boolean':
344
+ const booleanProps = sharedProps;
345
+ copyKeyIfDefined('default', schema, booleanProps);
346
+ copyExampleIfDefined(schema, booleanProps);
347
+ return Object.assign({ type: schema.type }, booleanProps);
348
+ case 'number':
349
+ case 'integer':
350
+ if (schema.enum) {
351
+ const numberEnumProps = sharedProps;
352
+ copyKeyIfDefined('default', schema, numberEnumProps);
353
+ copyExampleIfDefined(schema, numberEnumProps);
354
+ return Object.assign({ type: schema.type === 'number' ? 'enum<number>' : 'enum<integer>', enum: schema.enum }, numberEnumProps);
355
+ }
356
+ const numberProps = sharedProps;
357
+ copyKeyIfDefined('multipleOf', schema, numberProps);
358
+ copyKeyIfDefined('maximum', schema, numberProps);
359
+ copyKeyIfDefined('exclusiveMaximum', schema, numberProps);
360
+ copyKeyIfDefined('minimum', schema, numberProps);
361
+ copyKeyIfDefined('exclusiveMinimum', schema, numberProps);
362
+ copyKeyIfDefined('default', schema, numberProps);
363
+ copyExampleIfDefined(schema, numberProps);
364
+ return Object.assign({ type: schema.type }, numberProps);
365
+ case 'string':
366
+ if (schema.enum) {
367
+ const stringEnumProps = sharedProps;
368
+ copyKeyIfDefined('default', schema, stringEnumProps);
369
+ copyExampleIfDefined(schema, stringEnumProps);
370
+ return Object.assign({ type: 'enum<string>', enum: schema.enum }, stringEnumProps);
371
+ }
372
+ const stringProps = sharedProps;
373
+ copyKeyIfDefined('format', schema, stringProps);
374
+ copyKeyIfDefined('pattern', schema, stringProps);
375
+ copyKeyIfDefined('maxLength', schema, stringProps);
376
+ copyKeyIfDefined('minLength', schema, stringProps);
377
+ copyKeyIfDefined('default', schema, stringProps);
378
+ copyExampleIfDefined(schema, stringProps);
379
+ return Object.assign({ type: schema.type }, stringProps);
380
+ case 'array':
381
+ const arrayProps = sharedProps;
382
+ let items = undefined;
383
+ const itemsPath = [...path, 'items'];
384
+ try {
385
+ items =
386
+ // validator allows items to be null
387
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
388
+ typeof schema.items === 'object' && schema.items != null
389
+ ? this.convertSchemaRecursive(itemsPath, schema.items)
390
+ : [{ type: 'any' }];
391
+ }
392
+ catch (error) {
393
+ this.handleExistingError(error, itemsPath, 'error converting items schema');
394
+ items = [{ type: 'any' }];
395
+ }
396
+ copyKeyIfDefined('maxItems', schema, arrayProps);
397
+ copyKeyIfDefined('minItems', schema, arrayProps);
398
+ copyKeyIfDefined('uniqueItems', schema, arrayProps);
399
+ copyKeyIfDefined('default', schema, arrayProps);
400
+ copyExampleIfDefined(schema, arrayProps);
401
+ return Object.assign({ type: schema.type, items }, arrayProps);
402
+ case 'object':
403
+ const properties = this.convertProperties([...path, 'properties'], schema.properties, schema.required);
404
+ let additionalProperties = undefined;
405
+ const addlPropsPath = [...path, 'additionalProperties'];
406
+ try {
407
+ additionalProperties =
408
+ // validator allows additionalProperties to be null
409
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
410
+ typeof schema.additionalProperties === 'object' && schema.additionalProperties != null
411
+ ? this.convertSchemaRecursive(addlPropsPath, schema.additionalProperties)
412
+ : schema.additionalProperties;
413
+ }
414
+ catch (error) {
415
+ this.handleExistingError(error, addlPropsPath, 'error converting additionalProperties schema');
416
+ // don't need to assign additionalProperties because undefined = "any"
417
+ }
418
+ const objectProperties = sharedProps;
419
+ addKeyIfDefined('additionalProperties', additionalProperties, objectProperties);
420
+ copyKeyIfDefined('maxProperties', schema, objectProperties);
421
+ copyKeyIfDefined('minProperties', schema, objectProperties);
422
+ copyKeyIfDefined('default', schema, objectProperties);
423
+ copyExampleIfDefined(schema, objectProperties);
424
+ return Object.assign({ type: schema.type, properties }, objectProperties);
425
+ case 'null':
426
+ const nullProps = sharedProps;
427
+ copyKeyIfDefined('default', schema, nullProps);
428
+ copyExampleIfDefined(schema, nullProps);
429
+ return Object.assign({ type: schema.type }, nullProps);
430
+ default:
431
+ throw new InvalidSchemaError(path, `invalid schema type: ${schema.type}`);
432
+ }
433
+ });
434
+ if (!schemaArray[0]) {
435
+ throw new ConversionError(path, 'missing schema definition in position 0');
436
+ }
437
+ // must unpack first element to satisfy type
438
+ return [schemaArray[0], ...schemaArray.slice(1)];
439
+ }
440
+ convertProperties(path, properties, required) {
441
+ if (properties === undefined) {
442
+ return {};
443
+ }
444
+ const newEntries = Object.entries(properties).map(([name, schema]) => {
445
+ const propPath = [...path, name];
446
+ let convertedSchema;
447
+ try {
448
+ convertedSchema = this.convertSchemaRecursive(propPath, schema, (required === null || required === void 0 ? void 0 : required.includes(name)) ? true : undefined);
449
+ }
450
+ catch (error) {
451
+ this.handleExistingError(error, propPath, 'error converting property schema');
452
+ convertedSchema = [{ type: 'any' }];
453
+ }
454
+ return [name, convertedSchema];
455
+ });
456
+ return Object.fromEntries(newEntries);
457
+ }
458
+ static convert({ schema, required, path, location, safeParse, }) {
459
+ return new SchemaConverter(schema, required, path, location, safeParse).convert();
460
+ }
461
+ }
462
+ const copyExampleIfDefined = (source, destination) => {
463
+ var _a;
464
+ const example = ((_a = source.examples) === null || _a === void 0 ? void 0 : _a[0]) !== undefined ? source.examples[0] : source.example;
465
+ if (example !== undefined) {
466
+ destination.example = example;
467
+ }
468
+ };
469
+ const takeLast = (schema1, schema2, key) => {
470
+ var _a;
471
+ return (_a = schema2[key]) !== null && _a !== void 0 ? _a : schema1[key];
472
+ };
473
+ const combine = (schema1, schema2, key, transform) => {
474
+ var _a;
475
+ return schema1[key] !== undefined && schema2[key] !== undefined
476
+ ? transform(schema1[key], schema2[key])
477
+ : (_a = schema1[key]) !== null && _a !== void 0 ? _a : schema2[key];
478
+ };
479
+ /**
480
+ * Given an OpenAPI 3.1 schema, this function will attempt to determine the schema type
481
+ * based on the properties present in the schema. This is useful for assigning types to
482
+ * schemas that are missing a type.
483
+ *
484
+ * For example, if a schema has no type but has `schema.properties`, we can infer the
485
+ * intended type is `object`.
486
+ *
487
+ * Used in the Conversion step.
488
+ *
489
+ * @param schema
490
+ * @returns if exactly one type can be inferred, the string corresponding to that type; otherwise `undefined`
491
+ */
492
+ const inferType = (schema) => {
493
+ var _a, _b;
494
+ let type = undefined;
495
+ if (schema.format !== undefined ||
496
+ schema.pattern !== undefined ||
497
+ schema.minLength !== undefined ||
498
+ schema.maxLength !== undefined ||
499
+ ((_a = schema.enum) === null || _a === void 0 ? void 0 : _a.every((option) => typeof option === 'string'))) {
500
+ type = 'string';
501
+ }
502
+ if (schema.multipleOf !== undefined ||
503
+ schema.minimum !== undefined ||
504
+ schema.maximum !== undefined ||
505
+ schema.exclusiveMinimum !== undefined ||
506
+ schema.exclusiveMaximum !== undefined ||
507
+ ((_b = schema.enum) === null || _b === void 0 ? void 0 : _b.every((option) => typeof option === 'number'))) {
508
+ if (type !== undefined) {
509
+ return undefined;
510
+ }
511
+ type = 'number'; // less specific than 'integer'
512
+ }
513
+ if (('items' in schema && schema.items !== undefined) ||
514
+ schema.minItems !== undefined ||
515
+ schema.maxItems !== undefined ||
516
+ schema.uniqueItems !== undefined) {
517
+ if (type !== undefined) {
518
+ return undefined;
519
+ }
520
+ type = 'array';
521
+ }
522
+ if (schema.additionalProperties !== undefined ||
523
+ schema.properties !== undefined ||
524
+ schema.minProperties !== undefined ||
525
+ schema.maxProperties !== undefined) {
526
+ if (type !== undefined) {
527
+ return undefined;
528
+ }
529
+ type = 'object';
530
+ }
531
+ return type;
532
+ };
@@ -0,0 +1,12 @@
1
+ import { OpenAPIV3_1 } from 'openapi-types';
2
+ import { BaseConverter } from './BaseConverter.js';
3
+ import { SecurityOption } from './types/endpoint.js';
4
+ export declare class SecurityConverter extends BaseConverter {
5
+ readonly securityRequirements: OpenAPIV3_1.SecurityRequirementObject[] | undefined;
6
+ readonly securitySchemes: OpenAPIV3_1.ComponentsObject['securitySchemes'];
7
+ readonly safeParse: boolean;
8
+ private constructor();
9
+ private convert;
10
+ private addSecurityParameters;
11
+ static convert(securityRequirements: OpenAPIV3_1.SecurityRequirementObject[] | undefined, securitySchemes: OpenAPIV3_1.ComponentsObject['securitySchemes'], safeParse?: boolean): SecurityOption[];
12
+ }
@@ -0,0 +1,87 @@
1
+ import { BaseConverter } from './BaseConverter.js';
2
+ import { InvalidSchemaError } from './errors.js';
3
+ import { copyKeyIfDefined } from './utils.js';
4
+ export class SecurityConverter extends BaseConverter {
5
+ constructor(securityRequirements, securitySchemes, safeParse = false) {
6
+ super(safeParse);
7
+ this.securityRequirements = securityRequirements;
8
+ this.securitySchemes = securitySchemes;
9
+ this.safeParse = safeParse;
10
+ }
11
+ convert() {
12
+ if (this.securityRequirements === undefined || this.securityRequirements.length === 0) {
13
+ return [];
14
+ }
15
+ if (this.securitySchemes === undefined) {
16
+ this.handleNewError(InvalidSchemaError, ['#', 'components'], 'securitySchemes not defined');
17
+ return [];
18
+ }
19
+ // TODO(ronan): make this work for camel-case as well
20
+ return this.securityRequirements.map((security) => {
21
+ const title = Object.keys(security)
22
+ .map((securityName) => securityName.replace(/[_-]/g, ' '))
23
+ .join(' & ');
24
+ const parameterSections = {
25
+ query: {},
26
+ header: {},
27
+ cookie: {},
28
+ };
29
+ Object.keys(security).forEach((securityName) => {
30
+ var _a;
31
+ const securityScheme = (_a = this.securitySchemes) === null || _a === void 0 ? void 0 : _a[securityName];
32
+ if (securityScheme === undefined) {
33
+ this.handleNewError(InvalidSchemaError, ['#', 'components', 'securitySchemes'], `security scheme not defined: '${securityName}'`);
34
+ return;
35
+ }
36
+ this.addSecurityParameters(securityName, securityScheme, parameterSections);
37
+ });
38
+ return {
39
+ title,
40
+ parameters: parameterSections,
41
+ };
42
+ });
43
+ }
44
+ addSecurityParameters(securityName, securityScheme, parameterSections) {
45
+ switch (securityScheme.type) {
46
+ case 'apiKey': {
47
+ if (!['header', 'query', 'cookie'].includes(securityScheme.in)) {
48
+ this.handleNewError(InvalidSchemaError, ['#', 'components', 'securitySchemes', securityName], `invalid security scheme location provided: '${securityScheme.in}'`);
49
+ return;
50
+ }
51
+ const paramGroup = securityScheme.in;
52
+ const schema = { type: 'apiKey' };
53
+ copyKeyIfDefined('description', securityScheme, schema);
54
+ parameterSections[paramGroup][securityScheme.name] = schema;
55
+ return;
56
+ }
57
+ case 'http': {
58
+ const scheme = securityScheme.scheme;
59
+ if (scheme === 'basic' || scheme === 'bearer') {
60
+ const schema = {
61
+ type: 'http',
62
+ scheme,
63
+ };
64
+ copyKeyIfDefined('description', securityScheme, schema);
65
+ parameterSections.header['Authorization'] = schema;
66
+ }
67
+ else {
68
+ this.handleNewError(InvalidSchemaError, ['#', 'components', 'securitySchemes', securityName], `encountered unknown HTTP security scheme: '${securityScheme.scheme}'`);
69
+ return;
70
+ }
71
+ return;
72
+ }
73
+ case 'oauth2': {
74
+ const schema = { type: 'oauth2' };
75
+ copyKeyIfDefined('description', securityScheme, schema);
76
+ parameterSections.header['Authorization'] = schema;
77
+ return;
78
+ }
79
+ case 'openIdConnect': {
80
+ return;
81
+ }
82
+ }
83
+ }
84
+ static convert(securityRequirements, securitySchemes, safeParse) {
85
+ return new SecurityConverter(securityRequirements, securitySchemes, safeParse).convert();
86
+ }
87
+ }
@@ -0,0 +1,10 @@
1
+ import { OpenAPIV3_1 } from 'openapi-types';
2
+ import { BaseConverter } from './BaseConverter.js';
3
+ import { Server } from './types/endpoint.js';
4
+ export declare class ServersConverter extends BaseConverter {
5
+ readonly servers: OpenAPIV3_1.ServerObject[] | undefined;
6
+ private constructor();
7
+ private convert;
8
+ private convertVariables;
9
+ static convert(servers: OpenAPIV3_1.ServerObject[] | undefined): Server[] | undefined;
10
+ }