@askills/openapi-explorer-cli 0.0.3 → 0.0.4

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.
@@ -1,165 +0,0 @@
1
- import type {
2
- OpenAPISpec,
3
- EndpointSummary,
4
- SchemaSummary,
5
- SearchResult,
6
- } from "../types.ts";
7
-
8
- const HTTP_METHODS = [
9
- "get",
10
- "post",
11
- "put",
12
- "patch",
13
- "delete",
14
- "options",
15
- "head",
16
- ] as const;
17
- const METHOD_PRIORITY: Record<string, number> = {
18
- get: 0,
19
- post: 1,
20
- put: 2,
21
- patch: 3,
22
- delete: 4,
23
- options: 5,
24
- head: 6,
25
- };
26
-
27
- export function getEndpoints(
28
- spec: OpenAPISpec,
29
- tag?: string,
30
- ): EndpointSummary[] {
31
- const eps: EndpointSummary[] = [];
32
- for (const [path, pathItem] of Object.entries(spec.paths)) {
33
- for (const method of HTTP_METHODS) {
34
- const op = pathItem[method];
35
- if (!op) continue;
36
- if (tag && (!op.tags || !op.tags.includes(tag))) continue;
37
- eps.push({
38
- path,
39
- method,
40
- summary:
41
- op.summary || op.operationId || `${method.toUpperCase()} ${path}`,
42
- operationId: op.operationId,
43
- tags: op.tags || [],
44
- deprecated: op.deprecated || false,
45
- });
46
- }
47
- }
48
- eps.sort((a, b) => {
49
- if (a.path !== b.path) return a.path.localeCompare(b.path);
50
- return (
51
- (METHOD_PRIORITY[a.method] ?? 99) - (METHOD_PRIORITY[b.method] ?? 99)
52
- );
53
- });
54
- return eps;
55
- }
56
-
57
- export function getTags(spec: OpenAPISpec): { name: string; count: number }[] {
58
- const counts = new Map<string, number>();
59
- for (const pathItem of Object.values(spec.paths)) {
60
- for (const method of HTTP_METHODS) {
61
- const op = pathItem[method];
62
- if (!op?.tags) continue;
63
- for (const tag of op.tags) counts.set(tag, (counts.get(tag) || 0) + 1);
64
- }
65
- }
66
- return Array.from(counts.entries())
67
- .map(([name, count]) => ({ name, count }))
68
- .sort((a, b) => a.name.localeCompare(b.name));
69
- }
70
-
71
- export function getSchemas(spec: OpenAPISpec): SchemaSummary[] {
72
- const schemas = spec.components?.schemas || spec.definitions || {};
73
- return Object.entries(schemas)
74
- .map(([name, s]) => ({
75
- name,
76
- type: (s as any).type || "object",
77
- description: (s as any).description || (s as any).title,
78
- properties: (s as any).properties
79
- ? Object.keys((s as any).properties).length
80
- : 0,
81
- }))
82
- .sort((a, b) => a.name.localeCompare(b.name));
83
- }
84
-
85
- export function getSchemaDetail(spec: OpenAPISpec, name: string): unknown {
86
- return (spec.components?.schemas || spec.definitions || {})[name] || null;
87
- }
88
-
89
- export function getPathParams(path: string): string[] {
90
- const m = path.match(/\{(\w+)\}/g);
91
- return m ? m.map((s) => s.slice(1, -1)) : [];
92
- }
93
-
94
- export function searchSpec(spec: OpenAPISpec, query: string): SearchResult[] {
95
- const results: SearchResult[] = [];
96
- const lower = query.toLowerCase();
97
-
98
- for (const [path, pi] of Object.entries(spec.paths)) {
99
- for (const method of HTTP_METHODS) {
100
- const op = pi[method];
101
- if (!op) continue;
102
- const text = [
103
- path,
104
- method,
105
- op.summary,
106
- op.operationId,
107
- op.description,
108
- ...(op.tags || []),
109
- ]
110
- .filter(Boolean)
111
- .join(" ")
112
- .toLowerCase();
113
- if (text.includes(lower)) {
114
- results.push({
115
- type: "endpoint",
116
- path,
117
- method,
118
- match: `${method.toUpperCase()} ${path}`,
119
- summary: op.summary || op.operationId,
120
- });
121
- }
122
- }
123
- }
124
-
125
- const schemas = spec.components?.schemas || spec.definitions || {};
126
- for (const [name, s] of Object.entries(schemas)) {
127
- const sAny = s as any;
128
- if (
129
- [name, sAny.description, sAny.title]
130
- .filter(Boolean)
131
- .join(" ")
132
- .toLowerCase()
133
- .includes(lower)
134
- ) {
135
- results.push({
136
- type: "schema",
137
- schemaName: name,
138
- match: `Schema: ${name}`,
139
- summary: sAny.description,
140
- });
141
- }
142
- if (sAny.properties) {
143
- for (const [pn, ps] of Object.entries(sAny.properties)) {
144
- const psAny = ps as any;
145
- if (
146
- [pn, psAny.description]
147
- .filter(Boolean)
148
- .join(" ")
149
- .toLowerCase()
150
- .includes(lower)
151
- ) {
152
- results.push({
153
- type: "property",
154
- schemaName: name,
155
- propertyName: pn,
156
- match: `Schema ${name} > ${pn}`,
157
- summary: psAny.description,
158
- });
159
- }
160
- }
161
- }
162
- }
163
-
164
- return results;
165
- }
@@ -1,70 +0,0 @@
1
- import type { OpenAPISpec } from "../types.ts";
2
-
3
- export function resolveRef(spec: OpenAPISpec, ref: string): unknown {
4
- const parts = ref.replace(/^#\//, "").split("/");
5
- let cur: unknown = spec as unknown as Record<string, unknown>;
6
- for (const p of parts) {
7
- if (
8
- cur &&
9
- typeof cur === "object" &&
10
- p in (cur as Record<string, unknown>)
11
- ) {
12
- cur = (cur as Record<string, unknown>)[p];
13
- } else {
14
- return null;
15
- }
16
- }
17
- return cur;
18
- }
19
-
20
- export function deepResolve(
21
- schema: unknown,
22
- spec: OpenAPISpec,
23
- visited = new Set<string>(),
24
- ): unknown {
25
- if (!schema || typeof schema !== "object") return schema;
26
-
27
- const obj = schema as Record<string, unknown>;
28
-
29
- if (obj.$ref && typeof obj.$ref === "string") {
30
- if (visited.has(obj.$ref))
31
- return { $ref: obj.$ref, description: `(circular: ${obj.$ref})` };
32
- visited.add(obj.$ref);
33
- const resolved = resolveRef(spec, obj.$ref);
34
- if (resolved && typeof resolved === "object" && !Array.isArray(resolved)) {
35
- const base = deepResolve(resolved, spec, new Set(visited)) as Record<
36
- string,
37
- unknown
38
- >;
39
- const merged = { ...base };
40
- for (const k of Object.keys(obj)) if (k !== "$ref") merged[k] = obj[k];
41
- return merged;
42
- }
43
- return obj;
44
- }
45
-
46
- const result: Record<string, unknown> = {};
47
- for (const [k, v] of Object.entries(obj)) {
48
- if (k === "properties" && v && typeof v === "object") {
49
- const props: Record<string, unknown> = {};
50
- for (const [pn, ps] of Object.entries(v as Record<string, unknown>)) {
51
- props[pn] = deepResolve(ps, spec, new Set(visited));
52
- }
53
- result[k] = props;
54
- } else if (
55
- (k === "items" || k === "additionalProperties") &&
56
- v &&
57
- typeof v === "object" &&
58
- !Array.isArray(v)
59
- ) {
60
- result[k] = deepResolve(v, spec, new Set(visited));
61
- } else if (["oneOf", "anyOf", "allOf"].includes(k) && Array.isArray(v)) {
62
- result[k] = (v as unknown[]).map((s) =>
63
- deepResolve(s, spec, new Set(visited)),
64
- );
65
- } else {
66
- result[k] = v;
67
- }
68
- }
69
- return result;
70
- }
@@ -1,74 +0,0 @@
1
- import type { FormattedEndpoint } from "../types.ts";
2
- import { fmtSchema } from "./schema.ts";
3
-
4
- export function fmtEndpoint(ep: FormattedEndpoint): string {
5
- const lines: string[] = [`# ${ep.method.toUpperCase()} ${ep.path}`];
6
- if (ep.deprecated) lines.push("", "DEPRECATED");
7
- if (ep.summary) lines.push("", ep.summary);
8
- if (ep.description) lines.push("", ep.description);
9
- if (ep.operationId) lines.push("", `Operation ID: \`${ep.operationId}\``);
10
- if (ep.tags?.length)
11
- lines.push("", `Tags: ${ep.tags.map((t) => `\`${t}\``).join(", ")}`);
12
-
13
- if (ep.parameters?.length) {
14
- lines.push(
15
- "",
16
- "## Parameters",
17
- "",
18
- "| Name | In | Type | Required | Description |",
19
- "|------|----|------|----------|-------------|",
20
- );
21
- for (const p of ep.parameters) {
22
- lines.push(
23
- `| \`${p.name}\` | ${p.in} | ${p.schema?.type || p.type || "string"} | ${p.required ? "Yes" : "No"} | ${p.description || ""} |`,
24
- );
25
- }
26
- }
27
-
28
- if (ep.requestBody) {
29
- lines.push("", "## Request Body");
30
- if (ep.requestBody.required) lines.push("> Required");
31
- if (ep.requestBody.description) lines.push("", ep.requestBody.description);
32
- for (const [ct, mt] of Object.entries(ep.requestBody.content || {})) {
33
- lines.push(
34
- "",
35
- `${ct}:`,
36
- "```",
37
- fmtSchema((mt as any).schema || {}),
38
- "```",
39
- );
40
- }
41
- }
42
-
43
- if (ep.responses) {
44
- lines.push("", "## Responses");
45
- for (const [code, resp] of Object.entries(ep.responses)) {
46
- const r = resp as any;
47
- lines.push("", `### ${code}`, r.description);
48
- if (r.content) {
49
- for (const [ct, mt] of Object.entries(r.content)) {
50
- lines.push(
51
- "",
52
- `${ct}:`,
53
- "```",
54
- fmtSchema((mt as any).schema || {}),
55
- "```",
56
- );
57
- }
58
- }
59
- }
60
- }
61
-
62
- if (ep.security?.length) {
63
- lines.push("", "## Security");
64
- for (const sec of ep.security) {
65
- for (const [scheme, scopes] of Object.entries(sec)) {
66
- lines.push(
67
- `- \`${scheme}\`${Array.isArray(scopes) && scopes.length ? ` [${scopes.join(", ")}]` : ""}`,
68
- );
69
- }
70
- }
71
- }
72
-
73
- return lines.join("\n");
74
- }
@@ -1,66 +0,0 @@
1
- export function fmtSchema(schema: unknown, indent = 0): string {
2
- const pad = " ".repeat(indent);
3
- if (!schema || typeof schema !== "object") return `${pad}${String(schema)}`;
4
-
5
- const s = schema as Record<string, unknown>;
6
-
7
- if (s.$ref && typeof s.$ref === "string") {
8
- return `${pad}$ref: "${(s.$ref as string).split("/").pop()}"`;
9
- }
10
-
11
- if (s.enum && Array.isArray(s.enum)) {
12
- return `${pad}enum: [${s.enum.map((v) => JSON.stringify(v)).join(", ")}]`;
13
- }
14
-
15
- for (const kw of ["oneOf", "anyOf", "allOf"] as const) {
16
- if (s[kw] && Array.isArray(s[kw])) {
17
- const lines = [`${pad}${kw}:`];
18
- for (let i = 0; i < s[kw].length; i++) {
19
- if (i > 0) lines.push(`${pad} ---`);
20
- lines.push(fmtSchema(s[kw][i], indent + 1));
21
- }
22
- return lines.join("\n");
23
- }
24
- }
25
-
26
- const allLines: string[] = [];
27
-
28
- if (s.description && typeof s.description === "string") {
29
- allLines.push(`${pad}// ${s.description}`);
30
- }
31
-
32
- allLines.push(
33
- `${pad}type: ${s.type || "any"}${s.format ? `<${s.format}>` : ""}${s.nullable ? " | null" : ""}`,
34
- );
35
-
36
- if (s.properties && typeof s.properties === "object") {
37
- const req = new Set<string>(Array.isArray(s.required) ? s.required : []);
38
- for (const [pn, ps] of Object.entries(
39
- s.properties as Record<string, unknown>,
40
- )) {
41
- allLines.push(`${pad}${pn}${req.has(pn) ? " (required)" : ""}:`);
42
- allLines.push(fmtSchema(ps, indent + 1));
43
- }
44
- }
45
-
46
- if (s.items) {
47
- allLines.push(`${pad}items:`);
48
- allLines.push(fmtSchema(s.items, indent + 1));
49
- }
50
-
51
- if (s.additionalProperties && typeof s.additionalProperties === "object") {
52
- allLines.push(`${pad}additionalProperties:`);
53
- allLines.push(fmtSchema(s.additionalProperties, indent + 1));
54
- }
55
-
56
- if (s.example !== undefined)
57
- allLines.push(`${pad}example: ${JSON.stringify(s.example)}`);
58
- if (s.default !== undefined)
59
- allLines.push(`${pad}default: ${JSON.stringify(s.default)}`);
60
- for (const k of ["minLength", "maxLength", "minimum", "maximum"] as const) {
61
- if (s[k] !== undefined) allLines.push(`${pad}${k}: ${s[k] as number}`);
62
- }
63
- if (s.pattern) allLines.push(`${pad}pattern: ${s.pattern as string}`);
64
-
65
- return allLines.join("\n");
66
- }
package/src/index.ts DELETED
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
- import { dispatch } from "./cli/dispatch.js";
3
-
4
- async function main() {
5
- const output = await dispatch(process.argv);
6
- console.log(output);
7
- }
8
-
9
- main().catch((err) => {
10
- console.error(err instanceof Error ? err.message : String(err));
11
- process.exit(1);
12
- });
package/src/types.ts DELETED
@@ -1,60 +0,0 @@
1
- export interface OpenAPISpec {
2
- openapi?: string;
3
- swagger?: string;
4
- info: {
5
- title?: string;
6
- version?: string;
7
- description?: string;
8
- contact?: Record<string, string>;
9
- license?: { name: string; url?: string };
10
- };
11
- paths: Record<string, Record<string, any>>;
12
- components?: { schemas?: Record<string, any> };
13
- definitions?: Record<string, any>;
14
- servers?: { url: string }[];
15
- host?: string;
16
- basePath?: string;
17
- schemes?: string[];
18
- consumes?: string[];
19
- produces?: string[];
20
- }
21
-
22
- export interface EndpointSummary {
23
- path: string;
24
- method: string;
25
- summary: string;
26
- operationId?: string;
27
- tags: string[];
28
- deprecated: boolean;
29
- }
30
-
31
- export interface SchemaSummary {
32
- name: string;
33
- type: string;
34
- description?: string;
35
- properties: number;
36
- }
37
-
38
- export interface SearchResult {
39
- type: "endpoint" | "schema" | "property";
40
- path?: string;
41
- method?: string;
42
- schemaName?: string;
43
- propertyName?: string;
44
- match: string;
45
- summary?: string;
46
- }
47
-
48
- export interface FormattedEndpoint {
49
- path: string;
50
- method: string;
51
- summary?: string;
52
- description?: string;
53
- operationId?: string;
54
- tags: string[];
55
- deprecated: boolean;
56
- parameters?: any[];
57
- requestBody?: any;
58
- responses?: any;
59
- security?: any[];
60
- }