api-spec-cli 0.2.4 → 0.2.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/README.md +48 -1
- package/package.json +5 -3
- package/src/cli.js +222 -67
- package/src/commands/add.js +8 -6
- package/src/commands/auth.js +32 -32
- package/src/commands/call.js +4 -0
- package/src/commands/fetch.js +344 -344
- package/src/commands/grep.js +67 -67
- package/src/commands/list.js +13 -2
- package/src/commands/show.js +224 -224
- package/src/commands/skill.js +40 -0
- package/src/commands/specs.js +82 -82
- package/src/commands/types.js +167 -167
- package/src/commands/usage.js +15 -0
- package/src/commands/validate.js +295 -295
- package/src/dotenv.js +38 -0
- package/src/glob.js +34 -34
- package/src/mcp-client.js +23 -20
- package/src/oauth/auth-flow.js +2 -2
- package/src/oauth/provider.js +3 -4
- package/src/oauth/tokens.js +6 -0
- package/src/output.js +65 -61
- package/src/registry.js +79 -79
- package/src/resolve.js +21 -19
- package/src/secrets.js +46 -0
- package/src/skill/SKILL.md +112 -0
- package/src/usage.js +62 -0
package/src/commands/specs.js
CHANGED
|
@@ -1,82 +1,82 @@
|
|
|
1
|
-
import { parseArgs } from "../args.js";
|
|
2
|
-
import {
|
|
3
|
-
getRegistry,
|
|
4
|
-
saveRegistry,
|
|
5
|
-
getEntry,
|
|
6
|
-
removeCachedSpec,
|
|
7
|
-
saveCachedSpec,
|
|
8
|
-
allEntries,
|
|
9
|
-
} from "../registry.js";
|
|
10
|
-
import { fetchSpec } from "./fetch.js";
|
|
11
|
-
import { out } from "../output.js";
|
|
12
|
-
|
|
13
|
-
function findSection(registry, name) {
|
|
14
|
-
for (const section of ["mcp", "openapi", "graphql"]) {
|
|
15
|
-
if (registry[section]?.[name]) return section;
|
|
16
|
-
}
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function specsCmd(args) {
|
|
21
|
-
const { flags } = parseArgs(args);
|
|
22
|
-
const compact = flags.compact !== "false";
|
|
23
|
-
const registry = getRegistry();
|
|
24
|
-
|
|
25
|
-
const specs = allEntries(registry).map((e) => {
|
|
26
|
-
if (compact) {
|
|
27
|
-
return {
|
|
28
|
-
name: e.name,
|
|
29
|
-
type: e.type,
|
|
30
|
-
description: e.description || null,
|
|
31
|
-
enabled: e.enabled,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
return e;
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
out({ specs });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export async function registryMutate(action, args) {
|
|
41
|
-
const { positional } = parseArgs(args);
|
|
42
|
-
const name = positional[0];
|
|
43
|
-
if (!name) throw new Error(`Usage: spec ${action} <name>`);
|
|
44
|
-
|
|
45
|
-
const registry = getRegistry();
|
|
46
|
-
const section = findSection(registry, name);
|
|
47
|
-
if (!section) throw new Error(`No spec named '${name}'. Run 'spec specs' to see available.`);
|
|
48
|
-
|
|
49
|
-
if (action === "remove") {
|
|
50
|
-
delete registry[section][name];
|
|
51
|
-
saveRegistry(registry);
|
|
52
|
-
removeCachedSpec(name);
|
|
53
|
-
out({ ok: true, removed: name });
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (action === "enable") {
|
|
58
|
-
registry[section][name].enabled = true;
|
|
59
|
-
saveRegistry(registry);
|
|
60
|
-
out({ ok: true, enabled: name });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (action === "disable") {
|
|
65
|
-
registry[section][name].enabled = false;
|
|
66
|
-
saveRegistry(registry);
|
|
67
|
-
out({ ok: true, disabled: name });
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (action === "refresh") {
|
|
72
|
-
const entry = { ...registry[section][name], name, _section: section };
|
|
73
|
-
if (!entry.enabled) throw new Error(`Spec '${name}' is disabled. Enable it first.`);
|
|
74
|
-
const spec = await fetchSpec(entry);
|
|
75
|
-
saveCachedSpec(name, spec);
|
|
76
|
-
const count = spec.tools?.length ?? spec.operations?.length ?? 0;
|
|
77
|
-
out({ ok: true, refreshed: name, type: spec.type, count });
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
throw new Error(`Unknown registry action: ${action}`);
|
|
82
|
-
}
|
|
1
|
+
import { parseArgs } from "../args.js";
|
|
2
|
+
import {
|
|
3
|
+
getRegistry,
|
|
4
|
+
saveRegistry,
|
|
5
|
+
getEntry,
|
|
6
|
+
removeCachedSpec,
|
|
7
|
+
saveCachedSpec,
|
|
8
|
+
allEntries,
|
|
9
|
+
} from "../registry.js";
|
|
10
|
+
import { fetchSpec } from "./fetch.js";
|
|
11
|
+
import { out } from "../output.js";
|
|
12
|
+
|
|
13
|
+
function findSection(registry, name) {
|
|
14
|
+
for (const section of ["mcp", "openapi", "graphql"]) {
|
|
15
|
+
if (registry[section]?.[name]) return section;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function specsCmd(args) {
|
|
21
|
+
const { flags } = parseArgs(args);
|
|
22
|
+
const compact = flags.compact !== "false";
|
|
23
|
+
const registry = getRegistry();
|
|
24
|
+
|
|
25
|
+
const specs = allEntries(registry).map((e) => {
|
|
26
|
+
if (compact) {
|
|
27
|
+
return {
|
|
28
|
+
name: e.name,
|
|
29
|
+
type: e.type,
|
|
30
|
+
description: e.description || null,
|
|
31
|
+
enabled: e.enabled,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return e;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
out({ specs });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function registryMutate(action, args) {
|
|
41
|
+
const { positional } = parseArgs(args);
|
|
42
|
+
const name = positional[0];
|
|
43
|
+
if (!name) throw new Error(`Usage: spec ${action} <name>`);
|
|
44
|
+
|
|
45
|
+
const registry = getRegistry();
|
|
46
|
+
const section = findSection(registry, name);
|
|
47
|
+
if (!section) throw new Error(`No spec named '${name}'. Run 'spec specs' to see available.`);
|
|
48
|
+
|
|
49
|
+
if (action === "remove") {
|
|
50
|
+
delete registry[section][name];
|
|
51
|
+
saveRegistry(registry);
|
|
52
|
+
removeCachedSpec(name);
|
|
53
|
+
out({ ok: true, removed: name });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (action === "enable") {
|
|
58
|
+
registry[section][name].enabled = true;
|
|
59
|
+
saveRegistry(registry);
|
|
60
|
+
out({ ok: true, enabled: name });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (action === "disable") {
|
|
65
|
+
registry[section][name].enabled = false;
|
|
66
|
+
saveRegistry(registry);
|
|
67
|
+
out({ ok: true, disabled: name });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (action === "refresh") {
|
|
72
|
+
const entry = { ...registry[section][name], name, _section: section };
|
|
73
|
+
if (!entry.enabled) throw new Error(`Spec '${name}' is disabled. Enable it first.`);
|
|
74
|
+
const spec = await fetchSpec(entry);
|
|
75
|
+
saveCachedSpec(name, spec);
|
|
76
|
+
const count = spec.tools?.length ?? spec.operations?.length ?? 0;
|
|
77
|
+
out({ ok: true, refreshed: name, type: spec.type, count });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw new Error(`Unknown registry action: ${action}`);
|
|
82
|
+
}
|
package/src/commands/types.js
CHANGED
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
import { out } from "../output.js";
|
|
2
|
-
import { parseArgs } from "../args.js";
|
|
3
|
-
import { resolveSpec } from "../resolve.js";
|
|
4
|
-
|
|
5
|
-
export async function typesCmd(args) {
|
|
6
|
-
const { positional, flags } = parseArgs(args);
|
|
7
|
-
const { spec } = await resolveSpec(flags);
|
|
8
|
-
const target = positional[0];
|
|
9
|
-
|
|
10
|
-
if (spec.type === "openapi") {
|
|
11
|
-
showOpenAPISchema(spec, target, flags);
|
|
12
|
-
} else {
|
|
13
|
-
showGraphQLType(spec, target, flags);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function showOpenAPISchema(spec, target, flags) {
|
|
18
|
-
const schemas = spec.raw?.components?.schemas || spec.raw?.definitions || {};
|
|
19
|
-
|
|
20
|
-
if (!target) {
|
|
21
|
-
// List all schema names — just names, very compact
|
|
22
|
-
const names = Object.keys(schemas);
|
|
23
|
-
out({
|
|
24
|
-
type: "openapi",
|
|
25
|
-
count: names.length,
|
|
26
|
-
schemas: names,
|
|
27
|
-
});
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Find schema (case-insensitive)
|
|
32
|
-
const lower = target.toLowerCase();
|
|
33
|
-
const key = Object.keys(schemas).find((k) => k.toLowerCase() === lower);
|
|
34
|
-
|
|
35
|
-
if (!key) {
|
|
36
|
-
throw new Error(`Schema not found: ${target}. Run 'spec types' to list available schemas.`);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const schema = schemas[key];
|
|
40
|
-
const root = spec.raw;
|
|
41
|
-
|
|
42
|
-
// Resolve one level deep — don't recursively explode nested schemas
|
|
43
|
-
const resolved = resolveSchemaCompact(schema, root);
|
|
44
|
-
|
|
45
|
-
out({
|
|
46
|
-
name: key,
|
|
47
|
-
...resolved,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function showGraphQLType(spec, target, flags) {
|
|
52
|
-
const scalars = new Set(["String", "Int", "Float", "Boolean", "ID"]);
|
|
53
|
-
const userTypes =
|
|
54
|
-
spec.types?.filter((t) => !t.name.startsWith("__") && !scalars.has(t.name)) || [];
|
|
55
|
-
|
|
56
|
-
if (!target) {
|
|
57
|
-
// List type names grouped by kind — compact
|
|
58
|
-
const grouped = {};
|
|
59
|
-
for (const t of userTypes) {
|
|
60
|
-
if (!grouped[t.kind]) grouped[t.kind] = [];
|
|
61
|
-
grouped[t.kind].push(t.name);
|
|
62
|
-
}
|
|
63
|
-
out({
|
|
64
|
-
type: "graphql",
|
|
65
|
-
count: userTypes.length,
|
|
66
|
-
types: grouped,
|
|
67
|
-
});
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Find specific type
|
|
72
|
-
const lower = target.toLowerCase();
|
|
73
|
-
const type = userTypes.find((t) => t.name.toLowerCase() === lower);
|
|
74
|
-
|
|
75
|
-
if (!type) {
|
|
76
|
-
throw new Error(`Type not found: ${target}. Run 'spec types' to list available types.`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const result = {
|
|
80
|
-
name: type.name,
|
|
81
|
-
kind: type.kind,
|
|
82
|
-
description: type.description || null,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
if (type.fields) {
|
|
86
|
-
result.fields = type.fields.map((f) => ({
|
|
87
|
-
name: f.name,
|
|
88
|
-
type: flattenType(f.type),
|
|
89
|
-
args:
|
|
90
|
-
f.args?.length > 0
|
|
91
|
-
? f.args.map((a) => ({ name: a.name, type: flattenType(a.type) }))
|
|
92
|
-
: undefined,
|
|
93
|
-
}));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (type.inputFields) {
|
|
97
|
-
result.inputFields = type.inputFields.map((f) => ({
|
|
98
|
-
name: f.name,
|
|
99
|
-
type: flattenType(f.type),
|
|
100
|
-
defaultValue: f.defaultValue || undefined,
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (type.enumValues) {
|
|
105
|
-
result.enumValues = type.enumValues.map((e) => e.name);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
out(result);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function resolveSchemaCompact(schema, root) {
|
|
112
|
-
if (!schema) return schema;
|
|
113
|
-
|
|
114
|
-
if (schema.$ref) {
|
|
115
|
-
const path = schema.$ref.replace("#/", "").split("/");
|
|
116
|
-
let resolved = root;
|
|
117
|
-
for (const p of path) resolved = resolved?.[p];
|
|
118
|
-
return resolveSchemaCompact(resolved, root);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const result = {};
|
|
122
|
-
if (schema.type) result.type = schema.type;
|
|
123
|
-
if (schema.description) result.description = schema.description;
|
|
124
|
-
if (schema.required) result.required = schema.required;
|
|
125
|
-
if (schema.enum) result.enum = schema.enum;
|
|
126
|
-
|
|
127
|
-
if (schema.properties) {
|
|
128
|
-
result.properties = {};
|
|
129
|
-
for (const [key, val] of Object.entries(schema.properties)) {
|
|
130
|
-
if (val.$ref) {
|
|
131
|
-
// Just show the type name, don't resolve
|
|
132
|
-
const refName = val.$ref.split("/").pop();
|
|
133
|
-
result.properties[key] = { $ref: refName };
|
|
134
|
-
} else if (val.type === "array" && val.items?.$ref) {
|
|
135
|
-
const refName = val.items.$ref.split("/").pop();
|
|
136
|
-
result.properties[key] = { type: "array", items: refName };
|
|
137
|
-
} else {
|
|
138
|
-
result.properties[key] = { type: val.type || null };
|
|
139
|
-
if (val.enum) result.properties[key].enum = val.enum;
|
|
140
|
-
if (val.format) result.properties[key].format = val.format;
|
|
141
|
-
if (val.description) result.properties[key].description = val.description;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (schema.items) {
|
|
147
|
-
if (schema.items.$ref) {
|
|
148
|
-
result.items = schema.items.$ref.split("/").pop();
|
|
149
|
-
} else {
|
|
150
|
-
result.items = { type: schema.items.type };
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return result;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function flattenType(t) {
|
|
158
|
-
if (!t) return null;
|
|
159
|
-
if (t.name) return t.kind === "NON_NULL" ? `${t.name}!` : t.name;
|
|
160
|
-
if (t.ofType) {
|
|
161
|
-
const inner = flattenType(t.ofType);
|
|
162
|
-
if (t.kind === "LIST") return `[${inner}]`;
|
|
163
|
-
if (t.kind === "NON_NULL") return `${inner}!`;
|
|
164
|
-
return inner;
|
|
165
|
-
}
|
|
166
|
-
return t.kind;
|
|
167
|
-
}
|
|
1
|
+
import { out } from "../output.js";
|
|
2
|
+
import { parseArgs } from "../args.js";
|
|
3
|
+
import { resolveSpec } from "../resolve.js";
|
|
4
|
+
|
|
5
|
+
export async function typesCmd(args) {
|
|
6
|
+
const { positional, flags } = parseArgs(args);
|
|
7
|
+
const { spec } = await resolveSpec(flags);
|
|
8
|
+
const target = positional[0];
|
|
9
|
+
|
|
10
|
+
if (spec.type === "openapi") {
|
|
11
|
+
showOpenAPISchema(spec, target, flags);
|
|
12
|
+
} else {
|
|
13
|
+
showGraphQLType(spec, target, flags);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function showOpenAPISchema(spec, target, flags) {
|
|
18
|
+
const schemas = spec.raw?.components?.schemas || spec.raw?.definitions || {};
|
|
19
|
+
|
|
20
|
+
if (!target) {
|
|
21
|
+
// List all schema names — just names, very compact
|
|
22
|
+
const names = Object.keys(schemas);
|
|
23
|
+
out({
|
|
24
|
+
type: "openapi",
|
|
25
|
+
count: names.length,
|
|
26
|
+
schemas: names,
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Find schema (case-insensitive)
|
|
32
|
+
const lower = target.toLowerCase();
|
|
33
|
+
const key = Object.keys(schemas).find((k) => k.toLowerCase() === lower);
|
|
34
|
+
|
|
35
|
+
if (!key) {
|
|
36
|
+
throw new Error(`Schema not found: ${target}. Run 'spec types' to list available schemas.`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const schema = schemas[key];
|
|
40
|
+
const root = spec.raw;
|
|
41
|
+
|
|
42
|
+
// Resolve one level deep — don't recursively explode nested schemas
|
|
43
|
+
const resolved = resolveSchemaCompact(schema, root);
|
|
44
|
+
|
|
45
|
+
out({
|
|
46
|
+
name: key,
|
|
47
|
+
...resolved,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function showGraphQLType(spec, target, flags) {
|
|
52
|
+
const scalars = new Set(["String", "Int", "Float", "Boolean", "ID"]);
|
|
53
|
+
const userTypes =
|
|
54
|
+
spec.types?.filter((t) => !t.name.startsWith("__") && !scalars.has(t.name)) || [];
|
|
55
|
+
|
|
56
|
+
if (!target) {
|
|
57
|
+
// List type names grouped by kind — compact
|
|
58
|
+
const grouped = {};
|
|
59
|
+
for (const t of userTypes) {
|
|
60
|
+
if (!grouped[t.kind]) grouped[t.kind] = [];
|
|
61
|
+
grouped[t.kind].push(t.name);
|
|
62
|
+
}
|
|
63
|
+
out({
|
|
64
|
+
type: "graphql",
|
|
65
|
+
count: userTypes.length,
|
|
66
|
+
types: grouped,
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Find specific type
|
|
72
|
+
const lower = target.toLowerCase();
|
|
73
|
+
const type = userTypes.find((t) => t.name.toLowerCase() === lower);
|
|
74
|
+
|
|
75
|
+
if (!type) {
|
|
76
|
+
throw new Error(`Type not found: ${target}. Run 'spec types' to list available types.`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const result = {
|
|
80
|
+
name: type.name,
|
|
81
|
+
kind: type.kind,
|
|
82
|
+
description: type.description || null,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (type.fields) {
|
|
86
|
+
result.fields = type.fields.map((f) => ({
|
|
87
|
+
name: f.name,
|
|
88
|
+
type: flattenType(f.type),
|
|
89
|
+
args:
|
|
90
|
+
f.args?.length > 0
|
|
91
|
+
? f.args.map((a) => ({ name: a.name, type: flattenType(a.type) }))
|
|
92
|
+
: undefined,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (type.inputFields) {
|
|
97
|
+
result.inputFields = type.inputFields.map((f) => ({
|
|
98
|
+
name: f.name,
|
|
99
|
+
type: flattenType(f.type),
|
|
100
|
+
defaultValue: f.defaultValue || undefined,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (type.enumValues) {
|
|
105
|
+
result.enumValues = type.enumValues.map((e) => e.name);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
out(result);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function resolveSchemaCompact(schema, root) {
|
|
112
|
+
if (!schema) return schema;
|
|
113
|
+
|
|
114
|
+
if (schema.$ref) {
|
|
115
|
+
const path = schema.$ref.replace("#/", "").split("/");
|
|
116
|
+
let resolved = root;
|
|
117
|
+
for (const p of path) resolved = resolved?.[p];
|
|
118
|
+
return resolveSchemaCompact(resolved, root);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const result = {};
|
|
122
|
+
if (schema.type) result.type = schema.type;
|
|
123
|
+
if (schema.description) result.description = schema.description;
|
|
124
|
+
if (schema.required) result.required = schema.required;
|
|
125
|
+
if (schema.enum) result.enum = schema.enum;
|
|
126
|
+
|
|
127
|
+
if (schema.properties) {
|
|
128
|
+
result.properties = {};
|
|
129
|
+
for (const [key, val] of Object.entries(schema.properties)) {
|
|
130
|
+
if (val.$ref) {
|
|
131
|
+
// Just show the type name, don't resolve
|
|
132
|
+
const refName = val.$ref.split("/").pop();
|
|
133
|
+
result.properties[key] = { $ref: refName };
|
|
134
|
+
} else if (val.type === "array" && val.items?.$ref) {
|
|
135
|
+
const refName = val.items.$ref.split("/").pop();
|
|
136
|
+
result.properties[key] = { type: "array", items: refName };
|
|
137
|
+
} else {
|
|
138
|
+
result.properties[key] = { type: val.type || null };
|
|
139
|
+
if (val.enum) result.properties[key].enum = val.enum;
|
|
140
|
+
if (val.format) result.properties[key].format = val.format;
|
|
141
|
+
if (val.description) result.properties[key].description = val.description;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (schema.items) {
|
|
147
|
+
if (schema.items.$ref) {
|
|
148
|
+
result.items = schema.items.$ref.split("/").pop();
|
|
149
|
+
} else {
|
|
150
|
+
result.items = { type: schema.items.type };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function flattenType(t) {
|
|
158
|
+
if (!t) return null;
|
|
159
|
+
if (t.name) return t.kind === "NON_NULL" ? `${t.name}!` : t.name;
|
|
160
|
+
if (t.ofType) {
|
|
161
|
+
const inner = flattenType(t.ofType);
|
|
162
|
+
if (t.kind === "LIST") return `[${inner}]`;
|
|
163
|
+
if (t.kind === "NON_NULL") return `${inner}!`;
|
|
164
|
+
return inner;
|
|
165
|
+
}
|
|
166
|
+
return t.kind;
|
|
167
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { out } from "../output.js";
|
|
2
|
+
import { parseArgs } from "../args.js";
|
|
3
|
+
import { allUsage, topOperations } from "../usage.js";
|
|
4
|
+
|
|
5
|
+
export async function usageCmd(args) {
|
|
6
|
+
const { positional } = parseArgs(args);
|
|
7
|
+
const specName = positional[0];
|
|
8
|
+
|
|
9
|
+
if (specName) {
|
|
10
|
+
const operations = topOperations(specName, Infinity);
|
|
11
|
+
out({ spec: specName, operations });
|
|
12
|
+
} else {
|
|
13
|
+
out({ usage: allUsage() });
|
|
14
|
+
}
|
|
15
|
+
}
|