@xyd-js/openapi 0.1.0-xyd.10 → 0.1.0-xyd.13
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/CHANGELOG.md +24 -0
- package/__fixtures__/-2.complex.openai/input.yaml +39848 -0
- package/__fixtures__/-2.complex.openai/output.json +321646 -0
- package/__fixtures__/-2.complex.openai/pluginOasOpenai.ts +553 -0
- package/__fixtures__/1.basic/input.yaml +226 -0
- package/__fixtures__/1.basic/output.json +1919 -0
- package/__fixtures__/2.more/input.yaml +76 -0
- package/__fixtures__/2.more/output.json +292 -0
- package/__fixtures__/3.multiple-responses/input.yaml +48 -0
- package/__fixtures__/3.multiple-responses/output.json +266 -0
- package/__fixtures__/4.abc/input.yaml +639 -0
- package/__fixtures__/4.abc/output.json +3828 -0
- package/__fixtures__/5.xdocs.codeLanguages/input.yaml +231 -0
- package/__fixtures__/5.xdocs.codeLanguages/output.json +1879 -0
- package/__fixtures__/5.xdocs.sidebar/input.yaml +256 -0
- package/__fixtures__/5.xdocs.sidebar/output.json +843 -0
- package/__fixtures__/6.codeSamples/input.yaml +75 -0
- package/__fixtures__/6.codeSamples/output.json +293 -0
- package/__tests__/oapSchemaToReferences.test.ts +88 -0
- package/__tests__/utils.ts +81 -0
- package/dist/index.cjs +1859 -162
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -4
- package/dist/index.d.ts +36 -4
- package/dist/index.js +1855 -155
- package/dist/index.js.map +1 -1
- package/index.ts +10 -2
- package/package.json +11 -6
- package/src/const.ts +5 -1
- package/src/converters/oas-componentSchemas.ts +205 -0
- package/src/converters/oas-examples.ts +417 -0
- package/src/{parameters.ts → converters/oas-parameters.ts} +17 -3
- package/src/converters/oas-paths.ts +354 -0
- package/src/{requestBody.ts → converters/oas-requestBody.ts} +30 -10
- package/src/converters/oas-responses.ts +76 -0
- package/src/converters/oas-schema.ts +141 -0
- package/src/index.ts +13 -5
- package/src/oas-core.ts +579 -0
- package/src/types.ts +18 -0
- package/src/utils.ts +103 -90
- package/src/xdocs/index.ts +18 -0
- package/src/xdocs/pluginSidebar.ts +580 -0
- package/src/xdocs/types.ts +26 -0
- package/vitest.config.ts +7 -0
- package/src/examples.ts +0 -116
- package/src/paths.ts +0 -103
- package/src/properties.ts +0 -37
- package/src/responses.ts +0 -38
- package/src/schema.ts +0 -62
package/src/utils.ts
CHANGED
|
@@ -1,29 +1,108 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
|
|
3
4
|
import yaml from "js-yaml";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
5
|
+
import { OpenAPIV3 } from "openapi-types";
|
|
6
|
+
import GithubSlugger from 'github-slugger';
|
|
7
|
+
import $refParser, { ParserOptions } from "@apidevtools/json-schema-ref-parser";
|
|
8
|
+
|
|
9
|
+
import { ReferenceType } from "@xyd-js/uniform";
|
|
10
|
+
|
|
11
|
+
export function slug(str: string): string {
|
|
12
|
+
const slugger = new GithubSlugger();
|
|
13
|
+
return slugger.slug(str);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// deferencedOpenAPI reads an OpenAPI spec file and returns a dereferenced OpenAPIV3.Document
|
|
17
|
+
// dereferenced means that all $ref references are resolved automatically
|
|
18
|
+
export async function deferencedOpenAPI(openApiPath: string) {
|
|
19
|
+
const openApiSpec = await readOpenApiSpec(openApiPath);
|
|
20
|
+
if (!openApiSpec) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
|
|
26
|
+
const remoteOasPath = openApiPath.startsWith('http://') || openApiPath.startsWith('https://') ? true : false
|
|
27
|
+
|
|
28
|
+
const options: ParserOptions = {
|
|
29
|
+
dereference: {
|
|
30
|
+
onDereference(path: any, value: any, parent: any) {
|
|
31
|
+
if (value && typeof value === 'object') {
|
|
32
|
+
value.__UNSAFE_refPath = () => path
|
|
33
|
+
}
|
|
34
|
+
if (parent && typeof parent === 'object') {
|
|
35
|
+
parent.__UNSAFE_refPath = () => path
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} as ParserOptions;
|
|
6
40
|
|
|
7
|
-
|
|
41
|
+
if (remoteOasPath) {
|
|
42
|
+
if (!options.resolve) {
|
|
43
|
+
options.resolve = {}
|
|
44
|
+
}
|
|
8
45
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
46
|
+
options.resolve.file = {
|
|
47
|
+
read: async (file: any) => {
|
|
48
|
+
// 1) Convert absolute local path back into a repo-relative path:
|
|
49
|
+
// "/Users/.../docs/foo.md" → "docs/foo.md"
|
|
50
|
+
let rel = path.relative(cwd, file.url);
|
|
51
|
+
rel = rel.split(path.sep).join('/');
|
|
52
|
+
|
|
53
|
+
// 2) Resolve against your GitHub raw URL:
|
|
54
|
+
const absoluteUrl = new URL(rel, openApiPath).href;
|
|
55
|
+
// → "https://raw.githubusercontent.com/.../docs/foo.md"
|
|
56
|
+
|
|
57
|
+
// 3) Fetch it:
|
|
58
|
+
const res = await fetch(absoluteUrl);
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
throw new Error(`Failed to fetch ${absoluteUrl}: ${res.status}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let content;
|
|
64
|
+
if (file.extension === '.json' || file.extension === '.yaml' || file.extension === '.yml') {
|
|
65
|
+
// If the file is JSON or YAML, return the parsed content
|
|
66
|
+
if (file.extension === '.json') {
|
|
67
|
+
content = await res.json();
|
|
68
|
+
} else {
|
|
69
|
+
content = yaml.load(await res.text());
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
content = await res.text(); // hand back the Markdown
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return content;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
13
79
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
18
|
-
.join('');
|
|
80
|
+
await $refParser.dereference(openApiSpec, options);
|
|
81
|
+
|
|
82
|
+
return openApiSpec as OpenAPIV3.Document
|
|
19
83
|
}
|
|
20
84
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
25
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
85
|
+
// readOpenApiSpec reads an OpenAPI spec file or URL and returns the content
|
|
86
|
+
async function readOpenApiSpec(filePath: string) {
|
|
87
|
+
let content: string;
|
|
26
88
|
|
|
89
|
+
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
|
|
90
|
+
const response = await fetch(filePath);
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new Error(`Failed to fetch OpenAPI spec from URL: ${response.statusText}`);
|
|
93
|
+
}
|
|
94
|
+
content = await response.text();
|
|
95
|
+
} else {
|
|
96
|
+
try {
|
|
97
|
+
await fs.access(filePath);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.log(`⚠️ "${filePath}" is defined in the docs.json navigation but the file does not exist.`)
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
content = await fs.readFile(filePath, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
27
106
|
if (ext === '.yaml' || ext === '.yml') {
|
|
28
107
|
return yaml.load(content);
|
|
29
108
|
} else if (ext === '.json') {
|
|
@@ -33,17 +112,6 @@ function readOpenApiSpec(filePath: string) {
|
|
|
33
112
|
}
|
|
34
113
|
}
|
|
35
114
|
|
|
36
|
-
// deferencedOpenAPI reads an OpenAPI spec file and returns a dereferenced OpenAPIV3.Document
|
|
37
|
-
// dereferenced means that all $ref references are resolved automatically
|
|
38
|
-
export async function deferencedOpenAPI(openApiPath: string) {
|
|
39
|
-
const openApiSpec = readOpenApiSpec(openApiPath);
|
|
40
|
-
|
|
41
|
-
//@ts-ignore TODO: fix ts
|
|
42
|
-
await $refParser.dereference(openApiSpec);
|
|
43
|
-
|
|
44
|
-
return openApiSpec as OpenAPIV3.Document
|
|
45
|
-
}
|
|
46
|
-
|
|
47
115
|
// httpMethodToUniformMethod converts an HTTP method to a uniform ReferenceType
|
|
48
116
|
export function httpMethodToUniformMethod(method: string): ReferenceType | null {
|
|
49
117
|
switch (method) {
|
|
@@ -57,69 +125,14 @@ export function httpMethodToUniformMethod(method: string): ReferenceType | null
|
|
|
57
125
|
return ReferenceType.REST_HTTP_POST
|
|
58
126
|
case 'delete':
|
|
59
127
|
return ReferenceType.REST_HTTP_DELETE
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
128
|
+
case 'options':
|
|
129
|
+
return ReferenceType.REST_HTTP_OPTIONS
|
|
130
|
+
case 'head':
|
|
131
|
+
return ReferenceType.REST_HTTP_HEAD
|
|
132
|
+
case 'trace':
|
|
133
|
+
return ReferenceType.REST_HTTP_TRACE
|
|
66
134
|
default:
|
|
67
135
|
return null
|
|
68
136
|
}
|
|
69
137
|
}
|
|
70
138
|
|
|
71
|
-
// schemaToRequestBody generates a request body from an OpenAPI schema
|
|
72
|
-
function schemaToRequestBody(schema: OpenAPIV3.SchemaObject): string {
|
|
73
|
-
const requestBody: any = {};
|
|
74
|
-
|
|
75
|
-
if (schema.type === 'object' && schema.properties) {
|
|
76
|
-
for (const [key, value] of Object.entries(schema.properties)) {
|
|
77
|
-
if ((value as OpenAPIV3.SchemaObject).default !== undefined) {
|
|
78
|
-
requestBody[key] = (value as OpenAPIV3.SchemaObject).default;
|
|
79
|
-
} else {
|
|
80
|
-
requestBody[key] = null; // or some placeholder value
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return JSON.stringify(requestBody);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// generateRequestInitFromOpenAPIObject generates a RequestInit object from an OpenAPI object
|
|
89
|
-
export function generateRequestInitFromOapOperation(
|
|
90
|
-
urlPath: string,
|
|
91
|
-
operation: OpenAPIV3.OperationObject
|
|
92
|
-
): { url: string, reqInit: RequestInit } {
|
|
93
|
-
const reqInit: RequestInit = {}
|
|
94
|
-
let queryParams = '';
|
|
95
|
-
|
|
96
|
-
if (operation.parameters) {
|
|
97
|
-
const parameters = operation.parameters as OpenAPIV3.ParameterObject[]
|
|
98
|
-
|
|
99
|
-
const params = new URLSearchParams(
|
|
100
|
-
Object.entries(parameters).map(([key, value]) => [key, String(value)])
|
|
101
|
-
).toString();
|
|
102
|
-
|
|
103
|
-
queryParams += `?${params}`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (operation.requestBody) {
|
|
107
|
-
const reqBody = operation.requestBody as OpenAPIV3.RequestBodyObject;
|
|
108
|
-
const contentType = Object.keys(reqBody.content || {})[0];
|
|
109
|
-
|
|
110
|
-
if (contentType === "application/json") {
|
|
111
|
-
const schema = reqBody.content['application/json'].schema as OpenAPIV3.SchemaObject
|
|
112
|
-
|
|
113
|
-
reqInit.body = schemaToRequestBody(schema);
|
|
114
|
-
reqInit.headers = {
|
|
115
|
-
'Content-Type': 'application/json',
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
url: `${urlPath}${queryParams}`,
|
|
122
|
-
reqInit,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {OpenAPIV3} from "openapi-types";
|
|
2
|
+
import {XDocs} from "./types";
|
|
3
|
+
|
|
4
|
+
export function xDocsLanguages(oasDoc: OpenAPIV3.Document): string[] | null {
|
|
5
|
+
const xDocs = getXDocs(oasDoc);
|
|
6
|
+
if (!xDocs) {
|
|
7
|
+
return null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return xDocs?.codeLanguages ?? null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getXDocs(oasDoc: OpenAPIV3.Document): XDocs | null {
|
|
14
|
+
if (!("x-docs" in oasDoc)) {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
return oasDoc["x-docs"] as XDocs | null
|
|
18
|
+
}
|