specli 0.0.20 → 0.0.22
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/ai/tools.d.ts +1 -1
- package/dist/cli/compile.js +1 -1
- package/dist/cli/main.js +2 -2
- package/dist/cli/{capabilities.d.ts → model/capabilities.d.ts} +3 -3
- package/dist/cli/{command-id.js → model/command-id.js} +1 -1
- package/dist/cli/{command-model.d.ts → model/command-model.d.ts} +9 -8
- package/dist/cli/{command-model.js → model/command-model.js} +4 -4
- package/dist/cli/{naming.d.ts → model/naming.d.ts} +1 -1
- package/dist/cli/{naming.js → model/naming.js} +2 -2
- package/dist/cli/{schema.d.ts → model/schema.d.ts} +6 -5
- package/dist/cli/{auth-schemes.d.ts → parse/auth-schemes.d.ts} +1 -1
- package/dist/cli/{auth-schemes.js → parse/auth-schemes.js} +1 -1
- package/dist/cli/{operations.d.ts → parse/operations.d.ts} +1 -1
- package/dist/cli/{params.d.ts → parse/params.d.ts} +4 -3
- package/dist/cli/{params.js → parse/params.js} +1 -1
- package/dist/cli/{positional.d.ts → parse/positional.d.ts} +3 -3
- package/dist/cli/{request-body.d.ts → parse/request-body.d.ts} +3 -2
- package/dist/cli/{request-body.js → parse/request-body.js} +1 -1
- package/dist/cli/{server.d.ts → parse/servers.d.ts} +1 -1
- package/dist/cli/runtime/auth/resolve.d.ts +3 -2
- package/dist/cli/runtime/context.d.ts +9 -9
- package/dist/cli/runtime/context.js +9 -9
- package/dist/cli/runtime/execute.d.ts +7 -5
- package/dist/cli/runtime/execute.js +1 -1
- package/dist/cli/runtime/generated.d.ts +3 -3
- package/dist/cli/runtime/request.d.ts +4 -3
- package/dist/cli/runtime/server-url.d.ts +1 -1
- package/dist/cli/runtime/validate/coerce.d.ts +1 -1
- package/dist/cli/runtime/validate/schema.d.ts +2 -2
- package/dist/cli/{spec-id.d.ts → spec/id.d.ts} +1 -1
- package/dist/cli/{spec-id.js → spec/id.js} +1 -1
- package/dist/cli/{spec-loader.d.ts → spec/loader.d.ts} +1 -1
- package/dist/cli/{spec-loader.js → spec/loader.js} +4 -4
- package/package.json +3 -2
- package/dist/ai/tools.test.d.ts +0 -1
- package/dist/ai/tools.test.js +0 -49
- package/dist/cli/auth-requirements.test.d.ts +0 -1
- package/dist/cli/auth-requirements.test.js +0 -16
- package/dist/cli/auth-schemes.test.d.ts +0 -1
- package/dist/cli/auth-schemes.test.js +0 -56
- package/dist/cli/capabilities.test.d.ts +0 -1
- package/dist/cli/capabilities.test.js +0 -84
- package/dist/cli/command-id.test.d.ts +0 -1
- package/dist/cli/command-id.test.js +0 -27
- package/dist/cli/command-model.test.d.ts +0 -1
- package/dist/cli/command-model.test.js +0 -40
- package/dist/cli/naming.test.d.ts +0 -1
- package/dist/cli/naming.test.js +0 -75
- package/dist/cli/operations.test.d.ts +0 -1
- package/dist/cli/operations.test.js +0 -51
- package/dist/cli/params.test.d.ts +0 -1
- package/dist/cli/params.test.js +0 -62
- package/dist/cli/positional.test.d.ts +0 -1
- package/dist/cli/positional.test.js +0 -60
- package/dist/cli/request-body.test.d.ts +0 -1
- package/dist/cli/request-body.test.js +0 -31
- package/dist/cli/runtime/body-flags.test.d.ts +0 -1
- package/dist/cli/runtime/body-flags.test.js +0 -192
- package/dist/cli/runtime/request.test.d.ts +0 -1
- package/dist/cli/runtime/request.test.js +0 -332
- package/dist/cli/runtime/validate/coerce.test.d.ts +0 -1
- package/dist/cli/runtime/validate/coerce.test.js +0 -75
- package/dist/cli/server.test.d.ts +0 -1
- package/dist/cli/server.test.js +0 -49
- package/dist/compiled.d.ts +0 -2
- package/dist/compiled.js +0 -21
- package/dist/macros/env.d.ts +0 -10
- package/dist/macros/env.js +0 -22
- package/dist/macros/spec.d.ts +0 -5
- package/dist/macros/spec.js +0 -16
- package/dist/macros/version.d.ts +0 -4
- package/dist/macros/version.js +0 -13
- /package/dist/cli/{crypto.d.ts → core/crypto.d.ts} +0 -0
- /package/dist/cli/{crypto.js → core/crypto.js} +0 -0
- /package/dist/cli/{pluralize.d.ts → core/pluralize.d.ts} +0 -0
- /package/dist/cli/{pluralize.js → core/pluralize.js} +0 -0
- /package/dist/cli/{stable-json.d.ts → core/stable-json.d.ts} +0 -0
- /package/dist/cli/{stable-json.js → core/stable-json.js} +0 -0
- /package/dist/cli/{strings.d.ts → core/strings.d.ts} +0 -0
- /package/dist/cli/{strings.js → core/strings.js} +0 -0
- /package/dist/cli/{types.d.ts → core/types.d.ts} +0 -0
- /package/dist/cli/{types.js → core/types.js} +0 -0
- /package/dist/cli/{capabilities.js → model/capabilities.js} +0 -0
- /package/dist/cli/{command-id.d.ts → model/command-id.d.ts} +0 -0
- /package/dist/cli/{command-index.d.ts → model/command-index.d.ts} +0 -0
- /package/dist/cli/{command-index.js → model/command-index.js} +0 -0
- /package/dist/cli/{schema.js → model/schema.js} +0 -0
- /package/dist/cli/{auth-requirements.d.ts → parse/auth-requirements.d.ts} +0 -0
- /package/dist/cli/{auth-requirements.js → parse/auth-requirements.js} +0 -0
- /package/dist/cli/{operations.js → parse/operations.js} +0 -0
- /package/dist/cli/{positional.js → parse/positional.js} +0 -0
- /package/dist/cli/{schema-shape.d.ts → parse/schema-shape.d.ts} +0 -0
- /package/dist/cli/{schema-shape.js → parse/schema-shape.js} +0 -0
- /package/dist/cli/{server.js → parse/servers.js} +0 -0
- /package/dist/cli/{derive-name.d.ts → spec/derive-name.d.ts} +0 -0
- /package/dist/cli/{derive-name.js → spec/derive-name.js} +0 -0
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { generateBodyFlags } from "./body-flags.js";
|
|
4
|
-
import { buildRequest } from "./request.js";
|
|
5
|
-
import { createAjv, formatAjvErrors } from "./validate/index.js";
|
|
6
|
-
function makeAction(partial) {
|
|
7
|
-
return {
|
|
8
|
-
id: "test",
|
|
9
|
-
key: "POST /contacts",
|
|
10
|
-
action: "create",
|
|
11
|
-
pathArgs: [],
|
|
12
|
-
method: "POST",
|
|
13
|
-
path: "/contacts",
|
|
14
|
-
tags: [],
|
|
15
|
-
style: "rest",
|
|
16
|
-
positionals: [],
|
|
17
|
-
flags: [],
|
|
18
|
-
params: [],
|
|
19
|
-
auth: { alternatives: [] },
|
|
20
|
-
requestBody: {
|
|
21
|
-
required: true,
|
|
22
|
-
content: [
|
|
23
|
-
{
|
|
24
|
-
contentType: "application/json",
|
|
25
|
-
required: true,
|
|
26
|
-
schemaType: "object",
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
hasJson: true,
|
|
30
|
-
hasFormUrlEncoded: false,
|
|
31
|
-
hasMultipart: false,
|
|
32
|
-
bodyFlags: ["--data", "--file"],
|
|
33
|
-
preferredContentType: "application/json",
|
|
34
|
-
preferredSchema: undefined,
|
|
35
|
-
},
|
|
36
|
-
requestBodySchema: {
|
|
37
|
-
type: "object",
|
|
38
|
-
properties: {
|
|
39
|
-
name: { type: "string" },
|
|
40
|
-
},
|
|
41
|
-
required: ["name"],
|
|
42
|
-
},
|
|
43
|
-
...partial,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
describe("buildRequest (requestBody)", () => {
|
|
47
|
-
test("builds body from expanded body flags", async () => {
|
|
48
|
-
const prevHome = process.env.HOME;
|
|
49
|
-
const home = `${tmpdir()}/specli-test-${crypto.randomUUID()}`;
|
|
50
|
-
process.env.HOME = home;
|
|
51
|
-
try {
|
|
52
|
-
const action = makeAction();
|
|
53
|
-
const bodyFlagDefs = generateBodyFlags(action.requestBodySchema, new Set());
|
|
54
|
-
const { request, curl } = await buildRequest({
|
|
55
|
-
specId: "spec",
|
|
56
|
-
action,
|
|
57
|
-
positionalValues: [],
|
|
58
|
-
flagValues: { name: "A" }, // --name A
|
|
59
|
-
globals: {},
|
|
60
|
-
servers: [
|
|
61
|
-
{ url: "https://api.example.com", variables: [], variableNames: [] },
|
|
62
|
-
],
|
|
63
|
-
authSchemes: [],
|
|
64
|
-
bodyFlagDefs,
|
|
65
|
-
});
|
|
66
|
-
expect(request.headers.get("Content-Type")).toBe("application/json");
|
|
67
|
-
expect(await request.clone().text()).toBe('{"name":"A"}');
|
|
68
|
-
expect(curl).toContain("--data");
|
|
69
|
-
expect(curl).toContain('{"name":"A"}');
|
|
70
|
-
}
|
|
71
|
-
finally {
|
|
72
|
-
process.env.HOME = prevHome;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
test("throws when requestBody is required but missing", async () => {
|
|
76
|
-
const prevHome = process.env.HOME;
|
|
77
|
-
const home = `${tmpdir()}/specli-test-${crypto.randomUUID()}`;
|
|
78
|
-
process.env.HOME = home;
|
|
79
|
-
try {
|
|
80
|
-
const action = makeAction();
|
|
81
|
-
const bodyFlagDefs = generateBodyFlags(action.requestBodySchema, new Set());
|
|
82
|
-
await expect(() => buildRequest({
|
|
83
|
-
specId: "spec",
|
|
84
|
-
action,
|
|
85
|
-
positionalValues: [],
|
|
86
|
-
flagValues: {},
|
|
87
|
-
globals: {},
|
|
88
|
-
servers: [
|
|
89
|
-
{
|
|
90
|
-
url: "https://api.example.com",
|
|
91
|
-
variables: [],
|
|
92
|
-
variableNames: [],
|
|
93
|
-
},
|
|
94
|
-
],
|
|
95
|
-
authSchemes: [],
|
|
96
|
-
bodyFlagDefs,
|
|
97
|
-
})).toThrow("Required: --name");
|
|
98
|
-
}
|
|
99
|
-
finally {
|
|
100
|
-
process.env.HOME = prevHome;
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
test("throws friendly error for missing required expanded field", async () => {
|
|
104
|
-
const prevHome = process.env.HOME;
|
|
105
|
-
const home = `${tmpdir()}/specli-test-${crypto.randomUUID()}`;
|
|
106
|
-
process.env.HOME = home;
|
|
107
|
-
try {
|
|
108
|
-
// Schema with two fields, one required
|
|
109
|
-
const action = makeAction({
|
|
110
|
-
requestBodySchema: {
|
|
111
|
-
type: "object",
|
|
112
|
-
properties: {
|
|
113
|
-
name: { type: "string" },
|
|
114
|
-
email: { type: "string" },
|
|
115
|
-
},
|
|
116
|
-
required: ["name"],
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
const bodyFlagDefs = generateBodyFlags(action.requestBodySchema, new Set());
|
|
120
|
-
// Provide email but not name (the required one)
|
|
121
|
-
await expect(() => buildRequest({
|
|
122
|
-
specId: "spec",
|
|
123
|
-
action,
|
|
124
|
-
positionalValues: [],
|
|
125
|
-
flagValues: { email: "test@example.com" }, // --email (but missing --name)
|
|
126
|
-
globals: {},
|
|
127
|
-
servers: [
|
|
128
|
-
{
|
|
129
|
-
url: "https://api.example.com",
|
|
130
|
-
variables: [],
|
|
131
|
-
variableNames: [],
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
authSchemes: [],
|
|
135
|
-
bodyFlagDefs,
|
|
136
|
-
})).toThrow("Missing required fields: --name");
|
|
137
|
-
}
|
|
138
|
-
finally {
|
|
139
|
-
process.env.HOME = prevHome;
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
test("builds nested object from dot notation flags", async () => {
|
|
143
|
-
const prevHome = process.env.HOME;
|
|
144
|
-
const home = `${tmpdir()}/specli-test-${crypto.randomUUID()}`;
|
|
145
|
-
process.env.HOME = home;
|
|
146
|
-
try {
|
|
147
|
-
const action = makeAction({
|
|
148
|
-
requestBodySchema: {
|
|
149
|
-
type: "object",
|
|
150
|
-
properties: {
|
|
151
|
-
name: { type: "string" },
|
|
152
|
-
address: {
|
|
153
|
-
type: "object",
|
|
154
|
-
properties: {
|
|
155
|
-
street: { type: "string" },
|
|
156
|
-
city: { type: "string" },
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
required: ["name"],
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
const bodyFlagDefs = generateBodyFlags(action.requestBodySchema, new Set());
|
|
164
|
-
// Dot notation: --address.street and --address.city should create nested object
|
|
165
|
-
const { request } = await buildRequest({
|
|
166
|
-
specId: "spec",
|
|
167
|
-
action,
|
|
168
|
-
positionalValues: [],
|
|
169
|
-
flagValues: {
|
|
170
|
-
name: "Ada",
|
|
171
|
-
"address.street": "123 Main St", // Commander keeps dots in keys
|
|
172
|
-
"address.city": "NYC",
|
|
173
|
-
},
|
|
174
|
-
globals: {},
|
|
175
|
-
servers: [
|
|
176
|
-
{ url: "https://api.example.com", variables: [], variableNames: [] },
|
|
177
|
-
],
|
|
178
|
-
authSchemes: [],
|
|
179
|
-
bodyFlagDefs,
|
|
180
|
-
});
|
|
181
|
-
const body = JSON.parse(await request.clone().text());
|
|
182
|
-
expect(body).toEqual({
|
|
183
|
-
name: "Ada",
|
|
184
|
-
address: {
|
|
185
|
-
street: "123 Main St",
|
|
186
|
-
city: "NYC",
|
|
187
|
-
},
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
finally {
|
|
191
|
-
process.env.HOME = prevHome;
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
describe("buildRequest (query parameters)", () => {
|
|
196
|
-
test("builds query string from flag values", async () => {
|
|
197
|
-
const prevHome = process.env.HOME;
|
|
198
|
-
const home = `${tmpdir()}/specli-test-${crypto.randomUUID()}`;
|
|
199
|
-
process.env.HOME = home;
|
|
200
|
-
try {
|
|
201
|
-
const action = {
|
|
202
|
-
id: "test",
|
|
203
|
-
key: "GET /contacts",
|
|
204
|
-
action: "list",
|
|
205
|
-
pathArgs: [],
|
|
206
|
-
method: "GET",
|
|
207
|
-
path: "/contacts",
|
|
208
|
-
tags: [],
|
|
209
|
-
style: "rest",
|
|
210
|
-
positionals: [],
|
|
211
|
-
flags: [
|
|
212
|
-
{
|
|
213
|
-
flag: "--limit",
|
|
214
|
-
name: "limit",
|
|
215
|
-
in: "query",
|
|
216
|
-
type: "integer",
|
|
217
|
-
required: false,
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
flag: "--name",
|
|
221
|
-
name: "name",
|
|
222
|
-
in: "query",
|
|
223
|
-
type: "string",
|
|
224
|
-
required: false,
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
params: [
|
|
228
|
-
{
|
|
229
|
-
kind: "flag",
|
|
230
|
-
flag: "--limit",
|
|
231
|
-
name: "limit",
|
|
232
|
-
in: "query",
|
|
233
|
-
required: false,
|
|
234
|
-
type: "integer",
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
kind: "flag",
|
|
238
|
-
flag: "--name",
|
|
239
|
-
name: "name",
|
|
240
|
-
in: "query",
|
|
241
|
-
required: false,
|
|
242
|
-
type: "string",
|
|
243
|
-
},
|
|
244
|
-
],
|
|
245
|
-
auth: { alternatives: [] },
|
|
246
|
-
};
|
|
247
|
-
const { request } = await buildRequest({
|
|
248
|
-
specId: "spec",
|
|
249
|
-
action,
|
|
250
|
-
positionalValues: [],
|
|
251
|
-
flagValues: { limit: 10, name: "andrew" },
|
|
252
|
-
globals: {},
|
|
253
|
-
servers: [
|
|
254
|
-
{ url: "https://api.example.com", variables: [], variableNames: [] },
|
|
255
|
-
],
|
|
256
|
-
authSchemes: [],
|
|
257
|
-
});
|
|
258
|
-
expect(request.method).toBe("GET");
|
|
259
|
-
expect(request.url).toBe("https://api.example.com/contacts?limit=10&name=andrew");
|
|
260
|
-
}
|
|
261
|
-
finally {
|
|
262
|
-
process.env.HOME = prevHome;
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
test("handles array query parameters", async () => {
|
|
266
|
-
const prevHome = process.env.HOME;
|
|
267
|
-
const home = `${tmpdir()}/specli-test-${crypto.randomUUID()}`;
|
|
268
|
-
process.env.HOME = home;
|
|
269
|
-
try {
|
|
270
|
-
const action = {
|
|
271
|
-
id: "test",
|
|
272
|
-
key: "GET /contacts",
|
|
273
|
-
action: "list",
|
|
274
|
-
pathArgs: [],
|
|
275
|
-
method: "GET",
|
|
276
|
-
path: "/contacts",
|
|
277
|
-
tags: [],
|
|
278
|
-
style: "rest",
|
|
279
|
-
positionals: [],
|
|
280
|
-
flags: [
|
|
281
|
-
{
|
|
282
|
-
flag: "--tag",
|
|
283
|
-
name: "tag",
|
|
284
|
-
in: "query",
|
|
285
|
-
type: "array",
|
|
286
|
-
itemType: "string",
|
|
287
|
-
required: false,
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
params: [
|
|
291
|
-
{
|
|
292
|
-
kind: "flag",
|
|
293
|
-
flag: "--tag",
|
|
294
|
-
name: "tag",
|
|
295
|
-
in: "query",
|
|
296
|
-
required: false,
|
|
297
|
-
type: "array",
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
auth: { alternatives: [] },
|
|
301
|
-
};
|
|
302
|
-
const { request } = await buildRequest({
|
|
303
|
-
specId: "spec",
|
|
304
|
-
action,
|
|
305
|
-
positionalValues: [],
|
|
306
|
-
flagValues: { tag: ["vip", "active"] },
|
|
307
|
-
globals: {},
|
|
308
|
-
servers: [
|
|
309
|
-
{ url: "https://api.example.com", variables: [], variableNames: [] },
|
|
310
|
-
],
|
|
311
|
-
authSchemes: [],
|
|
312
|
-
});
|
|
313
|
-
expect(request.url).toBe("https://api.example.com/contacts?tag=vip&tag=active");
|
|
314
|
-
}
|
|
315
|
-
finally {
|
|
316
|
-
process.env.HOME = prevHome;
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
});
|
|
320
|
-
describe("formatAjvErrors", () => {
|
|
321
|
-
test("pretty prints required errors", () => {
|
|
322
|
-
const ajv = createAjv();
|
|
323
|
-
const validate = ajv.compile({
|
|
324
|
-
type: "object",
|
|
325
|
-
properties: { name: { type: "string" } },
|
|
326
|
-
required: ["name"],
|
|
327
|
-
});
|
|
328
|
-
validate({});
|
|
329
|
-
const msg = formatAjvErrors(validate.errors);
|
|
330
|
-
expect(msg).toBe("/ missing required property 'name'");
|
|
331
|
-
});
|
|
332
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { coerceArrayInput, coerceValue } from "./coerce.js";
|
|
3
|
-
describe("coerceValue", () => {
|
|
4
|
-
test("returns string as-is for string type", () => {
|
|
5
|
-
expect(coerceValue("hello", "string")).toBe("hello");
|
|
6
|
-
});
|
|
7
|
-
test("returns string as-is for unknown type", () => {
|
|
8
|
-
expect(coerceValue("hello", "unknown")).toBe("hello");
|
|
9
|
-
});
|
|
10
|
-
test("parses integer type", () => {
|
|
11
|
-
expect(coerceValue("42", "integer")).toBe(42);
|
|
12
|
-
expect(coerceValue("-10", "integer")).toBe(-10);
|
|
13
|
-
expect(coerceValue("0", "integer")).toBe(0);
|
|
14
|
-
});
|
|
15
|
-
test("throws for invalid integer", () => {
|
|
16
|
-
expect(() => coerceValue("abc", "integer")).toThrow("Expected integer");
|
|
17
|
-
});
|
|
18
|
-
test("truncates decimal for integer type (parseInt behavior)", () => {
|
|
19
|
-
// parseInt("12.5", 10) returns 12 - this is expected JS behavior
|
|
20
|
-
expect(coerceValue("12.5", "integer")).toBe(12);
|
|
21
|
-
});
|
|
22
|
-
test("parses number type", () => {
|
|
23
|
-
expect(coerceValue("42", "number")).toBe(42);
|
|
24
|
-
expect(coerceValue("3.14", "number")).toBe(3.14);
|
|
25
|
-
expect(coerceValue("-0.5", "number")).toBe(-0.5);
|
|
26
|
-
});
|
|
27
|
-
test("throws for invalid number", () => {
|
|
28
|
-
expect(() => coerceValue("abc", "number")).toThrow("Expected number");
|
|
29
|
-
});
|
|
30
|
-
test("parses boolean type", () => {
|
|
31
|
-
expect(coerceValue("true", "boolean")).toBe(true);
|
|
32
|
-
expect(coerceValue("false", "boolean")).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
test("throws for invalid boolean", () => {
|
|
35
|
-
expect(() => coerceValue("yes", "boolean")).toThrow("Expected boolean");
|
|
36
|
-
expect(() => coerceValue("1", "boolean")).toThrow("Expected boolean");
|
|
37
|
-
});
|
|
38
|
-
test("parses object type as JSON", () => {
|
|
39
|
-
expect(coerceValue('{"a":1}', "object")).toEqual({ a: 1 });
|
|
40
|
-
});
|
|
41
|
-
test("throws for invalid object JSON", () => {
|
|
42
|
-
expect(() => coerceValue("not json", "object")).toThrow("Expected JSON object");
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe("coerceArrayInput", () => {
|
|
46
|
-
test("parses comma-separated values", () => {
|
|
47
|
-
expect(coerceArrayInput("a,b,c", "string")).toEqual(["a", "b", "c"]);
|
|
48
|
-
});
|
|
49
|
-
test("trims whitespace in comma-separated values", () => {
|
|
50
|
-
expect(coerceArrayInput("a, b, c", "string")).toEqual(["a", "b", "c"]);
|
|
51
|
-
});
|
|
52
|
-
test("parses JSON array", () => {
|
|
53
|
-
expect(coerceArrayInput('["a","b","c"]', "string")).toEqual([
|
|
54
|
-
"a",
|
|
55
|
-
"b",
|
|
56
|
-
"c",
|
|
57
|
-
]);
|
|
58
|
-
});
|
|
59
|
-
test("coerces array items to specified type", () => {
|
|
60
|
-
expect(coerceArrayInput("1,2,3", "integer")).toEqual([1, 2, 3]);
|
|
61
|
-
expect(coerceArrayInput('["1","2","3"]', "integer")).toEqual([1, 2, 3]);
|
|
62
|
-
});
|
|
63
|
-
test("returns empty array for empty string", () => {
|
|
64
|
-
expect(coerceArrayInput("", "string")).toEqual([]);
|
|
65
|
-
expect(coerceArrayInput(" ", "string")).toEqual([]);
|
|
66
|
-
});
|
|
67
|
-
test("throws for invalid JSON array", () => {
|
|
68
|
-
expect(() => coerceArrayInput("[invalid", "string")).toThrow("Expected JSON array");
|
|
69
|
-
});
|
|
70
|
-
test("treats non-array JSON as comma-separated string", () => {
|
|
71
|
-
// If it doesn't start with '[', it's treated as comma-separated
|
|
72
|
-
// '{"a":1}' doesn't start with '[', so it's treated as a single value
|
|
73
|
-
expect(coerceArrayInput('{"a":1}', "string")).toEqual(['{"a":1}']);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/cli/server.test.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { listServers } from "./server.js";
|
|
3
|
-
describe("listServers", () => {
|
|
4
|
-
test("extracts server variables from template", () => {
|
|
5
|
-
const doc = {
|
|
6
|
-
openapi: "3.0.3",
|
|
7
|
-
servers: [
|
|
8
|
-
{
|
|
9
|
-
url: "https://{region}.api.example.com/{basePath}",
|
|
10
|
-
variables: {
|
|
11
|
-
region: {
|
|
12
|
-
default: "us",
|
|
13
|
-
enum: ["us", "eu"],
|
|
14
|
-
},
|
|
15
|
-
basePath: {
|
|
16
|
-
default: "v1",
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
],
|
|
21
|
-
};
|
|
22
|
-
const servers = listServers(doc);
|
|
23
|
-
expect(servers).toHaveLength(1);
|
|
24
|
-
expect(servers[0]?.variableNames).toEqual(["region", "basePath"]);
|
|
25
|
-
expect(servers[0]?.variables.map((v) => v.name)).toEqual([
|
|
26
|
-
"region",
|
|
27
|
-
"basePath",
|
|
28
|
-
]);
|
|
29
|
-
expect(servers[0]?.variables[0]?.enum).toEqual(["us", "eu"]);
|
|
30
|
-
});
|
|
31
|
-
test("includes servers defined on paths and operations", () => {
|
|
32
|
-
const doc = {
|
|
33
|
-
openapi: "3.0.3",
|
|
34
|
-
paths: {
|
|
35
|
-
"/v1/forecast": {
|
|
36
|
-
servers: [{ url: "https://api.a.example.com" }],
|
|
37
|
-
get: {
|
|
38
|
-
servers: [{ url: "https://api.b.example.com" }],
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
const servers = listServers(doc);
|
|
44
|
-
expect(servers.map((s) => s.url)).toEqual([
|
|
45
|
-
"https://api.a.example.com",
|
|
46
|
-
"https://api.b.example.com",
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
});
|
package/dist/compiled.d.ts
DELETED
package/dist/compiled.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
import { main } from "./cli/main.js";
|
|
3
|
-
import { env, envRequired } from "./macros/env.js" with { type: "macro" };
|
|
4
|
-
import { loadSpec } from "./macros/spec.js" with { type: "macro" };
|
|
5
|
-
import { version } from "./macros/version.js" with { type: "macro" };
|
|
6
|
-
// This entrypoint is intended to be compiled.
|
|
7
|
-
// All values are embedded via Bun macros at bundle-time.
|
|
8
|
-
const embeddedSpecText = await loadSpec(envRequired("SPECLI_SPEC"));
|
|
9
|
-
const cliName = env("SPECLI_NAME");
|
|
10
|
-
const server = env("SPECLI_SERVER");
|
|
11
|
-
const serverVars = env("SPECLI_SERVER_VARS");
|
|
12
|
-
const auth = env("SPECLI_AUTH");
|
|
13
|
-
const embeddedVersion = version();
|
|
14
|
-
await main(process.argv, {
|
|
15
|
-
embeddedSpecText,
|
|
16
|
-
cliName,
|
|
17
|
-
server,
|
|
18
|
-
serverVars: serverVars ? serverVars.split(",") : undefined,
|
|
19
|
-
auth,
|
|
20
|
-
version: embeddedVersion,
|
|
21
|
-
});
|
package/dist/macros/env.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bun macro: reads an environment variable at bundle-time.
|
|
3
|
-
* Returns undefined if the env var is not set.
|
|
4
|
-
*/
|
|
5
|
-
export declare function env(name: string): string | undefined;
|
|
6
|
-
/**
|
|
7
|
-
* Bun macro: reads a required environment variable at bundle-time.
|
|
8
|
-
* Throws if the env var is not set.
|
|
9
|
-
*/
|
|
10
|
-
export declare function envRequired(name: string): string;
|
package/dist/macros/env.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bun macro: reads an environment variable at bundle-time.
|
|
3
|
-
* Returns undefined if the env var is not set.
|
|
4
|
-
*/
|
|
5
|
-
export function env(name) {
|
|
6
|
-
if (!name)
|
|
7
|
-
throw new Error("env macro: missing variable name");
|
|
8
|
-
return process.env[name];
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Bun macro: reads a required environment variable at bundle-time.
|
|
12
|
-
* Throws if the env var is not set.
|
|
13
|
-
*/
|
|
14
|
-
export function envRequired(name) {
|
|
15
|
-
if (!name)
|
|
16
|
-
throw new Error("envRequired macro: missing variable name");
|
|
17
|
-
const value = process.env[name];
|
|
18
|
-
if (value === undefined) {
|
|
19
|
-
throw new Error(`Missing required env var: ${name}`);
|
|
20
|
-
}
|
|
21
|
-
return value;
|
|
22
|
-
}
|
package/dist/macros/spec.d.ts
DELETED
package/dist/macros/spec.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bun macro: loads an OpenAPI spec from a URL or file path at bundle-time.
|
|
3
|
-
* The spec text is inlined into the bundle.
|
|
4
|
-
*/
|
|
5
|
-
export async function loadSpec(spec) {
|
|
6
|
-
if (!spec)
|
|
7
|
-
throw new Error("loadSpec macro: missing spec path/URL");
|
|
8
|
-
if (/^https?:\/\//i.test(spec)) {
|
|
9
|
-
const res = await fetch(spec);
|
|
10
|
-
if (!res.ok) {
|
|
11
|
-
throw new Error(`Failed to fetch spec: ${res.status} ${res.statusText}`);
|
|
12
|
-
}
|
|
13
|
-
return await res.text();
|
|
14
|
-
}
|
|
15
|
-
return await Bun.file(spec).text();
|
|
16
|
-
}
|
package/dist/macros/version.d.ts
DELETED
package/dist/macros/version.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
/**
|
|
5
|
-
* Bun macro: reads the version from package.json at bundle-time.
|
|
6
|
-
*/
|
|
7
|
-
export function version() {
|
|
8
|
-
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const packageJsonPath = join(currentDir, "../../package.json");
|
|
10
|
-
const content = readFileSync(packageJsonPath, "utf-8");
|
|
11
|
-
const packageJson = JSON.parse(content);
|
|
12
|
-
return packageJson.version;
|
|
13
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|