@gjermundgaraba/effect-actions 0.15.0 → 0.17.0
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/Action.d.mts +3 -5
- package/dist/Action.mjs +3 -1
- package/dist/ActionCli.d.mts +2 -2
- package/dist/ActionCli.mjs +2 -1
- package/dist/ActionHttp.d.mts +3 -2
- package/dist/ActionHttp.mjs +3 -1
- package/dist/ActionMcp.d.mts +11 -4
- package/dist/ActionMcp.mjs +7 -3
- package/dist/ActionRpc.d.mts +5 -3
- package/dist/ActionRpc.mjs +2 -1
- package/dist/ActionToolkit.d.mts +3 -2
- package/dist/ActionToolkit.mjs +3 -1
- package/dist/Authentication.d.mts +1 -1
- package/dist/Authentication.mjs +1 -1
- package/dist/authentication/provider.d.mts +2 -2
- package/dist/authentication/refusal.mjs +1 -1
- package/dist/cli/commands.d.mts +3 -2
- package/dist/cli/commands.mjs +2 -1
- package/dist/contract/ast.mjs +10 -0
- package/dist/contract/call.d.mts +1 -1
- package/dist/contract/caller.d.mts +9 -0
- package/dist/contract/caller.mjs +12 -0
- package/dist/contract/{rules.d.mts → declared.d.mts} +6 -10
- package/dist/contract/declared.mjs +42 -0
- package/dist/contract/hints.mjs +14 -0
- package/dist/contract/implementation.d.mts +4 -3
- package/dist/contract/implementation.mjs +4 -8
- package/dist/contract/names.mjs +25 -0
- package/dist/contract/status.mjs +18 -0
- package/dist/http/client.d.mts +3 -2
- package/dist/mcp/client.d.mts +40 -7
- package/dist/mcp/client.mjs +60 -17
- package/dist/mcp/protocol.mjs +1 -1
- package/docs/Action.md +17 -3
- package/docs/ActionCli.md +1 -1
- package/docs/ActionMcp-failures.md +1 -0
- package/docs/ActionMcp.md +16 -16
- package/docs/ActionToolkit.md +25 -2
- package/docs/Authentication-failures.md +1 -1
- package/docs/CONTEXT.md +2 -1
- package/docs/README.md +4 -4
- package/docs/Testing.md +3 -1
- package/examples/README.md +10 -2
- package/examples/assistant/README.md +24 -20
- package/examples/assistant/src/agent.ts +11 -7
- package/examples/assistant/src/cli.ts +2 -2
- package/examples/assistant/src/platform/tasks-file.ts +38 -0
- package/examples/assistant/src/tasks/store.ts +6 -38
- package/examples/assistant/src/toolkit.ts +11 -1
- package/examples/assistant/test/{tasks/store.test.ts → platform/tasks-file.test.ts} +2 -1
- package/examples/mcp-tools/README.md +14 -11
- package/examples/mcp-tools/src/{printers/fake.ts → platform/fake-farm.ts} +2 -2
- package/examples/mcp-tools/src/platform/print-farm.ts +118 -0
- package/examples/mcp-tools/src/printers/farm.ts +6 -122
- package/examples/mcp-tools/src/server.ts +4 -4
- package/examples/mcp-tools/src/stdio.ts +4 -4
- package/examples/mcp-tools/test/printers/printers.test.ts +3 -3
- package/examples/mcp-tools/test/routes.test.ts +4 -4
- package/examples/notes-service/README.md +32 -0
- package/examples/notes-service/src/identity/authorize.ts +1 -0
- package/examples/notes-service/src/notes/Notes.ts +22 -5
- package/examples/notes-service/src/notes/handlers.ts +2 -2
- package/examples/notes-service/src/notes/store.ts +36 -6
- package/examples/notes-service/test/notes/idempotency.test.ts +132 -0
- package/examples/notes-service/test/routes.test.ts +40 -1
- package/package.json +5 -5
- package/dist/_virtual/_rolldown/runtime.mjs +0 -13
- package/dist/contract/rules.mjs +0 -100
package/dist/Action.d.mts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { Anyone } from "./contract/
|
|
1
|
+
import { Anyone } from "./contract/caller.mjs";
|
|
2
2
|
import { BuiltIn, BuiltIns, Forbidden, InvalidInput, Issue, Refusal, Unauthenticated } from "./contract/errors.mjs";
|
|
3
3
|
import { ActionOf, AnyImplementation, Authorize, BuildError, BuildServices, Holding, Implementation, Known, Member, Offered, OptionalUnless, Protected, RequestOf, SelectedOf, Served, Serving } from "./contract/implementation.mjs";
|
|
4
4
|
import { Call } from "./contract/call.mjs";
|
|
5
5
|
import { Image } from "./contract/media.mjs";
|
|
6
6
|
import { Context, Effect, Layer, Schema, Scope } from "effect";
|
|
7
|
-
|
|
8
|
-
export { Action, Any, AnyImplementation, Anyone, Authorize, BuildError, BuildServices, BuiltIn, Client, ClientOptions, Errors, Forbidden, Handler, Handlers, Image, Implementation, InvalidInput, Issue, Mcp, Options, Refusal, Unauthenticated, allowAll, byName, client, implement, layer, make };
|
|
9
|
-
}
|
|
7
|
+
//#region src/contract/Action.d.ts
|
|
10
8
|
type Codec = Schema.Codec<unknown, unknown, never, never>;
|
|
11
9
|
type Fields = {
|
|
12
10
|
readonly [key: string]: Codec;
|
|
@@ -213,4 +211,4 @@ export interface ClientOptions<A extends Any> {
|
|
|
213
211
|
*/
|
|
214
212
|
export declare function client<const Apps extends Served, const O extends ClientOptions<ActionOf<Member<Apps>>> = {}>(implementations: Apps, ...options: OptionalUnless<O, ClientOptions<ActionOf<Member<Apps>>> & O & NoInfer<Known<O, ClientOptions<Any>>>>): Effect.Effect<Client<Apps, Offered<O, ActionOf<Member<Apps>>>>, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | Scope.Scope>;
|
|
215
213
|
//#endregion
|
|
216
|
-
export {
|
|
214
|
+
export { type AnyImplementation, Anyone, type Authorize, type BuildError, type BuildServices, BuiltIn, Forbidden, Image, type Implementation, InvalidInput, type Issue, Refusal, Unauthenticated };
|
package/dist/Action.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Anyone } from "./contract/caller.mjs";
|
|
1
2
|
import { BuiltIn, Forbidden, InvalidInput, Refusal, Unauthenticated } from "./contract/errors.mjs";
|
|
2
|
-
import {
|
|
3
|
+
import { assertKnown, assertName, assertOnce } from "./contract/names.mjs";
|
|
4
|
+
import { assertOwnTags, errorList, projectedErrors } from "./contract/declared.mjs";
|
|
3
5
|
import { Implementation, builders, built, memoized, select, toList } from "./contract/implementation.mjs";
|
|
4
6
|
import { inputOf } from "./contract/call.mjs";
|
|
5
7
|
import { Image } from "./contract/media.mjs";
|
package/dist/ActionCli.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Declaring } from "./contract/rules.mjs";
|
|
2
1
|
import { BuiltIn } from "./contract/errors.mjs";
|
|
3
2
|
import { ActionOf, BuildError, BuildServices, Holding, Known, Member, RequestOf, Selected, SelectedOf, Selection, Served } from "./contract/implementation.mjs";
|
|
4
|
-
import {
|
|
3
|
+
import { Any } from "./Action.mjs";
|
|
5
4
|
import { VerifierError } from "./authentication/provider.mjs";
|
|
5
|
+
import { Declaring } from "./contract/declared.mjs";
|
|
6
6
|
import { Options, UserError } from "./cli/commands.mjs";
|
|
7
7
|
import { AnyHttp, Options as Options$2 } from "./http/client.mjs";
|
|
8
8
|
import "./ActionHttp.mjs";
|
package/dist/ActionCli.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { assertDistinct, assertKnown
|
|
1
|
+
import { assertDistinct, assertKnown } from "./contract/names.mjs";
|
|
2
|
+
import { bindingErrors, projectedErrors } from "./contract/declared.mjs";
|
|
2
3
|
import { Implementation, acquire, assertHeld, select, toList } from "./contract/implementation.mjs";
|
|
3
4
|
import { client } from "./ActionHttp.mjs";
|
|
4
5
|
import { command as command$1, failingAsDerived } from "./cli/commands.mjs";
|
package/dist/ActionHttp.d.mts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Anyone
|
|
1
|
+
import { Anyone } from "./contract/caller.mjs";
|
|
2
2
|
import { BuiltIn, BuiltIns } from "./contract/errors.mjs";
|
|
3
3
|
import { BuildError, Holding, Known, Member, MiddlewareOf, Protected, Selected, Served, Serving } from "./contract/implementation.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { Any, Errors } from "./Action.mjs";
|
|
5
5
|
import { Any as Any$1, DescriptorOf, Identity, Matching, PublicOnly, RemoteRequest, Required, ServedProvider, ServerBuildServices, VerifierError } from "./authentication/provider.mjs";
|
|
6
|
+
import { Certain, ErrorsOf } from "./contract/declared.mjs";
|
|
6
7
|
import { AnyHttp, Client, MethodError, Options } from "./http/client.mjs";
|
|
7
8
|
import { Context, Effect, FileSystem, Layer, Path, Schema, Types } from "effect";
|
|
8
9
|
import { Etag, HttpClient, HttpPlatform, HttpRouter } from "effect/http";
|
package/dist/ActionHttp.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { Anyone
|
|
1
|
+
import { Anyone } from "./contract/caller.mjs";
|
|
2
|
+
import { bindingErrors, bindingOf, projectedErrors } from "./contract/declared.mjs";
|
|
2
3
|
import { acquire, provideHandlers, servingOf } from "./contract/implementation.mjs";
|
|
3
4
|
import { providerOf } from "./authentication/provider.mjs";
|
|
4
5
|
import { inputParseOptions } from "./contract/call.mjs";
|
|
6
|
+
import { declared } from "./contract/status.mjs";
|
|
5
7
|
import { methods } from "./http/client.mjs";
|
|
6
8
|
import { SchemaErrors, schemaErrors } from "./http/schema-errors.mjs";
|
|
7
9
|
import { Context, Effect, Layer, Scope } from "effect";
|
package/dist/ActionMcp.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Protected, SelectedOf, Selection, Served, ServedRequest } from "./contract/implementation.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { Any } from "./Action.mjs";
|
|
3
3
|
import { Any as Any$1, Matching, ProviderOf, RemoteRequest, Required, ServerBuildServices } from "./authentication/provider.mjs";
|
|
4
4
|
import { Client, ClientOptions, McpCallError, Params, RequestOptions, client, request } from "./mcp/client.mjs";
|
|
5
5
|
import { Cause, Effect, Layer, Stdio } from "effect";
|
|
@@ -25,14 +25,21 @@ export interface Options<E = never, R = never, A extends Any = Any> extends Omit
|
|
|
25
25
|
readonly features?: Layer.Layer<never, E, R> | undefined;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* One Streamable HTTP MCP endpoint: `Options`, every native
|
|
29
|
-
* `allowedOrigins` among them, except `protocols` and
|
|
30
|
-
*
|
|
28
|
+
* One Streamable HTTP MCP endpoint: `Options`, `authentication`, `requestScoped`, and every native
|
|
29
|
+
* `McpServer.layerHttp` option, `allowedOrigins` among them, except `protocols` and
|
|
30
|
+
* `allowSessionTermination`, which are fixed.
|
|
31
31
|
*/
|
|
32
32
|
export interface LayerHttpOptions<E = never, R = never, A extends Any = Any> extends Options<E, R, A>, Omit<Parameters<typeof McpServer.layerHttp>[0], "protocols" | "path" | "allowSessionTermination"> {
|
|
33
33
|
/** The endpoint's route; defaults to `/mcp`. */
|
|
34
34
|
readonly path?: HttpRouter.PathInput;
|
|
35
35
|
readonly authentication?: Any$1;
|
|
36
|
+
/**
|
|
37
|
+
* Whether MCP 2026-07-28 clients may subscribe, holding a stream open for list changes and
|
|
38
|
+
* resource updates. Defaults to whether `features` are given: an endpoint of actions alone
|
|
39
|
+
* registers its tools once, before any client connects, and serves no resource. Pass `true`
|
|
40
|
+
* where a handler changes the server through `McpServer.McpServer`.
|
|
41
|
+
*/
|
|
42
|
+
readonly allowSubscriptions?: boolean | undefined;
|
|
36
43
|
/**
|
|
37
44
|
* `true` runs a server per request, as Effect's `McpServer.layerHttpRequestScoped` does, for a
|
|
38
45
|
* host such as Cloudflare Workers where nothing may outlive a request. It speaks MCP 2026-07-28
|
package/dist/ActionMcp.mjs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { Anyone, assertAuthentication
|
|
1
|
+
import { Anyone, assertAuthentication } from "./contract/caller.mjs";
|
|
2
|
+
import { assertOnce } from "./contract/names.mjs";
|
|
3
|
+
import { projectedErrors } from "./contract/declared.mjs";
|
|
4
|
+
import { hintsOf } from "./contract/hints.mjs";
|
|
2
5
|
import { acquire, provideHandlers, select, toList } from "./contract/implementation.mjs";
|
|
3
6
|
import { promote, providerOf } from "./authentication/provider.mjs";
|
|
4
7
|
import { inputParseOptions } from "./contract/call.mjs";
|
|
@@ -148,7 +151,7 @@ const namesItsMethod = (request) => Effect.match(request.json, {
|
|
|
148
151
|
onFailure: () => true,
|
|
149
152
|
onSuccess: (body) => !Predicate.hasProperty(body, "method") || body.method === request.headers["mcp-method"]
|
|
150
153
|
});
|
|
151
|
-
function layerHttp(apps, { actions, features, path = defaultPath, authentication, requestScoped, ...options }) {
|
|
154
|
+
function layerHttp(apps, { actions, features, path = defaultPath, authentication, requestScoped, allowSubscriptions = features !== void 0, ...options }) {
|
|
152
155
|
const selected = select(toList(apps), actions);
|
|
153
156
|
const served = selected.flatMap((app) => app.actions);
|
|
154
157
|
assertAuthentication(served, authentication);
|
|
@@ -160,7 +163,8 @@ function layerHttp(apps, { actions, features, path = defaultPath, authentication
|
|
|
160
163
|
...options,
|
|
161
164
|
path,
|
|
162
165
|
protocols,
|
|
163
|
-
allowSessionTermination: true
|
|
166
|
+
allowSessionTermination: true,
|
|
167
|
+
allowSubscriptions
|
|
164
168
|
}), features, authentication);
|
|
165
169
|
if (authentication === void 0) return transport;
|
|
166
170
|
const required = requiresAuthentication(served);
|
package/dist/ActionRpc.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { Anyone
|
|
1
|
+
import { Anyone } from "./contract/caller.mjs";
|
|
2
2
|
import { BuiltIn, BuiltIns } from "./contract/errors.mjs";
|
|
3
3
|
import { BuildError, Holding, Known, Member, MiddlewareOf, Protected, Selected, Served, Serving } from "./contract/implementation.mjs";
|
|
4
4
|
import { Call } from "./contract/call.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { Any as Any$1, Errors } from "./Action.mjs";
|
|
6
6
|
import { Any as Any$2, DescriptorOf, Identity, Matching, PublicOnly, RemoteRequest, Required, ServedProvider, ServerBuildServices, VerifierError } from "./authentication/provider.mjs";
|
|
7
|
+
import { Certain, ErrorsOf } from "./contract/declared.mjs";
|
|
7
8
|
import { Context, Effect, Layer, Schema, Scope, Types } from "effect";
|
|
8
9
|
import { HttpRouter } from "effect/http";
|
|
9
10
|
import { Rpc, RpcClient, RpcClientError, RpcGroup, RpcMiddleware, RpcServer } from "effect/rpc";
|
|
@@ -144,4 +145,5 @@ export type ClientOptions = Omit<NonNullable<Parameters<typeof RpcClient.make>[1
|
|
|
144
145
|
* or with Effect's own `RpcClientError` when the server could not be reached.
|
|
145
146
|
*/
|
|
146
147
|
export declare function client<const B extends Any>(binding: B, options?: ClientOptions): Effect.Effect<Client<B>, never, RpcClient.Protocol | Scope.Scope>;
|
|
147
|
-
//#endregion
|
|
148
|
+
//#endregion
|
|
149
|
+
export {};
|
package/dist/ActionRpc.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Anyone
|
|
1
|
+
import { Anyone } from "./contract/caller.mjs";
|
|
2
|
+
import { bindingErrors, bindingOf } from "./contract/declared.mjs";
|
|
2
3
|
import { acquire, provideHandlers, servingOf } from "./contract/implementation.mjs";
|
|
3
4
|
import { providerOf } from "./authentication/provider.mjs";
|
|
4
5
|
import { checked, decodeInput } from "./contract/call.mjs";
|
package/dist/ActionToolkit.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BuiltIns } from "./contract/errors.mjs";
|
|
2
2
|
import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Offered, OptionalUnless, RequestOf, SelectedOf, Served, Serving } from "./contract/implementation.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { Any } from "./Action.mjs";
|
|
4
4
|
import { Effect, Layer, Schema } from "effect";
|
|
5
5
|
import { Prompt, Tool, Toolkit } from "effect/ai";
|
|
6
6
|
//#region src/toolkit/ActionToolkit.d.ts
|
|
@@ -85,4 +85,5 @@ export interface Approval {
|
|
|
85
85
|
* `examples/kitchen-sink/src/assistant/handlers.ts`.
|
|
86
86
|
*/
|
|
87
87
|
export declare const openApprovals: ({ content: messages }: Prompt.Prompt) => ReadonlyArray<Approval>;
|
|
88
|
-
//#endregion
|
|
88
|
+
//#endregion
|
|
89
|
+
export {};
|
package/dist/ActionToolkit.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { assertOnce
|
|
1
|
+
import { assertOnce } from "./contract/names.mjs";
|
|
2
|
+
import { projectedErrors } from "./contract/declared.mjs";
|
|
3
|
+
import { hintsOf } from "./contract/hints.mjs";
|
|
2
4
|
import { Implementation, acquire, provideHandlers, select, toList } from "./contract/implementation.mjs";
|
|
3
5
|
import { Effect, Schema } from "effect";
|
|
4
6
|
import { Tool, Toolkit } from "effect/ai";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ErrorsOf } from "./contract/rules.mjs";
|
|
2
1
|
import { Refusal } from "./contract/errors.mjs";
|
|
3
2
|
import { Known, OptionalUnless } from "./contract/implementation.mjs";
|
|
4
3
|
import { Errors } from "./Action.mjs";
|
|
5
4
|
import { Any, Descriptor, Provider, Security } from "./authentication/provider.mjs";
|
|
5
|
+
import { ErrorsOf } from "./contract/declared.mjs";
|
|
6
6
|
import { Array, Context, Effect, Layer, Option, Redacted, Scope } from "effect";
|
|
7
7
|
import { HttpRouter, HttpServerResponse } from "effect/http";
|
|
8
8
|
import { HttpApiSecurity } from "effect/http-api";
|
package/dist/Authentication.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Unauthenticated, scopeToken } from "./contract/errors.mjs";
|
|
2
|
-
import { assertOwnTags, errorList } from "./contract/
|
|
2
|
+
import { assertOwnTags, errorList } from "./contract/declared.mjs";
|
|
3
3
|
import { providerOf } from "./authentication/provider.mjs";
|
|
4
4
|
import { answer, answerStepUp, bearer, challenge, declaredResponse, isRefusal, isStepUp, plain } from "./authentication/refusal.mjs";
|
|
5
5
|
import { Context, Effect, Layer, Match, Option, Predicate, Redacted } from "effect";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Anyone } from "../contract/
|
|
1
|
+
import { Anyone } from "../contract/caller.mjs";
|
|
2
2
|
import { Refusal } from "../contract/errors.mjs";
|
|
3
3
|
import { BuildServices, Protected, ServedRequest, Serving } from "../contract/implementation.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { Any as Any$1, Errors } from "../Action.mjs";
|
|
5
5
|
import { Context, Effect } from "effect";
|
|
6
6
|
import { HttpRouter, HttpServerResponse } from "effect/http";
|
|
7
7
|
import { HttpApiMiddleware, HttpApiSecurity } from "effect/http-api";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Forbidden, Refusal, Unauthenticated } from "../contract/errors.mjs";
|
|
2
|
-
import { declared, httpApiStatusOf } from "../contract/
|
|
2
|
+
import { declared, httpApiStatusOf } from "../contract/status.mjs";
|
|
3
3
|
import { Cause, Context, Effect, Exit, Option, Predicate, Ref, Schema } from "effect";
|
|
4
4
|
import { HttpServerResponse } from "effect/http";
|
|
5
5
|
//#region src/authentication/refusal.ts
|
package/dist/cli/commands.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../contract/errors.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { Any } from "../Action.mjs";
|
|
3
3
|
import { Effect, Runtime, Types } from "effect";
|
|
4
4
|
import { CliError, Command } from "effect/cli";
|
|
5
5
|
//#region src/cli/commands.d.ts
|
|
@@ -44,4 +44,5 @@ export declare class UserError<out E = unknown> extends CliError.UserError {
|
|
|
44
44
|
get reason(): E;
|
|
45
45
|
get [Runtime.errorExitCode](): number;
|
|
46
46
|
}
|
|
47
|
-
//#endregion
|
|
47
|
+
//#endregion
|
|
48
|
+
export {};
|
package/dist/cli/commands.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { literalValues, members, unsuspended } from "../contract/ast.mjs";
|
|
1
2
|
import { InvalidInput } from "../contract/errors.mjs";
|
|
2
|
-
import { assertDistinct
|
|
3
|
+
import { assertDistinct } from "../contract/names.mjs";
|
|
3
4
|
import { decodeInput } from "../contract/call.mjs";
|
|
4
5
|
import { withStderrConsole } from "../stdio/console.mjs";
|
|
5
6
|
import { Console, Effect, Option, Predicate, Record, Runtime, Schema, SchemaAST, Stdio, Stream } from "effect";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SchemaAST } from "effect";
|
|
2
|
+
//#region src/contract/ast.ts
|
|
3
|
+
const unsuspended = (ast) => SchemaAST.isSuspend(ast) ? unsuspended(ast.thunk()) : ast;
|
|
4
|
+
const members = (ast) => {
|
|
5
|
+
const resolved = unsuspended(ast);
|
|
6
|
+
return SchemaAST.isUnion(resolved) ? resolved.types.flatMap(members) : [resolved];
|
|
7
|
+
};
|
|
8
|
+
const literalValues = (ast) => SchemaAST.isLiteral(ast) ? [ast.literal] : SchemaAST.isEnum(ast) ? ast.enums.map(([, value]) => value) : [void 0];
|
|
9
|
+
//#endregion
|
|
10
|
+
export { literalValues, members, unsuspended };
|
package/dist/contract/call.d.mts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "../Action.mjs";
|
|
2
|
+
//#region src/contract/caller.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The caller of a public action, `caller: Action.Anyone`: anyone, signed in or not, and no
|
|
5
|
+
* authorizer runs for it. Registered globally, so two copies of the package agree on it, as they
|
|
6
|
+
* agree on a name.
|
|
7
|
+
*/
|
|
8
|
+
export declare const Anyone: unique symbol;
|
|
9
|
+
//#endregion
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/contract/caller.ts
|
|
2
|
+
/**
|
|
3
|
+
* The caller of a public action, `caller: Action.Anyone`: anyone, signed in or not, and no
|
|
4
|
+
* authorizer runs for it. Registered globally, so two copies of the package agree on it, as they
|
|
5
|
+
* agree on a name.
|
|
6
|
+
*/
|
|
7
|
+
const Anyone = Symbol.for("@gjermundgaraba/effect-actions/Anyone");
|
|
8
|
+
const assertAuthentication = (actions, authentication) => {
|
|
9
|
+
for (const action of actions) if (action.caller !== Anyone && (authentication === void 0 || action.caller.key !== authentication.identity.key)) throw new Error(`Protected action '${action.name}' requires its matching authentication descriptor`);
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { Anyone, assertAuthentication };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import "./caller.mjs";
|
|
2
|
+
import { Errors } from "../Action.mjs";
|
|
3
|
+
import { Types } from "effect";
|
|
4
|
+
//#region src/contract/declared.d.ts
|
|
4
5
|
type ListOf<G> = G extends Errors ? G : readonly [G];
|
|
5
6
|
/**
|
|
6
7
|
* The errors options `O` declare, as at run time: those `error` always gives, or, where it may
|
|
@@ -23,10 +24,5 @@ export interface Declaring {
|
|
|
23
24
|
readonly error: Errors;
|
|
24
25
|
} | undefined;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* authorizer runs for it. Registered globally, so two copies of the package agree on it, as they
|
|
29
|
-
* agree on a name.
|
|
30
|
-
*/
|
|
31
|
-
export declare const Anyone: unique symbol;
|
|
32
|
-
//#endregion
|
|
27
|
+
//#endregion
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { literalValues, members } from "./ast.mjs";
|
|
2
|
+
import { Anyone, assertAuthentication } from "./caller.mjs";
|
|
3
|
+
import { builtIns } from "./errors.mjs";
|
|
4
|
+
import { assertOnce } from "./names.mjs";
|
|
5
|
+
import { Predicate, Schema, SchemaAST } from "effect";
|
|
6
|
+
//#region src/contract/declared.ts
|
|
7
|
+
const errorList = (error) => error === void 0 ? [] : Schema.isSchema(error) ? [error] : error;
|
|
8
|
+
const projectedErrors = (action, surface = []) => [.../* @__PURE__ */ new Set([
|
|
9
|
+
...builtIns,
|
|
10
|
+
...action.error,
|
|
11
|
+
...surface
|
|
12
|
+
])];
|
|
13
|
+
/**
|
|
14
|
+
* Every error a binding's call of `action` declares: the built-in ones, the action's, the
|
|
15
|
+
* binding's and, where `action` is protected, its authentication's.
|
|
16
|
+
*/
|
|
17
|
+
const bindingErrors = (action, binding) => projectedErrors(action, action.caller === Anyone || binding.authentication === void 0 ? binding.error : [...binding.error, ...binding.authentication.error]);
|
|
18
|
+
const encodedTags = (ast) => members(SchemaAST.toEncoded(ast)).flatMap((member) => {
|
|
19
|
+
const tag = SchemaAST.isObjects(member) ? member.propertySignatures.find((property) => property.name === "_tag")?.type : void 0;
|
|
20
|
+
return tag === void 0 ? [] : members(tag).flatMap(literalValues).filter(Predicate.isString);
|
|
21
|
+
});
|
|
22
|
+
const builtInTags = new Set(builtIns.flatMap((error) => encodedTags(error.ast)));
|
|
23
|
+
const assertOwnTags = (what, errors) => {
|
|
24
|
+
const tag = errors.flatMap((error) => encodedTags(error.ast)).find((tag) => builtInTags.has(tag));
|
|
25
|
+
if (tag !== void 0) throw new Error(`${what}: error _tag "${tag}" is built in, and declared on every surface`);
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The checks `ActionHttp.make` and `ActionRpc.make` share: each action once, no built-in error in
|
|
29
|
+
* `error`, and the descriptor every protected action names. Returns `error` as a list.
|
|
30
|
+
*/
|
|
31
|
+
const bindingOf = (what, actions, options) => {
|
|
32
|
+
assertOnce("action", actions);
|
|
33
|
+
const error = errorList(options.error);
|
|
34
|
+
assertOwnTags(what, error);
|
|
35
|
+
assertAuthentication(actions, options.authentication);
|
|
36
|
+
return {
|
|
37
|
+
error,
|
|
38
|
+
authentication: options.authentication
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
export { assertOwnTags, bindingErrors, bindingOf, errorList, projectedErrors };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/contract/hints.ts
|
|
2
|
+
/**
|
|
3
|
+
* An action's MCP tool hints, MCP's defaults where its `mcp` leaves one out: a write destructive,
|
|
4
|
+
* a read not, no tool idempotent and every tool open-world. A given value passes through, as MCP
|
|
5
|
+
* calls `destructiveHint` and `idempotentHint` meaningless on a read-only tool.
|
|
6
|
+
*/
|
|
7
|
+
const hintsOf = ({ readOnly, mcp }) => ({
|
|
8
|
+
readOnlyHint: readOnly,
|
|
9
|
+
destructiveHint: mcp.destructiveHint ?? !readOnly,
|
|
10
|
+
idempotentHint: mcp.idempotentHint ?? false,
|
|
11
|
+
openWorldHint: mcp.openWorldHint ?? true
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
export { hintsOf };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Anyone } from "./
|
|
1
|
+
import { Anyone } from "./caller.mjs";
|
|
2
2
|
import { Refusal } from "./errors.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { Any } from "../Action.mjs";
|
|
4
4
|
import { Context, Effect, Layer, Scope } from "effect";
|
|
5
5
|
//#region src/contract/implementation.d.ts
|
|
6
6
|
/** Decoded values at the dispatch boundary every surface shares. */
|
|
@@ -192,4 +192,5 @@ export type BuildServices<App, Listed extends Any = OwnActions<App>> = App exten
|
|
|
192
192
|
readonly "~buildServices": infer RX;
|
|
193
193
|
readonly "~authorizeBuildServices": infer RAX;
|
|
194
194
|
} ? RX | ([Protected<Serving<App, Listed>>] extends [never] ? never : RAX) : never;
|
|
195
|
-
//#endregion
|
|
195
|
+
//#endregion
|
|
196
|
+
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Anyone, assertAuthentication } from "./caller.mjs";
|
|
1
2
|
import { Unauthenticated } from "./errors.mjs";
|
|
2
|
-
import {
|
|
3
|
+
import { assertDistinct, assertOnce } from "./names.mjs";
|
|
4
|
+
import { projectedErrors } from "./declared.mjs";
|
|
3
5
|
import { Array, Cause, Context, Effect, Exit, Layer, Option, Schema } from "effect";
|
|
4
6
|
//#region src/contract/implementation.ts
|
|
5
7
|
const uniqueKey = () => Math.random().toString(36).slice(2);
|
|
@@ -152,13 +154,7 @@ const dispatch = (action, handle, before) => {
|
|
|
152
154
|
} else reasons.push(reason);
|
|
153
155
|
return yield* Effect.failCause(Cause.fromReasons(reasons));
|
|
154
156
|
});
|
|
155
|
-
return (input) => {
|
|
156
|
-
const handled = Effect.withSpan(Effect.annotateLogs(Effect.suspend(() => handle(input)), actionAttributes), action.name, {
|
|
157
|
-
captureStackTrace: false,
|
|
158
|
-
attributes: actionAttributes
|
|
159
|
-
});
|
|
160
|
-
return Effect.scoped(authentication.pipe(Effect.andThen(authorization), Effect.andThen(handled), Effect.catchCause(declaredOrDefect)));
|
|
161
|
-
};
|
|
157
|
+
return (input) => Effect.scoped(authentication.pipe(Effect.andThen(authorization), Effect.andThen(Effect.suspend(() => handle(input))), Effect.catchCause(declaredOrDefect))).pipe(Effect.annotateLogs(actionAttributes), Effect.withSpan(action.name, { attributes: actionAttributes }, { captureStackTrace: false }));
|
|
162
158
|
};
|
|
163
159
|
//#endregion
|
|
164
160
|
export { Implementation, acquire, assertHeld, builders, built, memoized, provideHandlers, select, servingOf, toList };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/contract/names.ts
|
|
2
|
+
const validName = /^[A-Za-z0-9_-]{1,128}$/;
|
|
3
|
+
const assertName = (what, name) => {
|
|
4
|
+
if (!validName.test(name) || name === "then") throw new Error(`Invalid ${what}: ${name}`);
|
|
5
|
+
};
|
|
6
|
+
const assertDistinct = (what, items, nameOf, claimantOf = nameOf) => {
|
|
7
|
+
const seen = /* @__PURE__ */ new Map();
|
|
8
|
+
for (const item of items) {
|
|
9
|
+
const name = nameOf(item);
|
|
10
|
+
const claimant = claimantOf(item);
|
|
11
|
+
const other = seen.get(name);
|
|
12
|
+
if (other !== void 0) {
|
|
13
|
+
const by = other === claimant ? "" : `, claimed by ${other} and ${claimant}`;
|
|
14
|
+
throw new Error(`Duplicate ${what}: ${name}${by}`);
|
|
15
|
+
}
|
|
16
|
+
seen.set(name, claimant);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const assertOnce = (what, actions) => assertDistinct(what, actions, (action) => action.name);
|
|
20
|
+
const assertKnown = (what, keys, names) => {
|
|
21
|
+
const unknown = keys.filter((key) => !names.includes(key));
|
|
22
|
+
if (unknown.length > 0) throw new Error(`Unknown ${what}: ${unknown.join(", ")}`);
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { assertDistinct, assertKnown, assertName, assertOnce };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { unsuspended } from "./ast.mjs";
|
|
2
|
+
import { Schema, SchemaAST } from "effect";
|
|
3
|
+
//#region src/contract/status.ts
|
|
4
|
+
const httpApiStatusOf = SchemaAST.resolveAt("httpApiStatus");
|
|
5
|
+
const statusThroughSuspensions = (ast) => httpApiStatusOf(ast) ?? (SchemaAST.isSuspend(ast) ? statusThroughSuspensions(ast.thunk()) : void 0);
|
|
6
|
+
/**
|
|
7
|
+
* `error` as an endpoint declares it: each member of a union without a status of its own apart,
|
|
8
|
+
* each with its status, 422 without one.
|
|
9
|
+
*/
|
|
10
|
+
const declared = (error) => {
|
|
11
|
+
const status = statusThroughSuspensions(error.ast);
|
|
12
|
+
if (status !== void 0) return [httpApiStatusOf(error.ast) === void 0 ? error.annotate({ httpApiStatus: status }) : error];
|
|
13
|
+
const resolved = unsuspended(error.ast);
|
|
14
|
+
if (SchemaAST.isUnion(resolved) && resolved.checks === void 0 && resolved.encoding === void 0) return resolved.types.flatMap((member) => declared(Schema.make(member)));
|
|
15
|
+
return [error.annotate({ httpApiStatus: 422 })];
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { declared, httpApiStatusOf };
|
package/dist/http/client.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BuiltIns } from "../contract/errors.mjs";
|
|
2
2
|
import { Call } from "../contract/call.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { Any, Errors } from "../Action.mjs";
|
|
4
4
|
import { Any as Any$1, VerifierError } from "../authentication/provider.mjs";
|
|
5
5
|
import { Effect, Schema } from "effect";
|
|
6
6
|
import { HttpClient, HttpClientError } from "effect/http";
|
|
@@ -38,4 +38,5 @@ export interface AnyHttp {
|
|
|
38
38
|
* with what its descriptor's verifier declares.
|
|
39
39
|
*/
|
|
40
40
|
export type Client<H extends AnyHttp> = { readonly [A in H["actions"][number] as A["name"]]: Method<A, H["error"][number] | VerifierError<A, H["authentication"]>>; };
|
|
41
|
-
//#endregion
|
|
41
|
+
//#endregion
|
|
42
|
+
export {};
|
package/dist/mcp/client.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BuiltIn } from "../contract/errors.mjs";
|
|
2
2
|
import { Call } from "../contract/call.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { Any } from "../Action.mjs";
|
|
4
4
|
import { Any as Any$1, VerifierError } from "../authentication/provider.mjs";
|
|
5
5
|
import { Effect, Schema } from "effect";
|
|
6
6
|
import { Headers, HttpClient, HttpClientError, HttpClientRequest } from "effect/http";
|
|
@@ -45,15 +45,47 @@ export interface RequestOptions {
|
|
|
45
45
|
readonly url?: string;
|
|
46
46
|
readonly headers?: Headers.Input;
|
|
47
47
|
}
|
|
48
|
+
declare const StatusError_base: Schema.Class<StatusError, Schema.TaggedStruct<"StatusError", {
|
|
49
|
+
readonly status: Schema.Int;
|
|
50
|
+
readonly body: Schema.String;
|
|
51
|
+
}>, import("effect/Cause").YieldableError>;
|
|
52
|
+
/** The endpoint answered with an HTTP status other than 200, and a body no declared error decodes. */
|
|
53
|
+
declare class StatusError extends StatusError_base {}
|
|
54
|
+
declare const ProtocolError_base: Schema.Class<ProtocolError, Schema.TaggedStruct<"ProtocolError", {
|
|
55
|
+
readonly code: Schema.Finite;
|
|
56
|
+
readonly message: Schema.String;
|
|
57
|
+
}>, import("effect/Cause").YieldableError>;
|
|
58
|
+
/**
|
|
59
|
+
* The endpoint answered with a JSON-RPC error, such as the native server's for invalid arguments
|
|
60
|
+
* or an unknown tool.
|
|
61
|
+
*/
|
|
62
|
+
declare class ProtocolError extends ProtocolError_base {}
|
|
63
|
+
declare const NoReply_base: Schema.Class<NoReply, Schema.TaggedStruct<"NoReply", {
|
|
64
|
+
readonly body: Schema.String;
|
|
65
|
+
}>, import("effect/Cause").YieldableError>;
|
|
66
|
+
/** The endpoint answered 200 without a JSON-RPC reply to read, as JSON or as an event stream. */
|
|
67
|
+
declare class NoReply extends NoReply_base {}
|
|
68
|
+
declare const UndeclaredError_base: Schema.Class<UndeclaredError, Schema.TaggedStruct<"UndeclaredError", {
|
|
69
|
+
readonly text: Schema.String;
|
|
70
|
+
}>, import("effect/Cause").YieldableError>;
|
|
71
|
+
/** The tool returned an error whose text decodes as none of the action's declared errors. */
|
|
72
|
+
declare class UndeclaredError extends UndeclaredError_base {}
|
|
73
|
+
declare const NoStructuredContent_base: Schema.Class<NoStructuredContent, Schema.TaggedStruct<"NoStructuredContent", {
|
|
74
|
+
readonly body: Schema.String;
|
|
75
|
+
}>, import("effect/Cause").YieldableError>;
|
|
76
|
+
/** The tool returned a result without the structured content its action's success needs. */
|
|
77
|
+
declare class NoStructuredContent extends NoStructuredContent_base {}
|
|
48
78
|
declare const McpCallError_base: Schema.Class<McpCallError, Schema.TaggedStruct<"McpCallError", {
|
|
79
|
+
readonly tool: Schema.String;
|
|
80
|
+
readonly reason: Schema.Union<readonly [typeof StatusError, typeof ProtocolError, typeof NoReply, typeof UndeclaredError, typeof NoStructuredContent]>;
|
|
49
81
|
readonly message: Schema.String;
|
|
50
82
|
}>, import("effect/Cause").YieldableError>;
|
|
51
83
|
/**
|
|
52
|
-
* An answer a client method cannot read as the action's success or a declared error
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* decode as the success is a `SchemaError` instead,
|
|
56
|
-
* `HttpClientError`.
|
|
84
|
+
* An answer a client method cannot read as the action's success or a declared error: `reason`
|
|
85
|
+
* says which, with what the endpoint sent, so a caller branches on it with
|
|
86
|
+
* `Effect.catchReason("McpCallError", "StatusError", ...)`. `message` names the tool and the
|
|
87
|
+
* reason. Structured content that does not decode as the success is a `SchemaError` instead,
|
|
88
|
+
* and a request that fails an `HttpClientError`.
|
|
57
89
|
*/
|
|
58
90
|
export declare class McpCallError extends McpCallError_base {}
|
|
59
91
|
type CallError<A extends Any, D> = A["error"][number]["Type"] | VerifierError<A, D>["Type"] | BuiltIn | Schema.SchemaError | HttpClientError.HttpClientError | McpCallError;
|
|
@@ -84,4 +116,5 @@ export declare function client<const Actions extends ReadonlyArray<Any>, D exten
|
|
|
84
116
|
* a call its types would not send.
|
|
85
117
|
*/
|
|
86
118
|
export declare const request: (method: string, params?: Params, { headers, url }?: RequestOptions) => HttpClientRequest.HttpClientRequest;
|
|
87
|
-
//#endregion
|
|
119
|
+
//#endregion
|
|
120
|
+
export {};
|