@krak-stack/registry 0.1.16 → 0.1.18
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/lib/httpapi-cli.d.ts +11 -9
- package/dist/lib/httpapi-cli.js +78 -19
- package/dist/lib/httpapi-client.d.ts +2 -1
- package/dist/lib/httpapi-client.js +45 -3
- package/dist/lib/httpapi-mcp.js +52 -11
- package/dist/lib/httpapi-toolkit.js +52 -11
- package/package.json +1 -1
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import { Context, Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect";
|
|
1
|
+
import { Cause, Context, Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect";
|
|
2
2
|
import { Command } from "effect/unstable/cli";
|
|
3
3
|
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
4
|
-
import { ApiClient } from "./httpapi-client.js";
|
|
5
|
-
import { HttpApiSpec } from "./httpapi-helpers.js";
|
|
4
|
+
import { ApiClient, type ApiClientService } from "./httpapi-client.js";
|
|
5
|
+
import { HttpApiSpec, type HttpApiOperationEntry } from "./httpapi-helpers.js";
|
|
6
6
|
declare const HttpApiCli_base: Context.ServiceClass<HttpApiCli, "HttpApiCli", {
|
|
7
|
-
command: Command.Command<string, {}, {},
|
|
8
|
-
run: (args: ReadonlyArray<string>) => Effect.Effect<void,
|
|
7
|
+
command: Command.Command<string, {}, {}, unknown, never>;
|
|
8
|
+
run: (args: ReadonlyArray<string>) => Effect.Effect<void, unknown, Command.Environment>;
|
|
9
9
|
}> & {
|
|
10
10
|
readonly make: () => Effect.Effect<{
|
|
11
|
-
command: Command.Command<string, {}, {},
|
|
12
|
-
run: (args: ReadonlyArray<string>) => Effect.Effect<void,
|
|
11
|
+
command: Command.Command<string, {}, {}, unknown, never>;
|
|
12
|
+
run: (args: ReadonlyArray<string>) => Effect.Effect<void, unknown, Command.Environment>;
|
|
13
13
|
}, never, ApiClient | HttpApiSpec>;
|
|
14
14
|
};
|
|
15
15
|
export declare class HttpApiCli extends HttpApiCli_base {
|
|
16
16
|
static readonly layer: Layer.Layer<HttpApiCli, never, ApiClient | HttpApiSpec>;
|
|
17
17
|
}
|
|
18
|
-
export declare const
|
|
18
|
+
export declare const printHttpApiCliResult: (response: unknown, operation: HttpApiOperationEntry, client: ApiClientService) => Effect.Effect<void, unknown, never>;
|
|
19
|
+
export declare const makeHttpApiCliCommand: () => Effect.Effect<Command.Command<string, {}, {}, unknown, never>, never, ApiClient | HttpApiSpec>;
|
|
19
20
|
export declare const httpApiCliEnvironmentLayer: (args: ReadonlyArray<string>) => Layer.Layer<ChildProcessSpawner | FileSystem.FileSystem | Path.Path | Stdio.Stdio | Terminal.Terminal, never, never>;
|
|
20
|
-
export declare const httpApiCli: (args?: string[]) => Effect.Effect<void,
|
|
21
|
+
export declare const httpApiCli: (args?: string[]) => Effect.Effect<void, unknown, HttpApiCli | Command.Environment>;
|
|
22
|
+
export declare const formatHttpApiCliCause: <E>(cause: Cause.Cause<E>) => string;
|
|
21
23
|
export declare const runHttpApiCli: <E>(layer: Layer.Layer<HttpApiCli, E>, args?: string[]) => void;
|
|
22
24
|
export {};
|
package/dist/lib/httpapi-cli.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// ../../src/lib/httpapi-cli.ts
|
|
2
2
|
import {
|
|
3
|
+
Cause,
|
|
3
4
|
Console,
|
|
4
5
|
Context as Context3,
|
|
5
6
|
Effect as Effect3,
|
|
7
|
+
Exit,
|
|
6
8
|
FileSystem,
|
|
7
9
|
Layer as Layer3,
|
|
8
10
|
Path,
|
|
11
|
+
Schema as Schema3,
|
|
9
12
|
Stdio,
|
|
10
13
|
Stream,
|
|
11
14
|
Terminal
|
|
@@ -14,13 +17,22 @@ import { Command, Flag } from "effect/unstable/cli";
|
|
|
14
17
|
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
15
18
|
|
|
16
19
|
// ../../src/lib/httpapi-client.ts
|
|
17
|
-
import { Context, Effect, Layer, Schema } from "effect";
|
|
20
|
+
import { Context, Effect, Layer, Schema, SchemaTransformation } from "effect";
|
|
18
21
|
import { HttpClient } from "effect/unstable/http";
|
|
19
22
|
import {
|
|
20
|
-
|
|
23
|
+
HttpApi,
|
|
24
|
+
HttpApiClient as EffectHttpApiClient,
|
|
25
|
+
OpenApi
|
|
21
26
|
} from "effect/unstable/httpapi";
|
|
27
|
+
var UndefinedFromNull = Schema.Null.pipe(Schema.decodeTo(Schema.Undefined, SchemaTransformation.transform({
|
|
28
|
+
decode: () => {
|
|
29
|
+
return;
|
|
30
|
+
},
|
|
31
|
+
encode: () => null
|
|
32
|
+
})));
|
|
22
33
|
var HttpApiOperationResultValue = Schema.suspend(() => Schema.Union([
|
|
23
34
|
Schema.Null,
|
|
35
|
+
UndefinedFromNull,
|
|
24
36
|
Schema.String,
|
|
25
37
|
Schema.Number,
|
|
26
38
|
Schema.Boolean,
|
|
@@ -34,6 +46,37 @@ var HttpApiOperationResult = Schema.Union([
|
|
|
34
46
|
Schema.Undefined
|
|
35
47
|
]).annotate({ identifier: "HttpApiOperationResult" });
|
|
36
48
|
var encodeHttpApiOperationResult = Effect.fn("HttpApiClient.encodeOperationResult")((result) => Schema.encodeUnknownEffect(HttpApiOperationResult)(result).pipe(Effect.map((encoded) => encoded ?? null), Effect.mapError((cause) => new Error("HTTP API result is not serializable", { cause }))));
|
|
49
|
+
var reflectedSchema = (schema) => Schema.make(schema.ast);
|
|
50
|
+
var operationResultSchemas = (api) => {
|
|
51
|
+
const schemas = new Map;
|
|
52
|
+
HttpApi.reflect(api, {
|
|
53
|
+
onGroup: () => {
|
|
54
|
+
return;
|
|
55
|
+
},
|
|
56
|
+
onEndpoint: ({ endpoint, group, successes }) => {
|
|
57
|
+
const operationId = Context.getOrElse(endpoint.annotations, OpenApi.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
58
|
+
const operationSchemas = Array.from(successes.values()).flat().map(reflectedSchema);
|
|
59
|
+
if (operationSchemas.length === 1 && operationSchemas[0]) {
|
|
60
|
+
schemas.set(operationId, operationSchemas[0]);
|
|
61
|
+
} else if (operationSchemas.length > 1) {
|
|
62
|
+
schemas.set(operationId, Schema.Union(operationSchemas));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return schemas;
|
|
67
|
+
};
|
|
68
|
+
var makeHttpApiOperationResultEncoder = (api) => {
|
|
69
|
+
const schemas = operationResultSchemas(api);
|
|
70
|
+
return Effect.fn("HttpApiClient.encodeOperationResultWithSchema")((result, operation) => {
|
|
71
|
+
const operationId = operation.operation.operationId;
|
|
72
|
+
const schema = operationId ? schemas.get(operationId) : undefined;
|
|
73
|
+
if (!schema)
|
|
74
|
+
return encodeHttpApiOperationResult(result);
|
|
75
|
+
return Schema.encodeUnknownEffect(schema)(result).pipe(Effect.flatMap(encodeHttpApiOperationResult), Effect.mapError((cause) => new Error("HTTP API result does not match its success schema", {
|
|
76
|
+
cause
|
|
77
|
+
})));
|
|
78
|
+
});
|
|
79
|
+
};
|
|
37
80
|
var GeneratedOperation = Schema.declare((value) => value instanceof Function).annotate({ identifier: "GeneratedOperation" });
|
|
38
81
|
var GeneratedOperationEffect = Schema.declare((value) => Effect.isEffect(value)).annotate({ identifier: "GeneratedOperationEffect" });
|
|
39
82
|
var makeGeneratedClient = (api, http, baseUrl) => EffectHttpApiClient.makeWith(api, {
|
|
@@ -69,9 +112,10 @@ var executeGeneratedOperation = Effect.fn("HttpApiClient.executeGeneratedOperati
|
|
|
69
112
|
class ApiClient extends Context.Service()("ApiClient") {
|
|
70
113
|
static layer = (config) => Layer.effect(this, Effect.gen(function* () {
|
|
71
114
|
const http = yield* HttpClient.HttpClient;
|
|
115
|
+
const encodeResult = makeHttpApiOperationResultEncoder(config.api);
|
|
72
116
|
const client = yield* makeGeneratedClient(config.api, http, config.baseUrl);
|
|
73
117
|
return {
|
|
74
|
-
encodeResult: config.encodeResult ?? ((result) =>
|
|
118
|
+
encodeResult: config.encodeResult ?? ((result, operation) => encodeResult(result, operation)),
|
|
75
119
|
execute: Effect.fn("ApiClient.execute")(function* (options) {
|
|
76
120
|
return yield* executeGeneratedOperation(client, options);
|
|
77
121
|
})
|
|
@@ -89,7 +133,7 @@ import {
|
|
|
89
133
|
Schema as Schema2,
|
|
90
134
|
SchemaRepresentation
|
|
91
135
|
} from "effect";
|
|
92
|
-
import { HttpApi as HttpApi2, OpenApi } from "effect/unstable/httpapi";
|
|
136
|
+
import { HttpApi as HttpApi2, OpenApi as OpenApi2 } from "effect/unstable/httpapi";
|
|
93
137
|
var JsonObjectSchema = Schema2.Record(Schema2.String, Schema2.Json).annotate({
|
|
94
138
|
identifier: "HttpJsonObject",
|
|
95
139
|
title: "HTTP JSON object",
|
|
@@ -262,7 +306,7 @@ var decodeHttpApiOperationInput = Effect2.fn("Http.decodeApiOperationInput")(fun
|
|
|
262
306
|
query: pickParameters("query")
|
|
263
307
|
};
|
|
264
308
|
});
|
|
265
|
-
var
|
|
309
|
+
var reflectedSchema2 = (schema) => Schema2.make(schema.ast);
|
|
266
310
|
var reflectedOperationInputSchemas = (api) => {
|
|
267
311
|
const schemas = new Map;
|
|
268
312
|
HttpApi2.reflect(api, {
|
|
@@ -270,17 +314,17 @@ var reflectedOperationInputSchemas = (api) => {
|
|
|
270
314
|
return;
|
|
271
315
|
},
|
|
272
316
|
onEndpoint: ({ endpoint, group }) => {
|
|
273
|
-
const operationId = Context2.getOrElse(endpoint.annotations,
|
|
317
|
+
const operationId = Context2.getOrElse(endpoint.annotations, OpenApi2.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
274
318
|
const fields = {};
|
|
275
319
|
if (endpoint.params)
|
|
276
|
-
fields.params =
|
|
320
|
+
fields.params = reflectedSchema2(endpoint.params);
|
|
277
321
|
if (endpoint.query) {
|
|
278
|
-
fields.query = Schema2.optionalKey(
|
|
322
|
+
fields.query = Schema2.optionalKey(reflectedSchema2(endpoint.query));
|
|
279
323
|
}
|
|
280
324
|
if (endpoint.headers) {
|
|
281
|
-
fields.headers = Schema2.optionalKey(
|
|
325
|
+
fields.headers = Schema2.optionalKey(reflectedSchema2(endpoint.headers));
|
|
282
326
|
}
|
|
283
|
-
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(
|
|
327
|
+
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(reflectedSchema2));
|
|
284
328
|
if (payloadSchemas.length === 1 && payloadSchemas[0]) {
|
|
285
329
|
fields.body = payloadSchemas[0];
|
|
286
330
|
} else if (payloadSchemas.length > 1) {
|
|
@@ -312,7 +356,7 @@ class HttpApiSpec extends Context2.Service()("HttpApiSpec", {
|
|
|
312
356
|
if (!HttpApi2.isHttpApi(api)) {
|
|
313
357
|
throw new Error("HttpApiSpec requires a valid HttpApi");
|
|
314
358
|
}
|
|
315
|
-
const spec =
|
|
359
|
+
const spec = OpenApi2.fromApi(api);
|
|
316
360
|
const reflectedSchemas = reflectedOperationInputSchemas(api);
|
|
317
361
|
const operations = httpApiOperations({
|
|
318
362
|
spec,
|
|
@@ -406,6 +450,15 @@ var print = (value) => Console.log(value);
|
|
|
406
450
|
var formatOperations = (operations) => operations.map((operation) => `${operation.groupName} ${operation.name} ${operation.method.toUpperCase()} ${operation.path} ${operation.summary}`).join(`
|
|
407
451
|
`);
|
|
408
452
|
var listOperations = (operations) => print(formatOperations(operations));
|
|
453
|
+
var HttpApiCliResultStream = Schema3.declare((value) => Stream.isStream(value)).annotate({ identifier: "HttpApiCliResultStream" });
|
|
454
|
+
var printHttpApiCliResult = Effect3.fn("HttpApiCli.printResult")(function* (response, operation, client) {
|
|
455
|
+
const stream = Schema3.decodeUnknownOption(HttpApiCliResultStream)(response);
|
|
456
|
+
if (stream._tag === "Some") {
|
|
457
|
+
return yield* stream.value.pipe(Stream.mapEffect((event) => client.encodeResult(event, operation)), Stream.runForEach((event) => print(JSON.stringify(event))));
|
|
458
|
+
}
|
|
459
|
+
const encoded = yield* client.encodeResult(response, operation);
|
|
460
|
+
return yield* print(JSON.stringify(encoded, null, 2) ?? "null");
|
|
461
|
+
});
|
|
409
462
|
var callOperation = (operation, callConfig, client) => Effect3.gen(function* () {
|
|
410
463
|
const body = yield* parseJsonValue(callConfig.body, "--body");
|
|
411
464
|
const headers = yield* parseJsonObject(callConfig.headers, "--headers");
|
|
@@ -419,13 +472,11 @@ var callOperation = (operation, callConfig, client) => Effect3.gen(function* ()
|
|
|
419
472
|
},
|
|
420
473
|
input: { body, headers, params, query }
|
|
421
474
|
});
|
|
422
|
-
|
|
475
|
+
return yield* printHttpApiCliResult(response, {
|
|
423
476
|
method: operation.method,
|
|
424
477
|
path: operation.path,
|
|
425
478
|
operation: operation.operation
|
|
426
|
-
});
|
|
427
|
-
const formatted = JSON.stringify(encoded, null, 2) ?? "null";
|
|
428
|
-
return yield* print(formatted);
|
|
479
|
+
}, client);
|
|
429
480
|
});
|
|
430
481
|
var listCommand = (groups) => Command.make("list", {}, () => print(groups.map((group) => `${group.name} ${group.title} ${group.operations.length}`).join(`
|
|
431
482
|
`))).pipe(Command.withDescription("List command groups"));
|
|
@@ -469,16 +520,24 @@ var httpApiCli = (args = process.argv.slice(2)) => Effect3.gen(function* () {
|
|
|
469
520
|
const cli = yield* HttpApiCli;
|
|
470
521
|
return yield* cli.run(args);
|
|
471
522
|
});
|
|
523
|
+
var formatHttpApiCliCause = Cause.pretty;
|
|
472
524
|
var runHttpApiCli = (layer, args = process.argv.slice(2)) => {
|
|
473
|
-
Effect3.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
525
|
+
Effect3.runPromiseExit(httpApiCli(args).pipe(Effect3.provide(layer), Effect3.provide(httpApiCliEnvironmentLayer(args)))).then(Exit.match({
|
|
526
|
+
onSuccess: () => {
|
|
527
|
+
return;
|
|
528
|
+
},
|
|
529
|
+
onFailure: (cause) => {
|
|
530
|
+
console.error(formatHttpApiCliCause(cause));
|
|
531
|
+
process.exitCode = 1;
|
|
532
|
+
}
|
|
533
|
+
}));
|
|
477
534
|
};
|
|
478
535
|
export {
|
|
479
536
|
runHttpApiCli,
|
|
537
|
+
printHttpApiCliResult,
|
|
480
538
|
makeHttpApiCliCommand,
|
|
481
539
|
httpApiCliEnvironmentLayer,
|
|
482
540
|
httpApiCli,
|
|
541
|
+
formatHttpApiCliCause,
|
|
483
542
|
HttpApiCli
|
|
484
543
|
};
|
|
@@ -12,12 +12,13 @@ export type ApiClientExecuteOptions = {
|
|
|
12
12
|
readonly operation: HttpApiOperationEntry;
|
|
13
13
|
readonly input: HttpApiOperationInput;
|
|
14
14
|
};
|
|
15
|
-
export type HttpApiOperationResultValue = null | string | number | boolean | Date | Uint8Array | ReadonlyArray<HttpApiOperationResultValue> | {
|
|
15
|
+
export type HttpApiOperationResultValue = null | undefined | string | number | boolean | Date | Uint8Array | ReadonlyArray<HttpApiOperationResultValue> | {
|
|
16
16
|
readonly [key: string]: HttpApiOperationResultValue;
|
|
17
17
|
};
|
|
18
18
|
export declare const HttpApiOperationResult: Schema.Union<readonly [Schema.Codec<HttpApiOperationResultValue, Json, never, never>, Schema.Undefined]>;
|
|
19
19
|
export type HttpApiOperationResult = typeof HttpApiOperationResult.Type;
|
|
20
20
|
export declare const encodeHttpApiOperationResult: (result: unknown) => Effect.Effect<string | number | boolean | Schema.JsonArray | Schema.JsonObject | null, Error, never>;
|
|
21
|
+
export declare const makeHttpApiOperationResultEncoder: <Id extends string, Groups extends HttpApiGroup.Constraint>(api: HttpApi.HttpApi<Id, Groups>) => (result: unknown, operation: HttpApiOperationEntry) => Effect.Effect<string | number | boolean | Schema.JsonArray | Schema.JsonObject | null, Error, never>;
|
|
21
22
|
export type ApiClientService = {
|
|
22
23
|
readonly encodeResult: (result: ErrorOptions["cause"], operation: HttpApiOperationEntry) => Effect.Effect<Json, Error>;
|
|
23
24
|
readonly execute: (options: ApiClientExecuteOptions) => Effect.Effect<ErrorOptions["cause"], Error>;
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
// ../../src/lib/httpapi-client.ts
|
|
2
|
-
import { Context, Effect, Layer, Schema } from "effect";
|
|
2
|
+
import { Context, Effect, Layer, Schema, SchemaTransformation } from "effect";
|
|
3
3
|
import { HttpClient } from "effect/unstable/http";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
HttpApi,
|
|
6
|
+
HttpApiClient as EffectHttpApiClient,
|
|
7
|
+
OpenApi
|
|
6
8
|
} from "effect/unstable/httpapi";
|
|
9
|
+
var UndefinedFromNull = Schema.Null.pipe(Schema.decodeTo(Schema.Undefined, SchemaTransformation.transform({
|
|
10
|
+
decode: () => {
|
|
11
|
+
return;
|
|
12
|
+
},
|
|
13
|
+
encode: () => null
|
|
14
|
+
})));
|
|
7
15
|
var HttpApiOperationResultValue = Schema.suspend(() => Schema.Union([
|
|
8
16
|
Schema.Null,
|
|
17
|
+
UndefinedFromNull,
|
|
9
18
|
Schema.String,
|
|
10
19
|
Schema.Number,
|
|
11
20
|
Schema.Boolean,
|
|
@@ -19,6 +28,37 @@ var HttpApiOperationResult = Schema.Union([
|
|
|
19
28
|
Schema.Undefined
|
|
20
29
|
]).annotate({ identifier: "HttpApiOperationResult" });
|
|
21
30
|
var encodeHttpApiOperationResult = Effect.fn("HttpApiClient.encodeOperationResult")((result) => Schema.encodeUnknownEffect(HttpApiOperationResult)(result).pipe(Effect.map((encoded) => encoded ?? null), Effect.mapError((cause) => new Error("HTTP API result is not serializable", { cause }))));
|
|
31
|
+
var reflectedSchema = (schema) => Schema.make(schema.ast);
|
|
32
|
+
var operationResultSchemas = (api) => {
|
|
33
|
+
const schemas = new Map;
|
|
34
|
+
HttpApi.reflect(api, {
|
|
35
|
+
onGroup: () => {
|
|
36
|
+
return;
|
|
37
|
+
},
|
|
38
|
+
onEndpoint: ({ endpoint, group, successes }) => {
|
|
39
|
+
const operationId = Context.getOrElse(endpoint.annotations, OpenApi.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
40
|
+
const operationSchemas = Array.from(successes.values()).flat().map(reflectedSchema);
|
|
41
|
+
if (operationSchemas.length === 1 && operationSchemas[0]) {
|
|
42
|
+
schemas.set(operationId, operationSchemas[0]);
|
|
43
|
+
} else if (operationSchemas.length > 1) {
|
|
44
|
+
schemas.set(operationId, Schema.Union(operationSchemas));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return schemas;
|
|
49
|
+
};
|
|
50
|
+
var makeHttpApiOperationResultEncoder = (api) => {
|
|
51
|
+
const schemas = operationResultSchemas(api);
|
|
52
|
+
return Effect.fn("HttpApiClient.encodeOperationResultWithSchema")((result, operation) => {
|
|
53
|
+
const operationId = operation.operation.operationId;
|
|
54
|
+
const schema = operationId ? schemas.get(operationId) : undefined;
|
|
55
|
+
if (!schema)
|
|
56
|
+
return encodeHttpApiOperationResult(result);
|
|
57
|
+
return Schema.encodeUnknownEffect(schema)(result).pipe(Effect.flatMap(encodeHttpApiOperationResult), Effect.mapError((cause) => new Error("HTTP API result does not match its success schema", {
|
|
58
|
+
cause
|
|
59
|
+
})));
|
|
60
|
+
});
|
|
61
|
+
};
|
|
22
62
|
var GeneratedOperation = Schema.declare((value) => value instanceof Function).annotate({ identifier: "GeneratedOperation" });
|
|
23
63
|
var GeneratedOperationEffect = Schema.declare((value) => Effect.isEffect(value)).annotate({ identifier: "GeneratedOperationEffect" });
|
|
24
64
|
var makeGeneratedClient = (api, http, baseUrl) => EffectHttpApiClient.makeWith(api, {
|
|
@@ -54,9 +94,10 @@ var executeGeneratedOperation = Effect.fn("HttpApiClient.executeGeneratedOperati
|
|
|
54
94
|
class ApiClient extends Context.Service()("ApiClient") {
|
|
55
95
|
static layer = (config) => Layer.effect(this, Effect.gen(function* () {
|
|
56
96
|
const http = yield* HttpClient.HttpClient;
|
|
97
|
+
const encodeResult = makeHttpApiOperationResultEncoder(config.api);
|
|
57
98
|
const client = yield* makeGeneratedClient(config.api, http, config.baseUrl);
|
|
58
99
|
return {
|
|
59
|
-
encodeResult: config.encodeResult ?? ((result) =>
|
|
100
|
+
encodeResult: config.encodeResult ?? ((result, operation) => encodeResult(result, operation)),
|
|
60
101
|
execute: Effect.fn("ApiClient.execute")(function* (options) {
|
|
61
102
|
return yield* executeGeneratedOperation(client, options);
|
|
62
103
|
})
|
|
@@ -64,6 +105,7 @@ class ApiClient extends Context.Service()("ApiClient") {
|
|
|
64
105
|
}));
|
|
65
106
|
}
|
|
66
107
|
export {
|
|
108
|
+
makeHttpApiOperationResultEncoder,
|
|
67
109
|
encodeHttpApiOperationResult,
|
|
68
110
|
HttpApiOperationResult,
|
|
69
111
|
ApiClient
|
package/dist/lib/httpapi-mcp.js
CHANGED
|
@@ -3,13 +3,22 @@ import { Context as Context3, Effect as Effect3, Layer as Layer3, Option as Opti
|
|
|
3
3
|
import { McpSchema, McpServer } from "effect/unstable/ai";
|
|
4
4
|
|
|
5
5
|
// ../../src/lib/httpapi-client.ts
|
|
6
|
-
import { Context, Effect, Layer, Schema } from "effect";
|
|
6
|
+
import { Context, Effect, Layer, Schema, SchemaTransformation } from "effect";
|
|
7
7
|
import { HttpClient } from "effect/unstable/http";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
HttpApi,
|
|
10
|
+
HttpApiClient as EffectHttpApiClient,
|
|
11
|
+
OpenApi
|
|
10
12
|
} from "effect/unstable/httpapi";
|
|
13
|
+
var UndefinedFromNull = Schema.Null.pipe(Schema.decodeTo(Schema.Undefined, SchemaTransformation.transform({
|
|
14
|
+
decode: () => {
|
|
15
|
+
return;
|
|
16
|
+
},
|
|
17
|
+
encode: () => null
|
|
18
|
+
})));
|
|
11
19
|
var HttpApiOperationResultValue = Schema.suspend(() => Schema.Union([
|
|
12
20
|
Schema.Null,
|
|
21
|
+
UndefinedFromNull,
|
|
13
22
|
Schema.String,
|
|
14
23
|
Schema.Number,
|
|
15
24
|
Schema.Boolean,
|
|
@@ -23,6 +32,37 @@ var HttpApiOperationResult = Schema.Union([
|
|
|
23
32
|
Schema.Undefined
|
|
24
33
|
]).annotate({ identifier: "HttpApiOperationResult" });
|
|
25
34
|
var encodeHttpApiOperationResult = Effect.fn("HttpApiClient.encodeOperationResult")((result) => Schema.encodeUnknownEffect(HttpApiOperationResult)(result).pipe(Effect.map((encoded) => encoded ?? null), Effect.mapError((cause) => new Error("HTTP API result is not serializable", { cause }))));
|
|
35
|
+
var reflectedSchema = (schema) => Schema.make(schema.ast);
|
|
36
|
+
var operationResultSchemas = (api) => {
|
|
37
|
+
const schemas = new Map;
|
|
38
|
+
HttpApi.reflect(api, {
|
|
39
|
+
onGroup: () => {
|
|
40
|
+
return;
|
|
41
|
+
},
|
|
42
|
+
onEndpoint: ({ endpoint, group, successes }) => {
|
|
43
|
+
const operationId = Context.getOrElse(endpoint.annotations, OpenApi.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
44
|
+
const operationSchemas = Array.from(successes.values()).flat().map(reflectedSchema);
|
|
45
|
+
if (operationSchemas.length === 1 && operationSchemas[0]) {
|
|
46
|
+
schemas.set(operationId, operationSchemas[0]);
|
|
47
|
+
} else if (operationSchemas.length > 1) {
|
|
48
|
+
schemas.set(operationId, Schema.Union(operationSchemas));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return schemas;
|
|
53
|
+
};
|
|
54
|
+
var makeHttpApiOperationResultEncoder = (api) => {
|
|
55
|
+
const schemas = operationResultSchemas(api);
|
|
56
|
+
return Effect.fn("HttpApiClient.encodeOperationResultWithSchema")((result, operation) => {
|
|
57
|
+
const operationId = operation.operation.operationId;
|
|
58
|
+
const schema = operationId ? schemas.get(operationId) : undefined;
|
|
59
|
+
if (!schema)
|
|
60
|
+
return encodeHttpApiOperationResult(result);
|
|
61
|
+
return Schema.encodeUnknownEffect(schema)(result).pipe(Effect.flatMap(encodeHttpApiOperationResult), Effect.mapError((cause) => new Error("HTTP API result does not match its success schema", {
|
|
62
|
+
cause
|
|
63
|
+
})));
|
|
64
|
+
});
|
|
65
|
+
};
|
|
26
66
|
var GeneratedOperation = Schema.declare((value) => value instanceof Function).annotate({ identifier: "GeneratedOperation" });
|
|
27
67
|
var GeneratedOperationEffect = Schema.declare((value) => Effect.isEffect(value)).annotate({ identifier: "GeneratedOperationEffect" });
|
|
28
68
|
var makeGeneratedClient = (api, http, baseUrl) => EffectHttpApiClient.makeWith(api, {
|
|
@@ -58,9 +98,10 @@ var executeGeneratedOperation = Effect.fn("HttpApiClient.executeGeneratedOperati
|
|
|
58
98
|
class ApiClient extends Context.Service()("ApiClient") {
|
|
59
99
|
static layer = (config) => Layer.effect(this, Effect.gen(function* () {
|
|
60
100
|
const http = yield* HttpClient.HttpClient;
|
|
101
|
+
const encodeResult = makeHttpApiOperationResultEncoder(config.api);
|
|
61
102
|
const client = yield* makeGeneratedClient(config.api, http, config.baseUrl);
|
|
62
103
|
return {
|
|
63
|
-
encodeResult: config.encodeResult ?? ((result) =>
|
|
104
|
+
encodeResult: config.encodeResult ?? ((result, operation) => encodeResult(result, operation)),
|
|
64
105
|
execute: Effect.fn("ApiClient.execute")(function* (options) {
|
|
65
106
|
return yield* executeGeneratedOperation(client, options);
|
|
66
107
|
})
|
|
@@ -78,7 +119,7 @@ import {
|
|
|
78
119
|
Schema as Schema2,
|
|
79
120
|
SchemaRepresentation
|
|
80
121
|
} from "effect";
|
|
81
|
-
import { HttpApi as HttpApi2, OpenApi } from "effect/unstable/httpapi";
|
|
122
|
+
import { HttpApi as HttpApi2, OpenApi as OpenApi2 } from "effect/unstable/httpapi";
|
|
82
123
|
var JsonObjectSchema = Schema2.Record(Schema2.String, Schema2.Json).annotate({
|
|
83
124
|
identifier: "HttpJsonObject",
|
|
84
125
|
title: "HTTP JSON object",
|
|
@@ -251,7 +292,7 @@ var decodeHttpApiOperationInput = Effect2.fn("Http.decodeApiOperationInput")(fun
|
|
|
251
292
|
query: pickParameters("query")
|
|
252
293
|
};
|
|
253
294
|
});
|
|
254
|
-
var
|
|
295
|
+
var reflectedSchema2 = (schema) => Schema2.make(schema.ast);
|
|
255
296
|
var reflectedOperationInputSchemas = (api) => {
|
|
256
297
|
const schemas = new Map;
|
|
257
298
|
HttpApi2.reflect(api, {
|
|
@@ -259,17 +300,17 @@ var reflectedOperationInputSchemas = (api) => {
|
|
|
259
300
|
return;
|
|
260
301
|
},
|
|
261
302
|
onEndpoint: ({ endpoint, group }) => {
|
|
262
|
-
const operationId = Context2.getOrElse(endpoint.annotations,
|
|
303
|
+
const operationId = Context2.getOrElse(endpoint.annotations, OpenApi2.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
263
304
|
const fields = {};
|
|
264
305
|
if (endpoint.params)
|
|
265
|
-
fields.params =
|
|
306
|
+
fields.params = reflectedSchema2(endpoint.params);
|
|
266
307
|
if (endpoint.query) {
|
|
267
|
-
fields.query = Schema2.optionalKey(
|
|
308
|
+
fields.query = Schema2.optionalKey(reflectedSchema2(endpoint.query));
|
|
268
309
|
}
|
|
269
310
|
if (endpoint.headers) {
|
|
270
|
-
fields.headers = Schema2.optionalKey(
|
|
311
|
+
fields.headers = Schema2.optionalKey(reflectedSchema2(endpoint.headers));
|
|
271
312
|
}
|
|
272
|
-
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(
|
|
313
|
+
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(reflectedSchema2));
|
|
273
314
|
if (payloadSchemas.length === 1 && payloadSchemas[0]) {
|
|
274
315
|
fields.body = payloadSchemas[0];
|
|
275
316
|
} else if (payloadSchemas.length > 1) {
|
|
@@ -301,7 +342,7 @@ class HttpApiSpec extends Context2.Service()("HttpApiSpec", {
|
|
|
301
342
|
if (!HttpApi2.isHttpApi(api)) {
|
|
302
343
|
throw new Error("HttpApiSpec requires a valid HttpApi");
|
|
303
344
|
}
|
|
304
|
-
const spec =
|
|
345
|
+
const spec = OpenApi2.fromApi(api);
|
|
305
346
|
const reflectedSchemas = reflectedOperationInputSchemas(api);
|
|
306
347
|
const operations = httpApiOperations({
|
|
307
348
|
spec,
|
|
@@ -3,13 +3,22 @@ import { Effect as Effect3, Layer as Layer3, Schema as Schema3 } from "effect";
|
|
|
3
3
|
import { Tool, Toolkit } from "effect/unstable/ai";
|
|
4
4
|
|
|
5
5
|
// ../../src/lib/httpapi-client.ts
|
|
6
|
-
import { Context, Effect, Layer, Schema } from "effect";
|
|
6
|
+
import { Context, Effect, Layer, Schema, SchemaTransformation } from "effect";
|
|
7
7
|
import { HttpClient } from "effect/unstable/http";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
HttpApi,
|
|
10
|
+
HttpApiClient as EffectHttpApiClient,
|
|
11
|
+
OpenApi
|
|
10
12
|
} from "effect/unstable/httpapi";
|
|
13
|
+
var UndefinedFromNull = Schema.Null.pipe(Schema.decodeTo(Schema.Undefined, SchemaTransformation.transform({
|
|
14
|
+
decode: () => {
|
|
15
|
+
return;
|
|
16
|
+
},
|
|
17
|
+
encode: () => null
|
|
18
|
+
})));
|
|
11
19
|
var HttpApiOperationResultValue = Schema.suspend(() => Schema.Union([
|
|
12
20
|
Schema.Null,
|
|
21
|
+
UndefinedFromNull,
|
|
13
22
|
Schema.String,
|
|
14
23
|
Schema.Number,
|
|
15
24
|
Schema.Boolean,
|
|
@@ -23,6 +32,37 @@ var HttpApiOperationResult = Schema.Union([
|
|
|
23
32
|
Schema.Undefined
|
|
24
33
|
]).annotate({ identifier: "HttpApiOperationResult" });
|
|
25
34
|
var encodeHttpApiOperationResult = Effect.fn("HttpApiClient.encodeOperationResult")((result) => Schema.encodeUnknownEffect(HttpApiOperationResult)(result).pipe(Effect.map((encoded) => encoded ?? null), Effect.mapError((cause) => new Error("HTTP API result is not serializable", { cause }))));
|
|
35
|
+
var reflectedSchema = (schema) => Schema.make(schema.ast);
|
|
36
|
+
var operationResultSchemas = (api) => {
|
|
37
|
+
const schemas = new Map;
|
|
38
|
+
HttpApi.reflect(api, {
|
|
39
|
+
onGroup: () => {
|
|
40
|
+
return;
|
|
41
|
+
},
|
|
42
|
+
onEndpoint: ({ endpoint, group, successes }) => {
|
|
43
|
+
const operationId = Context.getOrElse(endpoint.annotations, OpenApi.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
44
|
+
const operationSchemas = Array.from(successes.values()).flat().map(reflectedSchema);
|
|
45
|
+
if (operationSchemas.length === 1 && operationSchemas[0]) {
|
|
46
|
+
schemas.set(operationId, operationSchemas[0]);
|
|
47
|
+
} else if (operationSchemas.length > 1) {
|
|
48
|
+
schemas.set(operationId, Schema.Union(operationSchemas));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return schemas;
|
|
53
|
+
};
|
|
54
|
+
var makeHttpApiOperationResultEncoder = (api) => {
|
|
55
|
+
const schemas = operationResultSchemas(api);
|
|
56
|
+
return Effect.fn("HttpApiClient.encodeOperationResultWithSchema")((result, operation) => {
|
|
57
|
+
const operationId = operation.operation.operationId;
|
|
58
|
+
const schema = operationId ? schemas.get(operationId) : undefined;
|
|
59
|
+
if (!schema)
|
|
60
|
+
return encodeHttpApiOperationResult(result);
|
|
61
|
+
return Schema.encodeUnknownEffect(schema)(result).pipe(Effect.flatMap(encodeHttpApiOperationResult), Effect.mapError((cause) => new Error("HTTP API result does not match its success schema", {
|
|
62
|
+
cause
|
|
63
|
+
})));
|
|
64
|
+
});
|
|
65
|
+
};
|
|
26
66
|
var GeneratedOperation = Schema.declare((value) => value instanceof Function).annotate({ identifier: "GeneratedOperation" });
|
|
27
67
|
var GeneratedOperationEffect = Schema.declare((value) => Effect.isEffect(value)).annotate({ identifier: "GeneratedOperationEffect" });
|
|
28
68
|
var makeGeneratedClient = (api, http, baseUrl) => EffectHttpApiClient.makeWith(api, {
|
|
@@ -58,9 +98,10 @@ var executeGeneratedOperation = Effect.fn("HttpApiClient.executeGeneratedOperati
|
|
|
58
98
|
class ApiClient extends Context.Service()("ApiClient") {
|
|
59
99
|
static layer = (config) => Layer.effect(this, Effect.gen(function* () {
|
|
60
100
|
const http = yield* HttpClient.HttpClient;
|
|
101
|
+
const encodeResult = makeHttpApiOperationResultEncoder(config.api);
|
|
61
102
|
const client = yield* makeGeneratedClient(config.api, http, config.baseUrl);
|
|
62
103
|
return {
|
|
63
|
-
encodeResult: config.encodeResult ?? ((result) =>
|
|
104
|
+
encodeResult: config.encodeResult ?? ((result, operation) => encodeResult(result, operation)),
|
|
64
105
|
execute: Effect.fn("ApiClient.execute")(function* (options) {
|
|
65
106
|
return yield* executeGeneratedOperation(client, options);
|
|
66
107
|
})
|
|
@@ -78,7 +119,7 @@ import {
|
|
|
78
119
|
Schema as Schema2,
|
|
79
120
|
SchemaRepresentation
|
|
80
121
|
} from "effect";
|
|
81
|
-
import { HttpApi as HttpApi2, OpenApi } from "effect/unstable/httpapi";
|
|
122
|
+
import { HttpApi as HttpApi2, OpenApi as OpenApi2 } from "effect/unstable/httpapi";
|
|
82
123
|
var JsonObjectSchema = Schema2.Record(Schema2.String, Schema2.Json).annotate({
|
|
83
124
|
identifier: "HttpJsonObject",
|
|
84
125
|
title: "HTTP JSON object",
|
|
@@ -251,7 +292,7 @@ var decodeHttpApiOperationInput = Effect2.fn("Http.decodeApiOperationInput")(fun
|
|
|
251
292
|
query: pickParameters("query")
|
|
252
293
|
};
|
|
253
294
|
});
|
|
254
|
-
var
|
|
295
|
+
var reflectedSchema2 = (schema) => Schema2.make(schema.ast);
|
|
255
296
|
var reflectedOperationInputSchemas = (api) => {
|
|
256
297
|
const schemas = new Map;
|
|
257
298
|
HttpApi2.reflect(api, {
|
|
@@ -259,17 +300,17 @@ var reflectedOperationInputSchemas = (api) => {
|
|
|
259
300
|
return;
|
|
260
301
|
},
|
|
261
302
|
onEndpoint: ({ endpoint, group }) => {
|
|
262
|
-
const operationId = Context2.getOrElse(endpoint.annotations,
|
|
303
|
+
const operationId = Context2.getOrElse(endpoint.annotations, OpenApi2.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
263
304
|
const fields = {};
|
|
264
305
|
if (endpoint.params)
|
|
265
|
-
fields.params =
|
|
306
|
+
fields.params = reflectedSchema2(endpoint.params);
|
|
266
307
|
if (endpoint.query) {
|
|
267
|
-
fields.query = Schema2.optionalKey(
|
|
308
|
+
fields.query = Schema2.optionalKey(reflectedSchema2(endpoint.query));
|
|
268
309
|
}
|
|
269
310
|
if (endpoint.headers) {
|
|
270
|
-
fields.headers = Schema2.optionalKey(
|
|
311
|
+
fields.headers = Schema2.optionalKey(reflectedSchema2(endpoint.headers));
|
|
271
312
|
}
|
|
272
|
-
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(
|
|
313
|
+
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(reflectedSchema2));
|
|
273
314
|
if (payloadSchemas.length === 1 && payloadSchemas[0]) {
|
|
274
315
|
fields.body = payloadSchemas[0];
|
|
275
316
|
} else if (payloadSchemas.length > 1) {
|
|
@@ -301,7 +342,7 @@ class HttpApiSpec extends Context2.Service()("HttpApiSpec", {
|
|
|
301
342
|
if (!HttpApi2.isHttpApi(api)) {
|
|
302
343
|
throw new Error("HttpApiSpec requires a valid HttpApi");
|
|
303
344
|
}
|
|
304
|
-
const spec =
|
|
345
|
+
const spec = OpenApi2.fromApi(api);
|
|
305
346
|
const reflectedSchemas = reflectedOperationInputSchemas(api);
|
|
306
347
|
const operations = httpApiOperations({
|
|
307
348
|
spec,
|