@gjermundgaraba/effect-actions 0.9.0 → 0.10.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 (68) hide show
  1. package/README.md +57 -50
  2. package/dist/Action.d.mts +285 -79
  3. package/dist/Action.mjs +124 -23
  4. package/dist/ActionCli.d.mts +102 -31
  5. package/dist/ActionCli.mjs +114 -16
  6. package/dist/ActionHttp.d.mts +170 -51
  7. package/dist/ActionHttp.mjs +130 -79
  8. package/dist/ActionMcp.d.mts +71 -21
  9. package/dist/ActionMcp.mjs +108 -81
  10. package/dist/ActionToolkit.d.mts +56 -27
  11. package/dist/ActionToolkit.mjs +16 -5
  12. package/dist/Authentication.d.mts +129 -33
  13. package/dist/Authentication.mjs +231 -38
  14. package/dist/Testing.d.mts +82 -57
  15. package/dist/Testing.mjs +89 -79
  16. package/dist/internal/actions.d.mts +5 -25
  17. package/dist/internal/actions.mjs +75 -27
  18. package/dist/internal/authentication.d.mts +102 -0
  19. package/dist/internal/authentication.mjs +11 -0
  20. package/dist/internal/call.d.mts +12 -0
  21. package/dist/internal/call.mjs +12 -0
  22. package/dist/internal/cli.d.mts +36 -22
  23. package/dist/internal/cli.mjs +311 -33
  24. package/dist/internal/client.d.mts +43 -0
  25. package/dist/internal/client.mjs +25 -0
  26. package/dist/internal/console.mjs +77 -0
  27. package/dist/internal/declared.d.mts +13 -0
  28. package/dist/internal/declared.mjs +34 -0
  29. package/dist/internal/errors.d.mts +77 -0
  30. package/dist/internal/errors.mjs +79 -0
  31. package/dist/internal/implementation.d.mts +171 -41
  32. package/dist/internal/implementation.mjs +147 -29
  33. package/dist/internal/mcp.d.mts +11 -0
  34. package/dist/internal/mcp.mjs +52 -0
  35. package/dist/internal/memory.d.mts +6 -0
  36. package/dist/internal/memory.mjs +38 -0
  37. package/dist/internal/refusal.mjs +67 -0
  38. package/dist/internal/schema-errors.mjs +20 -0
  39. package/dist/internal/tools.mjs +27 -57
  40. package/docs/Action.md +401 -73
  41. package/docs/ActionCli.md +280 -51
  42. package/docs/ActionHttp.md +266 -82
  43. package/docs/ActionMcp.md +149 -104
  44. package/docs/ActionToolkit.md +97 -35
  45. package/docs/Authentication.md +369 -62
  46. package/docs/CONTEXT.md +23 -36
  47. package/docs/README.md +29 -73
  48. package/docs/Testing.md +154 -77
  49. package/docs/guarantees.md +81 -43
  50. package/docs/setup.md +90 -27
  51. package/package.json +7 -15
  52. package/dist/ActionCatalog.d.mts +0 -30
  53. package/dist/ActionCatalog.mjs +0 -39
  54. package/dist/ActionCliClient.d.mts +0 -48
  55. package/dist/ActionCliClient.mjs +0 -43
  56. package/dist/ActionGroup.d.mts +0 -44
  57. package/dist/ActionGroup.mjs +0 -35
  58. package/dist/ActionHttpClient.d.mts +0 -33
  59. package/dist/ActionHttpClient.mjs +0 -16
  60. package/dist/TestingClient.d.mts +0 -17
  61. package/dist/TestingClient.mjs +0 -22
  62. package/dist/internal/fetchClient.d.mts +0 -6
  63. package/dist/internal/fetchClient.mjs +0 -8
  64. package/dist/internal/tools.d.mts +0 -22
  65. package/docs/ActionCatalog.md +0 -54
  66. package/docs/ActionCliClient.md +0 -63
  67. package/docs/ActionGroup.md +0 -128
  68. package/docs/ActionHttpClient.md +0 -80
@@ -1,29 +1,9 @@
1
- import { Action_d_exports, Any, Codec } from "../Action.mjs";
2
- import { HttpApiError } from "effect/http-api";
1
+ import "../Action.mjs";
2
+ import { SchemaAST } from "effect";
3
3
  //#region src/internal/actions.d.ts
