@gjermundgaraba/effect-actions 0.16.0 → 0.18.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.
Files changed (76) hide show
  1. package/README.md +3 -1
  2. package/dist/Action.d.mts +3 -5
  3. package/dist/Action.mjs +3 -1
  4. package/dist/ActionCli.d.mts +31 -42
  5. package/dist/ActionCli.mjs +6 -16
  6. package/dist/ActionHttp.d.mts +12 -8
  7. package/dist/ActionHttp.mjs +3 -1
  8. package/dist/ActionMcp.d.mts +1 -1
  9. package/dist/ActionMcp.mjs +4 -1
  10. package/dist/ActionRpc.d.mts +5 -3
  11. package/dist/ActionRpc.mjs +2 -1
  12. package/dist/ActionToolkit.d.mts +3 -2
  13. package/dist/ActionToolkit.mjs +3 -1
  14. package/dist/Authentication.d.mts +1 -1
  15. package/dist/Authentication.mjs +1 -1
  16. package/dist/authentication/provider.d.mts +2 -2
  17. package/dist/authentication/refusal.mjs +1 -1
  18. package/dist/cli/commands.d.mts +4 -3
  19. package/dist/cli/commands.mjs +6 -5
  20. package/dist/contract/ast.mjs +10 -0
  21. package/dist/contract/call.d.mts +1 -1
  22. package/dist/contract/caller.d.mts +9 -0
  23. package/dist/contract/caller.mjs +12 -0
  24. package/dist/contract/{rules.d.mts → declared.d.mts} +6 -10
  25. package/dist/contract/declared.mjs +42 -0
  26. package/dist/contract/hints.mjs +14 -0
  27. package/dist/contract/implementation.d.mts +4 -3
  28. package/dist/contract/implementation.mjs +4 -8
  29. package/dist/contract/names.mjs +25 -0
  30. package/dist/contract/status.mjs +18 -0
  31. package/dist/http/client.d.mts +19 -9
  32. package/dist/http/client.mjs +6 -1
  33. package/dist/mcp/client.d.mts +52 -12
  34. package/dist/mcp/client.mjs +62 -19
  35. package/dist/mcp/protocol.mjs +1 -1
  36. package/docs/Action.md +17 -3
  37. package/docs/ActionCli-failures.md +10 -9
  38. package/docs/ActionCli.md +22 -21
  39. package/docs/ActionHttp-failures.md +6 -0
  40. package/docs/ActionHttp.md +9 -4
  41. package/docs/ActionMcp.md +3 -3
  42. package/docs/ActionToolkit.md +25 -2
  43. package/docs/Authentication-failures.md +1 -1
  44. package/docs/CONTEXT.md +2 -1
  45. package/docs/README.md +4 -4
  46. package/docs/Testing.md +3 -1
  47. package/examples/README.md +10 -2
  48. package/examples/assistant/README.md +24 -20
  49. package/examples/assistant/src/agent.ts +11 -7
  50. package/examples/assistant/src/cli.ts +2 -2
  51. package/examples/assistant/src/platform/tasks-file.ts +38 -0
  52. package/examples/assistant/src/tasks/store.ts +6 -38
  53. package/examples/assistant/src/toolkit.ts +11 -1
  54. package/examples/assistant/test/{tasks/store.test.ts → platform/tasks-file.test.ts} +2 -1
  55. package/examples/kitchen-sink/src/cli.ts +19 -21
  56. package/examples/mcp-tools/README.md +14 -11
  57. package/examples/mcp-tools/src/{printers/fake.ts → platform/fake-farm.ts} +2 -2
  58. package/examples/mcp-tools/src/platform/print-farm.ts +118 -0
  59. package/examples/mcp-tools/src/printers/farm.ts +6 -122
  60. package/examples/mcp-tools/src/server.ts +4 -4
  61. package/examples/mcp-tools/src/stdio.ts +4 -4
  62. package/examples/mcp-tools/test/printers/printers.test.ts +3 -3
  63. package/examples/mcp-tools/test/routes.test.ts +4 -4
  64. package/examples/notes-service/README.md +34 -2
  65. package/examples/notes-service/src/cli.ts +3 -9
  66. package/examples/notes-service/src/commands.ts +40 -36
  67. package/examples/notes-service/src/identity/authorize.ts +1 -0
  68. package/examples/notes-service/src/notes/Notes.ts +22 -5
  69. package/examples/notes-service/src/notes/handlers.ts +2 -2
  70. package/examples/notes-service/src/notes/store.ts +36 -6
  71. package/examples/notes-service/test/commands.test.ts +13 -19
  72. package/examples/notes-service/test/notes/idempotency.test.ts +132 -0
  73. package/examples/notes-service/test/routes.test.ts +40 -1
  74. package/package.json +5 -5
  75. package/dist/_virtual/_rolldown/runtime.mjs +0 -13
  76. package/dist/contract/rules.mjs +0 -100
