specli 0.0.18 → 0.0.20

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.
Files changed (75) hide show
  1. package/README.md +3 -1
  2. package/dist/ai/tools.d.ts +6 -5
  3. package/dist/ai/tools.js +7 -20
  4. package/dist/ai/tools.test.js +7 -14
  5. package/dist/cli/compile.js +137 -61
  6. package/dist/cli.d.ts +0 -1
  7. package/dist/cli.js +0 -1
  8. package/package.json +1 -2
  9. package/src/ai/tools.test.ts +0 -83
  10. package/src/ai/tools.ts +0 -211
  11. package/src/cli/auth-requirements.test.ts +0 -27
  12. package/src/cli/auth-requirements.ts +0 -91
  13. package/src/cli/auth-schemes.test.ts +0 -66
  14. package/src/cli/auth-schemes.ts +0 -187
  15. package/src/cli/capabilities.test.ts +0 -94
  16. package/src/cli/capabilities.ts +0 -88
  17. package/src/cli/command-id.test.ts +0 -32
  18. package/src/cli/command-id.ts +0 -16
  19. package/src/cli/command-index.ts +0 -19
  20. package/src/cli/command-model.test.ts +0 -44
  21. package/src/cli/command-model.ts +0 -128
  22. package/src/cli/compile.ts +0 -109
  23. package/src/cli/crypto.ts +0 -9
  24. package/src/cli/derive-name.ts +0 -101
  25. package/src/cli/exec.ts +0 -72
  26. package/src/cli/main.ts +0 -255
  27. package/src/cli/naming.test.ts +0 -86
  28. package/src/cli/naming.ts +0 -224
  29. package/src/cli/operations.test.ts +0 -57
  30. package/src/cli/operations.ts +0 -152
  31. package/src/cli/params.test.ts +0 -70
  32. package/src/cli/params.ts +0 -71
  33. package/src/cli/pluralize.ts +0 -41
  34. package/src/cli/positional.test.ts +0 -65
  35. package/src/cli/positional.ts +0 -75
  36. package/src/cli/request-body.test.ts +0 -35
  37. package/src/cli/request-body.ts +0 -94
  38. package/src/cli/runtime/argv.ts +0 -14
  39. package/src/cli/runtime/auth/resolve.ts +0 -59
  40. package/src/cli/runtime/body-flags.test.ts +0 -261
  41. package/src/cli/runtime/body-flags.ts +0 -176
  42. package/src/cli/runtime/body.ts +0 -24
  43. package/src/cli/runtime/collect.ts +0 -6
  44. package/src/cli/runtime/compat.ts +0 -89
  45. package/src/cli/runtime/context.ts +0 -62
  46. package/src/cli/runtime/execute.ts +0 -147
  47. package/src/cli/runtime/generated.ts +0 -242
  48. package/src/cli/runtime/headers.ts +0 -37
  49. package/src/cli/runtime/index.ts +0 -3
  50. package/src/cli/runtime/profile/secrets.ts +0 -83
  51. package/src/cli/runtime/profile/store.ts +0 -100
  52. package/src/cli/runtime/request.test.ts +0 -375
  53. package/src/cli/runtime/request.ts +0 -390
  54. package/src/cli/runtime/server-url.ts +0 -45
  55. package/src/cli/runtime/template.ts +0 -26
  56. package/src/cli/runtime/validate/ajv.ts +0 -13
  57. package/src/cli/runtime/validate/coerce.test.ts +0 -98
  58. package/src/cli/runtime/validate/coerce.ts +0 -71
  59. package/src/cli/runtime/validate/error.ts +0 -29
  60. package/src/cli/runtime/validate/index.ts +0 -4
  61. package/src/cli/runtime/validate/schema.ts +0 -54
  62. package/src/cli/schema-shape.ts +0 -36
  63. package/src/cli/schema.ts +0 -76
  64. package/src/cli/server.test.ts +0 -55
  65. package/src/cli/server.ts +0 -167
  66. package/src/cli/spec-id.ts +0 -12
  67. package/src/cli/spec-loader.ts +0 -58
  68. package/src/cli/stable-json.ts +0 -35
  69. package/src/cli/strings.ts +0 -21
  70. package/src/cli/types.ts +0 -59
  71. package/src/cli.ts +0 -94
  72. package/src/compiled.ts +0 -24
  73. package/src/macros/env.ts +0 -21
  74. package/src/macros/spec.ts +0 -17
  75. package/src/macros/version.ts +0 -14