4
- /** One side of a schema-error policy: the error it answers with, and how to make it. */
5
- export interface SchemaErrorAnswer<E extends Codec> {
6
- /** A declared error schema; its `httpApiStatus` is the HTTP status of the answer. */
7
- readonly schema: E;
8
- readonly make: (failure: HttpApiError.HttpApiSchemaError) => NoInfer<E["Type"]>;
9
- }
10
4
  /**
11
- * Pure HTTP policy for native schema failures, split by whose fault they are. The
12
- * library owns the split, so every group draws it the same way.
5
+ * The caller of a public action: anyone, signed in or not. Registered globally, so two copies
6
+ * of the package agree on it, as they agree on a name.
13
7
  */
14
- export interface SchemaErrorPolicy<Invalid extends Codec = Codec, Internal extends Codec = Codec> {
15
- /** The request did not decode: its payload, or its params, headers or query. */
16
- readonly invalid: SchemaErrorAnswer<Invalid>;
17
- /** The handler's result did not encode: its body or its response headers. */
18
- readonly internal: SchemaErrorAnswer<Internal>;
19
- }
20
- /** The contract half of a group: what adapters and clients need, without `implement`. */
21
- export interface Actions<Name extends string = string, A extends ReadonlyArray<Any> = ReadonlyArray<Any>, PolicyError extends Codec = Codec> {
22
- readonly name: Name;
23
- readonly actions: A;
24
- /** How HTTP answers failed decoding or encoding; native behavior without one. MCP is unaffected. */
25
- readonly schemaError: SchemaErrorPolicy<PolicyError, PolicyError> | undefined;
26
- }
27
- /** The errors a group's policy may answer with, by lookup rather than a conditional type. */
28
- export type PolicyError<G extends Actions> = NonNullable<G["schemaError"]>[keyof SchemaErrorPolicy]["schema"];
8
+ export declare const Anyone: unique symbol;
29
9
  //#endregion
@@ -1,41 +1,89 @@
1
+ import { builtIns, statuses } from "./errors.mjs";
2
+ import { Predicate, Schema, SchemaAST } from "effect";
1
3
  //#region src/internal/actions.ts
2
- /** Each distinct error a policy may answer with, invalid first. */
3
- const policyErrors = (policy) => [.../* @__PURE__ */ new Set([policy.invalid.schema, policy.internal.schema])];
4
4
  /**
5
- * `HttpApiBuilder` reports these kinds while encoding the handler's answer, after the
6
- * handler ran; every other kind (`Payload`, `Params`, `Headers`, `Query`) comes from
7
- * decoding the request before it.
5
+ * The list an `error` option stands for: one schema, a list as given, or none, as
6
+ * `HttpApiEndpoint` takes it.
8
7
  */
9
- const responseKinds = /* @__PURE__ */ new Set(["Body", "ResponseHeaders"]);
10
- /** The policy's answer to one native failure: `internal` for the response side, else `invalid`. */
11
- const answerSchemaError = (policy, failure) => responseKinds.has(failure.kind) ? policy.internal.make(failure) : policy.invalid.make(failure);
8
+ const errorList = (error) => error === void 0 ? [] : Schema.isSchema(error) ? [error] : error;
12
9
  /**
13
- * An action's own failures plus the ones its surface answers with, which is what
14
- * a projection of that action declares. A schema the action already declares is
15
- * not repeated.
10
+ * What a projection of an action declares: the built-in errors, the action's own, and any
11
+ * its surface adds, such as a binding's. The built-ins come first, since a failure encodes
12
+ * and decodes with the first schema that accepts it, and they are classes, which accept
13
+ * only their own instances: a loose schema of the action's never captures one. A schema
14
+ * listed twice is not repeated.
16
15
  */
17
- const projectedErrors = (action, surface) => [.../* @__PURE__ */ new Set([...action.errors, ...surface ?? []])];
18
- const validName = /^[A-Za-z0-9_-]+$/;
16
+ const projectedErrors = (action, surface = []) => [.../* @__PURE__ */ new Set([
17
+ ...builtIns,
18
+ ...action.error,
19
+ ...surface
20
+ ])];
21
+ /**
22
+ * The caller of a public action: anyone, signed in or not. Registered globally, so two copies
23
+ * of the package agree on it, as they agree on a name.
24
+ */
25
+ const Anyone = Symbol.for("@gjermundgaraba/effect-actions/Anyone");
26
+ const validName = /^[A-Za-z0-9_-]{1,128}$/;
19
27
  /** Names become path segments, OpenAPI identifiers and client method keys. */
