@askills/openapi-explorer-cli 0.0.3 → 0.0.5
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/index.d.mts +1 -0
- package/dist/index.mjs +589 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +4 -1
- package/CHANGELOG.md +0 -13
- package/src/cli/args.ts +0 -59
- package/src/cli/dispatch.ts +0 -80
- package/src/commands/endpoint.ts +0 -119
- package/src/commands/info.ts +0 -44
- package/src/commands/paths.ts +0 -43
- package/src/commands/schema.ts +0 -22
- package/src/commands/schemas.ts +0 -45
- package/src/commands/search.ts +0 -49
- package/src/commands/tags.ts +0 -11
- package/src/core/loader.ts +0 -38
- package/src/core/queries.ts +0 -165
- package/src/core/resolve.ts +0 -70
- package/src/formatters/endpoint.ts +0 -74
- package/src/formatters/schema.ts +0 -66
- package/src/index.ts +0 -12
- package/src/types.ts +0 -60
- package/test/openapi.json +0 -30965
- package/test/swagger.test.ts +0 -778
- package/tsconfig.json +0 -10
- package/tsdown.config.ts +0 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/core/loader.ts","../src/core/queries.ts","../src/commands/info.ts","../src/commands/tags.ts","../src/commands/paths.ts","../src/core/resolve.ts","../src/formatters/schema.ts","../src/formatters/endpoint.ts","../src/commands/endpoint.ts","../src/commands/schemas.ts","../src/commands/schema.ts","../src/commands/search.ts","../src/cli/args.ts","../src/cli/dispatch.ts","../src/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport type { OpenAPISpec } from \"../types.ts\";\n\nexport async function loadSpec(source: string): Promise<OpenAPISpec> {\n const isUrl = /^https?:\\/\\//i.test(source);\n let raw: string;\n\n if (isUrl) {\n const res = await fetch(source, {\n headers: { Accept: \"application/json, text/plain\" },\n signal: AbortSignal.timeout(30000),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${source}`);\n raw = await res.text();\n } else {\n raw = await readFile(resolve(source), \"utf-8\");\n }\n\n const data = JSON.parse(raw) as OpenAPISpec;\n if (!data.openapi && !data.swagger)\n throw new Error(\"Not a valid OpenAPI/Swagger spec.\");\n if (!data.info) throw new Error(\"Invalid spec: missing 'info' field.\");\n data.info.title = data.info.title || \"Untitled API\";\n data.info.version = data.info.version || \"0.0.0\";\n data.paths = data.paths || {};\n return data;\n}\n\nexport function getServerUrl(spec: OpenAPISpec): string {\n if (spec.servers?.length) {\n let url = spec.servers[0]!.url;\n return url.endsWith(\"/\") ? url.slice(0, -1) : url;\n }\n if (spec.host)\n return `${spec.schemes?.[0] || \"https\"}://${spec.host}${spec.basePath || \"\"}`;\n return \"\";\n}\n","import type {\n OpenAPISpec,\n EndpointSummary,\n SchemaSummary,\n SearchResult,\n} from \"../types.ts\";\n\nconst HTTP_METHODS = [\n \"get\",\n \"post\",\n \"put\",\n \"patch\",\n \"delete\",\n \"options\",\n \"head\",\n] as const;\nconst METHOD_PRIORITY: Record<string, number> = {\n get: 0,\n post: 1,\n put: 2,\n patch: 3,\n delete: 4,\n options: 5,\n head: 6,\n};\n\nexport function getEndpoints(\n spec: OpenAPISpec,\n tag?: string,\n): EndpointSummary[] {\n const eps: EndpointSummary[] = [];\n for (const [path, pathItem] of Object.entries(spec.paths)) {\n for (const method of HTTP_METHODS) {\n const op = pathItem[method];\n if (!op) continue;\n if (tag && (!op.tags || !op.tags.includes(tag))) continue;\n eps.push({\n path,\n method,\n summary:\n op.summary || op.operationId || `${method.toUpperCase()} ${path}`,\n operationId: op.operationId,\n tags: op.tags || [],\n deprecated: op.deprecated || false,\n });\n }\n }\n eps.sort((a, b) => {\n if (a.path !== b.path) return a.path.localeCompare(b.path);\n return (\n (METHOD_PRIORITY[a.method] ?? 99) - (METHOD_PRIORITY[b.method] ?? 99)\n );\n });\n return eps;\n}\n\nexport function getTags(spec: OpenAPISpec): { name: string; count: number }[] {\n const counts = new Map<string, number>();\n for (const pathItem of Object.values(spec.paths)) {\n for (const method of HTTP_METHODS) {\n const op = pathItem[method];\n if (!op?.tags) continue;\n for (const tag of op.tags) counts.set(tag, (counts.get(tag) || 0) + 1);\n }\n }\n return Array.from(counts.entries())\n .map(([name, count]) => ({ name, count }))\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport function getSchemas(spec: OpenAPISpec): SchemaSummary[] {\n const schemas = spec.components?.schemas || spec.definitions || {};\n return Object.entries(schemas)\n .map(([name, s]) => ({\n name,\n type: (s as any).type || \"object\",\n description: (s as any).description || (s as any).title,\n properties: (s as any).properties\n ? Object.keys((s as any).properties).length\n : 0,\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport function getSchemaDetail(spec: OpenAPISpec, name: string): unknown {\n return (spec.components?.schemas || spec.definitions || {})[name] || null;\n}\n\nexport function getPathParams(path: string): string[] {\n const m = path.match(/\\{(\\w+)\\}/g);\n return m ? m.map((s) => s.slice(1, -1)) : [];\n}\n\nexport function searchSpec(spec: OpenAPISpec, query: string): SearchResult[] {\n const results: SearchResult[] = [];\n const lower = query.toLowerCase();\n\n for (const [path, pi] of Object.entries(spec.paths)) {\n for (const method of HTTP_METHODS) {\n const op = pi[method];\n if (!op) continue;\n const text = [\n path,\n method,\n op.summary,\n op.operationId,\n op.description,\n ...(op.tags || []),\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n if (text.includes(lower)) {\n results.push({\n type: \"endpoint\",\n path,\n method,\n match: `${method.toUpperCase()} ${path}`,\n summary: op.summary || op.operationId,\n });\n }\n }\n }\n\n const schemas = spec.components?.schemas || spec.definitions || {};\n for (const [name, s] of Object.entries(schemas)) {\n const sAny = s as any;\n if (\n [name, sAny.description, sAny.title]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase()\n .includes(lower)\n ) {\n results.push({\n type: \"schema\",\n schemaName: name,\n match: `Schema: ${name}`,\n summary: sAny.description,\n });\n }\n if (sAny.properties) {\n for (const [pn, ps] of Object.entries(sAny.properties)) {\n const psAny = ps as any;\n if (\n [pn, psAny.description]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase()\n .includes(lower)\n ) {\n results.push({\n type: \"property\",\n schemaName: name,\n propertyName: pn,\n match: `Schema ${name} > ${pn}`,\n summary: psAny.description,\n });\n }\n }\n }\n }\n\n return results;\n}\n","import type { OpenAPISpec } from \"../types.ts\";\nimport { loadSpec, getServerUrl } from \"../core/loader.ts\";\nimport { getEndpoints, getSchemas, getTags } from \"../core/queries.ts\";\n\nexport async function cmdInfo(source: string): Promise<string> {\n const spec = await loadSpec(source);\n const eps = getEndpoints(spec);\n const schemas = getSchemas(spec);\n const tags = getTags(spec);\n const url = getServerUrl(spec);\n const sv = spec.openapi || spec.swagger || \"unknown\";\n\n const lines: string[] = [\n `# ${spec.info.title}`,\n \"\",\n `Version: ${spec.info.version}`,\n `OpenAPI Version: ${sv}`,\n `Server: \\`${url || \"Not specified\"}\\``,\n \"\",\n spec.info.description || \"\",\n \"\",\n \"## Summary\",\n `- Endpoints: ${eps.length}`,\n `- Schemas: ${schemas.length}`,\n `- Tags: ${tags.length}`,\n ];\n\n if (spec.info.contact) {\n lines.push(\"\", \"## Contact\");\n for (const k of [\"name\", \"email\", \"url\"] as const) {\n const val = spec.info.contact[k];\n if (val)\n lines.push(`- ${k.charAt(0).toUpperCase() + k.slice(1)}: ${val}`);\n }\n }\n\n if (spec.info.license) {\n lines.push(\"\", \"## License\");\n lines.push(`- Name: ${spec.info.license.name}`);\n if (spec.info.license.url) lines.push(`- URL: ${spec.info.license.url}`);\n }\n\n return lines.join(\"\\n\");\n}\n","import { loadSpec } from \"../core/loader.ts\";\nimport { getTags } from \"../core/queries.ts\";\n\nexport async function cmdTags(source: string): Promise<string> {\n const spec = await loadSpec(source);\n const tags = getTags(spec);\n if (!tags.length) return \"No tags found.\";\n const lines = [\"# Tags\", \"\", \"| Tag | Endpoints |\", \"|-----|-----------|\"];\n for (const t of tags) lines.push(`| \\`${t.name}\\` | ${t.count} |`);\n return lines.join(\"\\n\");\n}\n","import { loadSpec } from \"../core/loader.ts\";\nimport { getEndpoints } from \"../core/queries.ts\";\n\nexport async function cmdPaths(\n source: string,\n tag?: string,\n limit = 50,\n offset = 0,\n): Promise<string> {\n const spec = await loadSpec(source);\n const all = getEndpoints(spec, tag);\n const paginated = all.slice(offset, offset + limit);\n\n if (!paginated.length) {\n return all.length === 0\n ? \"No endpoints found.\"\n : `No more endpoints (offset ${offset} of ${all.length}).`;\n }\n\n const hasMore = all.length > offset + paginated.length;\n const lines: string[] = [\"# Endpoints\"];\n if (tag) lines.push(`\\nFiltered by tag: \\`${tag}\\``);\n lines.push(`\\n${all.length} total (showing ${paginated.length})`, \"\");\n\n for (const ep of paginated) {\n lines.push(\n `### \\`${ep.method.toUpperCase()}\\` ${ep.path}${ep.deprecated ? \" ~~(deprecated)~~\" : \"\"}`,\n );\n if (ep.summary) lines.push(ep.summary);\n if (ep.operationId) lines.push(`- Operation ID: \\`${ep.operationId}\\``);\n if (ep.tags.length)\n lines.push(`- Tags: ${ep.tags.map((t) => `\\`${t}\\``).join(\", \")}`);\n lines.push(\"\");\n }\n\n if (hasMore) {\n lines.push(\n `> Showing ${paginated.length} of ${all.length}. Use --offset=${offset + paginated.length} to see more.`,\n );\n }\n\n return lines.join(\"\\n\");\n}\n","import type { OpenAPISpec } from \"../types.ts\";\n\nexport function resolveRef(spec: OpenAPISpec, ref: string): unknown {\n const parts = ref.replace(/^#\\//, \"\").split(\"/\");\n let cur: unknown = spec as unknown as Record<string, unknown>;\n for (const p of parts) {\n if (\n cur &&\n typeof cur === \"object\" &&\n p in (cur as Record<string, unknown>)\n ) {\n cur = (cur as Record<string, unknown>)[p];\n } else {\n return null;\n }\n }\n return cur;\n}\n\nexport function deepResolve(\n schema: unknown,\n spec: OpenAPISpec,\n visited = new Set<string>(),\n): unknown {\n if (!schema || typeof schema !== \"object\") return schema;\n\n const obj = schema as Record<string, unknown>;\n\n if (obj.$ref && typeof obj.$ref === \"string\") {\n if (visited.has(obj.$ref))\n return { $ref: obj.$ref, description: `(circular: ${obj.$ref})` };\n visited.add(obj.$ref);\n const resolved = resolveRef(spec, obj.$ref);\n if (resolved && typeof resolved === \"object\" && !Array.isArray(resolved)) {\n const base = deepResolve(resolved, spec, new Set(visited)) as Record<\n string,\n unknown\n >;\n const merged = { ...base };\n for (const k of Object.keys(obj)) if (k !== \"$ref\") merged[k] = obj[k];\n return merged;\n }\n return obj;\n }\n\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (k === \"properties\" && v && typeof v === \"object\") {\n const props: Record<string, unknown> = {};\n for (const [pn, ps] of Object.entries(v as Record<string, unknown>)) {\n props[pn] = deepResolve(ps, spec, new Set(visited));\n }\n result[k] = props;\n } else if (\n (k === \"items\" || k === \"additionalProperties\") &&\n v &&\n typeof v === \"object\" &&\n !Array.isArray(v)\n ) {\n result[k] = deepResolve(v, spec, new Set(visited));\n } else if ([\"oneOf\", \"anyOf\", \"allOf\"].includes(k) && Array.isArray(v)) {\n result[k] = (v as unknown[]).map((s) =>\n deepResolve(s, spec, new Set(visited)),\n );\n } else {\n result[k] = v;\n }\n }\n return result;\n}\n","export function fmtSchema(schema: unknown, indent = 0): string {\n const pad = \" \".repeat(indent);\n if (!schema || typeof schema !== \"object\") return `${pad}${String(schema)}`;\n\n const s = schema as Record<string, unknown>;\n\n if (s.$ref && typeof s.$ref === \"string\") {\n return `${pad}$ref: \"${(s.$ref as string).split(\"/\").pop()}\"`;\n }\n\n if (s.enum && Array.isArray(s.enum)) {\n return `${pad}enum: [${s.enum.map((v) => JSON.stringify(v)).join(\", \")}]`;\n }\n\n for (const kw of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n if (s[kw] && Array.isArray(s[kw])) {\n const lines = [`${pad}${kw}:`];\n for (let i = 0; i < s[kw].length; i++) {\n if (i > 0) lines.push(`${pad} ---`);\n lines.push(fmtSchema(s[kw][i], indent + 1));\n }\n return lines.join(\"\\n\");\n }\n }\n\n const allLines: string[] = [];\n\n if (s.description && typeof s.description === \"string\") {\n allLines.push(`${pad}// ${s.description}`);\n }\n\n allLines.push(\n `${pad}type: ${s.type || \"any\"}${s.format ? `<${s.format}>` : \"\"}${s.nullable ? \" | null\" : \"\"}`,\n );\n\n if (s.properties && typeof s.properties === \"object\") {\n const req = new Set<string>(Array.isArray(s.required) ? s.required : []);\n for (const [pn, ps] of Object.entries(\n s.properties as Record<string, unknown>,\n )) {\n allLines.push(`${pad}${pn}${req.has(pn) ? \" (required)\" : \"\"}:`);\n allLines.push(fmtSchema(ps, indent + 1));\n }\n }\n\n if (s.items) {\n allLines.push(`${pad}items:`);\n allLines.push(fmtSchema(s.items, indent + 1));\n }\n\n if (s.additionalProperties && typeof s.additionalProperties === \"object\") {\n allLines.push(`${pad}additionalProperties:`);\n allLines.push(fmtSchema(s.additionalProperties, indent + 1));\n }\n\n if (s.example !== undefined)\n allLines.push(`${pad}example: ${JSON.stringify(s.example)}`);\n if (s.default !== undefined)\n allLines.push(`${pad}default: ${JSON.stringify(s.default)}`);\n for (const k of [\"minLength\", \"maxLength\", \"minimum\", \"maximum\"] as const) {\n if (s[k] !== undefined) allLines.push(`${pad}${k}: ${s[k] as number}`);\n }\n if (s.pattern) allLines.push(`${pad}pattern: ${s.pattern as string}`);\n\n return allLines.join(\"\\n\");\n}\n","import type { FormattedEndpoint } from \"../types.ts\";\nimport { fmtSchema } from \"./schema.ts\";\n\nexport function fmtEndpoint(ep: FormattedEndpoint): string {\n const lines: string[] = [`# ${ep.method.toUpperCase()} ${ep.path}`];\n if (ep.deprecated) lines.push(\"\", \"DEPRECATED\");\n if (ep.summary) lines.push(\"\", ep.summary);\n if (ep.description) lines.push(\"\", ep.description);\n if (ep.operationId) lines.push(\"\", `Operation ID: \\`${ep.operationId}\\``);\n if (ep.tags?.length)\n lines.push(\"\", `Tags: ${ep.tags.map((t) => `\\`${t}\\``).join(\", \")}`);\n\n if (ep.parameters?.length) {\n lines.push(\n \"\",\n \"## Parameters\",\n \"\",\n \"| Name | In | Type | Required | Description |\",\n \"|------|----|------|----------|-------------|\",\n );\n for (const p of ep.parameters) {\n lines.push(\n `| \\`${p.name}\\` | ${p.in} | ${p.schema?.type || p.type || \"string\"} | ${p.required ? \"Yes\" : \"No\"} | ${p.description || \"\"} |`,\n );\n }\n }\n\n if (ep.requestBody) {\n lines.push(\"\", \"## Request Body\");\n if (ep.requestBody.required) lines.push(\"> Required\");\n if (ep.requestBody.description) lines.push(\"\", ep.requestBody.description);\n for (const [ct, mt] of Object.entries(ep.requestBody.content || {})) {\n lines.push(\n \"\",\n `${ct}:`,\n \"```\",\n fmtSchema((mt as any).schema || {}),\n \"```\",\n );\n }\n }\n\n if (ep.responses) {\n lines.push(\"\", \"## Responses\");\n for (const [code, resp] of Object.entries(ep.responses)) {\n const r = resp as any;\n lines.push(\"\", `### ${code}`, r.description);\n if (r.content) {\n for (const [ct, mt] of Object.entries(r.content)) {\n lines.push(\n \"\",\n `${ct}:`,\n \"```\",\n fmtSchema((mt as any).schema || {}),\n \"```\",\n );\n }\n }\n }\n }\n\n if (ep.security?.length) {\n lines.push(\"\", \"## Security\");\n for (const sec of ep.security) {\n for (const [scheme, scopes] of Object.entries(sec)) {\n lines.push(\n `- \\`${scheme}\\`${Array.isArray(scopes) && scopes.length ? ` [${scopes.join(\", \")}]` : \"\"}`,\n );\n }\n }\n }\n\n return lines.join(\"\\n\");\n}\n","import { loadSpec } from \"../core/loader.ts\";\nimport { getEndpoints, getPathParams } from \"../core/queries.ts\";\nimport { deepResolve } from \"../core/resolve.ts\";\nimport { fmtEndpoint } from \"../formatters/endpoint.ts\";\nimport type { FormattedEndpoint } from \"../types.ts\";\n\nexport async function cmdEndpoint(\n source: string,\n path: string,\n method: string,\n full = false,\n): Promise<string> {\n const spec = await loadSpec(source);\n method = method.toLowerCase();\n const op = spec.paths[path]?.[method];\n if (!op) {\n const similar = getEndpoints(spec)\n .filter((e) => e.path.includes(path) || path.includes(e.path))\n .slice(0, 5);\n const hint = similar.length\n ? \"\\n\\nDid you mean?\\n\" +\n similar.map((e) => ` ${e.method.toUpperCase()} ${e.path}`).join(\"\\n\")\n : \"\";\n throw new Error(`No ${method.toUpperCase()} ${path} found.${hint}`);\n }\n\n const paramNames = getPathParams(path);\n const opParams: any[] = op.parameters || [];\n // Swagger 2.0 models the request body as a parameter with in:\"body\" + schema,\n // separate from the path/query/header parameters.\n const bodyParam = opParams.find((p: any) => p.in === \"body\");\n const nonBodyParams = opParams.filter((p: any) => p.in !== \"body\");\n\n const mergedParams = [\n ...paramNames.map(\n (n) =>\n nonBodyParams.find((p: any) => p.name === n) || {\n name: n,\n in: \"path\",\n required: true,\n description: `Path param: ${n}`,\n schema: { type: \"string\" },\n },\n ),\n ...nonBodyParams.filter((p: any) => !paramNames.includes(p.name)),\n ];\n\n // Resolve $refs only under --full; otherwise leave schemas (with $ref) as-is.\n const resolve = (schema: any): any =>\n full && schema ? deepResolve(schema, spec) : schema;\n\n // Request body: OpenAPI 3 requestBody wins; otherwise build one from a\n // Swagger 2.0 body parameter so its schema renders in the Request Body\n // section (instead of \"object\" in the parameters table).\n let requestBody: any;\n if (op.requestBody) {\n requestBody = {\n description: op.requestBody.description,\n required: op.requestBody.required,\n content: Object.fromEntries(\n Object.entries(op.requestBody.content || {}).map(\n ([ct, mt]: [string, any]) => [ct, { schema: resolve(mt.schema) }],\n ),\n ),\n };\n } else if (bodyParam) {\n const ct =\n (op.consumes || spec.consumes || [\"application/json\"])[0] ||\n \"application/json\";\n requestBody = {\n description: bodyParam.description,\n required: bodyParam.required,\n content: { [ct]: { schema: resolve(bodyParam.schema) } },\n };\n }\n\n // Responses: OpenAPI 3 uses response.content; Swagger 2.0 uses a bare\n // response.schema + the operation's/root produces list.\n const produces = op.produces || spec.produces || [\"application/json\"];\n let responses: any;\n if (op.responses) {\n responses = Object.fromEntries(\n Object.entries(op.responses).map(([code, resp]: [string, any]) => {\n let content: any;\n if (resp.content) {\n content = Object.fromEntries(\n Object.entries(resp.content).map(([ct, mt]: [string, any]) => [\n ct,\n { schema: resolve(mt.schema) },\n ]),\n );\n } else if (resp.schema) {\n const ct = produces[0] || \"application/json\";\n content = { [ct]: { schema: resolve(resp.schema) } };\n }\n return [code, { description: resp.description, content }];\n }),\n );\n }\n\n const ep: FormattedEndpoint = {\n path,\n method,\n summary: op.summary,\n description: op.description,\n operationId: op.operationId,\n tags: op.tags || [],\n deprecated: op.deprecated || false,\n parameters: mergedParams.map((p: any) => ({\n ...p,\n schema: resolve(p.schema),\n })),\n requestBody,\n responses,\n security: op.security,\n };\n\n return fmtEndpoint(ep);\n}\n","import { loadSpec } from \"../core/loader.ts\";\nimport { getSchemas } from \"../core/queries.ts\";\n\nexport async function cmdSchemas(\n source: string,\n limit = 50,\n offset = 0,\n): Promise<string> {\n const spec = await loadSpec(source);\n const all = getSchemas(spec);\n const paginated = all.slice(offset, offset + limit);\n\n if (!paginated.length) {\n return all.length === 0\n ? \"No schemas defined.\"\n : `No more schemas (offset ${offset} of ${all.length}).`;\n }\n\n const hasMore = all.length > offset + paginated.length;\n const lines: string[] = [\n \"# Component Schemas\",\n \"\",\n `${all.length} total (showing ${paginated.length})`,\n \"\",\n \"| Name | Type | Properties | Description |\",\n \"|------|------|------------|-------------|\",\n ];\n\n for (const s of paginated) {\n lines.push(\n `| \\`${s.name}\\` | ${s.type} | ${s.properties} | ${(s.description || \"\").slice(0, 80)} |`,\n );\n }\n\n if (hasMore) {\n lines.push(\n `\\n> Showing ${paginated.length} of ${all.length}. Use --offset=${offset + paginated.length} to see more.`,\n );\n }\n\n lines.push(\n \"\\nUse `openapi-explorer schema <source> <schema_name>` for details.\",\n );\n return lines.join(\"\\n\");\n}\n","import { loadSpec } from \"../core/loader.ts\";\nimport { getSchemas, getSchemaDetail } from \"../core/queries.ts\";\nimport { fmtSchema } from \"../formatters/schema.ts\";\n\nexport async function cmdSchema(\n source: string,\n schemaName: string,\n): Promise<string> {\n const spec = await loadSpec(source);\n const schema = getSchemaDetail(spec, schemaName);\n if (!schema) {\n const suggestions = getSchemas(spec)\n .slice(0, 10)\n .map((s) => s.name);\n throw new Error(\n `Schema '${schemaName}' not found.\\n\\nAvailable:\\n${suggestions.map((s) => ` - ${s}`).join(\"\\n\")}`,\n );\n }\n return [`# Schema: ${schemaName}`, \"\", \"```\", fmtSchema(schema), \"```\"].join(\n \"\\n\",\n );\n}\n","import { loadSpec } from \"../core/loader.ts\";\nimport { searchSpec } from \"../core/queries.ts\";\n\nexport async function cmdSearch(\n source: string,\n query: string,\n): Promise<string> {\n const spec = await loadSpec(source);\n const results = searchSpec(spec, query);\n if (!results.length) return `No results for '${query}'.`;\n\n const lines: string[] = [\n `# Search Results for '${query}'`,\n \"\",\n `Found ${results.length} matches`,\n \"\",\n ];\n\n const eps = results.filter((r) => r.type === \"endpoint\");\n const schemas = results.filter((r) => r.type === \"schema\");\n const props = results.filter((r) => r.type === \"property\");\n\n if (eps.length) {\n lines.push(`## Endpoints (${eps.length})`);\n for (const e of eps)\n lines.push(\n `- ${e.method?.toUpperCase()} \\`${e.path}\\` - ${e.summary || \"\"}`,\n );\n lines.push(\"\");\n }\n\n if (schemas.length) {\n lines.push(`## Schemas (${schemas.length})`);\n for (const s of schemas)\n lines.push(`- ${s.schemaName} - ${s.summary || \"\"}`);\n lines.push(\"\");\n }\n\n if (props.length) {\n lines.push(`## Properties (${props.length})`);\n for (const p of props)\n lines.push(\n `- \\`${p.schemaName}.${p.propertyName}\\` - ${p.summary || \"\"}`,\n );\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n","export function parseArgs(argv: string[]): {\n command: string;\n source: string;\n flags: Record<string, string | boolean | number>;\n positional: string[];\n} {\n const args = argv.slice(2);\n if (!args.length || args[0] === \"--help\" || args[0] === \"-h\") {\n return { command: \"help\", source: \"\", flags: {}, positional: [] };\n }\n\n const command = args[0] ?? \"\";\n const source = args[1] ?? \"\";\n const positional: string[] = [];\n const flags: Record<string, string | boolean | number> = {};\n\n let i = 2;\n while (i < args.length) {\n const arg = args[i]!;\n if (arg.startsWith(\"--\")) {\n const key = arg.slice(2);\n if (key === \"full\") {\n flags[key] = true;\n i++;\n } else if (key === \"tag\" || key === \"limit\" || key === \"offset\") {\n flags[key] = args[i + 1] ?? \"\";\n i += 2;\n } else {\n flags[key] = args[i + 1] ?? true;\n i += 2;\n }\n } else {\n positional.push(arg);\n i++;\n }\n }\n\n return { command, source, flags, positional };\n}\n\nexport function printHelp(): string {\n return `Usage: openapi-explorer <command> <source> [args...]\n\nsource can be a URL (https://...) or a local file path.\n\nCommands:\n info <source> API overview\n tags <source> List tag groups\n paths <source> [--tag <t>] [--limit N] [--offset N] List endpoints\n endpoint <source> <path> <method> [--full] Endpoint detail\n schemas <source> [--limit N] [--offset N] List schemas\n schema <source> <schema_name> Schema detail\n search <source> <query> Full-text search\n\nExamples:\n openapi-explorer info https://petstore.swagger.io/v2/swagger.json\n openapi-explorer endpoint ./spec.json /pets/{petId} get --full\n openapi-explorer paths ./spec.json --tag pets`;\n}\n","import { cmdInfo } from \"../commands/info.ts\";\nimport { cmdTags } from \"../commands/tags.ts\";\nimport { cmdPaths } from \"../commands/paths.ts\";\nimport { cmdEndpoint } from \"../commands/endpoint.ts\";\nimport { cmdSchemas } from \"../commands/schemas.ts\";\nimport { cmdSchema } from \"../commands/schema.ts\";\nimport { cmdSearch } from \"../commands/search.ts\";\nimport { parseArgs, printHelp } from \"./args.ts\";\n\nexport async function dispatch(argv: string[]): Promise<string> {\n const { command, source, flags, positional } = parseArgs(argv);\n\n switch (command) {\n case \"help\":\n return printHelp();\n\n case \"info\":\n if (!source) throw new Error(\"Usage: swagger.mjs info <source>\");\n return await cmdInfo(source);\n\n case \"tags\":\n if (!source) throw new Error(\"Usage: swagger.mjs tags <source>\");\n return await cmdTags(source);\n\n case \"paths\": {\n if (!source)\n throw new Error(\n \"Usage: swagger.mjs paths <source> [--tag <t>] [--limit N] [--offset N]\",\n );\n const tag = typeof flags.tag === \"string\" ? flags.tag : undefined;\n const limit =\n typeof flags.limit === \"string\" ? parseInt(flags.limit, 10) || 50 : 50;\n const offset =\n typeof flags.offset === \"string\" ? parseInt(flags.offset, 10) || 0 : 0;\n return await cmdPaths(source, tag, limit, offset);\n }\n\n case \"endpoint\":\n case \"ep\": {\n const path = positional[0] || flags.path;\n const method = positional[1] || flags.method;\n if (!source || !path || !method) {\n throw new Error(\n \"Usage: swagger.mjs endpoint <source> <path> <method> [--full]\",\n );\n }\n const full = flags.full === true;\n return await cmdEndpoint(source, path as string, method as string, full);\n }\n\n case \"schemas\": {\n if (!source)\n throw new Error(\n \"Usage: swagger.mjs schemas <source> [--limit N] [--offset N]\",\n );\n const slimit =\n typeof flags.limit === \"string\" ? parseInt(flags.limit, 10) || 50 : 50;\n const soffset =\n typeof flags.offset === \"string\" ? parseInt(flags.offset, 10) || 0 : 0;\n return await cmdSchemas(source, slimit, soffset);\n }\n\n case \"schema\": {\n const schemaName = positional[0] || flags.name;\n if (!source || !schemaName)\n throw new Error(\"Usage: swagger.mjs schema <source> <schema_name>\");\n return await cmdSchema(source, schemaName as string);\n }\n\n case \"search\": {\n const query = positional[0] || flags.query;\n if (!source || !query)\n throw new Error(\"Usage: swagger.mjs search <source> <query>\");\n return await cmdSearch(source, query as string);\n }\n\n default:\n throw new Error(`Unknown command: ${command}`);\n }\n}\n","#!/usr/bin/env node\nimport { dispatch } from \"./cli/dispatch.js\";\n\nasync function main() {\n const output = await dispatch(process.argv);\n console.log(output);\n}\n\nmain().catch((err) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askills/openapi-explorer-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "CLI for progressive exploration of OpenAPI/Swagger API documentation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"url": "git+https://github.com/anuoua/skills.git",
|
|
11
11
|
"directory": "packages/openapi-explorer-cli"
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
13
16
|
"bin": {
|
|
14
17
|
"openapi-explorer": "dist/index.mjs"
|
|
15
18
|
},
|
package/CHANGELOG.md
DELETED
package/src/cli/args.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
export function parseArgs(argv: string[]): {
|
|
2
|
-
command: string;
|
|
3
|
-
source: string;
|
|
4
|
-
flags: Record<string, string | boolean | number>;
|
|
5
|
-
positional: string[];
|
|
6
|
-
} {
|
|
7
|
-
const args = argv.slice(2);
|
|
8
|
-
if (!args.length || args[0] === "--help" || args[0] === "-h") {
|
|
9
|
-
return { command: "help", source: "", flags: {}, positional: [] };
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const command = args[0] ?? "";
|
|
13
|
-
const source = args[1] ?? "";
|
|
14
|
-
const positional: string[] = [];
|
|
15
|
-
const flags: Record<string, string | boolean | number> = {};
|
|
16
|
-
|
|
17
|
-
let i = 2;
|
|
18
|
-
while (i < args.length) {
|
|
19
|
-
const arg = args[i]!;
|
|
20
|
-
if (arg.startsWith("--")) {
|
|
21
|
-
const key = arg.slice(2);
|
|
22
|
-
if (key === "full") {
|
|
23
|
-
flags[key] = true;
|
|
24
|
-
i++;
|
|
25
|
-
} else if (key === "tag" || key === "limit" || key === "offset") {
|
|
26
|
-
flags[key] = args[i + 1] ?? "";
|
|
27
|
-
i += 2;
|
|
28
|
-
} else {
|
|
29
|
-
flags[key] = args[i + 1] ?? true;
|
|
30
|
-
i += 2;
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
positional.push(arg);
|
|
34
|
-
i++;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return { command, source, flags, positional };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function printHelp(): string {
|
|
42
|
-
return `Usage: openapi-explorer <command> <source> [args...]
|
|
43
|
-
|
|
44
|
-
source can be a URL (https://...) or a local file path.
|
|
45
|
-
|
|
46
|
-
Commands:
|
|
47
|
-
info <source> API overview
|
|
48
|
-
tags <source> List tag groups
|
|
49
|
-
paths <source> [--tag <t>] [--limit N] [--offset N] List endpoints
|
|
50
|
-
endpoint <source> <path> <method> [--full] Endpoint detail
|
|
51
|
-
schemas <source> [--limit N] [--offset N] List schemas
|
|
52
|
-
schema <source> <schema_name> Schema detail
|
|
53
|
-
search <source> <query> Full-text search
|
|
54
|
-
|
|
55
|
-
Examples:
|
|
56
|
-
openapi-explorer info https://petstore.swagger.io/v2/swagger.json
|
|
57
|
-
openapi-explorer endpoint ./spec.json /pets/{petId} get --full
|
|
58
|
-
openapi-explorer paths ./spec.json --tag pets`;
|
|
59
|
-
}
|
package/src/cli/dispatch.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { cmdInfo } from "../commands/info.ts";
|
|
2
|
-
import { cmdTags } from "../commands/tags.ts";
|
|
3
|
-
import { cmdPaths } from "../commands/paths.ts";
|
|
4
|
-
import { cmdEndpoint } from "../commands/endpoint.ts";
|
|
5
|
-
import { cmdSchemas } from "../commands/schemas.ts";
|
|
6
|
-
import { cmdSchema } from "../commands/schema.ts";
|
|
7
|
-
import { cmdSearch } from "../commands/search.ts";
|
|
8
|
-
import { parseArgs, printHelp } from "./args.ts";
|
|
9
|
-
|
|
10
|
-
export async function dispatch(argv: string[]): Promise<string> {
|
|
11
|
-
const { command, source, flags, positional } = parseArgs(argv);
|
|
12
|
-
|
|
13
|
-
switch (command) {
|
|
14
|
-
case "help":
|
|
15
|
-
return printHelp();
|
|
16
|
-
|
|
17
|
-
case "info":
|
|
18
|
-
if (!source) throw new Error("Usage: swagger.mjs info <source>");
|
|
19
|
-
return await cmdInfo(source);
|
|
20
|
-
|
|
21
|
-
case "tags":
|
|
22
|
-
if (!source) throw new Error("Usage: swagger.mjs tags <source>");
|
|
23
|
-
return await cmdTags(source);
|
|
24
|
-
|
|
25
|
-
case "paths": {
|
|
26
|
-
if (!source)
|
|
27
|
-
throw new Error(
|
|
28
|
-
"Usage: swagger.mjs paths <source> [--tag <t>] [--limit N] [--offset N]",
|
|
29
|
-
);
|
|
30
|
-
const tag = typeof flags.tag === "string" ? flags.tag : undefined;
|
|
31
|
-
const limit =
|
|
32
|
-
typeof flags.limit === "string" ? parseInt(flags.limit, 10) || 50 : 50;
|
|
33
|
-
const offset =
|
|
34
|
-
typeof flags.offset === "string" ? parseInt(flags.offset, 10) || 0 : 0;
|
|
35
|
-
return await cmdPaths(source, tag, limit, offset);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
case "endpoint":
|
|
39
|
-
case "ep": {
|
|
40
|
-
const path = positional[0] || flags.path;
|
|
41
|
-
const method = positional[1] || flags.method;
|
|
42
|
-
if (!source || !path || !method) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
"Usage: swagger.mjs endpoint <source> <path> <method> [--full]",
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
const full = flags.full === true;
|
|
48
|
-
return await cmdEndpoint(source, path as string, method as string, full);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
case "schemas": {
|
|
52
|
-
if (!source)
|
|
53
|
-
throw new Error(
|
|
54
|
-
"Usage: swagger.mjs schemas <source> [--limit N] [--offset N]",
|
|
55
|
-
);
|
|
56
|
-
const slimit =
|
|
57
|
-
typeof flags.limit === "string" ? parseInt(flags.limit, 10) || 50 : 50;
|
|
58
|
-
const soffset =
|
|
59
|
-
typeof flags.offset === "string" ? parseInt(flags.offset, 10) || 0 : 0;
|
|
60
|
-
return await cmdSchemas(source, slimit, soffset);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
case "schema": {
|
|
64
|
-
const schemaName = positional[0] || flags.name;
|
|
65
|
-
if (!source || !schemaName)
|
|
66
|
-
throw new Error("Usage: swagger.mjs schema <source> <schema_name>");
|
|
67
|
-
return await cmdSchema(source, schemaName as string);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
case "search": {
|
|
71
|
-
const query = positional[0] || flags.query;
|
|
72
|
-
if (!source || !query)
|
|
73
|
-
throw new Error("Usage: swagger.mjs search <source> <query>");
|
|
74
|
-
return await cmdSearch(source, query as string);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
default:
|
|
78
|
-
throw new Error(`Unknown command: ${command}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
package/src/commands/endpoint.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { loadSpec } from "../core/loader.ts";
|
|
2
|
-
import { getEndpoints, getPathParams } from "../core/queries.ts";
|
|
3
|
-
import { deepResolve } from "../core/resolve.ts";
|
|
4
|
-
import { fmtEndpoint } from "../formatters/endpoint.ts";
|
|
5
|
-
import type { FormattedEndpoint } from "../types.ts";
|
|
6
|
-
|
|
7
|
-
export async function cmdEndpoint(
|
|
8
|
-
source: string,
|
|
9
|
-
path: string,
|
|
10
|
-
method: string,
|
|
11
|
-
full = false,
|
|
12
|
-
): Promise<string> {
|
|
13
|
-
const spec = await loadSpec(source);
|
|
14
|
-
method = method.toLowerCase();
|
|
15
|
-
const op = spec.paths[path]?.[method];
|
|
16
|
-
if (!op) {
|
|
17
|
-
const similar = getEndpoints(spec)
|
|
18
|
-
.filter((e) => e.path.includes(path) || path.includes(e.path))
|
|
19
|
-
.slice(0, 5);
|
|
20
|
-
const hint = similar.length
|
|
21
|
-
? "\n\nDid you mean?\n" +
|
|
22
|
-
similar.map((e) => ` ${e.method.toUpperCase()} ${e.path}`).join("\n")
|
|
23
|
-
: "";
|
|
24
|
-
throw new Error(`No ${method.toUpperCase()} ${path} found.${hint}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const paramNames = getPathParams(path);
|
|
28
|
-
const opParams: any[] = op.parameters || [];
|
|
29
|
-
// Swagger 2.0 models the request body as a parameter with in:"body" + schema,
|
|
30
|
-
// separate from the path/query/header parameters.
|
|
31
|
-
const bodyParam = opParams.find((p: any) => p.in === "body");
|
|
32
|
-
const nonBodyParams = opParams.filter((p: any) => p.in !== "body");
|
|
33
|
-
|
|
34
|
-
const mergedParams = [
|
|
35
|
-
...paramNames.map(
|
|
36
|
-
(n) =>
|
|
37
|
-
nonBodyParams.find((p: any) => p.name === n) || {
|
|
38
|
-
name: n,
|
|
39
|
-
in: "path",
|
|
40
|
-
required: true,
|
|
41
|
-
description: `Path param: ${n}`,
|
|
42
|
-
schema: { type: "string" },
|
|
43
|
-
},
|
|
44
|
-
),
|
|
45
|
-
...nonBodyParams.filter((p: any) => !paramNames.includes(p.name)),
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
// Resolve $refs only under --full; otherwise leave schemas (with $ref) as-is.
|
|
49
|
-
const resolve = (schema: any): any =>
|
|
50
|
-
full && schema ? deepResolve(schema, spec) : schema;
|
|
51
|
-
|
|
52
|
-
// Request body: OpenAPI 3 requestBody wins; otherwise build one from a
|
|
53
|
-
// Swagger 2.0 body parameter so its schema renders in the Request Body
|
|
54
|
-
// section (instead of "object" in the parameters table).
|
|
55
|
-
let requestBody: any;
|
|
56
|
-
if (op.requestBody) {
|
|
57
|
-
requestBody = {
|
|
58
|
-
description: op.requestBody.description,
|
|
59
|
-
required: op.requestBody.required,
|
|
60
|
-
content: Object.fromEntries(
|
|
61
|
-
Object.entries(op.requestBody.content || {}).map(
|
|
62
|
-
([ct, mt]: [string, any]) => [ct, { schema: resolve(mt.schema) }],
|
|
63
|
-
),
|
|
64
|
-
),
|
|
65
|
-
};
|
|
66
|
-
} else if (bodyParam) {
|
|
67
|
-
const ct =
|
|
68
|
-
(op.consumes || spec.consumes || ["application/json"])[0] ||
|
|
69
|
-
"application/json";
|
|
70
|
-
requestBody = {
|
|
71
|
-
description: bodyParam.description,
|
|
72
|
-
required: bodyParam.required,
|
|
73
|
-
content: { [ct]: { schema: resolve(bodyParam.schema) } },
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Responses: OpenAPI 3 uses response.content; Swagger 2.0 uses a bare
|
|
78
|
-
// response.schema + the operation's/root produces list.
|
|
79
|
-
const produces = op.produces || spec.produces || ["application/json"];
|
|
80
|
-
let responses: any;
|
|
81
|
-
if (op.responses) {
|
|
82
|
-
responses = Object.fromEntries(
|
|
83
|
-
Object.entries(op.responses).map(([code, resp]: [string, any]) => {
|
|
84
|
-
let content: any;
|
|
85
|
-
if (resp.content) {
|
|
86
|
-
content = Object.fromEntries(
|
|
87
|
-
Object.entries(resp.content).map(([ct, mt]: [string, any]) => [
|
|
88
|
-
ct,
|
|
89
|
-
{ schema: resolve(mt.schema) },
|
|
90
|
-
]),
|
|
91
|
-
);
|
|
92
|
-
} else if (resp.schema) {
|
|
93
|
-
const ct = produces[0] || "application/json";
|
|
94
|
-
content = { [ct]: { schema: resolve(resp.schema) } };
|
|
95
|
-
}
|
|
96
|
-
return [code, { description: resp.description, content }];
|
|
97
|
-
}),
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const ep: FormattedEndpoint = {
|
|
102
|
-
path,
|
|
103
|
-
method,
|
|
104
|
-
summary: op.summary,
|
|
105
|
-
description: op.description,
|
|
106
|
-
operationId: op.operationId,
|
|
107
|
-
tags: op.tags || [],
|
|
108
|
-
deprecated: op.deprecated || false,
|
|
109
|
-
parameters: mergedParams.map((p: any) => ({
|
|
110
|
-
...p,
|
|
111
|
-
schema: resolve(p.schema),
|
|
112
|
-
})),
|
|
113
|
-
requestBody,
|
|
114
|
-
responses,
|
|
115
|
-
security: op.security,
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
return fmtEndpoint(ep);
|
|
119
|
-
}
|
package/src/commands/info.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { OpenAPISpec } from "../types.ts";
|
|
2
|
-
import { loadSpec, getServerUrl } from "../core/loader.ts";
|
|
3
|
-
import { getEndpoints, getSchemas, getTags } from "../core/queries.ts";
|
|
4
|
-
|
|
5
|
-
export async function cmdInfo(source: string): Promise<string> {
|
|
6
|
-
const spec = await loadSpec(source);
|
|
7
|
-
const eps = getEndpoints(spec);
|
|
8
|
-
const schemas = getSchemas(spec);
|
|
9
|
-
const tags = getTags(spec);
|
|
10
|
-
const url = getServerUrl(spec);
|
|
11
|
-
const sv = spec.openapi || spec.swagger || "unknown";
|
|
12
|
-
|
|
13
|
-
const lines: string[] = [
|
|
14
|
-
`# ${spec.info.title}`,
|
|
15
|
-
"",
|
|
16
|
-
`Version: ${spec.info.version}`,
|
|
17
|
-
`OpenAPI Version: ${sv}`,
|
|
18
|
-
`Server: \`${url || "Not specified"}\``,
|
|
19
|
-
"",
|
|
20
|
-
spec.info.description || "",
|
|
21
|
-
"",
|
|
22
|
-
"## Summary",
|
|
23
|
-
`- Endpoints: ${eps.length}`,
|
|
24
|
-
`- Schemas: ${schemas.length}`,
|
|
25
|
-
`- Tags: ${tags.length}`,
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
if (spec.info.contact) {
|
|
29
|
-
lines.push("", "## Contact");
|
|
30
|
-
for (const k of ["name", "email", "url"] as const) {
|
|
31
|
-
const val = spec.info.contact[k];
|
|
32
|
-
if (val)
|
|
33
|
-
lines.push(`- ${k.charAt(0).toUpperCase() + k.slice(1)}: ${val}`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (spec.info.license) {
|
|
38
|
-
lines.push("", "## License");
|
|
39
|
-
lines.push(`- Name: ${spec.info.license.name}`);
|
|
40
|
-
if (spec.info.license.url) lines.push(`- URL: ${spec.info.license.url}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return lines.join("\n");
|
|
44
|
-
}
|
package/src/commands/paths.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { loadSpec } from "../core/loader.ts";
|
|
2
|
-
import { getEndpoints } from "../core/queries.ts";
|
|
3
|
-
|
|
4
|
-
export async function cmdPaths(
|
|
5
|
-
source: string,
|
|
6
|
-
tag?: string,
|
|
7
|
-
limit = 50,
|
|
8
|
-
offset = 0,
|
|
9
|
-
): Promise<string> {
|
|
10
|
-
const spec = await loadSpec(source);
|
|
11
|
-
const all = getEndpoints(spec, tag);
|
|
12
|
-
const paginated = all.slice(offset, offset + limit);
|
|
13
|
-
|
|
14
|
-
if (!paginated.length) {
|
|
15
|
-
return all.length === 0
|
|
16
|
-
? "No endpoints found."
|
|
17
|
-
: `No more endpoints (offset ${offset} of ${all.length}).`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const hasMore = all.length > offset + paginated.length;
|
|
21
|
-
const lines: string[] = ["# Endpoints"];
|
|
22
|
-
if (tag) lines.push(`\nFiltered by tag: \`${tag}\``);
|
|
23
|
-
lines.push(`\n${all.length} total (showing ${paginated.length})`, "");
|
|
24
|
-
|
|
25
|
-
for (const ep of paginated) {
|
|
26
|
-
lines.push(
|
|
27
|
-
`### \`${ep.method.toUpperCase()}\` ${ep.path}${ep.deprecated ? " ~~(deprecated)~~" : ""}`,
|
|
28
|
-
);
|
|
29
|
-
if (ep.summary) lines.push(ep.summary);
|
|
30
|
-
if (ep.operationId) lines.push(`- Operation ID: \`${ep.operationId}\``);
|
|
31
|
-
if (ep.tags.length)
|
|
32
|
-
lines.push(`- Tags: ${ep.tags.map((t) => `\`${t}\``).join(", ")}`);
|
|
33
|
-
lines.push("");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (hasMore) {
|
|
37
|
-
lines.push(
|
|
38
|
-
`> Showing ${paginated.length} of ${all.length}. Use --offset=${offset + paginated.length} to see more.`,
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return lines.join("\n");
|
|
43
|
-
}
|
package/src/commands/schema.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { loadSpec } from "../core/loader.ts";
|
|
2
|
-
import { getSchemas, getSchemaDetail } from "../core/queries.ts";
|
|
3
|
-
import { fmtSchema } from "../formatters/schema.ts";
|
|
4
|
-
|
|
5
|
-
export async function cmdSchema(
|
|
6
|
-
source: string,
|
|
7
|
-
schemaName: string,
|
|
8
|
-
): Promise<string> {
|
|
9
|
-
const spec = await loadSpec(source);
|
|
10
|
-
const schema = getSchemaDetail(spec, schemaName);
|
|
11
|
-
if (!schema) {
|
|
12
|
-
const suggestions = getSchemas(spec)
|
|
13
|
-
.slice(0, 10)
|
|
14
|
-
.map((s) => s.name);
|
|
15
|
-
throw new Error(
|
|
16
|
-
`Schema '${schemaName}' not found.\n\nAvailable:\n${suggestions.map((s) => ` - ${s}`).join("\n")}`,
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
return [`# Schema: ${schemaName}`, "", "```", fmtSchema(schema), "```"].join(
|
|
20
|
-
"\n",
|
|
21
|
-
);
|
|
22
|
-
}
|
package/src/commands/schemas.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { loadSpec } from "../core/loader.ts";
|
|
2
|
-
import { getSchemas } from "../core/queries.ts";
|
|
3
|
-
|
|
4
|
-
export async function cmdSchemas(
|
|
5
|
-
source: string,
|
|
6
|
-
limit = 50,
|
|
7
|
-
offset = 0,
|
|
8
|
-
): Promise<string> {
|
|
9
|
-
const spec = await loadSpec(source);
|
|
10
|
-
const all = getSchemas(spec);
|
|
11
|
-
const paginated = all.slice(offset, offset + limit);
|
|
12
|
-
|
|
13
|
-
if (!paginated.length) {
|
|
14
|
-
return all.length === 0
|
|
15
|
-
? "No schemas defined."
|
|
16
|
-
: `No more schemas (offset ${offset} of ${all.length}).`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const hasMore = all.length > offset + paginated.length;
|
|
20
|
-
const lines: string[] = [
|
|
21
|
-
"# Component Schemas",
|
|
22
|
-
"",
|
|
23
|
-
`${all.length} total (showing ${paginated.length})`,
|
|
24
|
-
"",
|
|
25
|
-
"| Name | Type | Properties | Description |",
|
|
26
|
-
"|------|------|------------|-------------|",
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
for (const s of paginated) {
|
|
30
|
-
lines.push(
|
|
31
|
-
`| \`${s.name}\` | ${s.type} | ${s.properties} | ${(s.description || "").slice(0, 80)} |`,
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (hasMore) {
|
|
36
|
-
lines.push(
|
|
37
|
-
`\n> Showing ${paginated.length} of ${all.length}. Use --offset=${offset + paginated.length} to see more.`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
lines.push(
|
|
42
|
-
"\nUse `openapi-explorer schema <source> <schema_name>` for details.",
|
|
43
|
-
);
|
|
44
|
-
return lines.join("\n");
|
|
45
|
-
}
|
package/src/commands/search.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { loadSpec } from "../core/loader.ts";
|
|
2
|
-
import { searchSpec } from "../core/queries.ts";
|
|
3
|
-
|
|
4
|
-
export async function cmdSearch(
|
|
5
|
-
source: string,
|
|
6
|
-
query: string,
|
|
7
|
-
): Promise<string> {
|
|
8
|
-
const spec = await loadSpec(source);
|
|
9
|
-
const results = searchSpec(spec, query);
|
|
10
|
-
if (!results.length) return `No results for '${query}'.`;
|
|
11
|
-
|
|
12
|
-
const lines: string[] = [
|
|
13
|
-
`# Search Results for '${query}'`,
|
|
14
|
-
"",
|
|
15
|
-
`Found ${results.length} matches`,
|
|
16
|
-
"",
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
const eps = results.filter((r) => r.type === "endpoint");
|
|
20
|
-
const schemas = results.filter((r) => r.type === "schema");
|
|
21
|
-
const props = results.filter((r) => r.type === "property");
|
|
22
|
-
|
|
23
|
-
if (eps.length) {
|
|
24
|
-
lines.push(`## Endpoints (${eps.length})`);
|
|
25
|
-
for (const e of eps)
|
|
26
|
-
lines.push(
|
|
27
|
-
`- ${e.method?.toUpperCase()} \`${e.path}\` - ${e.summary || ""}`,
|
|
28
|
-
);
|
|
29
|
-
lines.push("");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (schemas.length) {
|
|
33
|
-
lines.push(`## Schemas (${schemas.length})`);
|
|
34
|
-
for (const s of schemas)
|
|
35
|
-
lines.push(`- ${s.schemaName} - ${s.summary || ""}`);
|
|
36
|
-
lines.push("");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (props.length) {
|
|
40
|
-
lines.push(`## Properties (${props.length})`);
|
|
41
|
-
for (const p of props)
|
|
42
|
-
lines.push(
|
|
43
|
-
`- \`${p.schemaName}.${p.propertyName}\` - ${p.summary || ""}`,
|
|
44
|
-
);
|
|
45
|
-
lines.push("");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return lines.join("\n");
|
|
49
|
-
}
|
package/src/commands/tags.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { loadSpec } from "../core/loader.ts";
|
|
2
|
-
import { getTags } from "../core/queries.ts";
|
|
3
|
-
|
|
4
|
-
export async function cmdTags(source: string): Promise<string> {
|
|
5
|
-
const spec = await loadSpec(source);
|
|
6
|
-
const tags = getTags(spec);
|
|
7
|
-
if (!tags.length) return "No tags found.";
|
|
8
|
-
const lines = ["# Tags", "", "| Tag | Endpoints |", "|-----|-----------|"];
|
|
9
|
-
for (const t of tags) lines.push(`| \`${t.name}\` | ${t.count} |`);
|
|
10
|
-
return lines.join("\n");
|
|
11
|
-
}
|
package/src/core/loader.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import type { OpenAPISpec } from "../types.ts";
|
|
4
|
-
|
|
5
|
-
export async function loadSpec(source: string): Promise<OpenAPISpec> {
|
|
6
|
-
const isUrl = /^https?:\/\//i.test(source);
|
|
7
|
-
let raw: string;
|
|
8
|
-
|
|
9
|
-
if (isUrl) {
|
|
10
|
-
const res = await fetch(source, {
|
|
11
|
-
headers: { Accept: "application/json, text/plain" },
|
|
12
|
-
signal: AbortSignal.timeout(30000),
|
|
13
|
-
});
|
|
14
|
-
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${source}`);
|
|
15
|
-
raw = await res.text();
|
|
16
|
-
} else {
|
|
17
|
-
raw = await readFile(resolve(source), "utf-8");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const data = JSON.parse(raw) as OpenAPISpec;
|
|
21
|
-
if (!data.openapi && !data.swagger)
|
|
22
|
-
throw new Error("Not a valid OpenAPI/Swagger spec.");
|
|
23
|
-
if (!data.info) throw new Error("Invalid spec: missing 'info' field.");
|
|
24
|
-
data.info.title = data.info.title || "Untitled API";
|
|
25
|
-
data.info.version = data.info.version || "0.0.0";
|
|
26
|
-
data.paths = data.paths || {};
|
|
27
|
-
return data;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getServerUrl(spec: OpenAPISpec): string {
|
|
31
|
-
if (spec.servers?.length) {
|
|
32
|
-
let url = spec.servers[0]!.url;
|
|
33
|
-
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
34
|
-
}
|
|
35
|
-
if (spec.host)
|
|
36
|
-
return `${spec.schemes?.[0] || "https"}://${spec.host}${spec.basePath || ""}`;
|
|
37
|
-
return "";
|
|
38
|
-
}
|