@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.
@@ -0,0 +1 @@
1
+ export { };
package/dist/index.mjs ADDED
@@ -0,0 +1,589 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ //#region src/core/loader.ts
5
+ async function loadSpec(source) {
6
+ const isUrl = /^https?:\/\//i.test(source);
7
+ let raw;
8
+ if (isUrl) {
9
+ const res = await fetch(source, {
10
+ headers: { Accept: "application/json, text/plain" },
11
+ signal: AbortSignal.timeout(3e4)
12
+ });
13
+ if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${source}`);
14
+ raw = await res.text();
15
+ } else raw = await readFile(resolve(source), "utf-8");
16
+ const data = JSON.parse(raw);
17
+ if (!data.openapi && !data.swagger) throw new Error("Not a valid OpenAPI/Swagger spec.");
18
+ if (!data.info) throw new Error("Invalid spec: missing 'info' field.");
19
+ data.info.title = data.info.title || "Untitled API";
20
+ data.info.version = data.info.version || "0.0.0";
21
+ data.paths = data.paths || {};
22
+ return data;
23
+ }
24
+ function getServerUrl(spec) {
25
+ if (spec.servers?.length) {
26
+ let url = spec.servers[0].url;
27
+ return url.endsWith("/") ? url.slice(0, -1) : url;
28
+ }
29
+ if (spec.host) return `${spec.schemes?.[0] || "https"}://${spec.host}${spec.basePath || ""}`;
30
+ return "";
31
+ }
32
+ //#endregion
33
+ //#region src/core/queries.ts
34
+ const HTTP_METHODS = [
35
+ "get",
36
+ "post",
37
+ "put",
38
+ "patch",
39
+ "delete",
40
+ "options",
41
+ "head"
42
+ ];
43
+ const METHOD_PRIORITY = {
44
+ get: 0,
45
+ post: 1,
46
+ put: 2,
47
+ patch: 3,
48
+ delete: 4,
49
+ options: 5,
50
+ head: 6
51
+ };
52
+ function getEndpoints(spec, tag) {
53
+ const eps = [];
54
+ for (const [path, pathItem] of Object.entries(spec.paths)) for (const method of HTTP_METHODS) {
55
+ const op = pathItem[method];
56
+ if (!op) continue;
57
+ if (tag && (!op.tags || !op.tags.includes(tag))) continue;
58
+ eps.push({
59
+ path,
60
+ method,
61
+ summary: op.summary || op.operationId || `${method.toUpperCase()} ${path}`,
62
+ operationId: op.operationId,
63
+ tags: op.tags || [],
64
+ deprecated: op.deprecated || false
65
+ });
66
+ }
67
+ eps.sort((a, b) => {
68
+ if (a.path !== b.path) return a.path.localeCompare(b.path);
69
+ return (METHOD_PRIORITY[a.method] ?? 99) - (METHOD_PRIORITY[b.method] ?? 99);
70
+ });
71
+ return eps;
72
+ }
73
+ function getTags(spec) {
74
+ const counts = /* @__PURE__ */ new Map();
75
+ for (const pathItem of Object.values(spec.paths)) for (const method of HTTP_METHODS) {
76
+ const op = pathItem[method];
77
+ if (!op?.tags) continue;
78
+ for (const tag of op.tags) counts.set(tag, (counts.get(tag) || 0) + 1);
79
+ }
80
+ return Array.from(counts.entries()).map(([name, count]) => ({
81
+ name,
82
+ count
83
+ })).sort((a, b) => a.name.localeCompare(b.name));
84
+ }
85
+ function getSchemas(spec) {
86
+ const schemas = spec.components?.schemas || spec.definitions || {};
87
+ return Object.entries(schemas).map(([name, s]) => ({
88
+ name,
89
+ type: s.type || "object",
90
+ description: s.description || s.title,
91
+ properties: s.properties ? Object.keys(s.properties).length : 0
92
+ })).sort((a, b) => a.name.localeCompare(b.name));
93
+ }
94
+ function getSchemaDetail(spec, name) {
95
+ return (spec.components?.schemas || spec.definitions || {})[name] || null;
96
+ }
97
+ function getPathParams(path) {
98
+ const m = path.match(/\{(\w+)\}/g);
99
+ return m ? m.map((s) => s.slice(1, -1)) : [];
100
+ }
101
+ function searchSpec(spec, query) {
102
+ const results = [];
103
+ const lower = query.toLowerCase();
104
+ for (const [path, pi] of Object.entries(spec.paths)) for (const method of HTTP_METHODS) {
105
+ const op = pi[method];
106
+ if (!op) continue;
107
+ if ([
108
+ path,
109
+ method,
110
+ op.summary,
111
+ op.operationId,
112
+ op.description,
113
+ ...op.tags || []
114
+ ].filter(Boolean).join(" ").toLowerCase().includes(lower)) results.push({
115
+ type: "endpoint",
116
+ path,
117
+ method,
118
+ match: `${method.toUpperCase()} ${path}`,
119
+ summary: op.summary || op.operationId
120
+ });
121
+ }
122
+ const schemas = spec.components?.schemas || spec.definitions || {};
123
+ for (const [name, s] of Object.entries(schemas)) {
124
+ const sAny = s;
125
+ if ([
126
+ name,
127
+ sAny.description,
128
+ sAny.title
129
+ ].filter(Boolean).join(" ").toLowerCase().includes(lower)) results.push({
130
+ type: "schema",
131
+ schemaName: name,
132
+ match: `Schema: ${name}`,
133
+ summary: sAny.description
134
+ });
135
+ if (sAny.properties) for (const [pn, ps] of Object.entries(sAny.properties)) {
136
+ const psAny = ps;
137
+ if ([pn, psAny.description].filter(Boolean).join(" ").toLowerCase().includes(lower)) results.push({
138
+ type: "property",
139
+ schemaName: name,
140
+ propertyName: pn,
141
+ match: `Schema ${name} > ${pn}`,
142
+ summary: psAny.description
143
+ });
144
+ }
145
+ }
146
+ return results;
147
+ }
148
+ //#endregion
149
+ //#region src/commands/info.ts
150
+ async function cmdInfo(source) {
151
+ const spec = await loadSpec(source);
152
+ const eps = getEndpoints(spec);
153
+ const schemas = getSchemas(spec);
154
+ const tags = getTags(spec);
155
+ const url = getServerUrl(spec);
156
+ const sv = spec.openapi || spec.swagger || "unknown";
157
+ const lines = [
158
+ `# ${spec.info.title}`,
159
+ "",
160
+ `Version: ${spec.info.version}`,
161
+ `OpenAPI Version: ${sv}`,
162
+ `Server: \`${url || "Not specified"}\``,
163
+ "",
164
+ spec.info.description || "",
165
+ "",
166
+ "## Summary",
167
+ `- Endpoints: ${eps.length}`,
168
+ `- Schemas: ${schemas.length}`,
169
+ `- Tags: ${tags.length}`
170
+ ];
171
+ if (spec.info.contact) {
172
+ lines.push("", "## Contact");
173
+ for (const k of [
174
+ "name",
175
+ "email",
176
+ "url"
177
+ ]) {
178
+ const val = spec.info.contact[k];
179
+ if (val) lines.push(`- ${k.charAt(0).toUpperCase() + k.slice(1)}: ${val}`);
180
+ }
181
+ }
182
+ if (spec.info.license) {
183
+ lines.push("", "## License");
184
+ lines.push(`- Name: ${spec.info.license.name}`);
185
+ if (spec.info.license.url) lines.push(`- URL: ${spec.info.license.url}`);
186
+ }
187
+ return lines.join("\n");
188
+ }
189
+ //#endregion
190
+ //#region src/commands/tags.ts
191
+ async function cmdTags(source) {
192
+ const tags = getTags(await loadSpec(source));
193
+ if (!tags.length) return "No tags found.";
194
+ const lines = [
195
+ "# Tags",
196
+ "",
197
+ "| Tag | Endpoints |",
198
+ "|-----|-----------|"
199
+ ];
200
+ for (const t of tags) lines.push(`| \`${t.name}\` | ${t.count} |`);
201
+ return lines.join("\n");
202
+ }
203
+ //#endregion
204
+ //#region src/commands/paths.ts
205
+ async function cmdPaths(source, tag, limit = 50, offset = 0) {
206
+ const all = getEndpoints(await loadSpec(source), tag);
207
+ const paginated = all.slice(offset, offset + limit);
208
+ if (!paginated.length) return all.length === 0 ? "No endpoints found." : `No more endpoints (offset ${offset} of ${all.length}).`;
209
+ const hasMore = all.length > offset + paginated.length;
210
+ const lines = ["# Endpoints"];
211
+ if (tag) lines.push(`\nFiltered by tag: \`${tag}\``);
212
+ lines.push(`\n${all.length} total (showing ${paginated.length})`, "");
213
+ for (const ep of paginated) {
214
+ lines.push(`### \`${ep.method.toUpperCase()}\` ${ep.path}${ep.deprecated ? " ~~(deprecated)~~" : ""}`);
215
+ if (ep.summary) lines.push(ep.summary);
216
+ if (ep.operationId) lines.push(`- Operation ID: \`${ep.operationId}\``);
217
+ if (ep.tags.length) lines.push(`- Tags: ${ep.tags.map((t) => `\`${t}\``).join(", ")}`);
218
+ lines.push("");
219
+ }
220
+ if (hasMore) lines.push(`> Showing ${paginated.length} of ${all.length}. Use --offset=${offset + paginated.length} to see more.`);
221
+ return lines.join("\n");
222
+ }
223
+ //#endregion
224
+ //#region src/core/resolve.ts
225
+ function resolveRef(spec, ref) {
226
+ const parts = ref.replace(/^#\//, "").split("/");
227
+ let cur = spec;
228
+ for (const p of parts) if (cur && typeof cur === "object" && p in cur) cur = cur[p];
229
+ else return null;
230
+ return cur;
231
+ }
232
+ function deepResolve(schema, spec, visited = /* @__PURE__ */ new Set()) {
233
+ if (!schema || typeof schema !== "object") return schema;
234
+ const obj = schema;
235
+ if (obj.$ref && typeof obj.$ref === "string") {
236
+ if (visited.has(obj.$ref)) return {
237
+ $ref: obj.$ref,
238
+ description: `(circular: ${obj.$ref})`
239
+ };
240
+ visited.add(obj.$ref);
241
+ const resolved = resolveRef(spec, obj.$ref);
242
+ if (resolved && typeof resolved === "object" && !Array.isArray(resolved)) {
243
+ const merged = { ...deepResolve(resolved, spec, new Set(visited)) };
244
+ for (const k of Object.keys(obj)) if (k !== "$ref") merged[k] = obj[k];
245
+ return merged;
246
+ }
247
+ return obj;
248
+ }
249
+ const result = {};
250
+ for (const [k, v] of Object.entries(obj)) if (k === "properties" && v && typeof v === "object") {
251
+ const props = {};
252
+ for (const [pn, ps] of Object.entries(v)) props[pn] = deepResolve(ps, spec, new Set(visited));
253
+ result[k] = props;
254
+ } else if ((k === "items" || k === "additionalProperties") && v && typeof v === "object" && !Array.isArray(v)) result[k] = deepResolve(v, spec, new Set(visited));
255
+ else if ([
256
+ "oneOf",
257
+ "anyOf",
258
+ "allOf"
259
+ ].includes(k) && Array.isArray(v)) result[k] = v.map((s) => deepResolve(s, spec, new Set(visited)));
260
+ else result[k] = v;
261
+ return result;
262
+ }
263
+ //#endregion
264
+ //#region src/formatters/schema.ts
265
+ function fmtSchema(schema, indent = 0) {
266
+ const pad = " ".repeat(indent);
267
+ if (!schema || typeof schema !== "object") return `${pad}${String(schema)}`;
268
+ const s = schema;
269
+ if (s.$ref && typeof s.$ref === "string") return `${pad}$ref: "${s.$ref.split("/").pop()}"`;
270
+ if (s.enum && Array.isArray(s.enum)) return `${pad}enum: [${s.enum.map((v) => JSON.stringify(v)).join(", ")}]`;
271
+ for (const kw of [
272
+ "oneOf",
273
+ "anyOf",
274
+ "allOf"
275
+ ]) if (s[kw] && Array.isArray(s[kw])) {
276
+ const lines = [`${pad}${kw}:`];
277
+ for (let i = 0; i < s[kw].length; i++) {
278
+ if (i > 0) lines.push(`${pad} ---`);
279
+ lines.push(fmtSchema(s[kw][i], indent + 1));
280
+ }
281
+ return lines.join("\n");
282
+ }
283
+ const allLines = [];
284
+ if (s.description && typeof s.description === "string") allLines.push(`${pad}// ${s.description}`);
285
+ allLines.push(`${pad}type: ${s.type || "any"}${s.format ? `<${s.format}>` : ""}${s.nullable ? " | null" : ""}`);
286
+ if (s.properties && typeof s.properties === "object") {
287
+ const req = new Set(Array.isArray(s.required) ? s.required : []);
288
+ for (const [pn, ps] of Object.entries(s.properties)) {
289
+ allLines.push(`${pad}${pn}${req.has(pn) ? " (required)" : ""}:`);
290
+ allLines.push(fmtSchema(ps, indent + 1));
291
+ }
292
+ }
293
+ if (s.items) {
294
+ allLines.push(`${pad}items:`);
295
+ allLines.push(fmtSchema(s.items, indent + 1));
296
+ }
297
+ if (s.additionalProperties && typeof s.additionalProperties === "object") {
298
+ allLines.push(`${pad}additionalProperties:`);
299
+ allLines.push(fmtSchema(s.additionalProperties, indent + 1));
300
+ }
301
+ if (s.example !== void 0) allLines.push(`${pad}example: ${JSON.stringify(s.example)}`);
302
+ if (s.default !== void 0) allLines.push(`${pad}default: ${JSON.stringify(s.default)}`);
303
+ for (const k of [
304
+ "minLength",
305
+ "maxLength",
306
+ "minimum",
307
+ "maximum"
308
+ ]) if (s[k] !== void 0) allLines.push(`${pad}${k}: ${s[k]}`);
309
+ if (s.pattern) allLines.push(`${pad}pattern: ${s.pattern}`);
310
+ return allLines.join("\n");
311
+ }
312
+ //#endregion
313
+ //#region src/formatters/endpoint.ts
314
+ function fmtEndpoint(ep) {
315
+ const lines = [`# ${ep.method.toUpperCase()} ${ep.path}`];
316
+ if (ep.deprecated) lines.push("", "DEPRECATED");
317
+ if (ep.summary) lines.push("", ep.summary);
318
+ if (ep.description) lines.push("", ep.description);
319
+ if (ep.operationId) lines.push("", `Operation ID: \`${ep.operationId}\``);
320
+ if (ep.tags?.length) lines.push("", `Tags: ${ep.tags.map((t) => `\`${t}\``).join(", ")}`);
321
+ if (ep.parameters?.length) {
322
+ lines.push("", "## Parameters", "", "| Name | In | Type | Required | Description |", "|------|----|------|----------|-------------|");
323
+ for (const p of ep.parameters) lines.push(`| \`${p.name}\` | ${p.in} | ${p.schema?.type || p.type || "string"} | ${p.required ? "Yes" : "No"} | ${p.description || ""} |`);
324
+ }
325
+ if (ep.requestBody) {
326
+ lines.push("", "## Request Body");
327
+ if (ep.requestBody.required) lines.push("> Required");
328
+ if (ep.requestBody.description) lines.push("", ep.requestBody.description);
329
+ for (const [ct, mt] of Object.entries(ep.requestBody.content || {})) lines.push("", `${ct}:`, "```", fmtSchema(mt.schema || {}), "```");
330
+ }
331
+ if (ep.responses) {
332
+ lines.push("", "## Responses");
333
+ for (const [code, resp] of Object.entries(ep.responses)) {
334
+ const r = resp;
335
+ lines.push("", `### ${code}`, r.description);
336
+ if (r.content) for (const [ct, mt] of Object.entries(r.content)) lines.push("", `${ct}:`, "```", fmtSchema(mt.schema || {}), "```");
337
+ }
338
+ }
339
+ if (ep.security?.length) {
340
+ lines.push("", "## Security");
341
+ for (const sec of ep.security) for (const [scheme, scopes] of Object.entries(sec)) lines.push(`- \`${scheme}\`${Array.isArray(scopes) && scopes.length ? ` [${scopes.join(", ")}]` : ""}`);
342
+ }
343
+ return lines.join("\n");
344
+ }
345
+ //#endregion
346
+ //#region src/commands/endpoint.ts
347
+ async function cmdEndpoint(source, path, method, full = false) {
348
+ const spec = await loadSpec(source);
349
+ method = method.toLowerCase();
350
+ const op = spec.paths[path]?.[method];
351
+ if (!op) {
352
+ const similar = getEndpoints(spec).filter((e) => e.path.includes(path) || path.includes(e.path)).slice(0, 5);
353
+ const hint = similar.length ? "\n\nDid you mean?\n" + similar.map((e) => ` ${e.method.toUpperCase()} ${e.path}`).join("\n") : "";
354
+ throw new Error(`No ${method.toUpperCase()} ${path} found.${hint}`);
355
+ }
356
+ const paramNames = getPathParams(path);
357
+ const opParams = op.parameters || [];
358
+ const bodyParam = opParams.find((p) => p.in === "body");
359
+ const nonBodyParams = opParams.filter((p) => p.in !== "body");
360
+ const mergedParams = [...paramNames.map((n) => nonBodyParams.find((p) => p.name === n) || {
361
+ name: n,
362
+ in: "path",
363
+ required: true,
364
+ description: `Path param: ${n}`,
365
+ schema: { type: "string" }
366
+ }), ...nonBodyParams.filter((p) => !paramNames.includes(p.name))];
367
+ const resolve = (schema) => full && schema ? deepResolve(schema, spec) : schema;
368
+ let requestBody;
369
+ if (op.requestBody) requestBody = {
370
+ description: op.requestBody.description,
371
+ required: op.requestBody.required,
372
+ content: Object.fromEntries(Object.entries(op.requestBody.content || {}).map(([ct, mt]) => [ct, { schema: resolve(mt.schema) }]))
373
+ };
374
+ else if (bodyParam) {
375
+ const ct = (op.consumes || spec.consumes || ["application/json"])[0] || "application/json";
376
+ requestBody = {
377
+ description: bodyParam.description,
378
+ required: bodyParam.required,
379
+ content: { [ct]: { schema: resolve(bodyParam.schema) } }
380
+ };
381
+ }
382
+ const produces = op.produces || spec.produces || ["application/json"];
383
+ let responses;
384
+ if (op.responses) responses = Object.fromEntries(Object.entries(op.responses).map(([code, resp]) => {
385
+ let content;
386
+ if (resp.content) content = Object.fromEntries(Object.entries(resp.content).map(([ct, mt]) => [ct, { schema: resolve(mt.schema) }]));
387
+ else if (resp.schema) content = { [produces[0] || "application/json"]: { schema: resolve(resp.schema) } };
388
+ return [code, {
389
+ description: resp.description,
390
+ content
391
+ }];
392
+ }));
393
+ return fmtEndpoint({
394
+ path,
395
+ method,
396
+ summary: op.summary,
397
+ description: op.description,
398
+ operationId: op.operationId,
399
+ tags: op.tags || [],
400
+ deprecated: op.deprecated || false,
401
+ parameters: mergedParams.map((p) => ({
402
+ ...p,
403
+ schema: resolve(p.schema)
404
+ })),
405
+ requestBody,
406
+ responses,
407
+ security: op.security
408
+ });
409
+ }
410
+ //#endregion
411
+ //#region src/commands/schemas.ts
412
+ async function cmdSchemas(source, limit = 50, offset = 0) {
413
+ const all = getSchemas(await loadSpec(source));
414
+ const paginated = all.slice(offset, offset + limit);
415
+ if (!paginated.length) return all.length === 0 ? "No schemas defined." : `No more schemas (offset ${offset} of ${all.length}).`;
416
+ const hasMore = all.length > offset + paginated.length;
417
+ const lines = [
418
+ "# Component Schemas",
419
+ "",
420
+ `${all.length} total (showing ${paginated.length})`,
421
+ "",
422
+ "| Name | Type | Properties | Description |",
423
+ "|------|------|------------|-------------|"
424
+ ];
425
+ for (const s of paginated) lines.push(`| \`${s.name}\` | ${s.type} | ${s.properties} | ${(s.description || "").slice(0, 80)} |`);
426
+ if (hasMore) lines.push(`\n> Showing ${paginated.length} of ${all.length}. Use --offset=${offset + paginated.length} to see more.`);
427
+ lines.push("\nUse `openapi-explorer schema <source> <schema_name>` for details.");
428
+ return lines.join("\n");
429
+ }
430
+ //#endregion
431
+ //#region src/commands/schema.ts
432
+ async function cmdSchema(source, schemaName) {
433
+ const spec = await loadSpec(source);
434
+ const schema = getSchemaDetail(spec, schemaName);
435
+ if (!schema) {
436
+ const suggestions = getSchemas(spec).slice(0, 10).map((s) => s.name);
437
+ throw new Error(`Schema '${schemaName}' not found.\n\nAvailable:\n${suggestions.map((s) => ` - ${s}`).join("\n")}`);
438
+ }
439
+ return [
440
+ `# Schema: ${schemaName}`,
441
+ "",
442
+ "```",
443
+ fmtSchema(schema),
444
+ "```"
445
+ ].join("\n");
446
+ }
447
+ //#endregion
448
+ //#region src/commands/search.ts
449
+ async function cmdSearch(source, query) {
450
+ const results = searchSpec(await loadSpec(source), query);
451
+ if (!results.length) return `No results for '${query}'.`;
452
+ const lines = [
453
+ `# Search Results for '${query}'`,
454
+ "",
455
+ `Found ${results.length} matches`,
456
+ ""
457
+ ];
458
+ const eps = results.filter((r) => r.type === "endpoint");
459
+ const schemas = results.filter((r) => r.type === "schema");
460
+ const props = results.filter((r) => r.type === "property");
461
+ if (eps.length) {
462
+ lines.push(`## Endpoints (${eps.length})`);
463
+ for (const e of eps) lines.push(`- ${e.method?.toUpperCase()} \`${e.path}\` - ${e.summary || ""}`);
464
+ lines.push("");
465
+ }
466
+ if (schemas.length) {
467
+ lines.push(`## Schemas (${schemas.length})`);
468
+ for (const s of schemas) lines.push(`- ${s.schemaName} - ${s.summary || ""}`);
469
+ lines.push("");
470
+ }
471
+ if (props.length) {
472
+ lines.push(`## Properties (${props.length})`);
473
+ for (const p of props) lines.push(`- \`${p.schemaName}.${p.propertyName}\` - ${p.summary || ""}`);
474
+ lines.push("");
475
+ }
476
+ return lines.join("\n");
477
+ }
478
+ //#endregion
479
+ //#region src/cli/args.ts
480
+ function parseArgs(argv) {
481
+ const args = argv.slice(2);
482
+ if (!args.length || args[0] === "--help" || args[0] === "-h") return {
483
+ command: "help",
484
+ source: "",
485
+ flags: {},
486
+ positional: []
487
+ };
488
+ const command = args[0] ?? "";
489
+ const source = args[1] ?? "";
490
+ const positional = [];
491
+ const flags = {};
492
+ let i = 2;
493
+ while (i < args.length) {
494
+ const arg = args[i];
495
+ if (arg.startsWith("--")) {
496
+ const key = arg.slice(2);
497
+ if (key === "full") {
498
+ flags[key] = true;
499
+ i++;
500
+ } else if (key === "tag" || key === "limit" || key === "offset") {
501
+ flags[key] = args[i + 1] ?? "";
502
+ i += 2;
503
+ } else {
504
+ flags[key] = args[i + 1] ?? true;
505
+ i += 2;
506
+ }
507
+ } else {
508
+ positional.push(arg);
509
+ i++;
510
+ }
511
+ }
512
+ return {
513
+ command,
514
+ source,
515
+ flags,
516
+ positional
517
+ };
518
+ }
519
+ function printHelp() {
520
+ return `Usage: openapi-explorer <command> <source> [args...]
521
+
522
+ source can be a URL (https://...) or a local file path.
523
+
524
+ Commands:
525
+ info <source> API overview
526
+ tags <source> List tag groups
527
+ paths <source> [--tag <t>] [--limit N] [--offset N] List endpoints
528
+ endpoint <source> <path> <method> [--full] Endpoint detail
529
+ schemas <source> [--limit N] [--offset N] List schemas
530
+ schema <source> <schema_name> Schema detail
531
+ search <source> <query> Full-text search
532
+
533
+ Examples:
534
+ openapi-explorer info https://petstore.swagger.io/v2/swagger.json
535
+ openapi-explorer endpoint ./spec.json /pets/{petId} get --full
536
+ openapi-explorer paths ./spec.json --tag pets`;
537
+ }
538
+ //#endregion
539
+ //#region src/cli/dispatch.ts
540
+ async function dispatch(argv) {
541
+ const { command, source, flags, positional } = parseArgs(argv);
542
+ switch (command) {
543
+ case "help": return printHelp();
544
+ case "info":
545
+ if (!source) throw new Error("Usage: swagger.mjs info <source>");
546
+ return await cmdInfo(source);
547
+ case "tags":
548
+ if (!source) throw new Error("Usage: swagger.mjs tags <source>");
549
+ return await cmdTags(source);
550
+ case "paths":
551
+ if (!source) throw new Error("Usage: swagger.mjs paths <source> [--tag <t>] [--limit N] [--offset N]");
552
+ return await cmdPaths(source, typeof flags.tag === "string" ? flags.tag : void 0, typeof flags.limit === "string" ? parseInt(flags.limit, 10) || 50 : 50, typeof flags.offset === "string" ? parseInt(flags.offset, 10) || 0 : 0);
553
+ case "endpoint":
554
+ case "ep": {
555
+ const path = positional[0] || flags.path;
556
+ const method = positional[1] || flags.method;
557
+ if (!source || !path || !method) throw new Error("Usage: swagger.mjs endpoint <source> <path> <method> [--full]");
558
+ return await cmdEndpoint(source, path, method, flags.full === true);
559
+ }
560
+ case "schemas":
561
+ if (!source) throw new Error("Usage: swagger.mjs schemas <source> [--limit N] [--offset N]");
562
+ return await cmdSchemas(source, typeof flags.limit === "string" ? parseInt(flags.limit, 10) || 50 : 50, typeof flags.offset === "string" ? parseInt(flags.offset, 10) || 0 : 0);
563
+ case "schema": {
564
+ const schemaName = positional[0] || flags.name;
565
+ if (!source || !schemaName) throw new Error("Usage: swagger.mjs schema <source> <schema_name>");
566
+ return await cmdSchema(source, schemaName);
567
+ }
568
+ case "search": {
569
+ const query = positional[0] || flags.query;
570
+ if (!source || !query) throw new Error("Usage: swagger.mjs search <source> <query>");
571
+ return await cmdSearch(source, query);
572
+ }
573
+ default: throw new Error(`Unknown command: ${command}`);
574
+ }
575
+ }
576
+ //#endregion
577
+ //#region src/index.ts
578
+ async function main() {
579
+ const output = await dispatch(process.argv);
580
+ console.log(output);
581
+ }
582
+ main().catch((err) => {
583
+ console.error(err instanceof Error ? err.message : String(err));
584
+ process.exit(1);
585
+ });
586
+ //#endregion
587
+ export {};
588
+
589
+ //# sourceMappingURL=index.mjs.map