package/README.md CHANGED
@@ -72,7 +72,9 @@ implementation is also:
72
72
  ```ts
73
73
  const { toolkit, layer } = ActionToolkit.make(greeting); // native Effect AI Toolkit and its handler layer
74
74
  const cli = ActionCli.make(greeting, { name: "greetings" }); // greetings greet --name Ada
75
- const remote = ActionCli.remoteCommand(Http, Greet, { client: { baseUrl } }); // greet --name Ada, over HTTP
75
+ const remote = ActionCli.remoteCommand(Http, Greet, {
76
+ client: ActionHttp.client(Http, { baseUrl }),
77
+ }); // greet --name Ada, over HTTP
76
78
  const local = Action.client(greeting); // the HTTP client's methods, in process
77
79
  const rpc = ActionRpc.layer(ActionRpc.make([Greet]), greeting); // Effect RPC, on the protocol the host provides
78
80
  ```
package/dist/Action.d.mts CHANGED
@@ -1,12 +1,10 @@
1
- import { Anyone } from "./contract/rules.mjs";
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
- declare namespace Action_d_exports {
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 { Action_d_exports, type AnyImplementation, Anyone, type Authorize, type BuildError, type BuildServices, BuiltIn, Forbidden, Image, type Implementation, InvalidInput, type Issue, Refusal, Unauthenticated };
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 { Anyone, assertKnown, assertName, assertOnce, assertOwnTags, errorList, projectedErrors } from "./contract/rules.mjs";
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";
@@ -1,14 +1,13 @@
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 { Action_d_exports, Any } from "./Action.mjs";
5
- import { VerifierError } from "./authentication/provider.mjs";
3
+ import { Any } from "./Action.mjs";
4
+ import { Declaring } from "./contract/declared.mjs";
6
5
  import { Options, UserError } from "./cli/commands.mjs";
7
- import { AnyHttp, Options as Options$2 } from "./http/client.mjs";
6
+ import { AnyHttp, Client } from "./http/client.mjs";
8
7
  import "./ActionHttp.mjs";
9
- import { Effect, Schema } from "effect";
10
- import { HttpClient, HttpClientError } from "effect/http";
11
- import { Command } from "effect/cli";
8
+ import { Effect } from "effect";
9
+ import { HttpClient } from "effect/http";
10
+ import { CliError, Command } from "effect/cli";
12
11
  //#region src/cli/ActionCli.d.ts
13
12
  interface AnyBinding extends Declaring {
14
13
  readonly actions: ReadonlyArray<Any>;
@@ -24,7 +23,7 @@ interface AnyBinding extends Declaring {
24
23
  * `Effect.tapError(() => Console.error(…))`: a `UserError` wrapped around the result would
25
24
  * replace its JSON.
26
25
  */
27
- export declare const toUserError: (calls: AnyBinding | ReadonlyArray<Any>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, UserError<Exclude<E, import("effect/cli/CliError").CliError>> | Exclude<E, Exclude<E, import("effect/cli/CliError").CliError>>, R>;
26
+ export declare const toUserError: (calls: AnyBinding | ReadonlyArray<Any>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, UserError<Exclude<E, CliError.CliError>> | Exclude<E, Exclude<E, CliError.CliError>>, R>;
28
27
  /**
29
28
  * An aggregate command of the actions `A`: its name, the actions that are its subcommands,
30
29
  * and their options.
@@ -69,39 +68,28 @@ type Local<App, A extends Any> = Effect.Effect<A["success"]["Type"], A["error"][
69
68
  * as JSON when it is not a struct. The flag of an array of strings or numbers is repeated for each value, a boolean
70
69
  * field is a switch, `--no-<name>` sending `false` as a required one left out does, and a field's `description` is its flag's help. It needs what the authorization, its handler and its builder
71
70
  * need; the host provides the identity. It prints the result on stdout; a failure is a
72
- * `UserError`, which `Command.run` prints on stderr. `remoteCommand` calls a server instead.
71
+ * `UserError`, or an Effect CLI error as it is, such as a builder's `CliError.UserError`, which
72
+ * `Command.run` prints on stderr. `remoteCommand` calls a server instead.
73
73
  */
74
- export declare function command<const Apps extends Served, A extends ActionOf<Member<Apps>>, const O = {}>(implementations: Apps, action: A, options?: O & NoInfer<Options<A>> & NoInfer<Known<O, Options<Any>>>): Command.Command<string, never, {}, UserError<Effect.Error<Local<Owning<Member<Apps>, A>, A>>>, Effect.Services<Local<Owning<Member<Apps>, A>, A>>>;
74
+ export declare function command<const Apps extends Served, A extends ActionOf<Member<Apps>>, const O = {}>(implementations: Apps, action: A, options?: O & NoInfer<Options<A>> & NoInfer<Known<O, Options<Any>>>): Command.Command<string, never, {}, Failing<Effect.Error<Local<Owning<Member<Apps>, A>, A>>>, Effect.Services<Local<Owning<Member<Apps>, A>, A>>>;
75
75
  /**
76
76
  * Project one action of an HTTP binding into a native Effect CLI command, derived as `command`
77
- * derives it, that calls the action over HTTP through its `ActionHttp.client` method, made with
78
- * the `client` options on the host's `HttpClient`, and fails as the method does. It runs
79
- * nothing locally.
77
+ * derives it, that calls the action through `client`'s method and fails as the method does, or
78
+ * as the Effect building the client does. `client` is an Effect of the binding's client, such as
79
+ * `ActionHttp.client(Http, { baseUrl })`, run each time the command runs; omitted, it is
80
+ * `ActionHttp.client(binding)` on the host's `HttpClient`. It runs nothing locally.
80
81
  */
81
- export declare function remoteCommand<const H extends AnyHttp, A extends H["actions"][number], const O = {}>(binding: H, action: A, options?: O & NoInfer<RemoteCommandOptions<A>> & NoInfer<Known<O, RemoteCommandOptions<Any>>>): Command.Command<string, never, {}, HttpUserError<H, A>, HttpClient.HttpClient>;
82
- /** A remote command's options, its action `A`. */
83
- export type RemoteCommandOptions<A extends Any = Any> = Options<A> & {
84
- /**
85
- * Its client's options, as `ActionHttp.client` takes them: `baseUrl`, and
86
- * `transformClient` for credentials. They reach this command's requests alone.
87
- */
88
- readonly client?: Options$2;
89
- };
90
- /** A remote aggregate command's options, its actions `A`. */
91
- export type RemoteOptions<A extends Any = Any> = Options$1<A> & {
92
- /**
93
- * Every subcommand's client options, as `ActionHttp.client` takes them: `baseUrl`, and
94
- * `transformClient` for credentials. They reach this aggregate's requests alone. A URL or a
95
- * token read from `Config` goes instead into the `HttpClient` provided on the command, with
96
- * `Command.provideEffect(HttpClient.HttpClient, …)`: it is read when a command runs, so
97
- * `--help` needs neither, and a command of your own in `subcommands` calls through it too.
98
- * A required one that is not set fails that Effect with a `ConfigError`, which is no
99
- * command's failure and prints as a crash line: map the read,
100
- * `Effect.mapError((cause) => new CliError.UserError({ cause, userMessage: "TOKEN is not set" }))`.
101
- */
102
- readonly client?: Options$2;
82
+ export declare function remoteCommand<const H extends AnyHttp, A extends H["actions"][number], const O = {}, C extends ClientOf<H> | undefined = undefined>(binding: H, action: A, options?: O & NoInfer<Options<A>> & NoInfer<Known<O, RemoteKeys<Options<Any>>>> & {
83
+ readonly client?: C;
84
+ }): Command.Command<string, never, {}, Failing<CallFailure<H, C, A>>, CallServices<C, A>>;
85
+ type ClientOf<H extends AnyHttp> = Effect.Effect<Client<H, unknown, unknown>, unknown, unknown>;
86
+ type RemoteKeys<Keys> = Keys & {
87
+ readonly client?: unknown;
103
88
  };
104
- type HttpUserError<H extends AnyHttp, A extends Any> = UserError<A["error"][number]["Type"] | H["error"][number]["Type"] | VerifierError<A, H["authentication"]>["Type"] | BuiltIn | HttpClientError.HttpClientError | Schema.SchemaError>;
89
+ type Failing<F> = UserError<Exclude<F, CliError.CliError>> | Extract<F, CliError.CliError>;
90
+ type Called<M, A extends Any> = A extends unknown ? A["name"] extends keyof M ? M[A["name"]] extends ((...input: never) => infer Call) ? Call : never : never : never;
91
+ type CallFailure<H extends AnyHttp, C, A extends Any> = C extends Effect.Effect<infer M, infer CE, unknown> ? CE | Effect.Error<Called<M, A>> : Effect.Error<Called<Client<H>, A>>;
92
+ type CallServices<C, A extends Any> = C extends Effect.Effect<infer M, unknown, infer CR> ? CR | Effect.Services<Called<M, A>> : HttpClient.HttpClient;
105
93
  /**
106
94
  * Project every implemented action as a subcommand of one aggregate command, each named after
107
95
  * its action in kebab case and run in process. `commands` gives a subcommand the options
@@ -113,16 +101,17 @@ type HttpUserError<H extends AnyHttp, A extends Any> = UserError<A["error"][numb
113
101
  */
114
102
  export declare function make<const Apps extends Served, const O extends Selection<ActionOf<Member<Apps>>> = {}, const Own extends Subcommands = []>(implementations: Apps, options: O & NoInfer<Options$1<ActionOf<Member<Apps>>>> & NoInfer<KnownOptions<O, Options$1, ActionOf<Member<Apps>>>> & {
115
103
  readonly subcommands?: Own | undefined;
116
- }): Command.Command<string, {}, {}, UserError<Effect.Error<Local<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>>> | Command.Error<OwnCommand<Own>>, Effect.Services<Local<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>> | Command.Services<OwnCommand<Own>>>;
104
+ }): Command.Command<string, {}, {}, Failing<Effect.Error<Local<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>>> | Command.Error<OwnCommand<Own>>, Effect.Services<Local<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>> | Command.Services<OwnCommand<Own>>>;
117
105
  /**
118
106
  * Project every action of an HTTP binding as a subcommand of one aggregate command, derived as
119
- * `make` derives it, each calling its action over HTTP. `client` configures every derived
120
- * subcommand's client; `commands` gives one the options `command` takes; `subcommands` adds
121
- * commands of your own.
107
+ * `make` derives it, each calling its action through `client`'s method, as `remoteCommand`
108
+ * does. `commands` gives one the options `command` takes; `subcommands` adds commands of your
109
+ * own, which call through the same client by yielding the Effect given as `client`.
122
110
  */
123
- export declare function remote<const H extends AnyHttp, const O extends Selection<H["actions"][number]> = {}, const Own extends Subcommands = []>(binding: H, options: O & NoInfer<RemoteOptions<H["actions"][number]>> & NoInfer<KnownOptions<O, RemoteOptions<Any>, H["actions"][number]>> & {
111
+ export declare function remote<const H extends AnyHttp, const O extends Selection<H["actions"][number]> = {}, const Own extends Subcommands = [], C extends ClientOf<H> | undefined = undefined>(binding: H, options: O & NoInfer<Options$1<H["actions"][number]>> & NoInfer<KnownOptions<O, RemoteKeys<Options$1<Any>>, H["actions"][number]>> & {
112
+ readonly client?: C;
124
113
  readonly subcommands?: Own | undefined;
125
- }): Command.Command<string, {}, {}, HttpUserError<H, Selected<O, H["actions"][number]>> | Command.Error<OwnCommand<Own>>, HttpClient.HttpClient | Command.Services<OwnCommand<Own>>>;
114
+ }): Command.Command<string, {}, {}, Failing<CallFailure<H, C, Selected<O, H["actions"][number]>>> | Command.Error<OwnCommand<Own>>, CallServices<C, Selected<O, H["actions"][number]>> | Command.Services<OwnCommand<Own>>>;
126
115
  /**
127
116
  * A program whose stdout carries only its results, such as a CLI feeding scripts or an MCP
128
117
  * subprocess, applied last, before `NodeRuntime.runMain`: the default logger of every layer
@@ -1,4 +1,5 @@
1
- import { assertDistinct, assertKnown, bindingErrors, projectedErrors } from "./contract/rules.mjs";
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";
@@ -16,7 +17,7 @@ import { Command } from "effect/cli";
16
17
  * `Effect.tapError(() => Console.error(…))`: a `UserError` wrapped around the result would
17
18
  * replace its JSON.
18
19
  */
19
- const toUserError = (calls) => "actions" in calls ? failingAsDerived(calls.actions, (action) => bindingErrors(action, calls)) : failingAsDerived(calls, (action) => projectedErrors(action));
20
+ const toUserError = (calls) => "actions" in calls ? failingAsDerived(calls.actions.flatMap((action) => bindingErrors(action, calls))) : failingAsDerived(calls.flatMap((action) => projectedErrors(action)));
20
21
  const runWithBuildOfItsOwn = (app, action, input) => {
21
22
  const thisActionOnly = Implementation.share([action], app);
22
23
  return Effect.flatMap(acquire([thisActionOnly]), (bound) => {
@@ -36,7 +37,7 @@ const inProcess = (apps, action, options) => {
36
37
  };
37
38
  const overHttp = (binding, action, options, client$1) => {
38
39
  if (!binding.actions.includes(action)) throw new Error(`Action "${action.name}" is not in this HTTP binding`);
39
- return command$1(action, (input) => Effect.flatMap(client(binding, client$1), (methods) => {
40
+ return command$1(action, (input) => Effect.flatMap(client$1 ?? client(binding), (methods) => {
40
41
  const method = methods[action.name];
41
42
  if (method === void 0) throw new Error(`No client method for ${action.name}`);
42
43
  return method(input);
@@ -68,17 +69,12 @@ const checkedCommandsOf = (commands, actions) => {
68
69
  * as JSON when it is not a struct. The flag of an array of strings or numbers is repeated for each value, a boolean
69
70
  * field is a switch, `--no-<name>` sending `false` as a required one left out does, and a field's `description` is its flag's help. It needs what the authorization, its handler and its builder
70
71
  * need; the host provides the identity. It prints the result on stdout; a failure is a
71
- * `UserError`, which `Command.run` prints on stderr. `remoteCommand` calls a server instead.
72
+ * `UserError`, or an Effect CLI error as it is, such as a builder's `CliError.UserError`, which
73
+ * `Command.run` prints on stderr. `remoteCommand` calls a server instead.
72
74
  */
73
75
  function command(implementations, action, options) {
74
76
  return inProcess(toList(implementations), action, options);
75
77
  }
76
- /**
77
- * Project one action of an HTTP binding into a native Effect CLI command, derived as `command`
78
- * derives it, that calls the action over HTTP through its `ActionHttp.client` method, made with
79
- * the `client` options on the host's `HttpClient`, and fails as the method does. It runs
80
- * nothing locally.
81
- */
82
78
  function remoteCommand(binding, action, options) {
83
79
  const { client, ...syntax } = options ?? {};
84
80
  return overHttp(binding, action, options === void 0 ? void 0 : syntax, client);
@@ -98,12 +94,6 @@ function make(implementations, options) {
98
94
  const served = select(apps, options.actions);
99
95
  return aggregateCommand(options.name, served.flatMap((app) => app.actions), commands, options.subcommands ?? [], (action, syntax) => inProcess(served, action, syntax));
100
96
  }
101
- /**
102
- * Project every action of an HTTP binding as a subcommand of one aggregate command, derived as
103
- * `make` derives it, each calling its action over HTTP. `client` configures every derived
104
- * subcommand's client; `commands` gives one the options `command` takes; `subcommands` adds
105
- * commands of your own.
106
- */
107
97
  function remote(binding, options) {
108
98
  const listed = options.actions;
109
99
  if (listed !== void 0) assertHeld("the binding does not hold it", listed, binding.actions);
@@ -1,15 +1,16 @@
1
- import { Anyone, Certain, ErrorsOf } from "./contract/rules.mjs";
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 { Action_d_exports, Any, Errors } from "./Action.mjs";
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
- import { Etag, HttpClient, HttpPlatform, HttpRouter } from "effect/http";
9
+ import { Etag, HttpClient, HttpClientError, HttpPlatform, HttpRouter } from "effect/http";
9
10
  import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware } from "effect/http-api";
10
11
  //#region src/http/ActionHttp.d.ts
11
12
  /** What `fetchClient` takes: `client`'s options, and the `fetch` its calls send with. */
12
- export interface FetchClientOptions extends Options {
13
+ export interface FetchClientOptions<E = HttpClientError.HttpClientError, R = never> extends Options<E, R> {
13
14
  /**
14
15
  * What each call sends with. Omitted: the global `fetch`, looked up on every call, so one
15
16
  * installed after the client is built, such as a test's stub, is used.
@@ -115,18 +116,21 @@ export declare function layer<const H extends AnyHttp, const Apps extends Served
115
116
  * `InvalidInput`, `Unauthenticated` or `Forbidden`), a native `HttpClientError` when the
116
117
  * server could not be reached or answered with a status or body the contract does not
117
118
  * declare, or a `SchemaError` when the success does not decode. An input that does not encode
118
- * fails with `InvalidInput`, and nothing is sent.
119
+ * fails with `InvalidInput`, and nothing is sent. A call also fails with what the client
120
+ * `transformClient` returns fails with, as it is, and requires what that client requires.
119
121
  */
120
122
  export declare function client<const H extends AnyHttp>(binding: H, options?: Options): Effect.Effect<Client<H>, never, HttpClient.HttpClient>;
123
+ export declare function client<const H extends AnyHttp, E, R>(binding: H, options: Options<E, R>): Effect.Effect<Client<H, E, R>, never, HttpClient.HttpClient>;
121
124
  /**
122
125
  * `client` built once over `fetch`, for code that holds a client outside an Effect, such as
123
- * a browser app: its methods are the same Effects and require nothing, so each call runs
124
- * alone, as `Effect.runPromise(api.greet({ name }))`. The options are `client`'s, and
125
- * `fetch`.
126
+ * a browser app: its methods are the same Effects, which require only what `transformClient`
127
+ * requires, so a call without one runs alone, as `Effect.runPromise(api.greet({ name }))`. The
128
+ * options are `client`'s, and `fetch`.
126
129
  *
127
130
  * Each call sends with `options.fetch`, or with the global `fetch` as it is when the call
128
131
  * runs.
129
132
  */
130
133
  export declare function fetchClient<const H extends AnyHttp>(binding: H, options?: FetchClientOptions): Client<H>;
134
+ export declare function fetchClient<const H extends AnyHttp, E, R>(binding: H, options: FetchClientOptions<E, R>): Client<H, E, R>;
131
135
  //#endregion
132
136
  export { type AnyHttp as Any, type Client, type Options as ClientOptions, type MethodError, Options$1 as Options };
@@ -1,7 +1,9 @@
1
- import { Anyone, bindingErrors, bindingOf, declared, projectedErrors } from "./contract/rules.mjs";
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";
@@ -1,5 +1,5 @@
1
1
  import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Protected, SelectedOf, Selection, Served, ServedRequest } from "./contract/implementation.mjs";
2
- import { Action_d_exports, Any } from "./Action.mjs";
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";
@@ -1,4 +1,7 @@
1
- import { Anyone, assertAuthentication, assertOnce, hintsOf, projectedErrors } from "./contract/rules.mjs";
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";
@@ -1,9 +1,10 @@
1
- import { Anyone, Certain, ErrorsOf } from "./contract/rules.mjs";
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 { Action_d_exports, Any as Any$1, Errors } from "./Action.mjs";
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 {};
@@ -1,4 +1,5 @@
1
- import { Anyone, bindingErrors, bindingOf } from "./contract/rules.mjs";
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";
@@ -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 { Action_d_exports, Any } from "./Action.mjs";
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 {};
@@ -1,4 +1,6 @@
1
- import { assertOnce, hintsOf, projectedErrors } from "./contract/rules.mjs";
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";
@@ -1,5 +1,5 @@
1
1
  import { Unauthenticated, scopeToken } from "./contract/errors.mjs";
2
- import { assertOwnTags, errorList } from "./contract/rules.mjs";
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/rules.mjs";
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 { Action_d_exports, Any as Any$1, Errors } from "../Action.mjs";
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/rules.mjs";
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
@@ -1,6 +1,6 @@
1
1
  import "../contract/errors.mjs";
2
- import { Action_d_exports, Any } from "../Action.mjs";
3
- import { Effect, Runtime, Types } from "effect";
2
+ import { Any } from "../Action.mjs";
3
+ import { Effect, Option, Runtime, Types } from "effect";
4
4
  import { CliError, Command } from "effect/cli";
5
5
  //#region src/cli/commands.d.ts
6
6
  type Field<A extends Any> = A["input"]["Encoded"] extends (infer E) ? true extends Types.IsUnion<E> ? never : E extends ReadonlyArray<unknown> ? never : E extends object ? string extends keyof E ? never : Extract<keyof E, string> : never : never;
@@ -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 {};
@@ -1,5 +1,6 @@
1
+ import { literalValues, members, unsuspended } from "../contract/ast.mjs";
1
2
  import { InvalidInput } from "../contract/errors.mjs";
2
- import { assertDistinct, literalValues, members, unsuspended } from "../contract/rules.mjs";
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";
@@ -205,8 +206,8 @@ const userErrorOf = (errors) => {
205
206
  userMessage
206
207
  }))));
207
208
  };
208
- const failingAsDerived = (actions, errorsOf) => {
209
- const failure = userErrorOf([...new Set(actions.flatMap(errorsOf))]);
209
+ const failingAsDerived = (errors) => {
210
+ const failure = userErrorOf([...new Set(errors)]);
210
211
  return (self) => Effect.catchIf(self, (error) => !CliError.isCliError(error), failure);
211
212
  };
212
213
  const unreadableStdinField = (field, message) => new InvalidInput({
@@ -273,7 +274,7 @@ const command = (action, execute, options, errors) => {
273
274
  const name = options?.name ?? kebab(action.name);
274
275
  const render = options?.render;
275
276
  const { flags, positional, decode, stdinHelp } = inputConfig(action, options?.positional ?? [], options?.aliases ?? {}, options?.stdin);
276
- const failure = userErrorOf(errors);
277
+ const failing = failingAsDerived(errors);
277
278
  assertDistinct("flag", [
278
279
  ...Object.keys(flags).map((field) => [`--${kebab(field)}`, `field ${field}`]),
279
280
  ...render === void 0 ? [] : [["--json", "render's --json"]],
@@ -284,7 +285,7 @@ const command = (action, execute, options, errors) => {
284
285
  ...input,
285
286
  ...Object.fromEntries(positional.map(({ field }, index) => [field, values[index] ?? Option.none()]))
286
287
  });
287
- const run = (parsed, rendered) => decode(parsed).pipe(Effect.flatMap(execute), Effect.catch(failure), Effect.flatMap((value) => printedOutput(action, value, rendered)), withStderrConsole, Effect.flatMap(Option.match({
288
+ const run = (parsed, rendered) => decode(parsed).pipe(Effect.flatMap(execute), failing, Effect.flatMap((value) => printedOutput(action, value, rendered)), withStderrConsole, Effect.flatMap(Option.match({
288
289
  onNone: () => Effect.void,
289
290
  onSome: Console.log
290
291
  })));
@@ -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 };
@@ -1,6 +1,6 @@
1
1
  import "./errors.mjs";
2
2
  import "./implementation.mjs";
3
- import { Action_d_exports, Any } from "../Action.mjs";
3
+ import { Any } from "../Action.mjs";
4
4
  import { Effect, Schema } from "effect";
5
5
  //#region src/contract/call.d.ts
6
6
  /**
@@ -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 { Action_d_exports, Errors } from "../Action.mjs";
2
- import { SchemaAST, Types } from "effect";
3
- //#region src/contract/rules.d.ts
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
- * The caller of a public action, `caller: Action.Anyone`: anyone, signed in or not, and no
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 "./rules.mjs";
1
+ import { Anyone } from "./caller.mjs";
2
2
  import { Refusal } from "./errors.mjs";
3
- import { Action_d_exports, Any } from "../Action.mjs";
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 {};