api-spec-cli 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,163 +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 = spec.types?.filter((t) => !t.name.startsWith("__") && !scalars.has(t.name)) || [];
54
-
55
- if (!target) {
56
- // List type names grouped by kind — compact
57
- const grouped = {};
58
- for (const t of userTypes) {
59
- if (!grouped[t.kind]) grouped[t.kind] = [];
60
- grouped[t.kind].push(t.name);
61
- }
62
- out({
63
- type: "graphql",
64
- count: userTypes.length,
65
- types: grouped,
66
- });
67
- return;
68
- }
69
-
70
- // Find specific type
71
- const lower = target.toLowerCase();
72
- const type = userTypes.find((t) => t.name.toLowerCase() === lower);
73
-
74
- if (!type) {
75
- throw new Error(`Type not found: ${target}. Run 'spec types' to list available types.`);
76
- }
77
-
78
- const result = {
79
- name: type.name,
80
- kind: type.kind,
81
- description: type.description || null,
82
- };
83
-
84
- if (type.fields) {
85
- result.fields = type.fields.map((f) => ({
86
- name: f.name,
87
- type: flattenType(f.type),
88
- args: f.args?.length > 0 ? f.args.map((a) => ({ name: a.name, type: flattenType(a.type) })) : undefined,
89
- }));
90
- }
91
-
92
- if (type.inputFields) {
93
- result.inputFields = type.inputFields.map((f) => ({
94
- name: f.name,
95
- type: flattenType(f.type),
96
- defaultValue: f.defaultValue || undefined,
97
- }));
98
- }
99
-
100
- if (type.enumValues) {
101
- result.enumValues = type.enumValues.map((e) => e.name);
102
- }
103
-
104
- out(result);
105
- }
106
-
107
- function resolveSchemaCompact(schema, root) {
108
- if (!schema) return schema;
109
-
110
- if (schema.$ref) {
111
- const path = schema.$ref.replace("#/", "").split("/");
112
- let resolved = root;
113
- for (const p of path) resolved = resolved?.[p];
114
- return resolveSchemaCompact(resolved, root);
115
- }
116
-
117
- const result = {};
118
- if (schema.type) result.type = schema.type;
119
- if (schema.description) result.description = schema.description;
120
- if (schema.required) result.required = schema.required;
121
- if (schema.enum) result.enum = schema.enum;
122
-
123
- if (schema.properties) {
124
- result.properties = {};
125
- for (const [key, val] of Object.entries(schema.properties)) {
126
- if (val.$ref) {
127
- // Just show the type name, don't resolve
128
- const refName = val.$ref.split("/").pop();
129
- result.properties[key] = { $ref: refName };
130
- } else if (val.type === "array" && val.items?.$ref) {
131
- const refName = val.items.$ref.split("/").pop();
132
- result.properties[key] = { type: "array", items: refName };
133
- } else {
134
- result.properties[key] = { type: val.type || null };
135
- if (val.enum) result.properties[key].enum = val.enum;
136
- if (val.format) result.properties[key].format = val.format;
137
- if (val.description) result.properties[key].description = val.description;
138
- }
139
- }
140
- }
141
-
142
- if (schema.items) {
143
- if (schema.items.$ref) {
144
- result.items = schema.items.$ref.split("/").pop();
145
- } else {
146
- result.items = { type: schema.items.type };
147
- }
148
- }
149
-
150
- return result;
151
- }
152
-
153
- function flattenType(t) {
154
- if (!t) return null;
155
- if (t.name) return t.kind === "NON_NULL" ? `${t.name}!` : t.name;
156
- if (t.ofType) {
157
- const inner = flattenType(t.ofType);
158
- if (t.kind === "LIST") return `[${inner}]`;
159
- if (t.kind === "NON_NULL") return `${inner}!`;
160
- return inner;
161
- }
162
- return t.kind;
163
- }
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
+ }