20
28
  const assertName = (what, name) => {
21
29
  if (!validName.test(name) || name === "then") throw new Error(`Invalid ${what}: ${name}`);
22
30
  };
23
31
  /**
24
- * The one item of `items` named `name`, narrowed to that contract. A caller passes
25
- * a literal name, so a miss is a programming error reported as `Unknown <what>`.
32
+ * Names are checked by whoever owns them: an implementation, a binding, a CLI, or the MCP
33
+ * tools. `claimantOf` says who claims a name, so a clash between two claimants names both.
26
34
  */
27
- const selectNamed = (items, name, what) => {
28
- const item = items.find((candidate) => candidate.name === name);
29
- if (item === void 0) throw new Error(`Unknown ${what}`);
30
- return item;
31
- };
32
- /** Each namespace is checked by whoever owns it: a group, the routes, or the MCP tools. */
33
- const assertDistinct = (what, names) => {
34
- const seen = /* @__PURE__ */ new Set();
35
- for (const name of names) {
36
- if (seen.has(name)) throw new Error(`Duplicate ${what}: ${name}`);
37
- seen.add(name);
35
+ const assertDistinct = (what, items, nameOf, claimantOf = nameOf) => {
36
+ const seen = /* @__PURE__ */ new Map();
37
+ for (const item of items) {
38
+ const name = nameOf(item);
39
+ const claimant = claimantOf(item);
40
+ const other = seen.get(name);
41
+ if (other !== void 0) {
42
+ const by = other === claimant ? "" : `, claimed by ${other} and ${claimant}`;
43
+ throw new Error(`Duplicate ${what}: ${name}${by}`);
44
+ }
45
+ seen.set(name, claimant);
38
46
  }
39
47
  };
48
+ /** Refuse an action name `actions` hold twice. */
49
+ const assertOnce = (what, actions) => assertDistinct(what, actions, (action) => action.name);
50
+ /**
51
+ * Refuse a key of options keyed by action name, such as handlers, tools or commands, that no
52
+ * action of `names` has, so a stale option cannot outlive its action.
53
+ */
54
+ const assertKnown = (what, keys, names) => {
55
+ const unknown = keys.filter((key) => !names.includes(key));
56
+ if (unknown.length > 0) throw new Error(`Unknown ${what}: ${unknown.join(", ")}`);
57
+ };
58
+ /** `ast` with any suspension at its top resolved, as a recursive schema's is. */
59
+ const unsuspended = (ast) => SchemaAST.isSuspend(ast) ? unsuspended(ast.thunk()) : ast;
60
+ /** The members of a union, nested and suspended ones included, or the one type otherwise. */
61
+ const members = (ast) => {
62
+ const resolved = unsuspended(ast);
63
+ return SchemaAST.isUnion(resolved) ? resolved.types.flatMap(members) : [resolved];
64
+ };
65
+ /**
66
+ * The values a literal or an enum accepts, or `undefined` for any other type, which has no
67
+ * fixed set.
68
+ */
69
+ const literalValues = (ast) => SchemaAST.isLiteral(ast) ? [ast.literal] : SchemaAST.isEnum(ast) ? ast.enums.map(([, value]) => value) : [void 0];
70
+ /**
71
+ * The `_tag`s a schema's encoding carries: each member's of a union, every value of a union of
72
+ * literals or of an enum, and a suspended schema's, as a recursive error is written.
73
+ */
74
+ const tagsOf = (ast) => members(SchemaAST.toEncoded(ast)).flatMap((member) => {
75
+ const tag = SchemaAST.isObjects(member) ? member.propertySignatures.find((property) => property.name === "_tag")?.type : void 0;
76
+ return tag === void 0 ? [] : members(tag).flatMap(literalValues).filter(Predicate.isString);
77
+ });
78
+ /**
79
+ * Refuse an error that encodes with a built-in error's `_tag`, the built-in itself included:
80
+ * every endpoint and tool declares the built-in errors already, a client decoding the answer
81
+ * could not tell a look-alike from them, and a look-alike of a refusal would step up. Checked
82
+ * where the action or the binding is made, so every surface and client of it may rely on it.
83
+ */
84
+ const assertOwnTags = (what, errors) => {
85
+ const tag = errors.flatMap((error) => tagsOf(error.ast)).find((tag) => Object.hasOwn(statuses, tag));
86
+ if (tag !== void 0) throw new Error(`${what}: error _tag "${tag}" is built in, and declared on every surface`);
87
+ };
40
88
  //#endregion
41
- export { answerSchemaError, assertDistinct, assertName, policyErrors, projectedErrors, selectNamed };
89
+ export { Anyone, assertDistinct, assertKnown, assertName, assertOnce, assertOwnTags, errorList, literalValues, members, projectedErrors, unsuspended };
@@ -0,0 +1,102 @@
1
+ import { Anyone } from "./actions.mjs";
2
+ import { Refusal } from "./errors.mjs";
3
+ import { Protected, ServedRequest, Serving } from "./implementation.mjs";
4
+ import { Action_d_exports, Any as Any$1, Errors } from "../Action.mjs";
5
+ import { Context, Effect } from "effect";
6
+ import { HttpRouter, HttpServerResponse } from "effect/http";
7
+ import { HttpApiMiddleware, HttpApiSecurity } from "effect/http-api";
8
+ //#region src/internal/authentication.d.ts
9
+ /** One native security scheme: Bearer unless a descriptor names another. */
10
+ export type Security = HttpApiSecurity.HttpApiSecurity;
11
+ /** What a native scheme decodes: a `Redacted` token or key, or Basic credentials. */
12
+ export type Credential = HttpApiSecurity.HttpApiSecurity.Type<Security>;
13
+ /** The provider of a descriptor's verifier: a type of its own, apart from the actor. */
14
+ export interface Provider<I, Name extends string = string> {
15
+ readonly "~effect-actions/Authentication": I;
16
+ readonly "~effect-actions/Authentication/Name": Name;
17
+ }
18
+ /**
19
+ * The native security middleware a descriptor's protected endpoints carry, which provides
20
+ * the identity: an identifier of its own, which no `Authentication.layer` provides, so only
21
+ * `ActionHttp.layer` satisfies it, from the provider.
22
+ */
23
+ export interface SecurityMiddleware<I, Name extends string = string> {
24
+ readonly "~effect-actions/Authentication/Security": Name;
25
+ readonly "~effect/http-api/HttpApiMiddleware": {
26
+ readonly provides: I;
27
+ readonly requires: never;
28
+ readonly error: never;
29
+ readonly clientError: never;
30
+ readonly requiredForClient: false;
31
+ };
32
+ }
33
+ type Response = Effect.Effect<HttpServerResponse.HttpServerResponse, unknown, unknown>;
34
+ interface Runtime$1 {
35
+ /** The descriptor the provider was built from, the one a binding naming it must hold. */
36
+ readonly descriptor: unknown;
37
+ /** The native security middleware's implementation, keyed by its scheme. */
38
+ readonly middleware: Readonly<Record<string, (route: Response, options: {
39
+ readonly credential: Credential;
40
+ }) => Response>>;
41
+ /**
42
+ * A protected HTTP route as it leaves the layer's middleware, a step-up refusal it fails with
43
+ * answered with its challenge under Bearer; as it is under another scheme.
44
+ */
45
+ readonly stepUp: HttpApiMiddleware.HttpApiMiddleware<never, never, never>;
46
+ readonly http: <E, R>(route: Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>, optional: boolean) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R | HttpRouter.Provided>;
47
+ /**
48
+ * A route of the host's own: the request is decoded here, the route gets the identity
49
+ * itself, and a refusal it fails with is answered as an action route's.
50
+ */
51
+ readonly route: <E, R>(route: Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>) => Effect.Effect<HttpServerResponse.HttpServerResponse, Exclude<E, Refusal>, HttpRouter.Provided>;
52
+ }
53
+ /**
54
+ * What `Authentication.make` declares: its name, the identity it authenticates, its scheme,
55
+ * and what its verifier may fail with besides a refusal. The `~` keys are the library's own
56
+ * wiring, which no host composes.
57
+ */
58
+ export interface Descriptor<I, A, S extends Security, Name extends string = string, E extends Errors = readonly []> {
59
+ readonly name: Name;
60
+ readonly identity: Context.Key<I, A>;
61
+ readonly security: S;
62
+ /**
63
+ * What its verifier may fail with besides a refusal, as a list: every protected endpoint
64
+ * declares it, and every remote surface sends it with its status.
65
+ */
66
+ readonly error: E;
67
+ readonly "~provider": Context.Key<Provider<I, Name>, Runtime$1>;
68
+ readonly "~middleware": Context.Key<SecurityMiddleware<I, Name>, unknown>;
69
+ /** An MCP request's verified identity, which a protected tool's call promotes. */
70
+ readonly "~verified": Context.Key<never, unknown>;
71
+ }
72
+ export type Any = Descriptor<unknown, unknown, Security, string, Errors>;
73
+ export type Identity<A extends Any$1> = [A] extends [never] ? never : A["caller"] extends Context.Key<infer I, unknown> ? I : never;
74
+ /**
75
+ * The identity a descriptor for `A` must authenticate: `unknown` for an erased action, whose
76
+ * key is known only at run time, where `assertAuthentication` checks it.
77
+ */
78
+ type Covered<A extends Any$1> = Exclude<A["caller"], typeof Anyone> extends Context.Key<infer I, unknown> ? I : never;
79
+ export type Required<A extends Any$1> = [Protected<A>] extends [never] ? {
80
+ readonly authentication?: Any;
81
+ } : {
82
+ readonly authentication: Descriptor<Covered<Protected<A>>, unknown, Security, string, Errors>;
83
+ };
84
+ export type ProviderOf<D> = D extends Descriptor<infer I, unknown, Security, infer Name, Errors> ? Provider<I, Name> : never;
85
+ /** All protected actions on a surface use the descriptor's single identity key. */
86
+ export type Matching<A extends Any$1, D> = unknown extends Covered<Protected<A>> ? unknown : [Exclude<Covered<Protected<A>>, D extends Descriptor<infer I, unknown, Security, string, Errors> ? I : never>] extends [never] ? unknown : {
87
+ readonly "Authentication descriptor does not cover every protected action": never;
88
+ };
89
+ /** Keep public requirements separate, without rescanning all handlers for every action. */
90
+ export type RemoteRequest<App, A extends Any$1> = ServedRequest<App, Extract<A, {
91
+ readonly caller: typeof Anyone;
92
+ }>> | Exclude<ServedRequest<App, Protected<A>>, Identity<Protected<A>>>;
93
+ /**
94
+ * What a call of `A` under the descriptor `D` may fail with from its verifier: the errors `D`
95
+ * declares, for a protected action; none for a public one, which nothing verifies.
96
+ */
97
+ export type VerifierError<A extends Any$1, D> = A["caller"] extends typeof Anyone ? never : D extends {
98
+ readonly error: infer E extends Errors;
99
+ } ? E[number] : never;
100
+ export type ServedProvider<App, A extends Any$1, D> = [Protected<Serving<App, A>>] extends [never] ? never : ProviderOf<D>;
101
+ //#endregion
102
+ export { Runtime$1 as Runtime };
@@ -0,0 +1,11 @@
1
+ import { Unauthenticated } from "./errors.mjs";
2
+ import { Anyone } from "./actions.mjs";
3
+ import { Effect, Option } from "effect";
4
+ //#region src/internal/authentication.ts
5
+ const assertAuthentication = (actions, auth) => {
6
+ for (const action of actions) if (action.caller !== Anyone && (auth === void 0 || action.caller.key !== auth.identity.key)) throw new Error(`Protected action '${action.name}' requires its matching authentication descriptor`);
7
+ };
8
+ /** Remote promotion happens at tool entry, before shared dispatch, never in local calls. */
9
+ const promote = (auth, run) => Effect.flatMap(Effect.serviceOption(auth["~verified"]), (actor) => Option.isSome(actor) ? Effect.provideService(run, auth.identity, actor.value) : Effect.fail(new Unauthenticated()));
10
+ //#endregion
11
+ export { assertAuthentication, promote };
@@ -0,0 +1,12 @@
1
+ import "./implementation.mjs";
2
+ import { Action_d_exports, Any } from "../Action.mjs";
3
+ import { Effect, Schema } from "effect";
4
+ //#region src/internal/call.d.ts
5
+ /**
6
+ * A call of `A` answering `R`. It may leave its input out when `{}` is a valid encoded input,
7
+ * such as for an action declared without `input` or one whose every field has a decoding
8
+ * default, and then sends the input `{}` decodes to. Every client shares this rule:
9
+ * `ActionHttp.client`, `Testing.mcpClient` and `Action.client`.
10
+ */
11
+ export type Call<A extends Any, R> = {} extends A["input"]["Encoded"] ? (...input: [] | [input: A["input"]["Type"]]) => R : (input: A["input"]["Type"]) => R;
12
+ //#endregion
@@ -0,0 +1,12 @@
1
+ import { Effect, Schema } from "effect";
2
+ //#region src/internal/call.ts
3
+ /**
4
+ * The input a method of `action` sends for the arguments it was called with: a given
5
+ * argument as given, and none as the input `{}` decodes to, as a server decodes the `{}` it
6
+ * receives. `Call` allows none only when `{}` is a valid encoded input, which is what this
7
+ * decodes; decoding makes it one of the input's own values, such as an instance of an input
8
+ * class, or fills its defaults.
9
+ */
10
+ const inputOf = (action, args) => args.length === 0 ? Schema.decodeUnknownEffect(Schema.toCodecJson(action.input))({}) : Effect.succeed(args[0]);
11
+ //#endregion
12
+ export { inputOf };
@@ -1,29 +1,43 @@
1
- import "../Action.mjs";
2
- import { Effect, Schema } from "effect";
3
- import { Command } from "effect/cli";
1
+ import "./errors.mjs";
2
+ import { Action_d_exports, Any } from "../Action.mjs";
3
+ import { Effect, Runtime, Types } from "effect";
4
+ import { CliError, Command } from "effect/cli";
4
5
  //#region src/internal/cli.d.ts
5
- /** Options shared by the local and HTTP command projections. */
6
- interface CommonOptions<Output> {
7
- /** Override the command name. The action name is used by default. */
6
+ /**
7
+ * The fields of `A`'s input that may be positional: the named top-level fields of a struct
8
+ * or class input, as encoded. None for any other input, a record or a union included.
9
+ */
10
+ export 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;
11
+ /** How one command is named, takes its input and prints its result. */
12
+ export interface Options<A extends Any> {
13
+ /** Override the command name. The action name, in kebab case, is used by default. */
8
14
  readonly name?: string;
9
15
  /** Human output. Adds a `--json` flag to the command that selects JSON instead. */
10
- readonly render?: (output: Output) => string;
11
- }
12
- /** The default projection accepts a whole canonical JSON input with `--input`. */
13
- export interface JsonOptions<Output> extends CommonOptions<Output> {
14
- readonly parameters?: never;
15
- readonly input?: never;
16
- }
17
- /** Supply native CLI parameters and map their parsed values to canonical action JSON. */
18
- export interface ParametersOptions<Output, Parameters extends Command.Command.Config> extends CommonOptions<Output> {
19
- /** Native Effect CLI flags and arguments. */
20
- readonly parameters: Parameters;
21
- /** Maps native parsed parameters to the action's canonical JSON input. */
22
- readonly input: (parsed: Command.Command.Config.InferValue<Parameters>) => Schema.Json;
16
+ readonly render?: (output: A["success"]["Type"]) => string;
17
+ /**
18
+ * Input fields taken as positional arguments instead of flags, in this order. An
19
+ * optional field is an optional argument, so it follows every required one.
20
+ */
21
+ readonly positional?: ReadonlyArray<Field<A>>;
22
+ /**
23
+ * A short name per input field's flag, such as `{ limit: "n" }` for `-n`, given as
24
+ * `Param.withAlias` takes it.
25
+ */
26
+ readonly aliases?: { readonly [K in Field<A>]?: string; };
23
27
  }
24
28
  /**
25
- * A command either accepts whole JSON input or has explicit native CLI parameters.
26
- * The latter intentionally has no implicit `--input` mode.
29
+ * What a command fails with when its action fails: Effect CLI's own error for a handler's
30
+ * failure, which `Command.run` prints on stderr and marks reported, so `runMain` does not
31
+ * print it again. Its `cause` is the action's failure. The process exits with the cause's
32
+ * `Runtime.errorExitCode`, 1 by default.
27
33
  */
28
- export type Options<Output, Parameters extends Command.Command.Config = never> = JsonOptions<Output> | ParametersOptions<Output, Parameters>;
34
+ export declare class UserError<out E = unknown> extends CliError.UserError {
35
+ readonly cause: E;
36
+ /**
37
+ * The action's failure, as Effect names an error's nested one, so the native
38
+ * `Effect.catchReason("UserError", "UserNotFound", f)` and `catchReasons` match it by tag.
39
+ */
40
+ get reason(): E;
41
+ get [Runtime.errorExitCode](): number;
42
+ }
29
43
  //#endregion