@krak-stack/registry 0.1.37 → 0.1.39
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 +131 -7
- package/dist/components/ui/app-brand.js +17 -17
- package/dist/components/ui/code-block.js +4 -4
- package/dist/components/ui/copy-button.js +2 -2
- package/dist/components/ui/data-table.js +56 -56
- package/dist/components/ui/editing-locale-switcher.js +8 -8
- package/dist/components/ui/effect-form.js +12 -12
- package/dist/components/ui/error-component.js +6 -6
- package/dist/components/ui/file-picker.js +2 -2
- package/dist/components/ui/form.js +24 -24
- package/dist/components/ui/icon-input.js +5 -5
- package/dist/components/ui/locale-switcher.js +2 -2
- package/dist/components/ui/search-menu.js +2 -2
- package/dist/components/ui/sidebar-layout.js +1 -1
- package/dist/components/ui/theme-switcher.js +3 -3
- package/dist/lib/docs.js +52 -111
- package/dist/lib/docs.server.js +1 -29
- package/dist/lib/documentation-toolkit.js +0 -28
- package/dist/lib/httpapi-adapter.d.ts +73 -0
- package/dist/lib/httpapi-adapter.js +189 -0
- package/dist/lib/httpapi-cli.d.ts +4 -22
- package/dist/lib/httpapi-cli.js +227 -533
- package/dist/lib/httpapi-toolkit.d.ts +15 -12
- package/dist/lib/httpapi-toolkit.js +175 -381
- package/dist/lib/query.d.ts +2 -2
- package/dist/lib/query.js +18 -5
- package/dist/lib/webfetch-toolkit.d.ts +1 -1
- package/dist/lib/webfetch-toolkit.js +48 -50
- package/dist/oxlint/anti-slop/index.js +2 -2
- package/dist/services/agent/client/index.js +6 -6
- package/dist/services/agent/index.js +3 -3
- package/package.json +5 -13
- package/dist/lib/httpapi-client.d.ts +0 -30
- package/dist/lib/httpapi-client.js +0 -112
- package/dist/lib/httpapi-helpers.d.ts +0 -101
- package/dist/lib/httpapi-helpers.js +0 -286
- package/dist/lib/httpapi-mcp.d.ts +0 -20
- package/dist/lib/httpapi-mcp.js +0 -477
package/dist/lib/httpapi-cli.js
CHANGED
|
@@ -1,30 +1,44 @@
|
|
|
1
1
|
// ../../src/lib/httpapi-cli.ts
|
|
2
|
-
import {
|
|
3
|
-
Cause,
|
|
4
|
-
Console,
|
|
5
|
-
Context as Context3,
|
|
6
|
-
Effect as Effect3,
|
|
7
|
-
Exit,
|
|
8
|
-
FileSystem,
|
|
9
|
-
Layer as Layer3,
|
|
10
|
-
Option as Option2,
|
|
11
|
-
Path,
|
|
12
|
-
Schema as Schema3,
|
|
13
|
-
Stdio,
|
|
14
|
-
Stream,
|
|
15
|
-
Terminal
|
|
16
|
-
} from "effect";
|
|
2
|
+
import { Console, Effect as Effect2, Option as Option2, Schema as Schema2, Stream } from "effect";
|
|
17
3
|
import { Argument, Command, Flag } from "effect/cli";
|
|
18
|
-
import { ChildProcessSpawner } from "effect/process/ChildProcessSpawner";
|
|
19
4
|
|
|
20
|
-
// ../../src/lib/httpapi-
|
|
21
|
-
import { Context, Effect, Layer, Schema, SchemaTransformation } from "effect";
|
|
22
|
-
import { HttpClient } from "effect/http";
|
|
5
|
+
// ../../src/lib/httpapi-adapter.ts
|
|
23
6
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
7
|
+
Context,
|
|
8
|
+
Effect,
|
|
9
|
+
Layer,
|
|
10
|
+
Option,
|
|
11
|
+
Schema,
|
|
12
|
+
SchemaTransformation
|
|
13
|
+
} from "effect";
|
|
14
|
+
import { HttpClient } from "effect/http";
|
|
15
|
+
import { HttpApi, HttpApiClient, OpenApi } from "effect/http-api";
|
|
16
|
+
var HttpApiMethods = [
|
|
17
|
+
"get",
|
|
18
|
+
"post",
|
|
19
|
+
"put",
|
|
20
|
+
"patch",
|
|
21
|
+
"delete"
|
|
22
|
+
];
|
|
23
|
+
var HttpApiOperation = Schema.Struct({
|
|
24
|
+
operationId: Schema.optional(Schema.String),
|
|
25
|
+
summary: Schema.optional(Schema.String),
|
|
26
|
+
description: Schema.optional(Schema.String),
|
|
27
|
+
tags: Schema.optional(Schema.Array(Schema.String)),
|
|
28
|
+
parameters: Schema.optional(Schema.Array(Schema.Struct({
|
|
29
|
+
name: Schema.String,
|
|
30
|
+
in: Schema.Literals(["path", "query", "header", "cookie"]),
|
|
31
|
+
required: Schema.optional(Schema.Boolean),
|
|
32
|
+
description: Schema.optional(Schema.String),
|
|
33
|
+
schema: Schema.optional(Schema.Json)
|
|
34
|
+
}))),
|
|
35
|
+
requestBody: Schema.optional(Schema.Struct({
|
|
36
|
+
required: Schema.optional(Schema.Boolean),
|
|
37
|
+
content: Schema.optional(Schema.Record(Schema.String, Schema.Struct({
|
|
38
|
+
schema: Schema.optional(Schema.Json)
|
|
39
|
+
})))
|
|
40
|
+
}))
|
|
41
|
+
}).annotate({ identifier: "HttpApiOperation" });
|
|
28
42
|
var UndefinedFromNull = Schema.Null.pipe(Schema.decodeTo(Schema.Undefined, SchemaTransformation.transform({
|
|
29
43
|
decode: () => {
|
|
30
44
|
return;
|
|
@@ -46,419 +60,175 @@ var HttpApiOperationResult = Schema.Union([
|
|
|
46
60
|
HttpApiOperationResultValue,
|
|
47
61
|
Schema.Undefined
|
|
48
62
|
]).annotate({ identifier: "HttpApiOperationResult" });
|
|
49
|
-
var encodeHttpApiOperationResult = Effect.fn("
|
|
63
|
+
var encodeHttpApiOperationResult = Effect.fn("HttpApiAdapter.encodeOperationResult")((result) => Schema.encodeUnknownEffect(HttpApiOperationResult)(result).pipe(Effect.map((encoded) => encoded ?? null), Effect.mapError((cause) => new Error("HTTP API result is not serializable", { cause }))));
|
|
64
|
+
var encodeOperationResult = Effect.fn("HttpApiAdapter.encodeResult")((result, operation) => operation.successSchema ? Schema.encodeUnknownEffect(operation.successSchema)(result).pipe(Effect.flatMap(encodeHttpApiOperationResult), Effect.mapError((cause) => new Error("HTTP API result does not match its success schema", {
|
|
65
|
+
cause
|
|
66
|
+
}))) : encodeHttpApiOperationResult(result));
|
|
50
67
|
var reflectedSchema = (schema) => Schema.make(schema.ast);
|
|
51
|
-
var
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
68
|
+
var sanitizeHttpName = (name) => name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
69
|
+
var httpApiToolName = (entry) => (entry.operation.operationId || `${entry.method}_${entry.path.replace(/^\/api\//, "").replace(/[/:{}]/g, "_")}`).replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").slice(0, 64);
|
|
70
|
+
var makeCatalogue = (api, methods, include) => Effect.try({
|
|
71
|
+
try: () => {
|
|
72
|
+
if (!HttpApi.isHttpApi(api))
|
|
73
|
+
throw new Error("HttpApiAdapter requires a valid HttpApi");
|
|
74
|
+
const spec = OpenApi.fromApi(api);
|
|
75
|
+
const reflected = new Map;
|
|
76
|
+
HttpApi.reflect(api, {
|
|
77
|
+
onGroup: () => {
|
|
78
|
+
return;
|
|
79
|
+
},
|
|
80
|
+
onEndpoint: ({ endpoint, group, successes }) => {
|
|
81
|
+
const operationId = Context.getOrElse(endpoint.annotations, OpenApi.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
82
|
+
if (reflected.has(operationId))
|
|
83
|
+
throw new Error(`Duplicate HTTP API operation ID: ${operationId}`);
|
|
84
|
+
const fields = {};
|
|
85
|
+
if (endpoint.params)
|
|
86
|
+
fields.params = Schema.toType(reflectedSchema(endpoint.params));
|
|
87
|
+
if (endpoint.query)
|
|
88
|
+
fields.query = Schema.optionalKey(Schema.toType(reflectedSchema(endpoint.query)));
|
|
89
|
+
if (endpoint.headers)
|
|
90
|
+
fields.headers = Schema.optionalKey(Schema.toType(reflectedSchema(endpoint.headers)));
|
|
91
|
+
const payloads = Array.from(endpoint.payload.values()).flatMap(({ schemas }) => schemas.map(reflectedSchema));
|
|
92
|
+
if (payloads.length === 1 && payloads[0])
|
|
93
|
+
fields.body = payloads[0];
|
|
94
|
+
else if (payloads.length > 1)
|
|
95
|
+
fields.body = Schema.Union(payloads);
|
|
96
|
+
const input = Object.keys(fields).length === 0 ? Schema.Record(Schema.String, Schema.Never) : Schema.Struct(fields);
|
|
97
|
+
const inputSchema = Schema.make(Schema.toCodecJson(input).ast).annotate({
|
|
98
|
+
identifier: `${sanitizeHttpName(operationId)}ToolInput`
|
|
99
|
+
});
|
|
100
|
+
const successSchemas = Array.from(successes.values()).flat().map(reflectedSchema);
|
|
101
|
+
reflected.set(operationId, {
|
|
102
|
+
groupIdentifier: group.identifier,
|
|
103
|
+
endpointIdentifier: endpoint.identifier,
|
|
104
|
+
topLevel: group.topLevel,
|
|
105
|
+
inputSchema,
|
|
106
|
+
successSchema: successSchemas.length === 1 ? successSchemas[0] : successSchemas.length > 1 ? Schema.Union(successSchemas) : undefined
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
const names = new Set;
|
|
111
|
+
const operations = [];
|
|
112
|
+
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
113
|
+
for (const method of methods) {
|
|
114
|
+
const decoded = Schema.decodeUnknownOption(HttpApiOperation)(pathItem[method]);
|
|
115
|
+
if (Option.isNone(decoded))
|
|
116
|
+
continue;
|
|
117
|
+
const entry = { method, path, operation: decoded.value };
|
|
118
|
+
if (include && !include(entry))
|
|
119
|
+
continue;
|
|
120
|
+
const metadata = entry.operation.operationId ? reflected.get(entry.operation.operationId) : undefined;
|
|
121
|
+
if (!metadata)
|
|
122
|
+
throw new Error(`Missing endpoint schema for ${entry.operation.operationId}`);
|
|
123
|
+
const name = httpApiToolName(entry);
|
|
124
|
+
if (!name || names.has(name))
|
|
125
|
+
throw new Error(`Duplicate or empty HTTP API tool name: ${name}`);
|
|
126
|
+
names.add(name);
|
|
127
|
+
const document = Schema.toJsonSchemaDocument(metadata.inputSchema.annotate({ identifier: undefined }));
|
|
128
|
+
operations.push({
|
|
129
|
+
...entry,
|
|
130
|
+
...metadata,
|
|
131
|
+
name,
|
|
132
|
+
inputJsonSchema: {
|
|
133
|
+
...document.schema,
|
|
134
|
+
$defs: document.definitions
|
|
135
|
+
},
|
|
136
|
+
readOnly: method === "get",
|
|
137
|
+
destructive: method === "delete",
|
|
138
|
+
idempotent: method === "get" || method === "put" || method === "delete"
|
|
139
|
+
});
|
|
64
140
|
}
|
|
65
141
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const schemas = operationResultSchemas(api);
|
|
71
|
-
return Effect.fn("HttpApiClient.encodeOperationResultWithSchema")((result, operation) => {
|
|
72
|
-
const operationId = operation.operation.operationId;
|
|
73
|
-
const schema = operationId ? schemas.get(operationId) : undefined;
|
|
74
|
-
if (!schema)
|
|
75
|
-
return encodeHttpApiOperationResult(result);
|
|
76
|
-
return Schema.encodeUnknownEffect(schema)(result).pipe(Effect.flatMap(encodeHttpApiOperationResult), Effect.mapError((cause) => new Error("HTTP API result does not match its success schema", {
|
|
77
|
-
cause
|
|
78
|
-
})));
|
|
79
|
-
});
|
|
80
|
-
};
|
|
142
|
+
return { info: spec.info, operations };
|
|
143
|
+
},
|
|
144
|
+
catch: (cause) => new Error("Failed to build HTTP API adapter catalogue", { cause })
|
|
145
|
+
});
|
|
81
146
|
var GeneratedOperation = Schema.declare((value) => value instanceof Function).annotate({ identifier: "GeneratedOperation" });
|
|
82
147
|
var GeneratedOperationEffect = Schema.declare((value) => Effect.isEffect(value)).annotate({ identifier: "GeneratedOperationEffect" });
|
|
83
|
-
var
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!operationId) {
|
|
90
|
-
return yield* Effect.fail(new Error(`No generated API client operation for ${entry.method} ${entry.path}`));
|
|
91
|
-
}
|
|
92
|
-
const target = Object(client);
|
|
93
|
-
const groupName = Object.keys(target).filter((name) => operationId.startsWith(`${name}.`)).sort((a, b) => b.length - a.length)[0];
|
|
94
|
-
const group = groupName ? Object.entries(target).find(([name]) => name === groupName)?.[1] : target;
|
|
95
|
-
const endpointName = groupName ? operationId.slice(groupName.length + 1) : operationId;
|
|
96
|
-
const endpointCandidate = group instanceof Function ? undefined : Object.entries(Object(group)).find(([name]) => name === endpointName)?.[1];
|
|
97
|
-
const endpoint = Schema.decodeUnknownOption(GeneratedOperation)(endpointCandidate);
|
|
98
|
-
if (endpoint._tag === "None") {
|
|
99
|
-
return yield* Effect.fail(new Error(`No generated API client operation for ${operationId}`));
|
|
100
|
-
}
|
|
148
|
+
var executeGeneratedOperation = Effect.fn("HttpApiAdapter.executeGeneratedOperation")(function* (client, { operation, input }) {
|
|
149
|
+
const group = operation.topLevel ? client : Object.entries(Object(client)).find(([name]) => name === operation.groupIdentifier)?.[1];
|
|
150
|
+
const candidate = Object.entries(Object(group)).find(([name]) => name === operation.endpointIdentifier)?.[1];
|
|
151
|
+
const endpoint = Schema.decodeUnknownOption(GeneratedOperation)(candidate);
|
|
152
|
+
if (Option.isNone(endpoint))
|
|
153
|
+
return yield* Effect.fail(new Error(`No generated API client operation for ${operation.operation.operationId}`));
|
|
101
154
|
const result = Schema.decodeUnknownOption(GeneratedOperationEffect)(endpoint.value({
|
|
102
|
-
headers: input.headers,
|
|
103
|
-
params: input.params,
|
|
104
|
-
query: input.query,
|
|
155
|
+
headers: input.headers ?? {},
|
|
156
|
+
params: input.params ?? {},
|
|
157
|
+
query: input.query ?? {},
|
|
105
158
|
payload: input.body
|
|
106
159
|
}));
|
|
107
|
-
if (result
|
|
108
|
-
return yield* Effect.fail(new Error(`Generated API client operation ${operationId} is invalid`));
|
|
109
|
-
}
|
|
160
|
+
if (Option.isNone(result))
|
|
161
|
+
return yield* Effect.fail(new Error(`Generated API client operation ${operation.operation.operationId} is invalid`));
|
|
110
162
|
return yield* result.value;
|
|
111
163
|
});
|
|
112
164
|
|
|
113
|
-
class
|
|
114
|
-
static
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const client = yield*
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
import {
|
|
129
|
-
Context as Context2,
|
|
130
|
-
Effect as Effect2,
|
|
131
|
-
JsonSchema,
|
|
132
|
-
Layer as Layer2,
|
|
133
|
-
Option,
|
|
134
|
-
Schema as Schema2,
|
|
135
|
-
SchemaRepresentation
|
|
136
|
-
} from "effect";
|
|
137
|
-
import { HttpApi as HttpApi2, OpenApi as OpenApi2 } from "effect/http-api";
|
|
138
|
-
var JsonObjectSchema = Schema2.Record(Schema2.String, Schema2.Json).annotate({
|
|
139
|
-
identifier: "HttpJsonObject",
|
|
140
|
-
title: "HTTP JSON object",
|
|
141
|
-
description: "A JSON object passed to an HTTP API operation.",
|
|
142
|
-
examples: [{ id: "example-id" }]
|
|
143
|
-
});
|
|
144
|
-
var JsonObjectFromString = Schema2.fromJsonString(JsonObjectSchema);
|
|
145
|
-
var JsonValueFromString = Schema2.fromJsonString(Schema2.Json);
|
|
146
|
-
var JsonSchemaAnnotations = Schema2.Struct({
|
|
147
|
-
title: Schema2.optional(Schema2.String),
|
|
148
|
-
description: Schema2.optional(Schema2.String),
|
|
149
|
-
examples: Schema2.optional(Schema2.Array(Schema2.Json))
|
|
150
|
-
}).annotate({
|
|
151
|
-
identifier: "HttpJsonSchemaAnnotations",
|
|
152
|
-
title: "HTTP JSON Schema annotations",
|
|
153
|
-
description: "JSON Schema annotations surfaced on generated tool inputs."
|
|
154
|
-
});
|
|
155
|
-
var HttpApiToolInputSchema = Schema2.Struct({
|
|
156
|
-
body: Schema2.optional(Schema2.Json)
|
|
157
|
-
}).annotate({
|
|
158
|
-
identifier: "HttpApiToolInput",
|
|
159
|
-
title: "HTTP API tool input",
|
|
160
|
-
description: "Input accepted by an HTTP API-backed tool.",
|
|
161
|
-
examples: [{ body: { id: "example-id" } }]
|
|
162
|
-
});
|
|
163
|
-
var HttpApiMethods = [
|
|
164
|
-
"get",
|
|
165
|
-
"post",
|
|
166
|
-
"put",
|
|
167
|
-
"patch",
|
|
168
|
-
"delete"
|
|
169
|
-
];
|
|
170
|
-
var HttpApiOperationSchema = Schema2.declare((value) => Schema2.is(JsonObjectSchema)(value)).annotate({ identifier: "HttpApiOperation" });
|
|
171
|
-
var decodeHttpApiOperation = Schema2.decodeUnknownOption(HttpApiOperationSchema);
|
|
172
|
-
var toHttpError = (message, error) => new Error(message, { cause: error });
|
|
173
|
-
var sanitizeHttpName = (name) => name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
174
|
-
var httpApiToolName = (method, path, operation) => {
|
|
175
|
-
const fallback = `${method}_${path.replace(/^\/api\//, "").replace(/[/:{}]/g, "_")}`;
|
|
176
|
-
return (operation.operationId || fallback).replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").slice(0, 64);
|
|
177
|
-
};
|
|
178
|
-
var httpApiOperations = ({
|
|
179
|
-
spec,
|
|
180
|
-
methods = HttpApiMethods
|
|
181
|
-
}) => {
|
|
182
|
-
const operations = [];
|
|
183
|
-
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
184
|
-
for (const method of methods) {
|
|
185
|
-
const operation = decodeHttpApiOperation(pathItem[method]);
|
|
186
|
-
if (Option.isSome(operation)) {
|
|
187
|
-
operations.push({ method, path, operation: operation.value });
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return operations;
|
|
192
|
-
};
|
|
193
|
-
var httpApiToolEntries = Effect2.fn("Http.toolEntries")(function* (operations) {
|
|
194
|
-
return yield* Effect2.try({
|
|
195
|
-
try: () => {
|
|
196
|
-
const names = new Set;
|
|
197
|
-
return operations.map((entry) => {
|
|
198
|
-
const name = httpApiToolName(entry.method, entry.path, entry.operation);
|
|
199
|
-
if (!name || names.has(name)) {
|
|
200
|
-
throw new Error(`Duplicate or empty HTTP API tool name: ${name}`);
|
|
201
|
-
}
|
|
202
|
-
names.add(name);
|
|
203
|
-
return { ...entry, name };
|
|
204
|
-
});
|
|
205
|
-
},
|
|
206
|
-
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
var decodeAnnotations = Schema2.decodeUnknownOption(JsonSchemaAnnotations);
|
|
210
|
-
var schemaWithVisibleAnnotations = (schema) => {
|
|
211
|
-
const directAnnotations = decodeAnnotations(schema).pipe(Option.getOrElse(() => ({})));
|
|
212
|
-
const allOf = Array.isArray(schema.allOf) ? schema.allOf : [];
|
|
213
|
-
const annotations = allOf.reduce((acc, item) => ({
|
|
214
|
-
...acc,
|
|
215
|
-
...decodeAnnotations(item).pipe(Option.getOrElse(() => ({})))
|
|
216
|
-
}), directAnnotations);
|
|
217
|
-
const visible = { ...schema };
|
|
218
|
-
if ("title" in annotations && !("title" in schema)) {
|
|
219
|
-
visible.title = annotations.title;
|
|
220
|
-
}
|
|
221
|
-
if ("description" in annotations && !("description" in schema)) {
|
|
222
|
-
visible.description = annotations.description;
|
|
223
|
-
}
|
|
224
|
-
if ("examples" in annotations && !("examples" in schema)) {
|
|
225
|
-
visible.examples = annotations.examples;
|
|
226
|
-
}
|
|
227
|
-
return visible;
|
|
228
|
-
};
|
|
229
|
-
var referencedDefinitions = (schema, definitions) => {
|
|
230
|
-
const names = new Set;
|
|
231
|
-
const pending = [Schema2.decodeUnknownSync(Schema2.Json)(schema)];
|
|
232
|
-
while (pending.length > 0) {
|
|
233
|
-
const current = pending.pop();
|
|
234
|
-
if (Array.isArray(current)) {
|
|
235
|
-
pending.push(...current);
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
const record = Schema2.decodeUnknownOption(Schema2.Record(Schema2.String, Schema2.Json))(current);
|
|
239
|
-
if (Option.isNone(record))
|
|
240
|
-
continue;
|
|
241
|
-
for (const [key, value] of Object.entries(record.value)) {
|
|
242
|
-
if (key === "$ref" && Schema2.is(Schema2.String)(value)) {
|
|
243
|
-
const name = value.match(/^#\/(?:\$defs|components\/schemas)\/(.+)$/)?.[1];
|
|
244
|
-
if (name && !names.has(name) && definitions[name]) {
|
|
245
|
-
names.add(name);
|
|
246
|
-
pending.push(Schema2.decodeUnknownSync(Schema2.Json)(definitions[name]));
|
|
165
|
+
class HttpApiAdapter extends Context.Service()("@krak-stack/registry/HttpApiAdapter") {
|
|
166
|
+
static make = Effect.fn("HttpApiAdapter.make")(function* (config) {
|
|
167
|
+
const catalogue = yield* makeCatalogue(config.api, config.methods ?? HttpApiMethods, config.include);
|
|
168
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
169
|
+
const client = yield* HttpApiClient.makeWith(config.api, {
|
|
170
|
+
baseUrl: config.baseUrl,
|
|
171
|
+
httpClient
|
|
172
|
+
});
|
|
173
|
+
return HttpApiAdapter.of({
|
|
174
|
+
...catalogue,
|
|
175
|
+
encodeResult: config.encodeResult ?? encodeOperationResult,
|
|
176
|
+
execute: Effect.fn("HttpApiAdapter.execute")(function* (options) {
|
|
177
|
+
const operation = catalogue.operations.find((entry) => entry.method === options.operation.method && entry.path === options.operation.path && entry.operation.operationId === options.operation.operation.operationId);
|
|
178
|
+
if (!operation) {
|
|
179
|
+
return yield* Effect.fail(new Error("Operation is not in this HTTP API adapter catalogue"));
|
|
247
180
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return Object.fromEntries([...names].map((name) => [name, definitions[name]]));
|
|
254
|
-
};
|
|
255
|
-
var operationParameters = (parameters, location) => parameters.filter((parameter) => parameter.in === location);
|
|
256
|
-
var httpApiOperationInputSchema = (operation) => {
|
|
257
|
-
const parameters = operation.parameters ?? [];
|
|
258
|
-
const pathParameters = operationParameters(parameters, "path");
|
|
259
|
-
const queryParameters = operationParameters(parameters, "query");
|
|
260
|
-
const headerParameters = operationParameters(parameters, "header");
|
|
261
|
-
const properties = {};
|
|
262
|
-
const required = [];
|
|
263
|
-
const body = operation.requestBody?.content?.["application/json"]?.schema;
|
|
264
|
-
for (const parameter of [
|
|
265
|
-
...pathParameters,
|
|
266
|
-
...queryParameters,
|
|
267
|
-
...headerParameters
|
|
268
|
-
]) {
|
|
269
|
-
const property = {
|
|
270
|
-
...parameter.schema ? schemaWithVisibleAnnotations(parameter.schema) : { type: "string" }
|
|
271
|
-
};
|
|
272
|
-
if (parameter.description)
|
|
273
|
-
property.description = parameter.description;
|
|
274
|
-
properties[parameter.name] = property;
|
|
275
|
-
if (parameter.required)
|
|
276
|
-
required.push(parameter.name);
|
|
277
|
-
}
|
|
278
|
-
if (body) {
|
|
279
|
-
properties.body = body;
|
|
280
|
-
if (operation.requestBody?.required)
|
|
281
|
-
required.push("body");
|
|
282
|
-
}
|
|
283
|
-
return {
|
|
284
|
-
type: "object",
|
|
285
|
-
properties,
|
|
286
|
-
required,
|
|
287
|
-
additionalProperties: false
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
var decodeJsonObject = Schema2.decodeUnknownOption(JsonObjectSchema);
|
|
291
|
-
var decodeHttpApiOperationInput = Effect2.fn("Http.decodeApiOperationInput")(function* (input, operation) {
|
|
292
|
-
const payload = yield* Schema2.decodeUnknownEffect(JsonObjectSchema)(input ?? {}).pipe(Effect2.mapError(() => new Error("Tool input must be a JSON object")));
|
|
293
|
-
const decoded = yield* Schema2.decodeUnknownEffect(HttpApiToolInputSchema)(payload).pipe(Effect2.mapError(() => new Error("Tool input must be a JSON object")));
|
|
294
|
-
const parameters = operation.parameters ?? [];
|
|
295
|
-
const pickParameters = (location) => {
|
|
296
|
-
const nestedKey = location === "path" ? "params" : location;
|
|
297
|
-
const nested = decodeJsonObject(payload[nestedKey]).pipe(Option.getOrElse(() => ({})));
|
|
298
|
-
return Object.fromEntries(operationParameters(parameters, location).map((parameter) => [
|
|
299
|
-
parameter.name,
|
|
300
|
-
nested[parameter.name] ?? payload[parameter.name]
|
|
301
|
-
]).filter(([, value]) => value !== undefined));
|
|
302
|
-
};
|
|
303
|
-
return {
|
|
304
|
-
...decoded,
|
|
305
|
-
headers: pickParameters("header"),
|
|
306
|
-
params: pickParameters("path"),
|
|
307
|
-
query: pickParameters("query")
|
|
308
|
-
};
|
|
309
|
-
});
|
|
310
|
-
var reflectedSchema2 = (schema) => Schema2.make(schema.ast);
|
|
311
|
-
var reflectedOperationInputSchemas = (api) => {
|
|
312
|
-
const schemas = new Map;
|
|
313
|
-
HttpApi2.reflect(api, {
|
|
314
|
-
onGroup: () => {
|
|
315
|
-
return;
|
|
316
|
-
},
|
|
317
|
-
onEndpoint: ({ endpoint, group }) => {
|
|
318
|
-
const operationId = Context2.getOrElse(endpoint.annotations, OpenApi2.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
319
|
-
const fields = {};
|
|
320
|
-
if (endpoint.params)
|
|
321
|
-
fields.params = reflectedSchema2(endpoint.params);
|
|
322
|
-
if (endpoint.query) {
|
|
323
|
-
fields.query = Schema2.optionalKey(reflectedSchema2(endpoint.query));
|
|
324
|
-
}
|
|
325
|
-
if (endpoint.headers) {
|
|
326
|
-
fields.headers = Schema2.optionalKey(reflectedSchema2(endpoint.headers));
|
|
327
|
-
}
|
|
328
|
-
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(reflectedSchema2));
|
|
329
|
-
if (payloadSchemas.length === 1 && payloadSchemas[0]) {
|
|
330
|
-
fields.body = payloadSchemas[0];
|
|
331
|
-
} else if (payloadSchemas.length > 1) {
|
|
332
|
-
fields.body = Schema2.Union(payloadSchemas);
|
|
333
|
-
}
|
|
334
|
-
const inputSchema = Object.keys(fields).length === 0 ? Schema2.Record(Schema2.String, Schema2.Never) : Schema2.Struct(fields);
|
|
335
|
-
schemas.set(operationId, inputSchema.annotate({
|
|
336
|
-
identifier: `${sanitizeHttpName(operationId)}ToolInput`
|
|
337
|
-
}));
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
return schemas;
|
|
341
|
-
};
|
|
342
|
-
var parseJsonObject = Effect2.fn("Http.parseJsonObject")(function* (value, label) {
|
|
343
|
-
if (!value)
|
|
344
|
-
return {};
|
|
345
|
-
return yield* Schema2.decodeUnknownEffect(JsonObjectFromString)(value).pipe(Effect2.mapError(() => new Error(`${label} must be a JSON object`)));
|
|
346
|
-
});
|
|
347
|
-
var parseJsonValue = Effect2.fn("Http.parseJsonValue")(function* (value, label) {
|
|
348
|
-
if (!value)
|
|
349
|
-
return;
|
|
350
|
-
return yield* Schema2.decodeUnknownEffect(JsonValueFromString)(value).pipe(Effect2.mapError(() => new Error(`${label} must be valid JSON`)));
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
class HttpApiSpec extends Context2.Service()("HttpApiSpec", {
|
|
354
|
-
make: (config) => Effect2.try({
|
|
355
|
-
try: () => {
|
|
356
|
-
const api = config.api;
|
|
357
|
-
if (!HttpApi2.isHttpApi(api)) {
|
|
358
|
-
throw new Error("HttpApiSpec requires a valid HttpApi");
|
|
359
|
-
}
|
|
360
|
-
const spec = OpenApi2.fromApi(api);
|
|
361
|
-
const reflectedSchemas = reflectedOperationInputSchemas(api);
|
|
362
|
-
const operations = httpApiOperations({
|
|
363
|
-
spec,
|
|
364
|
-
methods: config.methods
|
|
365
|
-
}).filter((operation) => config.include?.(operation) ?? true);
|
|
366
|
-
const operationJsonSchema = (operation) => {
|
|
367
|
-
const schema = httpApiOperationInputSchema(operation);
|
|
368
|
-
const document = JsonSchema.fromSchemaOpenApi3_1({
|
|
369
|
-
...schema,
|
|
370
|
-
$defs: spec.components?.schemas
|
|
181
|
+
return yield* executeGeneratedOperation(client, {
|
|
182
|
+
operation,
|
|
183
|
+
input: options.input
|
|
371
184
|
});
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
result.$defs = usedDefinitions;
|
|
377
|
-
}
|
|
378
|
-
return result;
|
|
379
|
-
};
|
|
380
|
-
return {
|
|
381
|
-
info: spec.info,
|
|
382
|
-
operations,
|
|
383
|
-
decodeOperationInput: decodeHttpApiOperationInput,
|
|
384
|
-
operationJsonSchema,
|
|
385
|
-
operationSchema: (operation) => {
|
|
386
|
-
const reflected = operation.operationId ? reflectedSchemas.get(operation.operationId) : undefined;
|
|
387
|
-
if (reflected)
|
|
388
|
-
return reflected;
|
|
389
|
-
const document = JsonSchema.fromSchemaOpenApi3_1(operationJsonSchema(operation));
|
|
390
|
-
return reflectedSchema2(SchemaRepresentation.fromJsonSchemaDocument(document));
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
},
|
|
394
|
-
catch: (error) => toHttpError("Failed to build HTTP API spec", error)
|
|
395
|
-
})
|
|
396
|
-
}) {
|
|
397
|
-
static layer = (config) => Layer2.effect(this, this.make(config));
|
|
185
|
+
})
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
static layer = (config) => Layer.effect(this, this.make(config));
|
|
398
189
|
}
|
|
399
190
|
|
|
400
191
|
// ../../src/lib/httpapi-cli.ts
|
|
401
|
-
var CliJsonSchema =
|
|
402
|
-
type:
|
|
403
|
-
description:
|
|
404
|
-
enum:
|
|
405
|
-
items:
|
|
406
|
-
properties:
|
|
407
|
-
required:
|
|
408
|
-
pattern:
|
|
409
|
-
allOf:
|
|
410
|
-
anyOf:
|
|
411
|
-
$ref:
|
|
412
|
-
$defs:
|
|
192
|
+
var CliJsonSchema = Schema2.Struct({
|
|
193
|
+
type: Schema2.optional(Schema2.Union([Schema2.String, Schema2.Array(Schema2.String)])),
|
|
194
|
+
description: Schema2.optional(Schema2.String),
|
|
195
|
+
enum: Schema2.optional(Schema2.Array(Schema2.Json)),
|
|
196
|
+
items: Schema2.optional(Schema2.Json),
|
|
197
|
+
properties: Schema2.optional(Schema2.Record(Schema2.String, Schema2.Json)),
|
|
198
|
+
required: Schema2.optional(Schema2.Array(Schema2.String)),
|
|
199
|
+
pattern: Schema2.optional(Schema2.String),
|
|
200
|
+
allOf: Schema2.optional(Schema2.Array(Schema2.Json)),
|
|
201
|
+
anyOf: Schema2.optional(Schema2.Array(Schema2.Json)),
|
|
202
|
+
$ref: Schema2.optional(Schema2.String),
|
|
203
|
+
$defs: Schema2.optional(Schema2.Record(Schema2.String, Schema2.Json))
|
|
413
204
|
}).annotate({ identifier: "HttpApiCliJsonSchema" });
|
|
414
|
-
var decodeCliJsonSchema =
|
|
415
|
-
var JsonFromString =
|
|
416
|
-
var CliParsedOption =
|
|
205
|
+
var decodeCliJsonSchema = Schema2.decodeUnknownOption(CliJsonSchema);
|
|
206
|
+
var JsonFromString = Schema2.fromJsonString(Schema2.Json);
|
|
207
|
+
var CliParsedOption = Schema2.declare((value) => Option2.isOption(value) && Option2.match(value, {
|
|
417
208
|
onNone: () => true,
|
|
418
|
-
onSome:
|
|
209
|
+
onSome: Schema2.is(Schema2.Json)
|
|
419
210
|
})).annotate({ identifier: "HttpApiCliParsedOption" });
|
|
420
|
-
var CliParsedConfig =
|
|
421
|
-
var
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
make: () => Effect3.gen(function* () {
|
|
430
|
-
const spec = yield* HttpApiSpec;
|
|
431
|
-
const command = yield* makeHttpApiCliCommand();
|
|
432
|
-
return {
|
|
433
|
-
command,
|
|
434
|
-
run: (args) => Command.runWith(command, { version: spec.info.version })(args)
|
|
435
|
-
};
|
|
436
|
-
})
|
|
437
|
-
}) {
|
|
438
|
-
static layer = Layer3.effect(this, this.make());
|
|
439
|
-
}
|
|
211
|
+
var CliParsedConfig = Schema2.Record(Schema2.String, Schema2.Union([Schema2.Json, CliParsedOption])).annotate({ identifier: "HttpApiCliParsedConfig" });
|
|
212
|
+
var makeHttpApiCli = Effect2.fn("HttpApiCli.make")(function* () {
|
|
213
|
+
const spec = yield* HttpApiAdapter;
|
|
214
|
+
const command = yield* makeHttpApiCliCommand();
|
|
215
|
+
return {
|
|
216
|
+
command,
|
|
217
|
+
run: (args) => Command.runWith(command, { version: spec.info.version })(args)
|
|
218
|
+
};
|
|
219
|
+
});
|
|
440
220
|
var toHttpApiCliName = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
|
|
441
221
|
var fallbackName = (value, fallback) => toHttpApiCliName(value) || fallback;
|
|
442
222
|
var operationIdParts = (operation) => operation.operationId?.split(".").filter(Boolean) ?? [];
|
|
443
223
|
var operationGroupName = (operation) => fallbackName(operationIdParts(operation)[0] ?? operation.tags?.[0] ?? "default", "default");
|
|
444
224
|
var operationGroupTitle = (operation) => operation.tags?.[0] ?? operationGroupName(operation);
|
|
445
225
|
var operationName = (method, path, operation) => fallbackName(operationIdParts(operation).at(-1) ?? `${method}_${path.replace(/^\/api\//, "").replace(/[/:{}]/g, "_")}`, `${method}_operation`);
|
|
446
|
-
var toCliOperation = ({
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
operation,
|
|
451
|
-
|
|
452
|
-
}) => ({
|
|
453
|
-
groupName: operationGroupName(operation),
|
|
454
|
-
groupTitle: operationGroupTitle(operation),
|
|
455
|
-
name: operationName(method, path, operation),
|
|
456
|
-
method,
|
|
457
|
-
path,
|
|
458
|
-
summary: operation.summary ?? operation.description ?? "",
|
|
459
|
-
operation,
|
|
460
|
-
inputSchema,
|
|
461
|
-
inputJsonSchema
|
|
226
|
+
var toCliOperation = (entry) => ({
|
|
227
|
+
...entry,
|
|
228
|
+
groupName: operationGroupName(entry.operation),
|
|
229
|
+
groupTitle: operationGroupTitle(entry.operation),
|
|
230
|
+
name: operationName(entry.method, entry.path, entry.operation),
|
|
231
|
+
summary: entry.operation.summary ?? entry.operation.description ?? ""
|
|
462
232
|
});
|
|
463
233
|
var httpApiCliOperationGroups = (operations) => {
|
|
464
234
|
const groups = new Map;
|
|
@@ -503,29 +273,19 @@ var schemaType = (schema) => {
|
|
|
503
273
|
return type;
|
|
504
274
|
return schemaType(effectiveSchema(schema));
|
|
505
275
|
};
|
|
506
|
-
var schemaEnum = (schema) =>
|
|
507
|
-
var
|
|
508
|
-
const effective = effectiveSchema(schema);
|
|
509
|
-
if (effective.pattern?.includes("\\d"))
|
|
510
|
-
return true;
|
|
511
|
-
return (effective.allOf ?? []).some((part) => hasNumericPattern(cliJsonSchema(part)));
|
|
512
|
-
};
|
|
513
|
-
var isBooleanSchema = (schema) => {
|
|
514
|
-
const values = schemaEnum(schema);
|
|
515
|
-
return schemaType(schema) === "boolean" || values.includes("true") && values.includes("false");
|
|
516
|
-
};
|
|
517
|
-
var jsonFlag = (name) => Flag.String(name).pipe(Flag.mapTryCatch((value) => Schema3.decodeUnknownSync(JsonFromString)(value), () => `--${name} must be valid JSON`));
|
|
276
|
+
var schemaEnum = (schema) => Schema2.decodeUnknownOption(Schema2.Array(Schema2.String))(effectiveSchema(schema).enum).pipe(Option2.getOrElse(() => []));
|
|
277
|
+
var jsonFlag = (name) => Flag.String(name).pipe(Flag.mapTryCatch((value) => Schema2.decodeUnknownSync(JsonFromString)(value), () => `--${name} must be valid JSON`));
|
|
518
278
|
var primitiveFlag = (name, schema, required) => {
|
|
519
279
|
const type = schemaType(schema);
|
|
520
280
|
const choices = schemaEnum(schema);
|
|
521
281
|
let flag;
|
|
522
|
-
if (
|
|
282
|
+
if (type === "boolean") {
|
|
523
283
|
flag = required ? Flag.Literals(name, ["true", "false"]).pipe(Flag.map((value) => value === "true")) : Flag.Boolean(name);
|
|
524
284
|
} else if (choices.length > 0) {
|
|
525
285
|
flag = Flag.Literals(name, choices);
|
|
526
286
|
} else if (type === "integer") {
|
|
527
287
|
flag = Flag.Int(name);
|
|
528
|
-
} else if (type === "number"
|
|
288
|
+
} else if (type === "number") {
|
|
529
289
|
flag = Flag.Finite(name);
|
|
530
290
|
} else if (type === "object" || type === "array") {
|
|
531
291
|
flag = jsonFlag(name);
|
|
@@ -544,7 +304,7 @@ var primitiveArgument = (name, schema) => {
|
|
|
544
304
|
argument = Argument.Literals(name, choices);
|
|
545
305
|
} else if (type === "integer") {
|
|
546
306
|
argument = Argument.Int(name);
|
|
547
|
-
} else if (type === "number"
|
|
307
|
+
} else if (type === "number") {
|
|
548
308
|
argument = Argument.Finite(name);
|
|
549
309
|
} else {
|
|
550
310
|
argument = Argument.String(name);
|
|
@@ -556,26 +316,26 @@ var operationInputDrafts = (operation) => {
|
|
|
556
316
|
const document = cliJsonSchema(operation.inputJsonSchema);
|
|
557
317
|
const definitions = document.$defs ?? {};
|
|
558
318
|
const resolveSchema = (value) => {
|
|
559
|
-
const schema = cliJsonSchema(value);
|
|
319
|
+
const schema = effectiveSchema(cliJsonSchema(value));
|
|
560
320
|
const name = schema.$ref?.match(/^#\/(?:\$defs|components\/schemas)\/(.+)$/)?.[1];
|
|
561
321
|
return name && definitions[name] ? effectiveSchema(cliJsonSchema(definitions[name])) : effectiveSchema(schema);
|
|
562
322
|
};
|
|
563
323
|
const properties = document.properties ?? {};
|
|
564
|
-
const drafts =
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
{
|
|
324
|
+
const drafts = [];
|
|
325
|
+
for (const location of ["params", "headers", "query"]) {
|
|
326
|
+
const group = resolveSchema(properties[location]);
|
|
327
|
+
const required = new Set(group.required ?? []);
|
|
328
|
+
for (const [name, schema] of Object.entries(group.properties ?? {})) {
|
|
329
|
+
drafts.push({
|
|
570
330
|
location,
|
|
571
|
-
name
|
|
572
|
-
preferredName: fallbackName(
|
|
573
|
-
required:
|
|
574
|
-
schema: resolveSchema(
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
const body = resolveSchema(properties.body
|
|
331
|
+
name,
|
|
332
|
+
preferredName: fallbackName(name, location),
|
|
333
|
+
required: required.has(name),
|
|
334
|
+
schema: resolveSchema(schema)
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const body = resolveSchema(properties.body);
|
|
579
339
|
if (schemaType(body) === "object" && body.properties) {
|
|
580
340
|
const required = new Set(body.required ?? []);
|
|
581
341
|
for (const [name, schema] of Object.entries(body.properties)) {
|
|
@@ -587,12 +347,12 @@ var operationInputDrafts = (operation) => {
|
|
|
587
347
|
schema: resolveSchema(schema)
|
|
588
348
|
});
|
|
589
349
|
}
|
|
590
|
-
} else if (
|
|
350
|
+
} else if (properties.body) {
|
|
591
351
|
drafts.push({
|
|
592
352
|
location: "body",
|
|
593
353
|
name: "body",
|
|
594
354
|
preferredName: "body",
|
|
595
|
-
required:
|
|
355
|
+
required: document.required?.includes("body") ?? false,
|
|
596
356
|
schema: body
|
|
597
357
|
});
|
|
598
358
|
}
|
|
@@ -618,21 +378,20 @@ var operationInputs = (operation) => {
|
|
|
618
378
|
location: draft.location,
|
|
619
379
|
name: draft.name,
|
|
620
380
|
optionName,
|
|
621
|
-
wireString: draft.location !== "body" && schemaType(draft.schema) === "string",
|
|
622
381
|
param: draft.location === "params" ? primitiveArgument(optionName, draft.schema) : primitiveFlag(optionName, draft.schema, draft.required)
|
|
623
382
|
};
|
|
624
383
|
});
|
|
625
384
|
};
|
|
626
|
-
var HttpApiCliResultStream =
|
|
627
|
-
var printHttpApiCliResult =
|
|
628
|
-
const stream =
|
|
385
|
+
var HttpApiCliResultStream = Schema2.declare((value) => Stream.isStream(value)).annotate({ identifier: "HttpApiCliResultStream" });
|
|
386
|
+
var printHttpApiCliResult = Effect2.fn("HttpApiCli.printResult")(function* (response, operation, client) {
|
|
387
|
+
const stream = Schema2.decodeUnknownOption(HttpApiCliResultStream)(response);
|
|
629
388
|
if (stream._tag === "Some") {
|
|
630
389
|
return yield* stream.value.pipe(Stream.mapEffect((event) => client.encodeResult(event, operation)), Stream.runForEach((event) => print(JSON.stringify(event))));
|
|
631
390
|
}
|
|
632
391
|
const encoded = yield* client.encodeResult(response, operation);
|
|
633
392
|
return yield* print(JSON.stringify(encoded, null, 2) ?? "null");
|
|
634
393
|
});
|
|
635
|
-
var callOperation = (operation, inputs, callConfig, client)
|
|
394
|
+
var callOperation = Effect2.fn("HttpApiCli.callOperation")(function* (operation, inputs, callConfig, client) {
|
|
636
395
|
const emptyValues = () => ({});
|
|
637
396
|
const assembled = {
|
|
638
397
|
body: emptyValues(),
|
|
@@ -640,46 +399,29 @@ var callOperation = (operation, inputs, callConfig, client) => Effect3.gen(funct
|
|
|
640
399
|
params: emptyValues(),
|
|
641
400
|
query: emptyValues()
|
|
642
401
|
};
|
|
643
|
-
for (const
|
|
644
|
-
const parsed = callConfig[
|
|
402
|
+
for (const input of inputs) {
|
|
403
|
+
const parsed = callConfig[input.configKey];
|
|
645
404
|
const value = Option2.isOption(parsed) ? Option2.getOrUndefined(parsed) : parsed;
|
|
646
405
|
if (value !== undefined) {
|
|
647
|
-
assembled[
|
|
406
|
+
assembled[input.location][input.name] = value;
|
|
648
407
|
}
|
|
649
408
|
}
|
|
650
|
-
const bodyInput = inputs.some((
|
|
409
|
+
const bodyInput = inputs.some((input) => input.location === "body" && input.name === "body") ? assembled.body.body : Object.keys(assembled.body).length > 0 || cliJsonSchema(operation.inputJsonSchema).required?.includes("body") ? assembled.body : undefined;
|
|
651
410
|
const rawInput = {};
|
|
652
|
-
if (inputs.some((
|
|
411
|
+
if (inputs.some((input) => input.location === "params")) {
|
|
653
412
|
rawInput.params = assembled.params;
|
|
654
413
|
}
|
|
655
|
-
if (inputs.some((
|
|
414
|
+
if (inputs.some((input) => input.location === "headers")) {
|
|
656
415
|
rawInput.headers = assembled.headers;
|
|
657
416
|
}
|
|
658
|
-
if (inputs.some((
|
|
417
|
+
if (inputs.some((input) => input.location === "query")) {
|
|
659
418
|
rawInput.query = assembled.query;
|
|
660
419
|
}
|
|
661
420
|
if (bodyInput !== undefined)
|
|
662
421
|
rawInput.body = bodyInput;
|
|
663
|
-
const
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
operation: {
|
|
667
|
-
method: operation.method,
|
|
668
|
-
path: operation.path,
|
|
669
|
-
operation: operation.operation
|
|
670
|
-
},
|
|
671
|
-
input: {
|
|
672
|
-
body: input.body,
|
|
673
|
-
headers: input.headers ?? {},
|
|
674
|
-
params: input.params ?? {},
|
|
675
|
-
query: input.query ?? {}
|
|
676
|
-
}
|
|
677
|
-
});
|
|
678
|
-
return yield* printHttpApiCliResult(response, {
|
|
679
|
-
method: operation.method,
|
|
680
|
-
path: operation.path,
|
|
681
|
-
operation: operation.operation
|
|
682
|
-
}, client);
|
|
422
|
+
const input = yield* Schema2.decodeUnknownEffect(operation.inputSchema)(rawInput).pipe(Effect2.mapError((error) => new Error(`Invalid input for ${operation.groupName} ${operation.name}: ${error.message}`)));
|
|
423
|
+
const response = yield* client.execute({ operation, input });
|
|
424
|
+
return yield* printHttpApiCliResult(response, operation, client);
|
|
683
425
|
});
|
|
684
426
|
var listCommand = (groups) => Command.make("list", {}, () => print([
|
|
685
427
|
"GROUP\tTITLE\tCOMMANDS",
|
|
@@ -687,79 +429,31 @@ var listCommand = (groups) => Command.make("list", {}, () => print([
|
|
|
687
429
|
].join(`
|
|
688
430
|
`))).pipe(Command.withDescription("List command groups"));
|
|
689
431
|
var groupListCommand = (group) => Command.make("list", {}, () => listOperations(group.operations)).pipe(Command.withDescription(`List ${group.title} operations`));
|
|
690
|
-
var operationCommand = (operation, client) =>
|
|
432
|
+
var operationCommand = (operation, client) => Effect2.sync(() => {
|
|
691
433
|
const inputs = operationInputs(operation);
|
|
692
434
|
const config = Object.fromEntries(inputs.map((input) => [input.configKey, input.param]));
|
|
693
|
-
return Command.make(operation.name, config, (callConfig) =>
|
|
435
|
+
return Command.make(operation.name, config, (callConfig) => Schema2.decodeUnknownEffect(CliParsedConfig)(callConfig).pipe(Effect2.flatMap((parsed) => callOperation(operation, inputs, parsed, client)))).pipe(Command.withDescription(operation.summary || `${operation.method.toUpperCase()} ${operation.path}`), Command.withExamples([
|
|
694
436
|
{
|
|
695
437
|
command: `${operation.groupName} ${operation.name}`,
|
|
696
438
|
description: `${operation.method.toUpperCase()} ${operation.path}`
|
|
697
439
|
}
|
|
698
440
|
]));
|
|
699
441
|
});
|
|
700
|
-
var groupCommand = (group, client) =>
|
|
442
|
+
var groupCommand = (group, client) => Effect2.all(group.operations.map((operation) => operationCommand(operation, client))).pipe(Effect2.map((operationCommands) => Command.make(group.name).pipe(Command.withDescription(group.title), Command.withSubcommands([
|
|
701
443
|
groupListCommand(group),
|
|
702
444
|
...operationCommands
|
|
703
445
|
]))));
|
|
704
|
-
var makeHttpApiCliCommand =
|
|
705
|
-
const spec = yield*
|
|
706
|
-
const client =
|
|
446
|
+
var makeHttpApiCliCommand = Effect2.fn("HttpApiCli.makeCommand")(function* () {
|
|
447
|
+
const spec = yield* HttpApiAdapter;
|
|
448
|
+
const client = spec;
|
|
707
449
|
const name = fallbackName(spec.info.title, "api");
|
|
708
|
-
const groups = yield*
|
|
709
|
-
try: () => httpApiCliOperationGroups(spec.operations.map(
|
|
710
|
-
...operation,
|
|
711
|
-
inputSchema: spec.operationSchema(operation.operation),
|
|
712
|
-
inputJsonSchema: spec.operationJsonSchema(operation.operation)
|
|
713
|
-
}))),
|
|
450
|
+
const groups = yield* Effect2.try({
|
|
451
|
+
try: () => httpApiCliOperationGroups(spec.operations.map(toCliOperation)),
|
|
714
452
|
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
715
453
|
});
|
|
716
|
-
const groupCommands = yield*
|
|
454
|
+
const groupCommands = yield* Effect2.all(groups.map((group) => groupCommand(group, client)));
|
|
717
455
|
return Command.make(name).pipe(Command.withDescription(spec.info.description ?? spec.info.title), Command.withSubcommands([listCommand(groups), ...groupCommands]));
|
|
718
456
|
});
|
|
719
|
-
var cliEnvironmentLayer = (args) => Layer3.mergeAll(FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({ args: Effect3.succeed(Array.from(args)) }), Layer3.succeed(Terminal.Terminal, Terminal.make({
|
|
720
|
-
columns: Effect3.sync(() => process.stdout.columns ?? 80),
|
|
721
|
-
rows: Effect3.sync(() => process.stdout.rows ?? 24),
|
|
722
|
-
readInput: Effect3.die("Terminal input is not supported"),
|
|
723
|
-
readLine: Effect3.die("Terminal input is not supported"),
|
|
724
|
-
display: (text) => Effect3.sync(() => process.stdout.write(text))
|
|
725
|
-
})), Layer3.succeed(ChildProcessSpawner, ChildProcessSpawner.of({
|
|
726
|
-
spawn: () => Effect3.die("Child processes are not supported"),
|
|
727
|
-
exitCode: () => Effect3.die("Child processes are not supported"),
|
|
728
|
-
streamString: () => Stream.die("Child processes are not supported"),
|
|
729
|
-
streamLines: () => Stream.die("Child processes are not supported"),
|
|
730
|
-
lines: () => Effect3.die("Child processes are not supported"),
|
|
731
|
-
string: () => Effect3.die("Child processes are not supported")
|
|
732
|
-
})));
|
|
733
|
-
var httpApiCliEnvironmentLayer = (args) => cliEnvironmentLayer(args);
|
|
734
|
-
var httpApiCli = (args = process.argv.slice(2)) => Effect3.gen(function* () {
|
|
735
|
-
const cli = yield* HttpApiCli;
|
|
736
|
-
return yield* cli.run(args);
|
|
737
|
-
});
|
|
738
|
-
var formatHttpApiCliCause = (cause) => {
|
|
739
|
-
const errors = Cause.prettyErrors(cause);
|
|
740
|
-
if (errors.length === 0)
|
|
741
|
-
return Cause.pretty(cause);
|
|
742
|
-
return errors.map((error) => error.message ? `${error.name}: ${error.message}` : error.name).join(`
|
|
743
|
-
`);
|
|
744
|
-
};
|
|
745
|
-
var runHttpApiCli = (layer, args = process.argv.slice(2)) => {
|
|
746
|
-
Effect3.runPromiseExit(httpApiCli(args).pipe(Effect3.provide(layer), Effect3.provide(httpApiCliEnvironmentLayer(args)))).then(Exit.match({
|
|
747
|
-
onSuccess: () => {
|
|
748
|
-
return;
|
|
749
|
-
},
|
|
750
|
-
onFailure: (cause) => {
|
|
751
|
-
console.error(formatHttpApiCliCause(cause));
|
|
752
|
-
process.exitCode = 1;
|
|
753
|
-
}
|
|
754
|
-
}));
|
|
755
|
-
};
|
|
756
457
|
export {
|
|
757
|
-
|
|
758
|
-
formatHttpApiCliCause,
|
|
759
|
-
httpApiCli,
|
|
760
|
-
httpApiCliEnvironmentLayer,
|
|
761
|
-
makeHttpApiCliCommand,
|
|
762
|
-
printHttpApiCliResult,
|
|
763
|
-
runHttpApiCli,
|
|
764
|
-
toHttpApiCliName
|
|
458
|
+
makeHttpApiCli
|
|
765
459
|
};
|