openapi-docs-mcp 0.1.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.
- package/LICENSE +21 -0
- package/README.md +253 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +92 -0
- package/dist/cli.js.map +1 -0
- package/dist/formatter/operation.d.ts +4 -0
- package/dist/formatter/operation.d.ts.map +1 -0
- package/dist/formatter/operation.js +135 -0
- package/dist/formatter/operation.js.map +1 -0
- package/dist/formatter/schema.d.ts +7 -0
- package/dist/formatter/schema.d.ts.map +1 -0
- package/dist/formatter/schema.js +142 -0
- package/dist/formatter/schema.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +89 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/openapi/loader.d.ts +9 -0
- package/dist/openapi/loader.d.ts.map +1 -0
- package/dist/openapi/loader.js +125 -0
- package/dist/openapi/loader.js.map +1 -0
- package/dist/openapi/operations.d.ts +13 -0
- package/dist/openapi/operations.d.ts.map +1 -0
- package/dist/openapi/operations.js +71 -0
- package/dist/openapi/operations.js.map +1 -0
- package/dist/search/search.d.ts +4 -0
- package/dist/search/search.d.ts.map +1 -0
- package/dist/search/search.js +117 -0
- package/dist/search/search.js.map +1 -0
- package/dist/service.d.ts +21 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +40 -0
- package/dist/service.js.map +1 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { asObject, asString } from "../openapi/operations.js";
|
|
2
|
+
export function formatSchema(document, schema, options = {}) {
|
|
3
|
+
return formatSchemaNode(document, schema, {
|
|
4
|
+
maxDepth: options.maxDepth ?? 5,
|
|
5
|
+
maxProperties: options.maxProperties ?? 100,
|
|
6
|
+
}, { depth: 0, references: new Set() });
|
|
7
|
+
}
|
|
8
|
+
export function listSchemaNames(document) {
|
|
9
|
+
const root = document;
|
|
10
|
+
const components = asObject(root.components);
|
|
11
|
+
const schemas = asObject(components?.schemas) ?? asObject(root.definitions);
|
|
12
|
+
return schemas ? Object.keys(schemas).sort((a, b) => a.localeCompare(b)) : [];
|
|
13
|
+
}
|
|
14
|
+
export function findSchema(document, name) {
|
|
15
|
+
const root = document;
|
|
16
|
+
const components = asObject(root.components);
|
|
17
|
+
const schemas = asObject(components?.schemas) ?? asObject(root.definitions);
|
|
18
|
+
return schemas?.[name];
|
|
19
|
+
}
|
|
20
|
+
function formatSchemaNode(document, rawSchema, options, state) {
|
|
21
|
+
if (typeof rawSchema === "boolean")
|
|
22
|
+
return rawSchema;
|
|
23
|
+
const schema = asObject(rawSchema);
|
|
24
|
+
if (!schema)
|
|
25
|
+
return rawSchema ?? null;
|
|
26
|
+
const ref = asString(schema.$ref);
|
|
27
|
+
if (ref) {
|
|
28
|
+
const refName = decodeURIComponent(ref.split("/").at(-1) ?? ref);
|
|
29
|
+
if (state.references.has(ref)) {
|
|
30
|
+
return { $ref: ref, name: refName, circular: true };
|
|
31
|
+
}
|
|
32
|
+
if (state.depth >= options.maxDepth) {
|
|
33
|
+
return { $ref: ref, name: refName, truncated: "maxDepth" };
|
|
34
|
+
}
|
|
35
|
+
const resolved = resolveLocalReference(document, ref);
|
|
36
|
+
if (resolved === undefined)
|
|
37
|
+
return { $ref: ref, name: refName, unresolved: true };
|
|
38
|
+
const references = new Set(state.references);
|
|
39
|
+
references.add(ref);
|
|
40
|
+
return {
|
|
41
|
+
$ref: ref,
|
|
42
|
+
name: refName,
|
|
43
|
+
schema: formatSchemaNode(document, resolved, options, {
|
|
44
|
+
depth: state.depth + 1,
|
|
45
|
+
references,
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (state.depth >= options.maxDepth) {
|
|
50
|
+
return { type: schema.type ?? "object", truncated: "maxDepth" };
|
|
51
|
+
}
|
|
52
|
+
const output = {};
|
|
53
|
+
copyKnownValues(schema, output, [
|
|
54
|
+
"type",
|
|
55
|
+
"format",
|
|
56
|
+
"title",
|
|
57
|
+
"description",
|
|
58
|
+
"nullable",
|
|
59
|
+
"readOnly",
|
|
60
|
+
"writeOnly",
|
|
61
|
+
"deprecated",
|
|
62
|
+
"default",
|
|
63
|
+
"example",
|
|
64
|
+
"examples",
|
|
65
|
+
"enum",
|
|
66
|
+
"const",
|
|
67
|
+
"minimum",
|
|
68
|
+
"maximum",
|
|
69
|
+
"minLength",
|
|
70
|
+
"maxLength",
|
|
71
|
+
"pattern",
|
|
72
|
+
]);
|
|
73
|
+
if (Array.isArray(schema.required))
|
|
74
|
+
output.required = schema.required;
|
|
75
|
+
const properties = asObject(schema.properties);
|
|
76
|
+
if (properties) {
|
|
77
|
+
const entries = Object.entries(properties);
|
|
78
|
+
output.properties = Object.fromEntries(entries.slice(0, options.maxProperties).map(([name, value]) => [
|
|
79
|
+
name,
|
|
80
|
+
formatSchemaNode(document, value, options, {
|
|
81
|
+
depth: state.depth + 1,
|
|
82
|
+
references: new Set(state.references),
|
|
83
|
+
}),
|
|
84
|
+
]));
|
|
85
|
+
if (entries.length > options.maxProperties) {
|
|
86
|
+
output.truncatedProperties = entries.length - options.maxProperties;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (schema.items !== undefined) {
|
|
90
|
+
output.items = formatSchemaNode(document, schema.items, options, {
|
|
91
|
+
depth: state.depth + 1,
|
|
92
|
+
references: new Set(state.references),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (schema.additionalProperties !== undefined) {
|
|
96
|
+
output.additionalProperties =
|
|
97
|
+
typeof schema.additionalProperties === "boolean"
|
|
98
|
+
? schema.additionalProperties
|
|
99
|
+
: formatSchemaNode(document, schema.additionalProperties, options, {
|
|
100
|
+
depth: state.depth + 1,
|
|
101
|
+
references: new Set(state.references),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
for (const keyword of ["allOf", "oneOf", "anyOf"]) {
|
|
105
|
+
const variants = schema[keyword];
|
|
106
|
+
if (Array.isArray(variants)) {
|
|
107
|
+
output[keyword] = variants.map((variant) => formatSchemaNode(document, variant, options, {
|
|
108
|
+
depth: state.depth + 1,
|
|
109
|
+
references: new Set(state.references),
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (schema.not !== undefined) {
|
|
114
|
+
output.not = formatSchemaNode(document, schema.not, options, {
|
|
115
|
+
depth: state.depth + 1,
|
|
116
|
+
references: new Set(state.references),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return output;
|
|
120
|
+
}
|
|
121
|
+
export function resolveLocalReference(document, ref) {
|
|
122
|
+
if (!ref.startsWith("#/"))
|
|
123
|
+
return undefined;
|
|
124
|
+
const parts = ref
|
|
125
|
+
.slice(2)
|
|
126
|
+
.split("/")
|
|
127
|
+
.map((part) => part.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
128
|
+
let current = document;
|
|
129
|
+
for (const part of parts) {
|
|
130
|
+
current = asObject(current)?.[part];
|
|
131
|
+
if (current === undefined)
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return current;
|
|
135
|
+
}
|
|
136
|
+
function copyKnownValues(source, target, keys) {
|
|
137
|
+
for (const key of keys) {
|
|
138
|
+
if (source[key] !== undefined)
|
|
139
|
+
target[key] = source[key];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/formatter/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createOpenApiContext, loadOpenApi } from "./openapi/loader.js";
|
|
2
|
+
export { createMcpServer } from "./mcp/server.js";
|
|
3
|
+
export { OpenApiService } from "./service.js";
|
|
4
|
+
export { searchOperations } from "./search/search.js";
|
|
5
|
+
export type { OpenApiContext, OperationEntry, SearchOptions, SearchResult, } from "./types.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAK/C,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAmGlE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const depthSchema = z.number().int().min(1).max(12).default(5);
|
|
4
|
+
export function createMcpServer(service) {
|
|
5
|
+
const server = new McpServer({
|
|
6
|
+
name: "openapi-docs-mcp",
|
|
7
|
+
version: "0.1.0",
|
|
8
|
+
});
|
|
9
|
+
server.registerTool("search_api", {
|
|
10
|
+
title: "Search OpenAPI operations",
|
|
11
|
+
description: "Search API operations by natural-language keywords, path, tag, description, or operationId. Returns ranked lightweight candidates; call get_api for full details.",
|
|
12
|
+
inputSchema: z.object({
|
|
13
|
+
query: z.string().default("").describe("Keywords such as 异常分页列表 or create user"),
|
|
14
|
+
method: z.string().optional().describe("Optional HTTP method filter"),
|
|
15
|
+
tag: z.string().optional().describe("Optional exact tag filter"),
|
|
16
|
+
limit: z.number().int().min(1).max(50).default(10),
|
|
17
|
+
}),
|
|
18
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
19
|
+
}, async ({ query, method, tag, limit }) => success({
|
|
20
|
+
results: service.search({
|
|
21
|
+
query,
|
|
22
|
+
limit,
|
|
23
|
+
...(method !== undefined ? { method } : {}),
|
|
24
|
+
...(tag !== undefined ? { tag } : {}),
|
|
25
|
+
}),
|
|
26
|
+
totalOperations: service.context.operations.length,
|
|
27
|
+
}));
|
|
28
|
+
server.registerTool("get_api", {
|
|
29
|
+
title: "Get one OpenAPI operation",
|
|
30
|
+
description: "Get the request parameters, expanded request body, responses, tags, and metadata for one operation. Prefer the id returned by search_api.",
|
|
31
|
+
inputSchema: z
|
|
32
|
+
.object({
|
|
33
|
+
id: z.string().optional().describe("Stable id returned by search_api"),
|
|
34
|
+
path: z.string().optional().describe("Exact OpenAPI path"),
|
|
35
|
+
method: z.string().optional().describe("HTTP method used with path"),
|
|
36
|
+
maxDepth: depthSchema,
|
|
37
|
+
})
|
|
38
|
+
.refine((value) => Boolean(value.id || value.path), {
|
|
39
|
+
message: "Provide id or path",
|
|
40
|
+
}),
|
|
41
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
42
|
+
}, async ({ id, path, method, maxDepth }) => {
|
|
43
|
+
const api = service.getApi({
|
|
44
|
+
...(id !== undefined ? { id } : {}),
|
|
45
|
+
...(path !== undefined ? { path } : {}),
|
|
46
|
+
...(method !== undefined ? { method } : {}),
|
|
47
|
+
}, { maxDepth });
|
|
48
|
+
return api
|
|
49
|
+
? success(api)
|
|
50
|
+
: failure("Operation not found. Use search_api to obtain a valid id.");
|
|
51
|
+
});
|
|
52
|
+
server.registerTool("get_schema", {
|
|
53
|
+
title: "Get one OpenAPI schema",
|
|
54
|
+
description: "Get a named component schema with bounded local $ref expansion and circular-reference protection.",
|
|
55
|
+
inputSchema: z.object({
|
|
56
|
+
name: z.string().min(1).describe("Exact component schema name"),
|
|
57
|
+
maxDepth: depthSchema,
|
|
58
|
+
}),
|
|
59
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
60
|
+
}, async ({ name, maxDepth }) => {
|
|
61
|
+
const schema = service.getSchema(name, { maxDepth });
|
|
62
|
+
return schema
|
|
63
|
+
? success(schema)
|
|
64
|
+
: failure(`Schema not found: ${name}`, {
|
|
65
|
+
availableSchemas: service.listSchemas().slice(0, 100),
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
server.registerTool("list_groups", {
|
|
69
|
+
title: "List OpenAPI groups",
|
|
70
|
+
description: "List tags with their operation counts.",
|
|
71
|
+
inputSchema: z.object({}),
|
|
72
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
73
|
+
}, async () => success({ groups: service.listGroups() }));
|
|
74
|
+
return server;
|
|
75
|
+
}
|
|
76
|
+
function success(data) {
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
79
|
+
structuredContent: data,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function failure(message, details = {}) {
|
|
83
|
+
return {
|
|
84
|
+
isError: true,
|
|
85
|
+
content: [{ type: "text", text: message }],
|
|
86
|
+
structuredContent: { error: message, ...details },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { JsonObject, OpenApiContext } from "../types.js";
|
|
2
|
+
export interface LoadOpenApiOptions {
|
|
3
|
+
timeoutMs?: number;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
validationMode?: "compatible" | "strict";
|
|
6
|
+
}
|
|
7
|
+
export declare function loadOpenApi(source: string, options?: LoadOpenApiOptions): Promise<OpenApiContext>;
|
|
8
|
+
export declare function createOpenApiContext(input: string | JsonObject, source?: string, options?: Pick<LoadOpenApiOptions, "validationMode">): Promise<OpenApiContext>;
|
|
9
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/openapi/loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAK9D,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;CAC1C;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC,CAMzB;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,MAAM,SAAW,EACjB,OAAO,GAAE,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAM,GACvD,OAAO,CAAC,cAAc,CAAC,CAgCzB"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { validate } from "@scalar/openapi-parser";
|
|
4
|
+
import { extractOperations } from "./operations.js";
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
6
|
+
export async function loadOpenApi(source, options = {}) {
|
|
7
|
+
const content = isHttpUrl(source)
|
|
8
|
+
? await fetchDocument(source, options)
|
|
9
|
+
: await readFile(resolve(source), "utf8");
|
|
10
|
+
return createOpenApiContext(content, source, options);
|
|
11
|
+
}
|
|
12
|
+
export async function createOpenApiContext(input, source = "memory", options = {}) {
|
|
13
|
+
const result = await validate(input);
|
|
14
|
+
if (!result.valid) {
|
|
15
|
+
const specification = result.specification;
|
|
16
|
+
const compatibleErrors = specification
|
|
17
|
+
? result.errors.filter((error) => isCompatibleUnicodeReferenceError(error, specification))
|
|
18
|
+
: [];
|
|
19
|
+
const fatalErrors = result.errors.filter((error) => !compatibleErrors.includes(error));
|
|
20
|
+
if (options.validationMode === "strict" || !specification || fatalErrors.length > 0) {
|
|
21
|
+
throw new Error(`Invalid OpenAPI document: ${formatValidationErrors(result.errors)}`);
|
|
22
|
+
}
|
|
23
|
+
const document = specification;
|
|
24
|
+
assertDocumentShape(document);
|
|
25
|
+
return {
|
|
26
|
+
document,
|
|
27
|
+
source,
|
|
28
|
+
version: result.version ?? getDocumentVersion(document),
|
|
29
|
+
operations: extractOperations(document),
|
|
30
|
+
validationWarnings: summarizeWarnings(compatibleErrors),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const document = result.specification;
|
|
34
|
+
return {
|
|
35
|
+
document,
|
|
36
|
+
source,
|
|
37
|
+
version: result.version,
|
|
38
|
+
operations: extractOperations(document),
|
|
39
|
+
validationWarnings: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function isHttpUrl(value) {
|
|
43
|
+
return /^https?:\/\//i.test(value);
|
|
44
|
+
}
|
|
45
|
+
async function fetchDocument(url, options) {
|
|
46
|
+
const response = await fetch(url, {
|
|
47
|
+
...(options.headers ? { headers: options.headers } : {}),
|
|
48
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`Failed to load OpenAPI document: ${response.status} ${response.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
return response.text();
|
|
54
|
+
}
|
|
55
|
+
function isCompatibleUnicodeReferenceError(error, document) {
|
|
56
|
+
const messageMatches = error.message === "Property $ref is not expected to be here" ||
|
|
57
|
+
error.message.includes("contains non-ASCII characters");
|
|
58
|
+
if (!messageMatches || typeof error.path !== "string")
|
|
59
|
+
return false;
|
|
60
|
+
const value = resolveJsonPointer(document, error.path);
|
|
61
|
+
const ref = typeof value === "string"
|
|
62
|
+
? value
|
|
63
|
+
: value && typeof value === "object"
|
|
64
|
+
? value.$ref
|
|
65
|
+
: undefined;
|
|
66
|
+
if (typeof ref !== "string" || !ref.startsWith("#/") || !/[^\x00-\x7F]/.test(ref)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return resolveJsonPointer(document, ref.slice(1)) !== undefined;
|
|
70
|
+
}
|
|
71
|
+
function resolveJsonPointer(root, pointer) {
|
|
72
|
+
if (pointer === "")
|
|
73
|
+
return root;
|
|
74
|
+
if (!pointer.startsWith("/"))
|
|
75
|
+
return undefined;
|
|
76
|
+
let current = root;
|
|
77
|
+
for (const rawPart of pointer.slice(1).split("/")) {
|
|
78
|
+
const part = decodeURIComponent(rawPart.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
79
|
+
if (!current || typeof current !== "object" || Array.isArray(current))
|
|
80
|
+
return undefined;
|
|
81
|
+
current = current[part];
|
|
82
|
+
}
|
|
83
|
+
return current;
|
|
84
|
+
}
|
|
85
|
+
function formatValidationErrors(errors) {
|
|
86
|
+
return errors
|
|
87
|
+
.slice(0, 20)
|
|
88
|
+
.map((error) => {
|
|
89
|
+
const path = Array.isArray(error.path)
|
|
90
|
+
? error.path.join(".")
|
|
91
|
+
: typeof error.path === "string"
|
|
92
|
+
? error.path
|
|
93
|
+
: "document";
|
|
94
|
+
return `${path}: ${error.message}`;
|
|
95
|
+
})
|
|
96
|
+
.join("; ");
|
|
97
|
+
}
|
|
98
|
+
function summarizeWarnings(errors) {
|
|
99
|
+
const counts = new Map();
|
|
100
|
+
for (const error of errors) {
|
|
101
|
+
const message = error.message.includes("contains non-ASCII characters")
|
|
102
|
+
? "Local $ref contains non-ASCII characters"
|
|
103
|
+
: error.message;
|
|
104
|
+
counts.set(message, (counts.get(message) ?? 0) + 1);
|
|
105
|
+
}
|
|
106
|
+
return [...counts.entries()].map(([message, count]) => ({ message, count }));
|
|
107
|
+
}
|
|
108
|
+
function assertDocumentShape(document) {
|
|
109
|
+
const root = document;
|
|
110
|
+
if (typeof root.openapi !== "string" && typeof root.swagger !== "string") {
|
|
111
|
+
throw new Error("Invalid OpenAPI document: missing openapi or swagger version");
|
|
112
|
+
}
|
|
113
|
+
if (!root.info || typeof root.info !== "object") {
|
|
114
|
+
throw new Error("Invalid OpenAPI document: missing info object");
|
|
115
|
+
}
|
|
116
|
+
if (!root.paths || typeof root.paths !== "object") {
|
|
117
|
+
throw new Error("Invalid OpenAPI document: missing paths object");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function getDocumentVersion(document) {
|
|
121
|
+
const root = document;
|
|
122
|
+
const value = typeof root.openapi === "string" ? root.openapi : root.swagger;
|
|
123
|
+
return typeof value === "string" ? value : "unknown";
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/openapi/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OpenAPI } from "@scalar/openapi-types";
|
|
2
|
+
import type { HttpMethod, JsonObject, OperationEntry } from "../types.js";
|
|
3
|
+
export declare const HTTP_METHODS: readonly HttpMethod[];
|
|
4
|
+
export declare function extractOperations(document: OpenAPI.Document): OperationEntry[];
|
|
5
|
+
export declare function findOperation(operations: OperationEntry[], input: {
|
|
6
|
+
id?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
method?: string;
|
|
9
|
+
}): OperationEntry | undefined;
|
|
10
|
+
export declare function asObject(value: unknown): JsonObject | undefined;
|
|
11
|
+
export declare function asString(value: unknown): string | undefined;
|
|
12
|
+
export declare function asStringArray(value: unknown): string[];
|
|
13
|
+
//# sourceMappingURL=operations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../src/openapi/operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE1E,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAU7C,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,GAAG,cAAc,EAAE,CAmC9E;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,cAAc,EAAE,EAC5B,KAAK,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,cAAc,GAAG,SAAS,CAc5B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAI/D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE3D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAEtD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const HTTP_METHODS = [
|
|
2
|
+
"get",
|
|
3
|
+
"put",
|
|
4
|
+
"post",
|
|
5
|
+
"delete",
|
|
6
|
+
"options",
|
|
7
|
+
"head",
|
|
8
|
+
"patch",
|
|
9
|
+
"trace",
|
|
10
|
+
"query",
|
|
11
|
+
];
|
|
12
|
+
export function extractOperations(document) {
|
|
13
|
+
const root = document;
|
|
14
|
+
const paths = asObject(root.paths);
|
|
15
|
+
if (!paths)
|
|
16
|
+
return [];
|
|
17
|
+
const operations = [];
|
|
18
|
+
for (const [path, rawPathItem] of Object.entries(paths)) {
|
|
19
|
+
const pathItem = asObject(rawPathItem);
|
|
20
|
+
if (!pathItem)
|
|
21
|
+
continue;
|
|
22
|
+
for (const method of HTTP_METHODS) {
|
|
23
|
+
const operation = asObject(pathItem[method]);
|
|
24
|
+
if (!operation)
|
|
25
|
+
continue;
|
|
26
|
+
const operationId = asString(operation.operationId);
|
|
27
|
+
const entry = {
|
|
28
|
+
id: operationId || `${method.toUpperCase()} ${path}`,
|
|
29
|
+
method,
|
|
30
|
+
path,
|
|
31
|
+
tags: asStringArray(operation.tags),
|
|
32
|
+
operation,
|
|
33
|
+
pathItem,
|
|
34
|
+
};
|
|
35
|
+
const summary = asString(operation.summary);
|
|
36
|
+
const description = asString(operation.description);
|
|
37
|
+
if (summary)
|
|
38
|
+
entry.summary = summary;
|
|
39
|
+
if (description)
|
|
40
|
+
entry.description = description;
|
|
41
|
+
if (operationId)
|
|
42
|
+
entry.operationId = operationId;
|
|
43
|
+
operations.push(entry);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return operations.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
|
|
47
|
+
}
|
|
48
|
+
export function findOperation(operations, input) {
|
|
49
|
+
if (input.id) {
|
|
50
|
+
const exact = operations.find((entry) => entry.id === input.id);
|
|
51
|
+
if (exact)
|
|
52
|
+
return exact;
|
|
53
|
+
}
|
|
54
|
+
if (input.path) {
|
|
55
|
+
const method = input.method?.toLowerCase();
|
|
56
|
+
return operations.find((entry) => entry.path === input.path && (!method || entry.method === method));
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
export function asObject(value) {
|
|
61
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
62
|
+
? value
|
|
63
|
+
: undefined;
|
|
64
|
+
}
|
|
65
|
+
export function asString(value) {
|
|
66
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
67
|
+
}
|
|
68
|
+
export function asStringArray(value) {
|
|
69
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.js","sourceRoot":"","sources":["../../src/openapi/operations.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,YAAY,GAA0B;IACjD,KAAK;IACL,KAAK;IACL,MAAM;IACN,QAAQ;IACR,SAAS;IACT,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;CACR,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,QAA0B;IAC1D,MAAM,IAAI,GAAG,QAAiC,CAAC;IAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7C,IAAI,CAAC,SAAS;gBAAE,SAAS;YAEzB,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YACpD,MAAM,KAAK,GAAmB;gBAC5B,EAAE,EAAE,WAAW,IAAI,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,IAAI,EAAE;gBACpD,MAAM;gBACN,IAAI;gBACJ,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC;gBACnC,SAAS;gBACT,QAAQ;aACT,CAAC;YACF,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC5C,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YACpD,IAAI,OAAO;gBAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;YACrC,IAAI,WAAW;gBAAE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;YACjD,IAAI,WAAW;gBAAE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;YACjD,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC9B,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CACjE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,UAA4B,EAC5B,KAAsD;IAEtD,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC;QAChE,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;QAC3C,OAAO,UAAU,CAAC,IAAI,CACpB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAC7E,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAAoB;QACvB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACtG,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { OperationEntry, SearchOptions, SearchResult } from "../types.js";
|
|
2
|
+
export declare function searchOperations(operations: OperationEntry[], options: SearchOptions): SearchResult[];
|
|
3
|
+
export declare function normalize(value: string): string;
|
|
4
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/search/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU/E,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,EAAE,EAC5B,OAAO,EAAE,aAAa,GACrB,YAAY,EAAE,CAahB;AAmED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/C"}
|