@mintlify/common 1.0.1206 → 1.0.1207
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/openapi/additionalOperations.d.ts +1 -3
- package/dist/openapi/additionalOperations.js +3 -6
- package/dist/openapi/discriminators.d.ts +4 -0
- package/dist/openapi/discriminators.js +121 -0
- package/dist/openapi/documentPositions.d.ts +10 -0
- package/dist/openapi/documentPositions.js +154 -0
- package/dist/openapi/encodings.d.ts +3 -0
- package/dist/openapi/encodings.js +59 -0
- package/dist/openapi/generateOpenApiPages.js +18 -18
- package/dist/openapi/getOpenApiOperationMethodAndEndpoint.js +4 -2
- package/dist/openapi/headerNames.d.ts +3 -0
- package/dist/openapi/headerNames.js +25 -0
- package/dist/openapi/pathItems.d.ts +7 -0
- package/dist/openapi/pathItems.js +97 -0
- package/dist/openapi/querystringParameters.d.ts +1 -3
- package/dist/openapi/querystringParameters.js +9 -45
- package/dist/openapi/servers.d.ts +6 -0
- package/dist/openapi/servers.js +50 -0
- package/dist/openapi/validate.js +41 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/guards.d.ts +1 -1
- package/dist/types/guards.js +2 -2
- package/package.json +4 -4
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type PathItemSources } from './pathItems.js';
|
|
2
2
|
import type { OpenApiValidateError } from './validate.js';
|
|
3
|
-
type PathItemSources = Partial<Pick<OpenAPIV3_2.Document, 'paths' | 'webhooks' | 'components'>>;
|
|
4
3
|
export declare const validateAdditionalOperations: (document: PathItemSources) => OpenApiValidateError[];
|
|
5
|
-
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getPathItemOperations, isAdditionalHttpMethod,
|
|
2
|
-
|
|
1
|
+
import { getPathItemOperations, isAdditionalHttpMethod, } from '@mintlify/validation';
|
|
2
|
+
import { escapePointer, operationPointer, } from './pathItems.js';
|
|
3
3
|
const callbackPathItemsOf = (location, pathItem) => {
|
|
4
4
|
if (!pathItem || '$ref' in pathItem)
|
|
5
5
|
return [];
|
|
@@ -7,14 +7,11 @@ const callbackPathItemsOf = (location, pathItem) => {
|
|
|
7
7
|
const callbacks = operation.callbacks;
|
|
8
8
|
if (!callbacks)
|
|
9
9
|
return [];
|
|
10
|
-
const operationPointer = isFixedHttpMethod(method)
|
|
11
|
-
? escapePointer(method)
|
|
12
|
-
: `additionalOperations/${escapePointer(method)}`;
|
|
13
10
|
return Object.entries(callbacks).flatMap(([name, callback]) => {
|
|
14
11
|
if (!callback || '$ref' in callback)
|
|
15
12
|
return [];
|
|
16
13
|
return Object.entries(callback).flatMap(([expression, item]) => {
|
|
17
|
-
const itemLocation = `${location}/${operationPointer}/callbacks/${escapePointer(name)}/${escapePointer(expression)}`;
|
|
14
|
+
const itemLocation = `${location}/${operationPointer(method)}/callbacks/${escapePointer(name)}/${escapePointer(expression)}`;
|
|
18
15
|
const entry = [itemLocation, item];
|
|
19
16
|
return [entry, ...callbackPathItemsOf(itemLocation, item)];
|
|
20
17
|
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type PathItemSources } from './pathItems.js';
|
|
2
|
+
import type { OpenApiValidateError } from './validate.js';
|
|
3
|
+
export declare const resolveDiscriminatorTargetPointer: (target: string) => string | undefined;
|
|
4
|
+
export declare const validateDiscriminatorDefaults: (document: PathItemSources) => OpenApiValidateError[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { isObject } from '../types/guards.js';
|
|
2
|
+
import { forEachDocumentPosition } from './documentPositions.js';
|
|
3
|
+
import { escapePointer } from './pathItems.js';
|
|
4
|
+
const unescapePointerToken = (token) => token.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
5
|
+
export const resolveDiscriminatorTargetPointer = (target) => {
|
|
6
|
+
if (target.startsWith('#')) {
|
|
7
|
+
try {
|
|
8
|
+
const pointer = decodeURIComponent(target.slice(1));
|
|
9
|
+
return pointer.startsWith('/') ? pointer : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch (_a) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const name = target.startsWith('./') ? target.slice(2) : target;
|
|
16
|
+
return `/components/schemas/${escapePointer(name)}`;
|
|
17
|
+
};
|
|
18
|
+
const ALIAS_KEYS = new Set(['$ref', 'summary', 'description']);
|
|
19
|
+
const componentSchemaName = (pointer) => {
|
|
20
|
+
const match = /^\/components\/schemas\/([^/]+)$/.exec(pointer);
|
|
21
|
+
return (match === null || match === void 0 ? void 0 : match[1]) === undefined ? undefined : unescapePointerToken(match[1]);
|
|
22
|
+
};
|
|
23
|
+
export const validateDiscriminatorDefaults = (document) => {
|
|
24
|
+
const errors = [];
|
|
25
|
+
const componentSchemas = isObject(document.components)
|
|
26
|
+
? document.components.schemas
|
|
27
|
+
: undefined;
|
|
28
|
+
const schemaExists = (pointer) => {
|
|
29
|
+
const name = pointer === undefined ? undefined : componentSchemaName(pointer);
|
|
30
|
+
return (name !== undefined &&
|
|
31
|
+
isObject(componentSchemas) &&
|
|
32
|
+
Object.prototype.hasOwnProperty.call(componentSchemas, name) &&
|
|
33
|
+
isObject(componentSchemas[name]));
|
|
34
|
+
};
|
|
35
|
+
const canonicalSchemaPointer = (pointer) => {
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
let current = pointer;
|
|
38
|
+
while (!seen.has(current)) {
|
|
39
|
+
seen.add(current);
|
|
40
|
+
const name = componentSchemaName(current);
|
|
41
|
+
const schema = name === undefined || !isObject(componentSchemas) ? undefined : componentSchemas[name];
|
|
42
|
+
if (!isObject(schema) ||
|
|
43
|
+
typeof schema.$ref !== 'string' ||
|
|
44
|
+
!Object.keys(schema).every((key) => ALIAS_KEYS.has(key))) {
|
|
45
|
+
return current;
|
|
46
|
+
}
|
|
47
|
+
const next = resolveDiscriminatorTargetPointer(schema.$ref);
|
|
48
|
+
if (next === undefined || !schemaExists(next))
|
|
49
|
+
return current;
|
|
50
|
+
current = next;
|
|
51
|
+
}
|
|
52
|
+
return current;
|
|
53
|
+
};
|
|
54
|
+
const variantPointers = (schema) => {
|
|
55
|
+
const members = [
|
|
56
|
+
...(Array.isArray(schema.oneOf) ? schema.oneOf : []),
|
|
57
|
+
...(Array.isArray(schema.anyOf) ? schema.anyOf : []),
|
|
58
|
+
];
|
|
59
|
+
if (members.length === 0)
|
|
60
|
+
return undefined;
|
|
61
|
+
return members.flatMap((member) => {
|
|
62
|
+
if (!isObject(member) || typeof member.$ref !== 'string')
|
|
63
|
+
return [];
|
|
64
|
+
const pointer = resolveDiscriminatorTargetPointer(member.$ref);
|
|
65
|
+
return pointer === undefined ? [] : [canonicalSchemaPointer(pointer)];
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const componentSchema = (pointer) => {
|
|
69
|
+
const name = componentSchemaName(pointer);
|
|
70
|
+
const schema = name === undefined || !isObject(componentSchemas) ? undefined : componentSchemas[name];
|
|
71
|
+
return isObject(schema) ? schema : undefined;
|
|
72
|
+
};
|
|
73
|
+
const compositionOf = (schema, seen) => {
|
|
74
|
+
const own = Object.assign(Object.assign(Object.assign({}, ('oneOf' in schema ? { oneOf: schema.oneOf } : {})), ('anyOf' in schema ? { anyOf: schema.anyOf } : {})), ('allOf' in schema ? { allOf: schema.allOf } : {}));
|
|
75
|
+
if (typeof schema.$ref !== 'string')
|
|
76
|
+
return own;
|
|
77
|
+
const pointer = resolveDiscriminatorTargetPointer(schema.$ref);
|
|
78
|
+
const canonical = pointer === undefined ? undefined : canonicalSchemaPointer(pointer);
|
|
79
|
+
if (canonical === undefined || seen.has(canonical))
|
|
80
|
+
return own;
|
|
81
|
+
seen.add(canonical);
|
|
82
|
+
const target = componentSchema(canonical);
|
|
83
|
+
return target === undefined ? own : Object.assign(Object.assign({}, compositionOf(target, seen)), own);
|
|
84
|
+
};
|
|
85
|
+
const reachableVariantPointers = (schema, seen) => {
|
|
86
|
+
const composition = compositionOf(schema, seen);
|
|
87
|
+
const own = variantPointers(composition);
|
|
88
|
+
const nested = (Array.isArray(composition.allOf) ? composition.allOf : []).flatMap((member) => {
|
|
89
|
+
if (!isObject(member))
|
|
90
|
+
return [];
|
|
91
|
+
const inner = reachableVariantPointers(member, seen);
|
|
92
|
+
return inner === undefined ? [] : [inner];
|
|
93
|
+
});
|
|
94
|
+
if (own === undefined && nested.length === 0)
|
|
95
|
+
return undefined;
|
|
96
|
+
return [...(own !== null && own !== void 0 ? own : []), ...nested.flat()];
|
|
97
|
+
};
|
|
98
|
+
forEachDocumentPosition(document, {
|
|
99
|
+
onSchema: (path, schema) => {
|
|
100
|
+
const discriminator = schema.discriminator;
|
|
101
|
+
if (!isObject(discriminator) || typeof discriminator.defaultMapping !== 'string')
|
|
102
|
+
return;
|
|
103
|
+
const target = discriminator.defaultMapping;
|
|
104
|
+
const pointer = resolveDiscriminatorTargetPointer(target);
|
|
105
|
+
const variants = reachableVariantPointers(schema, new Set());
|
|
106
|
+
const exists = schemaExists(pointer);
|
|
107
|
+
const selectable = exists &&
|
|
108
|
+
(variants === undefined ||
|
|
109
|
+
(pointer !== undefined && variants.includes(canonicalSchemaPointer(pointer))));
|
|
110
|
+
if (!selectable) {
|
|
111
|
+
errors.push({
|
|
112
|
+
path: `${path}/discriminator/defaultMapping`,
|
|
113
|
+
message: exists
|
|
114
|
+
? `defaultMapping ${target} is not one of the schema's oneOf/anyOf variants`
|
|
115
|
+
: `defaultMapping ${target} does not name a schema in mapping or components.schemas`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return errors;
|
|
121
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type PathItemSources } from './pathItems.js';
|
|
2
|
+
type Visitor = (path: string, value: Record<string, unknown>) => void;
|
|
3
|
+
export type DocumentVisitors = {
|
|
4
|
+
onMediaType?: Visitor;
|
|
5
|
+
onSchema?: Visitor;
|
|
6
|
+
onParameter?: Visitor;
|
|
7
|
+
onHeaderName?: (path: string, name: string) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const forEachDocumentPosition: (document: PathItemSources, visitors: DocumentVisitors) => void;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { getPathItemOperations } from '@mintlify/validation';
|
|
2
|
+
import { isObject } from '../types/guards.js';
|
|
3
|
+
import { escapePointer, forEachPathItem, operationPointer, } from './pathItems.js';
|
|
4
|
+
const SCHEMA_KEYWORDS = [
|
|
5
|
+
'additionalProperties',
|
|
6
|
+
'contains',
|
|
7
|
+
'contentSchema',
|
|
8
|
+
'else',
|
|
9
|
+
'if',
|
|
10
|
+
'items',
|
|
11
|
+
'not',
|
|
12
|
+
'propertyNames',
|
|
13
|
+
'then',
|
|
14
|
+
'unevaluatedItems',
|
|
15
|
+
'unevaluatedProperties',
|
|
16
|
+
];
|
|
17
|
+
const SCHEMA_MAP_KEYWORDS = [
|
|
18
|
+
'$defs',
|
|
19
|
+
'dependentSchemas',
|
|
20
|
+
'patternProperties',
|
|
21
|
+
'properties',
|
|
22
|
+
];
|
|
23
|
+
const SCHEMA_LIST_KEYWORDS = ['allOf', 'anyOf', 'oneOf', 'prefixItems'];
|
|
24
|
+
const visitSchema = (value, path, walk) => {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
if (!isObject(value) || walk.seen.has(value))
|
|
27
|
+
return;
|
|
28
|
+
walk.seen.add(value);
|
|
29
|
+
(_b = (_a = walk.visitors).onSchema) === null || _b === void 0 ? void 0 : _b.call(_a, path, value);
|
|
30
|
+
for (const keyword of SCHEMA_KEYWORDS) {
|
|
31
|
+
visitSchema(value[keyword], `${path}/${keyword}`, walk);
|
|
32
|
+
}
|
|
33
|
+
for (const keyword of SCHEMA_MAP_KEYWORDS) {
|
|
34
|
+
const entries = value[keyword];
|
|
35
|
+
if (!isObject(entries))
|
|
36
|
+
continue;
|
|
37
|
+
for (const [name, child] of Object.entries(entries)) {
|
|
38
|
+
visitSchema(child, `${path}/${keyword}/${escapePointer(name)}`, walk);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const keyword of SCHEMA_LIST_KEYWORDS) {
|
|
42
|
+
const list = value[keyword];
|
|
43
|
+
if (!Array.isArray(list))
|
|
44
|
+
continue;
|
|
45
|
+
list.forEach((child, index) => visitSchema(child, `${path}/${keyword}/${index}`, walk));
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const visitEncoding = (value, path, walk) => {
|
|
49
|
+
if (!isObject(value) || walk.seen.has(value))
|
|
50
|
+
return;
|
|
51
|
+
walk.seen.add(value);
|
|
52
|
+
visitHeaderMap(value.headers, `${path}/headers`, walk);
|
|
53
|
+
visitEncodings(value, path, walk);
|
|
54
|
+
};
|
|
55
|
+
const visitEncodings = (value, path, walk) => {
|
|
56
|
+
if (isObject(value.encoding)) {
|
|
57
|
+
for (const [name, encoding] of Object.entries(value.encoding)) {
|
|
58
|
+
visitEncoding(encoding, `${path}/encoding/${escapePointer(name)}`, walk);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (Array.isArray(value.prefixEncoding)) {
|
|
62
|
+
value.prefixEncoding.forEach((encoding, index) => visitEncoding(encoding, `${path}/prefixEncoding/${index}`, walk));
|
|
63
|
+
}
|
|
64
|
+
visitEncoding(value.itemEncoding, `${path}/itemEncoding`, walk);
|
|
65
|
+
};
|
|
66
|
+
const visitMedia = (value, path, walk) => {
|
|
67
|
+
var _a, _b;
|
|
68
|
+
if (!isObject(value))
|
|
69
|
+
return;
|
|
70
|
+
if (!('$ref' in value))
|
|
71
|
+
(_b = (_a = walk.visitors).onMediaType) === null || _b === void 0 ? void 0 : _b.call(_a, path, value);
|
|
72
|
+
visitSchema(value.schema, `${path}/schema`, walk);
|
|
73
|
+
visitSchema(value.itemSchema, `${path}/itemSchema`, walk);
|
|
74
|
+
visitEncodings(value, path, walk);
|
|
75
|
+
};
|
|
76
|
+
const visitContent = (value, path, walk) => {
|
|
77
|
+
if (!isObject(value))
|
|
78
|
+
return;
|
|
79
|
+
for (const [mediaType, media] of Object.entries(value)) {
|
|
80
|
+
visitMedia(media, `${path}/${escapePointer(mediaType)}`, walk);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const visitParameterOrHeader = (value, path, walk) => {
|
|
84
|
+
if (!isObject(value) || '$ref' in value)
|
|
85
|
+
return;
|
|
86
|
+
visitSchema(value.schema, `${path}/schema`, walk);
|
|
87
|
+
visitContent(value.content, `${path}/content`, walk);
|
|
88
|
+
};
|
|
89
|
+
const visitParameter = (value, path, walk) => {
|
|
90
|
+
var _a, _b;
|
|
91
|
+
if (!isObject(value) || '$ref' in value)
|
|
92
|
+
return;
|
|
93
|
+
(_b = (_a = walk.visitors).onParameter) === null || _b === void 0 ? void 0 : _b.call(_a, path, value);
|
|
94
|
+
visitParameterOrHeader(value, path, walk);
|
|
95
|
+
};
|
|
96
|
+
const visitComponentMap = (value, path, walk, visit) => {
|
|
97
|
+
if (!isObject(value))
|
|
98
|
+
return;
|
|
99
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
100
|
+
visit(entry, `${path}/${escapePointer(name)}`, walk);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const visitHeaderMap = (value, path, walk) => {
|
|
104
|
+
var _a, _b;
|
|
105
|
+
if (!isObject(value))
|
|
106
|
+
return;
|
|
107
|
+
for (const [name, header] of Object.entries(value)) {
|
|
108
|
+
const entryPath = `${path}/${escapePointer(name)}`;
|
|
109
|
+
(_b = (_a = walk.visitors).onHeaderName) === null || _b === void 0 ? void 0 : _b.call(_a, entryPath, name);
|
|
110
|
+
visitParameterOrHeader(header, entryPath, walk);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const visitRequestBody = (value, path, walk) => {
|
|
114
|
+
if (!isObject(value) || '$ref' in value)
|
|
115
|
+
return;
|
|
116
|
+
visitContent(value.content, `${path}/content`, walk);
|
|
117
|
+
};
|
|
118
|
+
const visitResponse = (value, path, walk) => {
|
|
119
|
+
if (!isObject(value) || '$ref' in value)
|
|
120
|
+
return;
|
|
121
|
+
visitHeaderMap(value.headers, `${path}/headers`, walk);
|
|
122
|
+
visitContent(value.content, `${path}/content`, walk);
|
|
123
|
+
};
|
|
124
|
+
export const forEachDocumentPosition = (document, visitors) => {
|
|
125
|
+
var _a, _b, _c, _d, _e;
|
|
126
|
+
const walk = { visitors, seen: new Set() };
|
|
127
|
+
const components = (_a = document.components) !== null && _a !== void 0 ? _a : {};
|
|
128
|
+
for (const [name, schema] of Object.entries((_b = components.schemas) !== null && _b !== void 0 ? _b : {})) {
|
|
129
|
+
visitSchema(schema, `/components/schemas/${escapePointer(name)}`, walk);
|
|
130
|
+
}
|
|
131
|
+
visitComponentMap(components.parameters, '/components/parameters', walk, visitParameter);
|
|
132
|
+
visitComponentMap(components.headers, '/components/headers', walk, visitParameterOrHeader);
|
|
133
|
+
for (const [name, body] of Object.entries((_c = components.requestBodies) !== null && _c !== void 0 ? _c : {})) {
|
|
134
|
+
visitRequestBody(body, `/components/requestBodies/${escapePointer(name)}`, walk);
|
|
135
|
+
}
|
|
136
|
+
for (const [name, response] of Object.entries((_d = components.responses) !== null && _d !== void 0 ? _d : {})) {
|
|
137
|
+
visitResponse(response, `/components/responses/${escapePointer(name)}`, walk);
|
|
138
|
+
}
|
|
139
|
+
for (const [name, media] of Object.entries((_e = components.mediaTypes) !== null && _e !== void 0 ? _e : {})) {
|
|
140
|
+
visitMedia(media, `/components/mediaTypes/${escapePointer(name)}`, walk);
|
|
141
|
+
}
|
|
142
|
+
forEachPathItem(document, (location, pathItem) => {
|
|
143
|
+
var _a, _b, _c;
|
|
144
|
+
((_a = pathItem.parameters) !== null && _a !== void 0 ? _a : []).forEach((parameter, index) => visitParameter(parameter, `${location}/parameters/${index}`, walk));
|
|
145
|
+
for (const [method, operation] of getPathItemOperations(pathItem)) {
|
|
146
|
+
const operationPath = `${location}/${operationPointer(method)}`;
|
|
147
|
+
((_b = operation.parameters) !== null && _b !== void 0 ? _b : []).forEach((parameter, index) => visitParameter(parameter, `${operationPath}/parameters/${index}`, walk));
|
|
148
|
+
visitRequestBody(operation.requestBody, `${operationPath}/requestBody`, walk);
|
|
149
|
+
for (const [code, response] of Object.entries((_c = operation.responses) !== null && _c !== void 0 ? _c : {})) {
|
|
150
|
+
visitResponse(response, `${operationPath}/responses/${escapePointer(code)}`, walk);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { getComponentNameFromRef } from '@mintlify/validation';
|
|
2
|
+
import { isObject } from '../types/guards.js';
|
|
3
|
+
import { forEachDocumentPosition } from './documentPositions.js';
|
|
4
|
+
const resolveLocalSchema = (schema, componentSchemas, seen = new Set()) => {
|
|
5
|
+
if (!isObject(schema) || typeof schema.$ref !== 'string')
|
|
6
|
+
return schema;
|
|
7
|
+
const name = getComponentNameFromRef(schema.$ref, 'schemas');
|
|
8
|
+
if (name === undefined || seen.has(name))
|
|
9
|
+
return undefined;
|
|
10
|
+
return resolveLocalSchema(componentSchemas[name], componentSchemas, seen.add(name));
|
|
11
|
+
};
|
|
12
|
+
const isArraySchema = (schema, componentSchemas, seen = new Set()) => {
|
|
13
|
+
if (!isObject(schema) || seen.has(schema))
|
|
14
|
+
return false;
|
|
15
|
+
seen.add(schema);
|
|
16
|
+
if (schema.type !== undefined) {
|
|
17
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
18
|
+
return types.includes('array') && types.every((type) => type === 'array' || type === 'null');
|
|
19
|
+
}
|
|
20
|
+
if ('items' in schema || 'prefixItems' in schema) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
if (typeof schema.$ref === 'string' &&
|
|
24
|
+
isArraySchema(resolveLocalSchema(schema, componentSchemas), componentSchemas, seen)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(schema.allOf) &&
|
|
28
|
+
schema.allOf.some((member) => isArraySchema(member, componentSchemas, seen))) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
const alternatives = [
|
|
32
|
+
...(Array.isArray(schema.oneOf) ? schema.oneOf : []),
|
|
33
|
+
...(Array.isArray(schema.anyOf) ? schema.anyOf : []),
|
|
34
|
+
];
|
|
35
|
+
return (alternatives.some((member) => !isNullSchema(resolveLocalSchema(member, componentSchemas))) &&
|
|
36
|
+
alternatives.every((member) => isNullSchema(resolveLocalSchema(member, componentSchemas)) ||
|
|
37
|
+
isArraySchema(member, componentSchemas, new Set(seen))));
|
|
38
|
+
};
|
|
39
|
+
const isNullSchema = (schema) => isObject(schema) &&
|
|
40
|
+
(schema.type === 'null' ||
|
|
41
|
+
(Array.isArray(schema.type) && schema.type.every((type) => type === 'null')));
|
|
42
|
+
export const validatePositionalEncodings = (document) => {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
const errors = [];
|
|
45
|
+
const componentSchemas = (_b = (_a = document.components) === null || _a === void 0 ? void 0 : _a.schemas) !== null && _b !== void 0 ? _b : {};
|
|
46
|
+
forEachDocumentPosition(document, {
|
|
47
|
+
onMediaType: (path, media) => {
|
|
48
|
+
if (('prefixEncoding' in media || 'itemEncoding' in media) &&
|
|
49
|
+
!('itemSchema' in media) &&
|
|
50
|
+
!isArraySchema(media.schema, componentSchemas)) {
|
|
51
|
+
errors.push({
|
|
52
|
+
path,
|
|
53
|
+
message: 'prefixEncoding and itemEncoding require itemSchema or an array schema',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
return errors;
|
|
59
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getPathItemOperations } from '@mintlify/validation';
|
|
1
|
+
import { getPathItemOperations, resolvePathItemReference, } from '@mintlify/validation';
|
|
2
2
|
import jsYaml from 'js-yaml';
|
|
3
3
|
import { optionallyAddLeadingSlash } from '../fs/optionallySlash.js';
|
|
4
4
|
import { mergePageGroups } from '../navigation/groupAccess.js';
|
|
@@ -73,7 +73,7 @@ function getXMintGroups(pathObject, operationObject) {
|
|
|
73
73
|
return groups;
|
|
74
74
|
}
|
|
75
75
|
export function generateOpenApiPages(opts) {
|
|
76
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
76
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
77
77
|
const { spec, specFilePath, outputDirectory } = opts;
|
|
78
78
|
const outDir = outputDirectory !== null && outputDirectory !== void 0 ? outputDirectory : DEFAULT_OUTPUT_DIR;
|
|
79
79
|
const openApiFilePathFromRoot = optionallyAddLeadingSlash(specFilePath);
|
|
@@ -88,15 +88,15 @@ export function generateOpenApiPages(opts) {
|
|
|
88
88
|
{
|
|
89
89
|
filename: (_a = specFilePath.split('/').pop()) !== null && _a !== void 0 ? _a : 'spec',
|
|
90
90
|
spec: spec,
|
|
91
|
-
originalFileLocation:
|
|
91
|
+
originalFileLocation: openApiFilePathFromRoot,
|
|
92
92
|
},
|
|
93
93
|
];
|
|
94
94
|
// Process paths
|
|
95
|
-
for (const [apiPath,
|
|
96
|
-
if (!
|
|
95
|
+
for (const [apiPath, rawPathItem] of Object.entries((_b = spec.paths) !== null && _b !== void 0 ? _b : {})) {
|
|
96
|
+
if (!rawPathItem || typeof rawPathItem !== 'object')
|
|
97
97
|
continue;
|
|
98
98
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
99
|
-
const pathItem =
|
|
99
|
+
const pathItem = ((_c = resolvePathItemReference(rawPathItem, spec.components)) !== null && _c !== void 0 ? _c : rawPathItem);
|
|
100
100
|
for (const [method, pathOperation] of getPathItemOperations(pathItem)) {
|
|
101
101
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
102
102
|
const operation = pathOperation;
|
|
@@ -105,10 +105,10 @@ export function generateOpenApiPages(opts) {
|
|
|
105
105
|
const xMint = operation['x-mint'];
|
|
106
106
|
const xMintGroups = getXMintGroups(pathItem, operation);
|
|
107
107
|
const tagPath = resolveTags(operation.tags);
|
|
108
|
-
const tagName = (
|
|
109
|
-
const groupName = (
|
|
110
|
-
let title = (
|
|
111
|
-
let folder = (
|
|
108
|
+
const tagName = (_d = tagPath.at(-1)) === null || _d === void 0 ? void 0 : _d.name;
|
|
109
|
+
const groupName = (_e = tagPath.at(-1)) === null || _e === void 0 ? void 0 : _e.label;
|
|
110
|
+
let title = (_f = prepareStringToBeValidFilename(operation.summary)) !== null && _f !== void 0 ? _f : `${method.toLowerCase()}-${prepareStringToBeValidFilename(apiPath)}`;
|
|
111
|
+
let folder = (_g = prepareStringToBeValidFilename(tagName)) !== null && _g !== void 0 ? _g : '';
|
|
112
112
|
let base = posixJoin(outDir, folder, title);
|
|
113
113
|
if (xMint === null || xMint === void 0 ? void 0 : xMint.href) {
|
|
114
114
|
const href = xMint.href.startsWith('/') ? xMint.href.slice(1) : xMint.href;
|
|
@@ -133,7 +133,7 @@ export function generateOpenApiPages(opts) {
|
|
|
133
133
|
if (badge !== undefined && (xMintMetadata === null || xMintMetadata === void 0 ? void 0 : xMintMetadata.tag) === undefined) {
|
|
134
134
|
xMintMetadata = Object.assign(Object.assign({}, xMintMetadata), { tag: badge });
|
|
135
135
|
}
|
|
136
|
-
const page = Object.assign(Object.assign({ title: (
|
|
136
|
+
const page = Object.assign(Object.assign({ title: (_j = (_h = xMintMetadata === null || xMintMetadata === void 0 ? void 0 : xMintMetadata.title) !== null && _h !== void 0 ? _h : titleTag) !== null && _j !== void 0 ? _j : slugToTitle(filenameWithoutExtension), description: (_k = xMintMetadata === null || xMintMetadata === void 0 ? void 0 : xMintMetadata.description) !== null && _k !== void 0 ? _k : description, deprecated: (_l = xMintMetadata === null || xMintMetadata === void 0 ? void 0 : xMintMetadata.deprecated) !== null && _l !== void 0 ? _l : operation.deprecated, openapi: openapiMetaTag, href: posixResolve('/', filenameWithoutExtension), method, path: apiPath, tag: groupName }, (nestedTags ? { tagPath } : {})), { hidden: operation['x-hidden'] === true, xMintMetadata, xMintContent: xMint === null || xMint === void 0 ? void 0 : xMint.content, version: opts.version });
|
|
137
137
|
pages.push(page);
|
|
138
138
|
}
|
|
139
139
|
}
|
|
@@ -147,7 +147,7 @@ export function generateOpenApiPages(opts) {
|
|
|
147
147
|
if (!webhookObject || typeof webhookObject !== 'object')
|
|
148
148
|
continue;
|
|
149
149
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
150
|
-
const pathItem = webhookObject;
|
|
150
|
+
const pathItem = ((_m = resolvePathItemReference(webhookObject, spec.components)) !== null && _m !== void 0 ? _m : webhookObject);
|
|
151
151
|
for (const [, pathOperation] of getPathItemOperations(pathItem)) {
|
|
152
152
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
153
153
|
const operation = pathOperation;
|
|
@@ -155,10 +155,10 @@ export function generateOpenApiPages(opts) {
|
|
|
155
155
|
continue;
|
|
156
156
|
const xMint = operation['x-mint'];
|
|
157
157
|
const tagPath = resolveTags(operation.tags);
|
|
158
|
-
const tagName = (
|
|
159
|
-
const groupName = (
|
|
160
|
-
let title = (
|
|
161
|
-
let folder = (
|
|
158
|
+
const tagName = (_o = tagPath.at(-1)) === null || _o === void 0 ? void 0 : _o.name;
|
|
159
|
+
const groupName = (_p = tagPath.at(-1)) === null || _p === void 0 ? void 0 : _p.label;
|
|
160
|
+
let title = (_q = prepareStringToBeValidFilename(operation.summary)) !== null && _q !== void 0 ? _q : `${prepareStringToBeValidFilename(webhook)}`;
|
|
161
|
+
let folder = (_r = prepareStringToBeValidFilename(tagName)) !== null && _r !== void 0 ? _r : '';
|
|
162
162
|
let base = posixJoin(outDir, folder, title);
|
|
163
163
|
if (xMint === null || xMint === void 0 ? void 0 : xMint.href) {
|
|
164
164
|
const href = xMint.href.startsWith('/') ? xMint.href.slice(1) : xMint.href;
|
|
@@ -175,9 +175,9 @@ export function generateOpenApiPages(opts) {
|
|
|
175
175
|
path: webhook,
|
|
176
176
|
});
|
|
177
177
|
const badge = resolveBadge(operation.tags);
|
|
178
|
-
const xMintMetadata = badge !== undefined && ((
|
|
178
|
+
const xMintMetadata = badge !== undefined && ((_s = xMint === null || xMint === void 0 ? void 0 : xMint.metadata) === null || _s === void 0 ? void 0 : _s.tag) === undefined
|
|
179
179
|
? Object.assign(Object.assign({}, xMint === null || xMint === void 0 ? void 0 : xMint.metadata), { tag: badge }) : xMint === null || xMint === void 0 ? void 0 : xMint.metadata;
|
|
180
|
-
const page = Object.assign(Object.assign({ title: (
|
|
180
|
+
const page = Object.assign(Object.assign({ title: (_u = (_t = xMint === null || xMint === void 0 ? void 0 : xMint.metadata) === null || _t === void 0 ? void 0 : _t.title) !== null && _u !== void 0 ? _u : slugToTitle(filenameWithoutExtension), description: (_w = (_v = xMint === null || xMint === void 0 ? void 0 : xMint.metadata) === null || _v === void 0 ? void 0 : _v.description) !== null && _w !== void 0 ? _w : operation.description, deprecated: (_y = (_x = xMint === null || xMint === void 0 ? void 0 : xMint.metadata) === null || _x === void 0 ? void 0 : _x.deprecated) !== null && _y !== void 0 ? _y : operation.deprecated, openapi: openapiMetaTag, href: posixResolve('/', filenameWithoutExtension), method: 'webhook', path: webhook, tag: groupName }, (nestedTags ? { tagPath } : {})), { hidden: operation['x-hidden'] === true, isWebhook: true, xMintMetadata, xMintContent: xMint === null || xMint === void 0 ? void 0 : xMint.content, version: opts.version });
|
|
181
181
|
pages.push(page);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { httpMethods, isFixedHttpMethod, parseHttpMethod } from '@mintlify/validation';
|
|
1
|
+
import { httpMethods, isFixedHttpMethod, parseHttpMethod, resolvePathItemReference, } from '@mintlify/validation';
|
|
2
2
|
import { findOpenApiPath } from './findOpenApiPath.js';
|
|
3
3
|
import { potentiallyParseOpenApiString } from './parseOpenApiString.js';
|
|
4
4
|
export const getOpenApiOperationMethodAndEndpoint = (endpointStr, openApiFiles) => {
|
|
5
|
+
var _a;
|
|
5
6
|
const potentiallyParsedOpenApiString = potentiallyParseOpenApiString(endpointStr);
|
|
6
7
|
if (potentiallyParsedOpenApiString == undefined) {
|
|
7
8
|
return undefined;
|
|
@@ -15,7 +16,8 @@ export const getOpenApiOperationMethodAndEndpoint = (endpointStr, openApiFiles)
|
|
|
15
16
|
const match = findOpenApiPath(openApiFile.paths, endpoint);
|
|
16
17
|
const filenameMatches = !filename || filename === file.filename || filename === file.originalFileLocation;
|
|
17
18
|
if (match && filenameMatches) {
|
|
18
|
-
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- resolved items keep the PathItemObject shape
|
|
20
|
+
path = ((_a = resolvePathItemReference(match.pathItem, 'components' in openApiFile ? openApiFile.components : undefined)) !== null && _a !== void 0 ? _a : match.pathItem);
|
|
19
21
|
apiFile = openApiFile;
|
|
20
22
|
matchedEndpoint = match.pathKey;
|
|
21
23
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { forEachDocumentPosition } from './documentPositions.js';
|
|
2
|
+
// RFC 9110 field names are tokens: letters, digits and these symbols, nothing else
|
|
3
|
+
const TOKEN_SYMBOLS = new Set("!#$%&'*+-.^_`|~");
|
|
4
|
+
const isTokenChar = (char) => (char >= 'a' && char <= 'z') ||
|
|
5
|
+
(char >= 'A' && char <= 'Z') ||
|
|
6
|
+
(char >= '0' && char <= '9') ||
|
|
7
|
+
TOKEN_SYMBOLS.has(char);
|
|
8
|
+
const isValidHeaderName = (name) => typeof name === 'string' && name.length > 0 && [...name].every(isTokenChar);
|
|
9
|
+
export const validateHeaderNames = (document) => {
|
|
10
|
+
const errors = [];
|
|
11
|
+
const report = (path, name) => errors.push({ path, message: `Header name ${name} is not a valid HTTP field name` });
|
|
12
|
+
forEachDocumentPosition(document, {
|
|
13
|
+
onParameter: (path, parameter) => {
|
|
14
|
+
if (parameter.in === 'header' && typeof parameter.name === 'string') {
|
|
15
|
+
if (!isValidHeaderName(parameter.name))
|
|
16
|
+
report(`${path}/name`, parameter.name);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
onHeaderName: (path, name) => {
|
|
20
|
+
if (!isValidHeaderName(name))
|
|
21
|
+
report(path, name);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return errors;
|
|
25
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type HttpMethod, type OpenAPIV3_2 } from '@mintlify/validation';
|
|
2
|
+
export type PathItemSources = Partial<Pick<OpenAPIV3_2.Document, 'paths' | 'webhooks' | 'components'>>;
|
|
3
|
+
export type PathItemLike = OpenAPIV3_2.PathItemObject | OpenAPIV3_2.ReferenceObject;
|
|
4
|
+
export declare const escapePointer: (segment: string) => string;
|
|
5
|
+
export declare const operationPointer: (method: HttpMethod) => string;
|
|
6
|
+
export declare const resolvePathItem: (pathItem: PathItemLike, components: OpenAPIV3_2.ComponentsObject | undefined, seen?: Set<string>) => OpenAPIV3_2.PathItemObject;
|
|
7
|
+
export declare const forEachPathItem: (document: PathItemSources, visit: (location: string, pathItem: OpenAPIV3_2.PathItemObject) => void) => void;
|