package/src/ai/tools.ts DELETED
@@ -1,211 +0,0 @@
1
- /**
2
- * AI SDK tools for specli
3
- *
4
- * Provides tools for AI agents to explore and execute OpenAPI specs.
5
- *
6
- * @example
7
- * ```ts
8
- * import { specli } from "specli/ai";
9
- * import { generateText } from "ai";
10
- *
11
- * const result = await generateText({
12
- * model: yourModel,
13
- * tools: {
14
- * api: specli({ spec: "https://api.example.com/openapi.json" }),
15
- * },
16
- * prompt: "List all users",
17
- * });
18
- * ```
19
- */
20
-
21
- import { tool } from "ai";
22
- import { z } from "zod";
23
-
24
- import type { CommandAction } from "../cli/command-model.js";
25
- import { buildRuntimeContext } from "../cli/runtime/context.js";
26
- import { execute } from "../cli/runtime/execute.js";
27
- import type { RuntimeGlobals } from "../cli/runtime/request.js";
28
-
29
- export type SpecliToolOptions = {
30
- /** The OpenAPI spec URL or file path */
31
- spec: string;
32
- /** Override the server/base URL */
33
- server?: string;
34
- /** Server URL template variables */
35
- serverVars?: Record<string, string>;
36
- /** Bearer token for authentication */
37
- bearerToken?: string;
38
- /** API key for authentication */
39
- apiKey?: string;
40
- /** Basic auth credentials */
41
- basicAuth?: { username: string; password: string };
42
- /** Auth scheme to use (if multiple are available) */
43
- authScheme?: string;
44
- };
45
-
46
- // Cache contexts to avoid reloading spec on every call
47
- const contextCache = new Map<
48
- string,
49
- Awaited<ReturnType<typeof buildRuntimeContext>>
50
- >();
51
-
52
- async function getContext(spec: string) {
53
- let ctx = contextCache.get(spec);
54
- if (!ctx) {
55
- ctx = await buildRuntimeContext({ spec });
56
- contextCache.set(spec, ctx);
57
- }
58
- return ctx;
59
- }
60
-
61
- function findAction(
62
- ctx: Awaited<ReturnType<typeof buildRuntimeContext>>,
63
- resource: string,
64
- action: string,
65
- ): CommandAction | undefined {
66
- const r = ctx.commands.resources.find(
67
- (r) => r.resource.toLowerCase() === resource.toLowerCase(),
68
- );
69
- return r?.actions.find(
70
- (a) => a.action.toLowerCase() === action.toLowerCase(),
71
- );
72
- }
73
-
74
- /**
75
- * Create an AI SDK tool for interacting with an OpenAPI spec.
76
- */
77
- export function specli(options: SpecliToolOptions) {
78
- const {
79
- spec,
80
- server,
81
- serverVars,
82
- bearerToken,
83
- apiKey,
84
- basicAuth,
85
- authScheme,
86
- } = options;
87
-
88
- return tool({
89
- description: `Execute API operations. Commands: "list" (show resources/actions), "help" (action details), "exec" (call API).`,
90
- inputSchema: z.object({
91
- command: z.enum(["list", "help", "exec"]).describe("Command to run"),
92
- resource: z.string().optional().describe("Resource name (e.g. users)"),
93
- action: z
94
- .string()
95
- .optional()
96
- .describe("Action name (e.g. list, get, create)"),
97
- args: z.array(z.string()).optional().describe("Positional arguments"),
98
- flags: z
99
- .record(z.string(), z.unknown())
100
- .optional()
101
- .describe("Named flags"),
102
- }),
103
- execute: async ({ command, resource, action, args, flags }) => {
104
- const ctx = await getContext(spec);
105
-
106
- if (command === "list") {
107
- return {
108
- resources: ctx.commands.resources.map((r) => ({
109
- name: r.resource,
110
- actions: r.actions.map((a) => ({
111
- name: a.action,
112
- summary: a.summary,
113
- method: a.method,
114
- path: a.path,
115
- args: a.positionals.map((p) => p.name),
116
- requiredFlags: a.flags
117
- .filter((f) => f.required)
118
- .map((f) => f.flag),
119
- })),
120
- })),
121
- };
122
- }
123
-
124
- if (command === "help") {
125
- if (!resource) return { error: "Missing resource" };
126
- const r = ctx.commands.resources.find(
127
- (r) => r.resource.toLowerCase() === resource.toLowerCase(),
128
- );
129
- if (!r) return { error: `Unknown resource: ${resource}` };
130
- if (!action) {
131
- return {
132
- resource: r.resource,
133
- actions: r.actions.map((a) => a.action),
134
- };
135
- }
136
- const a = r.actions.find(
137
- (a) => a.action.toLowerCase() === action.toLowerCase(),
138
- );
139
- if (!a) return { error: `Unknown action: ${action}` };
140
- return {
141
- action: a.action,
142
- method: a.method,
143
- path: a.path,
144
- summary: a.summary,
145
- args: a.positionals.map((p) => ({
146
- name: p.name,
147
- description: p.description,
148
- })),
149
- flags: a.flags.map((f) => ({
150
- name: f.flag,
151
- type: f.type,
152
- required: f.required,
153
- description: f.description,
154
- })),
155
- };
156
- }
157
-
158
- if (command === "exec") {
159
- if (!resource || !action)
160
- return { error: "Missing resource or action" };
161
- const actionDef = findAction(ctx, resource, action);
162
- if (!actionDef) return { error: `Unknown: ${resource} ${action}` };
163
-
164
- const positionalValues = args ?? [];
165
- if (positionalValues.length < actionDef.positionals.length) {
166
- return {
167
- error: `Missing args: ${actionDef.positionals
168
- .slice(positionalValues.length)
169
- .map((p) => p.name)
170
- .join(", ")}`,
171
- };
172
- }
173
-
174
- const globals: RuntimeGlobals = {
175
- server,
176
- serverVar: serverVars
177
- ? Object.entries(serverVars).map(([k, v]) => `${k}=${v}`)
178
- : undefined,
179
- auth: authScheme,
180
- bearerToken,
181
- apiKey,
182
- username: basicAuth?.username,
183
- password: basicAuth?.password,
184
- };
185
-
186
- try {
187
- const result = await execute({
188
- specId: ctx.loaded.id,
189
- action: actionDef,
190
- positionalValues,
191
- flagValues: flags ?? {},
192
- globals,
193
- servers: ctx.servers,
194
- authSchemes: ctx.authSchemes,
195
- });
196
- return { status: result.status, ok: result.ok, body: result.body };
197
- } catch (err) {
198
- return { error: err instanceof Error ? err.message : String(err) };
199
- }
200
- }
201
-
202
- return { error: `Unknown command: ${command}` };
203
- },
204
- });
205
- }
206
-
207
- /** Clear cached spec context */
208
- export function clearSpecliCache(spec?: string): void {
209
- if (spec) contextCache.delete(spec);
210
- else contextCache.clear();
211
- }
@@ -1,27 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { summarizeAuth } from "./auth-requirements.js";
4
- import type { AuthScheme } from "./auth-schemes.js";
5
-
6
- describe("summarizeAuth", () => {
7
- test("uses operation-level security when present", () => {
8
- const schemes: AuthScheme[] = [{ key: "oauth", kind: "oauth2" }];
9
-
10
- const summary = summarizeAuth(
11
- [{ oauth: ["read:ping"] }],
12
- [{ oauth: ["read:other"] }],
13
- schemes,
14
- );
15
-
16
- expect(summary.alternatives).toEqual([
17
- [{ key: "oauth", scopes: ["read:ping"] }],
18
- ]);
19
- });
20
-
21
- test("empty operation security disables auth", () => {
22
- const schemes: AuthScheme[] = [{ key: "oauth", kind: "oauth2" }];
23
-
24
- const summary = summarizeAuth([], [{ oauth: ["read:other"] }], schemes);
25
- expect(summary.alternatives).toEqual([]);
26
- });
27
- });
@@ -1,91 +0,0 @@
1
- import type { AuthScheme } from "./auth-schemes.js";
2
- import type { SecurityRequirement } from "./types.js";
3
-
4
- export type AuthRequirement = {
5
- key: string;
6
- scopes: string[];
7
- };
8
-
9
- export type AuthSummary = {
10
- // Alternatives: any one of these sets is sufficient.
11
- alternatives: AuthRequirement[][];
12
- };
13
-
14
- function isSecurityRequirement(value: unknown): value is SecurityRequirement {
15
- if (!value || typeof value !== "object") return false;
16
- if (Array.isArray(value)) return false;
17
-
18
- for (const [k, v] of Object.entries(value)) {
19
- if (typeof k !== "string") return false;
20
- if (!Array.isArray(v)) return false;
21
- if (!v.every((s) => typeof s === "string")) return false;
22
- }
23
-
24
- return true;
25
- }
26
-
27
- function normalizeSecurity(value: unknown): {
28
- requirements: SecurityRequirement[];
29
- source: "none" | "empty" | "non-empty";
30
- } {
31
- if (value == null) return { requirements: [], source: "none" };
32
- if (!Array.isArray(value)) return { requirements: [], source: "none" };
33
-
34
- const reqs = value.filter(isSecurityRequirement);
35
- if (reqs.length === 0) return { requirements: [], source: "empty" };
36
- return { requirements: reqs, source: "non-empty" };
37
- }
38
-
39
- export function summarizeAuth(
40
- operationSecurity: unknown,
41
- globalSecurity: unknown,
42
- knownSchemes: AuthScheme[],
43
- ): AuthSummary {
44
- // Per spec:
45
- // - operation security overrides root
46
- // - empty array [] means "no auth"
47
- const op = normalizeSecurity(operationSecurity);
48
- if (op.source === "non-empty") {
49
- return { alternatives: toAlternatives(op.requirements, knownSchemes) };
50
- }
51
- if (op.source === "empty") {
52
- return { alternatives: [] };
53
- }
54
-
55
- const global = normalizeSecurity(globalSecurity);
56
- if (global.source === "non-empty") {
57
- return { alternatives: toAlternatives(global.requirements, knownSchemes) };
58
- }
59
-
60
- return { alternatives: [] };
61
- }
62
-
63
- function toAlternatives(
64
- requirements: SecurityRequirement[],
65
- knownSchemes: AuthScheme[],
66
- ): AuthRequirement[][] {
67
- const known = new Set(knownSchemes.map((s) => s.key));
68
-
69
- return requirements.map((req) => {
70
- const out: AuthRequirement[] = [];
71
- for (const [key, scopes] of Object.entries(req)) {
72
- out.push({
73
- key,
74
- scopes: Array.isArray(scopes) ? scopes : [],
75
- });
76
- }
77
-
78
- // Stable order.
79
- out.sort((a, b) => a.key.localeCompare(b.key));
80
-
81
- // Prefer known schemes first.
82
- out.sort((a, b) => {
83
- const ak = known.has(a.key) ? 0 : 1;
84
- const bk = known.has(b.key) ? 0 : 1;
85
- if (ak !== bk) return ak - bk;
86
- return a.key.localeCompare(b.key);
87
- });
88
-
89
- return out;
90
- });
91
- }
@@ -1,66 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { listAuthSchemes } from "./auth-schemes.js";
4
- import type { OpenApiDoc } from "./types.js";
5
-
6
- describe("listAuthSchemes", () => {
7
- test("parses bearer + apiKey", () => {
8
- const doc: OpenApiDoc = {
9
- openapi: "3.0.3",
10
- components: {
11
- securitySchemes: {
12
- bearerAuth: {
13
- type: "http",
14
- scheme: "bearer",
15
- bearerFormat: "JWT",
16
- },
17
- apiKeyAuth: {
18
- type: "apiKey",
19
- in: "header",
20
- name: "X-API-Key",
21
- },
22
- },
23
- },
24
- };
25
-
26
- const schemes = listAuthSchemes(doc);
27
- expect(schemes).toHaveLength(2);
28
-
29
- const bearer = schemes.find((s) => s.key === "bearerAuth");
30
- expect(bearer?.kind).toBe("http-bearer");
31
-
32
- const apiKey = schemes.find((s) => s.key === "apiKeyAuth");
33
- expect(apiKey?.kind).toBe("api-key");
34
- expect(apiKey?.in).toBe("header");
35
- expect(apiKey?.name).toBe("X-API-Key");
36
- });
37
-
38
- test("parses oauth2 flows", () => {
39
- const doc = {
40
- openapi: "3.0.3",
41
- components: {
42
- securitySchemes: {
43
- oauth: {
44
- type: "oauth2",
45
- flows: {
46
- clientCredentials: {
47
- tokenUrl: "https://example.com/oauth/token",
48
- scopes: {
49
- "read:ping": "read ping",
50
- },
51
- },
52
- },
53
- },
54
- },
55
- },
56
- } as const;
57
-
58
- const schemes = listAuthSchemes(doc);
59
- const oauth = schemes.find((s) => s.key === "oauth");
60
- expect(oauth?.kind).toBe("oauth2");
61
- expect(oauth?.oauthFlows?.clientCredentials?.tokenUrl).toBe(
62
- "https://example.com/oauth/token",
63
- );
64
- expect(oauth?.oauthFlows?.clientCredentials?.scopes).toEqual(["read:ping"]);
65
- });
66
- });
@@ -1,187 +0,0 @@
1
- import { kebabCase } from "./strings.js";
2
- import type { OpenApiDoc } from "./types.js";
3
-
4
- export type AuthSchemeKind =
5
- | "http-bearer"
6
- | "http-basic"
7
- | "api-key"
8
- | "oauth2"
9
- | "openIdConnect"
10
- | "unknown";
11
-
12
- export type AuthScheme = {
13
- key: string;
14
- kind: AuthSchemeKind;
15
- name?: string;
16
- in?: "header" | "query" | "cookie";
17
- scheme?: string;
18
- bearerFormat?: string;
19
- description?: string;
20
-
21
- // oauth2/openid only (subset of spec, enough to derive flags + docs)
22
- oauthFlows?: OAuthFlows;
23
- openIdConnectUrl?: string;
24
- };
25
-
26
- export type OAuthFlow = {
27
- authorizationUrl?: string;
28
- tokenUrl?: string;
29
- refreshUrl?: string;
30
- scopes: string[];
31
- };
32
-
33
- export type OAuthFlows = Partial<
34
- Record<
35
- "implicit" | "password" | "clientCredentials" | "authorizationCode",
36
- OAuthFlow
37
- >
38
- >;
39
-
40
- type RawOAuthFlow = {
41
- authorizationUrl?: unknown;
42
- tokenUrl?: unknown;
43
- refreshUrl?: unknown;
44
- scopes?: unknown;
45
- };
46
-
47
- type RawOAuthFlows = {
48
- implicit?: RawOAuthFlow;
49
- password?: RawOAuthFlow;
50
- clientCredentials?: RawOAuthFlow;
51
- authorizationCode?: RawOAuthFlow;
52
- };
53
-
54
- type RawSecurityScheme = {
55
- type?: string;
56
- description?: string;
57
- name?: string;
58
- in?: string;
59
- scheme?: string;
60
- bearerFormat?: string;
61
- flows?: RawOAuthFlows;
62
- openIdConnectUrl?: string;
63
- };
64
-
65
- function parseOAuthFlow(flow: RawOAuthFlow | undefined): OAuthFlow | undefined {
66
- if (!flow) return undefined;
67
- const scopesObj = flow.scopes;
68
- const scopes =
69
- scopesObj && typeof scopesObj === "object" && !Array.isArray(scopesObj)
70
- ? Object.keys(scopesObj as Record<string, unknown>)
71
- : [];
72
-
73
- return {
74
- authorizationUrl:
75
- typeof flow.authorizationUrl === "string"
76
- ? flow.authorizationUrl
77
- : undefined,
78
- tokenUrl: typeof flow.tokenUrl === "string" ? flow.tokenUrl : undefined,
79
- refreshUrl:
80
- typeof flow.refreshUrl === "string" ? flow.refreshUrl : undefined,
81
- scopes: scopes.sort(),
82
- };
83
- }
84
-
85
- function parseOAuthFlows(
86
- flows: RawOAuthFlows | undefined,
87
- ): OAuthFlows | undefined {
88
- if (!flows) return undefined;
89
- const out: OAuthFlows = {};
90
-
91
- const implicit = parseOAuthFlow(flows.implicit);
92
- if (implicit) out.implicit = implicit;
93
-
94
- const password = parseOAuthFlow(flows.password);
95
- if (password) out.password = password;
96
-
97
- const clientCredentials = parseOAuthFlow(flows.clientCredentials);
98
- if (clientCredentials) out.clientCredentials = clientCredentials;
99
-
100
- const authorizationCode = parseOAuthFlow(flows.authorizationCode);
101
- if (authorizationCode) out.authorizationCode = authorizationCode;
102
-
103
- return Object.keys(out).length ? out : undefined;
104
- }
105
-
106
- export function listAuthSchemes(doc: OpenApiDoc): AuthScheme[] {
107
- const schemes = doc.components?.securitySchemes;
108
- if (!schemes || typeof schemes !== "object") return [];
109
-
110
- const out: AuthScheme[] = [];
111
-
112
- for (const [key, raw] of Object.entries(schemes)) {
113
- if (!raw || typeof raw !== "object") continue;
114
- const s = raw as RawSecurityScheme;
115
-
116
- const type = s.type;
117
- if (type === "http") {
118
- const scheme = (s.scheme ?? "").toLowerCase();
119
- if (scheme === "bearer") {
120
- out.push({
121
- key,
122
- kind: "http-bearer",
123
- scheme: scheme,
124
- bearerFormat: s.bearerFormat,
125
- description: s.description,
126
- });
127
- } else if (scheme === "basic") {
128
- out.push({
129
- key,
130
- kind: "http-basic",
131
- scheme: scheme,
132
- description: s.description,
133
- });
134
- } else {
135
- out.push({
136
- key,
137
- kind: "unknown",
138
- scheme: s.scheme,
139
- description: s.description,
140
- });
141
- }
142
- continue;
143
- }
144
-
145
- if (type === "apiKey") {
146
- const where = s.in;
147
- const loc =
148
- where === "header" || where === "query" || where === "cookie"
149
- ? where
150
- : undefined;
151
- out.push({
152
- key,
153
- kind: "api-key",
154
- name: s.name,
155
- in: loc,
156
- description: s.description,
157
- });
158
- continue;
159
- }
160
-
161
- if (type === "oauth2") {
162
- out.push({
163
- key,
164
- kind: "oauth2",
165
- description: s.description,
166
- oauthFlows: parseOAuthFlows(s.flows),
167
- });
168
- continue;
169
- }
170
-
171
- if (type === "openIdConnect") {
172
- out.push({
173
- key,
174
- kind: "openIdConnect",
175
- description: s.description,
176
- openIdConnectUrl: s.openIdConnectUrl,
177
- });
178
- continue;
179
- }
180
-
181
- out.push({ key, kind: "unknown", description: s.description });
182
- }
183
-
184
- // Stable order.
185
- out.sort((a, b) => kebabCase(a.key).localeCompare(kebabCase(b.key)));
186
- return out;
187
- }
@@ -1,94 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import type { AuthScheme } from "./auth-schemes.js";
3
- import { deriveCapabilities } from "./capabilities.js";
4
- import type { CommandModel } from "./command-model.js";
5
- import type { ServerInfo } from "./server.js";
6
- import type { NormalizedOperation, OpenApiDoc } from "./types.js";
7
-
8
- describe("deriveCapabilities", () => {
9
- test("reports requestBody + server vars", () => {
10
- const doc: OpenApiDoc = {
11
- openapi: "3.0.3",
12
- security: [{ bearerAuth: [] }],
13
- };
14
-
15
- const servers: ServerInfo[] = [
16
- {
17
- url: "https://{region}.api.example.com",
18
- variables: [],
19
- variableNames: ["region"],
20
- },
21
- ];
22
-
23
- const authSchemes: AuthScheme[] = [
24
- { key: "bearerAuth", kind: "http-bearer" },
25
- ];
26
-
27
- const operations: NormalizedOperation[] = [
28
- {
29
- key: "POST /contacts",
30
- method: "POST",
31
- path: "/contacts",
32
- tags: [],
33
- parameters: [],
34
- requestBody: {
35
- required: true,
36
- contentTypes: ["application/json"],
37
- schemasByContentType: { "application/json": { type: "object" } },
38
- },
39
- },
40
- ];
41
-
42
- const commands: CommandModel = {
43
- resources: [
44
- {
45
- resource: "contacts",
46
- actions: [
47
- {
48
- id: "x",
49
- key: "POST /contacts",
50
- action: "create",
51
- pathArgs: [],
52
- method: "POST",
53
- path: "/contacts",
54
- tags: [],
55
- style: "rest",
56
- positionals: [],
57
- flags: [],
58
- params: [],
59
- auth: { alternatives: [] },
60
- requestBody: {
61
- required: true,
62
- content: [
63
- {
64
- contentType: "application/json",
65
- required: true,
66
- schemaType: "object",
67
- },
68
- ],
69
- hasJson: true,
70
- hasFormUrlEncoded: false,
71
- hasMultipart: false,
72
- bodyFlags: ["--data", "--file"],
73
- preferredContentType: "application/json",
74
- },
75
- },
76
- ],
77
- },
78
- ],
79
- };
80
-
81
- const caps = deriveCapabilities({
82
- doc,
83
- servers,
84
- authSchemes,
85
- operations,
86
- commands,
87
- });
88
- expect(caps.servers.hasVariables).toBe(true);
89
- expect(caps.operations.hasRequestBodies).toBe(true);
90
- expect(caps.commands.hasRequestBodies).toBe(true);
91
- expect(caps.auth.hasSecurityRequirements).toBe(true);
92
- expect(caps.auth.kinds).toEqual(["http-bearer"]);
93
- });
94
- });