@gjermundgaraba/effect-actions 0.18.2 → 0.20.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 (155) hide show
  1. package/README.md +20 -47
  2. package/dist/Action.d.mts +96 -49
  3. package/dist/Action.mjs +12 -16
  4. package/dist/ActionCli.d.mts +82 -44
  5. package/dist/ActionCli.mjs +54 -35
  6. package/dist/ActionHttp.d.mts +84 -45
  7. package/dist/ActionHttp.mjs +3 -4
  8. package/dist/ActionMcp.d.mts +54 -29
  9. package/dist/ActionMcp.mjs +33 -15
  10. package/dist/ActionRpc.d.mts +48 -37
  11. package/dist/ActionRpc.mjs +3 -4
  12. package/dist/ActionToolkit.d.mts +32 -15
  13. package/dist/ActionToolkit.mjs +11 -9
  14. package/dist/Authentication.d.mts +92 -42
  15. package/dist/Authentication.mjs +38 -37
  16. package/dist/Testing.d.mts +23 -27
  17. package/dist/Testing.mjs +1 -2
  18. package/dist/authentication/provider.d.mts +1 -6
  19. package/dist/authentication/provider.mjs +2 -3
  20. package/dist/authentication/refusal.mjs +3 -3
  21. package/dist/cli/commands.d.mts +34 -13
  22. package/dist/cli/commands.mjs +48 -24
  23. package/dist/contract/ast.mjs +1 -2
  24. package/dist/contract/call.d.mts +1 -3
  25. package/dist/contract/call.mjs +1 -2
  26. package/dist/contract/caller.d.mts +1 -3
  27. package/dist/contract/caller.mjs +2 -3
  28. package/dist/contract/declared.d.mts +0 -2
  29. package/dist/contract/declared.mjs +28 -16
  30. package/dist/contract/errors.d.mts +42 -19
  31. package/dist/contract/errors.mjs +43 -19
  32. package/dist/contract/hints.mjs +4 -4
  33. package/dist/contract/implementation.d.mts +7 -14
  34. package/dist/contract/implementation.mjs +31 -19
  35. package/dist/contract/media.d.mts +7 -5
  36. package/dist/contract/media.mjs +7 -4
  37. package/dist/contract/names.mjs +15 -14
  38. package/dist/contract/status.mjs +4 -12
  39. package/dist/http/client.d.mts +15 -6
  40. package/dist/http/client.mjs +2 -3
  41. package/dist/http/schema-errors.mjs +1 -2
  42. package/dist/mcp/client.d.mts +21 -10
  43. package/dist/mcp/client.mjs +8 -11
  44. package/dist/mcp/protocol.mjs +1 -2
  45. package/dist/stdio/console.mjs +1 -2
  46. package/dist/testing/memory.d.mts +1 -3
  47. package/dist/testing/memory.mjs +1 -2
  48. package/docs/Action-failures.md +6 -15
  49. package/docs/Action.md +159 -191
  50. package/docs/ActionCli-failures.md +12 -92
  51. package/docs/ActionCli.md +102 -156
  52. package/docs/ActionHttp-failures.md +14 -76
  53. package/docs/ActionHttp.md +150 -176
  54. package/docs/ActionMcp-failures.md +2 -22
  55. package/docs/ActionMcp.md +79 -170
  56. package/docs/ActionRpc-failures.md +3 -12
  57. package/docs/ActionRpc.md +183 -120
  58. package/docs/ActionToolkit-failures.md +3 -10
  59. package/docs/ActionToolkit.md +112 -109
  60. package/docs/Authentication-advanced.md +171 -108
  61. package/docs/Authentication-failures.md +11 -91
  62. package/docs/Authentication.md +114 -135
  63. package/docs/README.md +45 -30
  64. package/docs/Testing-failures.md +2 -6
  65. package/docs/Testing.md +126 -159
  66. package/docs/setup-failures.md +8 -32
  67. package/docs/setup.md +42 -38
  68. package/examples/README.md +6 -10
  69. package/examples/assistant/README.md +20 -38
  70. package/examples/assistant/src/agent.ts +2 -3
  71. package/examples/assistant/src/cli.ts +4 -9
  72. package/examples/assistant/src/commands.ts +6 -4
  73. package/examples/assistant/src/identity/authorize.ts +3 -3
  74. package/examples/assistant/src/tasks/Tasks.ts +1 -2
  75. package/examples/assistant/src/tasks/handlers.ts +3 -3
  76. package/examples/assistant/src/toolkit.ts +6 -8
  77. package/examples/assistant/test/commands.test.ts +16 -11
  78. package/examples/assistant/test/tasks/tasks.test.ts +0 -2
  79. package/examples/assistant/test/toolkit.test.ts +0 -1
  80. package/examples/kitchen-sink/README.md +30 -52
  81. package/examples/kitchen-sink/src/Actions.ts +2 -3
  82. package/examples/kitchen-sink/src/Api.ts +3 -4
  83. package/examples/kitchen-sink/src/admin/Admin.ts +1 -1
  84. package/examples/kitchen-sink/src/admin/authenticate.ts +3 -3
  85. package/examples/kitchen-sink/src/admin/authorize.ts +1 -2
  86. package/examples/kitchen-sink/src/agent.ts +1 -1
  87. package/examples/kitchen-sink/src/assistant/handlers.ts +1 -6
  88. package/examples/kitchen-sink/src/attachments/Attachments.ts +4 -8
  89. package/examples/kitchen-sink/src/attachments/download.ts +5 -8
  90. package/examples/kitchen-sink/src/attachments/handlers.ts +2 -3
  91. package/examples/kitchen-sink/src/browser.ts +3 -4
  92. package/examples/kitchen-sink/src/cli.ts +6 -7
  93. package/examples/kitchen-sink/src/config.ts +2 -0
  94. package/examples/kitchen-sink/src/identity/Identity.ts +7 -7
  95. package/examples/kitchen-sink/src/identity/authenticate.ts +3 -5
  96. package/examples/kitchen-sink/src/identity/authorize.ts +4 -3
  97. package/examples/kitchen-sink/src/identity/quota.ts +1 -1
  98. package/examples/kitchen-sink/src/issues/Issues.ts +2 -2
  99. package/examples/kitchen-sink/src/issues/assistant.ts +3 -3
  100. package/examples/kitchen-sink/src/issues/guide.ts +2 -3
  101. package/examples/kitchen-sink/src/ops.ts +3 -6
  102. package/examples/kitchen-sink/src/request-policy.ts +8 -4
  103. package/examples/kitchen-sink/src/routes.ts +4 -6
  104. package/examples/kitchen-sink/src/seed.ts +0 -1
  105. package/examples/kitchen-sink/src/server.ts +11 -12
  106. package/examples/kitchen-sink/src/stdio.ts +3 -2
  107. package/examples/kitchen-sink/src/workspaces/tenant.ts +3 -1
  108. package/examples/kitchen-sink/test/admin/admin.test.ts +0 -1
  109. package/examples/kitchen-sink/test/assistant/assistant.test.ts +1 -1
  110. package/examples/kitchen-sink/test/attachments/attachments.test.ts +1 -2
  111. package/examples/kitchen-sink/test/identity/identity.test.ts +0 -1
  112. package/examples/kitchen-sink/test/issues/issues.test.ts +0 -2
  113. package/examples/kitchen-sink/test/request-policy.test.ts +39 -3
  114. package/examples/kitchen-sink/test/routes.test.ts +3 -3
  115. package/examples/live-rpc/README.md +8 -49
  116. package/examples/live-rpc/src/browser.ts +8 -41
  117. package/examples/live-rpc/src/chat/Chat.ts +9 -3
  118. package/examples/live-rpc/src/chat/room.ts +1 -1
  119. package/examples/live-rpc/src/client.ts +2 -3
  120. package/examples/live-rpc/src/page.ts +34 -0
  121. package/examples/live-rpc/src/routes.ts +6 -7
  122. package/examples/live-rpc/src/server.ts +6 -4
  123. package/examples/live-rpc/test/browser.test.ts +6 -5
  124. package/examples/live-rpc/test/routes.test.ts +16 -15
  125. package/examples/live-rpc/test/server.test.ts +1 -1
  126. package/examples/mcp-tools/README.md +22 -45
  127. package/examples/mcp-tools/src/printers/Printers.ts +3 -7
  128. package/examples/mcp-tools/src/routes.ts +0 -7
  129. package/examples/mcp-tools/src/server.ts +1 -1
  130. package/examples/mcp-tools/test/printers/printers.test.ts +2 -3
  131. package/examples/mcp-tools/test/routes.test.ts +2 -3
  132. package/examples/notes-service/README.md +9 -43
  133. package/examples/notes-service/src/cli.ts +0 -3
  134. package/examples/notes-service/src/commands.ts +7 -17
  135. package/examples/notes-service/src/identity/authorize.ts +2 -5
  136. package/examples/notes-service/src/identity/verifier.ts +3 -4
  137. package/examples/notes-service/src/notes/Notes.ts +2 -6
  138. package/examples/notes-service/src/notes/store.ts +5 -1
  139. package/examples/notes-service/src/routes.ts +6 -8
  140. package/examples/notes-service/src/server.ts +4 -3
  141. package/examples/notes-service/src/stdio.ts +4 -3
  142. package/examples/notes-service/test/commands.test.ts +1 -1
  143. package/examples/notes-service/test/notes/as.ts +6 -0
  144. package/examples/notes-service/test/notes/idempotency.test.ts +1 -4
  145. package/examples/notes-service/test/notes/in-process.test.ts +50 -0
  146. package/examples/notes-service/test/notes/notes.test.ts +3 -42
  147. package/examples/notes-service/test/routes.test.ts +2 -2
  148. package/examples/quickstart/README.md +1 -1
  149. package/examples/quickstart/src/client.ts +1 -0
  150. package/examples/quickstart/src/server.ts +2 -4
  151. package/examples/quickstart/test/{greeting/greeting.test.ts → routes.test.ts} +3 -3
  152. package/package.json +12 -16
  153. package/docs/Action-in-process.md +0 -93
  154. package/docs/CONTEXT.md +0 -46
  155. package/docs/guarantees.md +0 -44
@@ -1,11 +1,12 @@
1
1
  import { literalValues, members, unsuspended } from "../contract/ast.mjs";
2
2
  import { InvalidInput } from "../contract/errors.mjs";
3
3
  import { assertDistinct } from "../contract/names.mjs";
4
+ import { errorSchemas } from "../contract/declared.mjs";
4
5
  import { decodeInput } from "../contract/call.mjs";
5
6
  import { withStderrConsole } from "../stdio/console.mjs";
6
7
  import { Console, Effect, Option, Predicate, Record, Runtime, Schema, SchemaAST, Stdio, Stream } from "effect";
7
8
  import { CliError, Command, Flag, Param } from "effect/cli";
8
- //#region src/cli/commands.ts
9
+ // src/cli/commands.ts
9
10
  const kebab = (name) => {
10
11
  const kebabbed = name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
11
12
  return kebabbed === "" ? name : kebabbed;
@@ -152,28 +153,30 @@ const fieldParams = (encoded, input, positional, aliases) => {
152
153
  });
153
154
  };
154
155
  const positionalOrder = (params, positional) => {
155
- assertDistinct("positional argument", positional, (field) => field);
156
+ assertDistinct("positional argument", positional, (field) => field, { fix: "list each field once" });
156
157
  const ordered = positional.map((field) => {
157
158
  const found = params.find((param) => param.field === field);
158
- if (found === void 0) throw new Error(`Not an input field: ${field}`);
159
+ if (found === void 0) throw new Error(`Not an input field: ${field} (positional names fields of the action's input)`);
159
160
  return found;
160
161
  });
161
162
  const firstOptional = ordered.findIndex((param) => param.optional);
162
163
  const misplaced = ordered.slice(firstOptional === -1 ? ordered.length : firstOptional).find((param) => !param.optional);
163
- if (misplaced !== void 0) throw new Error(`Required positional argument after an optional one: ${misplaced.field}`);
164
+ if (misplaced !== void 0) throw new Error(`Required positional argument after an optional one: ${misplaced.field} (list every required field first)`);
164
165
  const repeatedBeforeLast = ordered.slice(0, -1).find((param) => param.repeats);
165
- if (repeatedBeforeLast !== void 0) throw new Error(`Repeated positional argument before another one: ${repeatedBeforeLast.field}`);
166
+ if (repeatedBeforeLast !== void 0) throw new Error(`Repeated positional argument before another one: ${repeatedBeforeLast.field} (list an array field last)`);
166
167
  return ordered;
167
168
  };
168
169
  const wholeInputFlag = (encoded) => Flag.String("input").pipe(Flag.withSchema(jsonOfAcceptedKindOrText(encoded)), Flag.withDescription("Whole action input as JSON; omitted, {}"), Flag.optional);
169
170
  const printedOutput = (action, value, render) => Effect.map(Effect.orDie(Schema.encodeEffect(Schema.toCodecJson(action.success))(value)), (encoded) => render === void 0 && SchemaAST.isVoid(action.success.ast) ? Option.none() : Option.some(render === void 0 ? JSON.stringify(encoded, null, 2) : render(value)));
170
171
  /**
171
172
  * What a command fails with when its action fails: Effect CLI's `UserError`, typed with the
172
- * action's failure as its `cause` and `reason`, and whose message is its JSON, which `Command.run`
173
- * prints on stderr and marks reported, so `runMain` does not print it again. The process exits with
174
- * the cause's `Runtime.errorExitCode`, 1 by default. A type only: after `Command.run`, match the
175
- * reason by its tag, `Effect.catchReason("UserError", "UserNotFound", f)`. A command of your own
176
- * fails with one through `toUserError`; any other failure is reported as a crash, `ERROR (#n)`.
173
+ * action's failure as its `cause` and `reason`, and whose `userMessage` is its JSON, or for a
174
+ * failure no schema encodes, as `command` says. A type only.
175
+ *
176
+ * **Gotchas**
177
+ *
178
+ * - `Command.run` has printed it before a recovery runs, so a recovery decides what follows, such as the exit code, not the text: for text of your own, provide `CliOutput.layer(Object.assign({}, CliOutput.defaultFormatter(), { formatError }))`, or pass `renderErrors: false` to `Command.run` and print every failure, the parser's too.
179
+ * - A run also fails with Effect CLI's own errors, such as a flag that does not parse, and only a `UserError` has a `userMessage`: match `Effect.catchTag("UserError", …)` before reading one.
177
180
  */
178
181
  var UserError = class extends CliError.UserError {
179
182
  /**
@@ -207,7 +210,7 @@ const userErrorOf = (errors) => {
207
210
  }))));
208
211
  };
209
212
  const failingAsDerived = (errors) => {
210
- const failure = userErrorOf([...new Set(errors)]);
213
+ const failure = userErrorOf(errorSchemas(errors));
211
214
  return (self) => Effect.catchIf(self, (error) => !CliError.isCliError(error), failure);
212
215
  };
213
216
  const unreadableStdinField = (field, message) => new InvalidInput({
@@ -223,7 +226,7 @@ const fieldValueFromText = (field) => {
223
226
  };
224
227
  const stdinTextOf = (field) => Effect.gen(function* () {
225
228
  const stdio = yield* Effect.serviceOption(Stdio.Stdio);
226
- if (Option.isNone(stdio)) return yield* Effect.die(/* @__PURE__ */ new Error("Stdio is not provided"));
229
+ if (Option.isNone(stdio)) return yield* Effect.die(/* @__PURE__ */ new Error("Stdio is not provided (provide NodeServices.layer, or Stdio.layerTest in a test)"));
227
230
  if (yield* stdio.value.stdinIsTerminal) return yield* unreadableStdinField(field, `${field} is read from stdin, which is a terminal: pipe it in`);
228
231
  return (yield* stdio.value.stdin.pipe(Stream.decodeText(), Stream.mkString, Effect.mapError((error) => unreadableStdinField(field, `stdin cannot be read: ${error.message}`)))).replace(/[\r\n]+$/, "");
229
232
  });
@@ -235,8 +238,8 @@ const inputConfig = (action, positional, aliases, stdin) => {
235
238
  const params = fieldParams(encoded, unsuspended(action.input.ast), positional, aliases);
236
239
  const fromStdin = Option.map(Option.fromUndefinedOr(stdin), (field) => {
237
240
  const property = encoded.propertySignatures.find(({ name }) => name === field);
238
- if (property === void 0 || SchemaAST.isOptional(property.type)) throw new Error(`Not a required input field: ${field}`);
239
- if (positional.includes(field)) throw new Error(`Both positional and stdin: ${field}`);
241
+ if (property === void 0 || SchemaAST.isOptional(property.type)) throw new Error(`Not a required input field: ${field} (stdin names a required field of the action's input)`);
242
+ if (positional.includes(field)) throw new Error(`Both positional and stdin: ${field} (take it from one of them)`);
240
243
  const description = params.find((param) => param.field === field)?.description;
241
244
  return {
242
245
  field,
@@ -246,7 +249,7 @@ const inputConfig = (action, positional, aliases, stdin) => {
246
249
  });
247
250
  const flagged = new Set(params.map(({ field }) => field).filter((field) => !positional.includes(field) && field !== stdin));
248
251
  const stray = Object.keys(aliases).find((field) => !flagged.has(field));
249
- if (stray !== void 0) throw new Error(`Not a flag's input field: ${stray}`);
252
+ if (stray !== void 0) throw new Error(`Not a flag's input field: ${stray} (aliases name fields read from flags, neither positional nor stdin)`);
250
253
  return {
251
254
  flags: Object.fromEntries(params.flatMap(({ field, param }) => flagged.has(field) ? [[field, param]] : [])),
252
255
  positional: positionalOrder(params, positional),
@@ -260,9 +263,10 @@ const inputConfig = (action, positional, aliases, stdin) => {
260
263
  stdinHelp: Option.getOrUndefined(Option.map(fromStdin, ({ help }) => help))
261
264
  };
262
265
  }
263
- if (positional.length > 0) throw new Error(`Positional arguments need named input fields: ${positional.join(", ")}`);
264
- if (Object.keys(aliases).length > 0) throw new Error(`Aliases need named input fields: ${Object.keys(aliases).join(", ")}`);
265
- if (stdin !== void 0) throw new Error(`Stdin needs named input fields: ${stdin}`);
266
+ const asWhole = "(the input is not a struct, so it is one --input flag: drop the option)";
267
+ if (positional.length > 0) throw new Error(`Positional arguments need named input fields: ${positional.join(", ")} ${asWhole}`);
268
+ if (Object.keys(aliases).length > 0) throw new Error(`Aliases need named input fields: ${Object.keys(aliases).join(", ")} ${asWhole}`);
269
+ if (stdin !== void 0) throw new Error(`Stdin needs named input fields: ${stdin} ${asWhole}`);
266
270
  return {
267
271
  flags: { input: wholeInputFlag(encoded) },
268
272
  positional: [],
@@ -270,16 +274,37 @@ const inputConfig = (action, positional, aliases, stdin) => {
270
274
  stdinHelp: void 0
271
275
  };
272
276
  };
277
+ const aliasFlag = (alias) => alias.length === 1 ? `-${alias}` : `--${alias}`;
273
278
  const command = (action, execute, options, errors) => {
274
279
  const name = options?.name ?? kebab(action.name);
280
+ const description = options?.description ?? action.description;
275
281
  const render = options?.render;
276
282
  const { flags, positional, decode, stdinHelp } = inputConfig(action, options?.positional ?? [], options?.aliases ?? {}, options?.stdin);
277
283
  const failing = failingAsDerived(errors);
278
284
  assertDistinct("flag", [
279
- ...Object.keys(flags).map((field) => [`--${kebab(field)}`, `field ${field}`]),
280
- ...render === void 0 ? [] : [["--json", "render's --json"]],
281
- ...Object.entries(options?.aliases ?? {}).flatMap(([field, alias]) => Predicate.isString(alias) ? [[`--${alias.replace(/^-+/, "")}`, `alias of field ${field}`]] : [])
282
- ], ([flag]) => flag, ([, claimant]) => claimant);
285
+ ...Object.keys(flags).map((field) => [
286
+ kebab(field),
287
+ `--${kebab(field)}`,
288
+ `field ${field}`
289
+ ]),
290
+ ...render === void 0 ? [] : [[
291
+ "json",
292
+ "--json",
293
+ "render's --json"
294
+ ]],
295
+ ...Object.entries(options?.aliases ?? {}).flatMap(([field, alias]) => {
296
+ const bare = Predicate.isString(alias) ? alias.replace(/^-+/, "") : void 0;
297
+ return bare === void 0 ? [] : [[
298
+ bare,
299
+ aliasFlag(bare),
300
+ `alias of field ${field}`
301
+ ]];
302
+ })
303
+ ], ([key]) => key, {
304
+ labelOf: ([, label]) => label,
305
+ claimantOf: ([, , claimant]) => claimant,
306
+ fix: render === void 0 ? "rename the field or the alias" : "rename the field or the alias, or drop `render`"
307
+ });
283
308
  const args = positional.map(({ param }) => param);
284
309
  const parsedFields = (input, values) => ({
285
310
  ...input,
@@ -297,7 +322,6 @@ const command = (action, execute, options, errors) => {
297
322
  args,
298
323
  json: renderedCommandJsonFlag
299
324
  }, ({ input, args: values, json }) => run(parsedFields(input, values), json ? void 0 : render));
300
- return stdinHelp === void 0 ? Command.withDescription(command, action.description) : command.pipe(Command.withDescription(`${action.description}\n\n${stdinHelp}`), Command.withShortDescription(action.description));
325
+ return stdinHelp === void 0 ? Command.withDescription(command, description) : command.pipe(Command.withDescription(`${description}\n\n${stdinHelp}`), Command.withShortDescription(description));
301
326
  };
302
- //#endregion
303
327
  export { UserError, command, failingAsDerived, kebab };
@@ -1,10 +1,9 @@
1
1
  import { SchemaAST } from "effect";
2
- //#region src/contract/ast.ts
2
+ // src/contract/ast.ts
3
3
  const unsuspended = (ast) => SchemaAST.isSuspend(ast) ? unsuspended(ast.thunk()) : ast;
4
4
  const members = (ast) => {
5
5
  const resolved = unsuspended(ast);
6
6
  return SchemaAST.isUnion(resolved) ? resolved.types.flatMap(members) : [resolved];
7
7
  };
8
8
  const literalValues = (ast) => SchemaAST.isLiteral(ast) ? [ast.literal] : SchemaAST.isEnum(ast) ? ast.enums.map(([, value]) => value) : [void 0];
9
- //#endregion
10
9
  export { literalValues, members, unsuspended };
@@ -2,12 +2,10 @@ import "./errors.mjs";
2
2
  import "./implementation.mjs";
3
3
  import { Any } from "../Action.mjs";
4
4
  import { Effect, Schema } from "effect";
5
- //#region src/contract/call.d.ts
6
5
  /**
7
6
  * A call of `A` answering `R`. It may leave its input out when `{}` is a valid encoded input,
8
7
  * such as for an action declared without `input` or one whose every field has a decoding
9
8
  * default, and then sends the input `{}` decodes to. Every client shares this rule:
10
9
  * `ActionHttp.client`, `ActionRpc.client`, `ActionMcp.client` and `Action.client`.
11
10
  */
12
- export type Call<A extends Any, R> = {} extends A["input"]["Encoded"] ? (...input: [] | [input: A["input"]["Type"]]) => R : (input: A["input"]["Type"]) => R;
13
- //#endregion
11
+ export type Call<A extends Any, R> = {} extends A["input"]["Encoded"] ? (...input: [] | [input: A["input"]["Type"]]) => R : (input: A["input"]["Type"]) => R;
@@ -1,6 +1,6 @@
1
1
  import { InvalidInput } from "./errors.mjs";
2
2
  import { Effect, Schema } from "effect";
3
- //#region src/contract/call.ts
3
+ // src/contract/call.ts
4
4
  /**
5
5
  * How HTTP, RPC, MCP and a command decode a request's input: reporting every issue, and refusing a
6
6
  * field the input does not declare.
@@ -19,5 +19,4 @@ const checked = (action, send) => {
19
19
  const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(action.input), { errors: "all" });
20
20
  return (...args) => inputOf(action, args).pipe(Effect.tap(encode), Effect.mapError(InvalidInput.fromSchemaError), Effect.flatMap(send));
21
21
  };
22
- //#endregion
23
22
  export { checked, decodeInput, inputOf, inputParseOptions };
@@ -1,9 +1,7 @@
1
1
  import "../Action.mjs";
2
- //#region src/contract/caller.d.ts
3
2
  /**
4
3
  * The caller of a public action, `caller: Action.Anyone`: anyone, signed in or not, and no
5
4
  * authorizer runs for it. Registered globally, so two copies of the package agree on it, as they
6
5
  * agree on a name.
7
6
  */
8
- export declare const Anyone: unique symbol;
9
- //#endregion
7
+ export declare const Anyone: unique symbol;
@@ -1,4 +1,4 @@
1
- //#region src/contract/caller.ts
1
+ // src/contract/caller.ts
2
2
  /**
3
3
  * The caller of a public action, `caller: Action.Anyone`: anyone, signed in or not, and no
4
4
  * authorizer runs for it. Registered globally, so two copies of the package agree on it, as they
@@ -6,7 +6,6 @@
6
6
  */
7
7
  const Anyone = Symbol.for("@gjermundgaraba/effect-actions/Anyone");
8
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`);
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 (give it the authentication whose identity the action's caller names)`);
10
10
  };
11
- //#endregion
12
11
  export { Anyone, assertAuthentication };
@@ -1,7 +1,6 @@
1
1
  import "./caller.mjs";
2
2
  import { Errors } from "../Action.mjs";
3
3
  import { Types } from "effect";
4
- //#region src/contract/declared.d.ts
5
4
  type ListOf<G> = G extends Errors ? G : readonly [G];
6
5
  /**
7
6
  * The errors options `O` declare, as at run time: those `error` always gives, or, where it may
@@ -24,5 +23,4 @@ export interface Declaring {
24
23
  readonly error: Errors;
25
24
  } | undefined;
26
25
  }
27
- //#endregion
28
26
  export {};
@@ -2,14 +2,23 @@ import { literalValues, members } from "./ast.mjs";
2
2
  import { Anyone, assertAuthentication } from "./caller.mjs";
3
3
  import { builtIns } from "./errors.mjs";
4
4
  import { assertOnce } from "./names.mjs";
5
+ import { httpApiStatusOf } from "./status.mjs";
5
6
  import { Predicate, Schema, SchemaAST } from "effect";
6
- //#region src/contract/declared.ts
7
+ // src/contract/declared.ts
7
8
  const errorList = (error) => error === void 0 ? [] : Schema.isSchema(error) ? [error] : error;
8
- const projectedErrors = (action, surface = []) => [.../* @__PURE__ */ new Set([
9
+ const ownsNothing = (ast) => httpApiStatusOf(ast) === void 0 && ast.checks === void 0 && ast.encoding === void 0;
10
+ const schemasOf = (ast) => !ownsNothing(ast) ? [ast] : SchemaAST.isSuspend(ast) ? schemasOf(ast.thunk()) : SchemaAST.isUnion(ast) ? ast.types.flatMap(schemasOf) : [ast];
11
+ /** `errors` as the schemas a call decodes a failure with, in order, each once. */
12
+ const errorSchemas = (errors) => {
13
+ const schemas = /* @__PURE__ */ new Map();
14
+ for (const error of errors) for (const ast of schemasOf(error.ast)) if (!schemas.has(ast)) schemas.set(ast, ast === error.ast ? error : Schema.make(ast));
15
+ return [...schemas.values()];
16
+ };
17
+ const projectedErrors = (action, surface = []) => errorSchemas([
9
18
  ...builtIns,
10
19
  ...action.error,
11
20
  ...surface
12
- ])];
21
+ ]);
13
22
  /**
14
23
  * Every error a binding's call of `action` declares: the built-in ones, the action's, the
15
24
  * binding's and, where `action` is protected, its authentication's.
@@ -19,24 +28,27 @@ const encodedTags = (ast) => members(SchemaAST.toEncoded(ast)).flatMap((member)
19
28
  const tag = SchemaAST.isObjects(member) ? member.propertySignatures.find((property) => property.name === "_tag")?.type : void 0;
20
29
  return tag === void 0 ? [] : members(tag).flatMap(literalValues).filter(Predicate.isString);
21
30
  });
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`);
31
+ const builtInTags = new Set(builtIns.flatMap((schema) => encodedTags(schema.ast)));
32
+ /** Throws where two of the schemas in `errors` encode with one `_tag`. */
33
+ const assertDistinctTags = (what, errors) => {
34
+ const seen = /* @__PURE__ */ new Set();
35
+ for (const schema of errorSchemas(errors)) for (const tag of new Set(encodedTags(schema.ast))) {
36
+ if (seen.has(tag)) throw new Error(`${what}: error _tag "${tag}" names two error schemas, which a client cannot tell apart (reuse one schema value, or merge them into one whose fields tell them apart${builtInTags.has(tag) ? "; for a built-in's tag, fail with the built-in itself" : ""})`);
37
+ seen.add(tag);
38
+ }
26
39
  };
27
40
  /**
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.
41
+ * The checks `ActionHttp.make` and `ActionRpc.make` share: each action once, the descriptor every
42
+ * protected action names, and each call's errors distinct by `_tag`. Returns the binding's `error`, as a list, and `authentication`.
30
43
  */
31
44
  const bindingOf = (what, actions, options) => {
32
45
  assertOnce("action", actions);
33
- const error = errorList(options.error);
34
- assertOwnTags(what, error);
35
- assertAuthentication(actions, options.authentication);
36
- return {
37
- error,
46
+ const bound = {
47
+ error: errorList(options.error),
38
48
  authentication: options.authentication
39
49
  };
50
+ assertAuthentication(actions, bound.authentication);
51
+ for (const action of actions) assertDistinctTags(`${what} of "${action.name}"`, bindingErrors(action, bound));
52
+ return bound;
40
53
  };
41
- //#endregion
42
- export { assertOwnTags, bindingErrors, bindingOf, errorList, projectedErrors };
54
+ export { assertDistinctTags, bindingErrors, bindingOf, errorList, errorSchemas, projectedErrors };
@@ -1,5 +1,4 @@
1
1
  import { Schema } from "effect";
2
- //#region src/contract/errors.d.ts
3
2
  declare const Issue: Schema.Struct<{
4
3
  readonly path: Schema.$Array<Schema.Union<readonly [Schema.String, Schema.Finite]>>;
5
4
  readonly message: Schema.String;
@@ -18,27 +17,45 @@ declare const InvalidInput_base: Schema.Class<InvalidInput, Schema.TaggedStruct<
18
17
  }>>>;
19
18
  }>, import("effect/Cause").YieldableError>;
20
19
  /**
21
- * The request's input does not decode against the action's input. HTTP answers every such
22
- * request with it, its message the schema's own description of what is wrong, and `issues`
23
- * the same description an issue at a time, for a caller that points at a field. A handler
24
- * failing with it for input that decodes but cannot be served may name its own.
20
+ * The request's input does not decode against the action's input: its message is the schema's
21
+ * own description of what is wrong, and `issues` the same an issue at a time, for a caller that
22
+ * points at a field. A handler fails with it, naming `issues` of its own, for input that decodes
23
+ * but cannot be served, instead of declaring a second error for bad input.
24
+ *
25
+ * **Gotchas**
26
+ *
27
+ * - Raised by the library for input that does not decode, before `authorize` and the handler; what each surface answers is on `ActionHttp.layer`, `ActionMcp.layerHttp` and `runStdio`, `ActionRpc.layer`, `ActionToolkit.make` and `ActionCli.command`.
28
+ * - Failed by a handler, it is answered as a declared error on every surface.
29
+ * - What receives encoded input, a route, an MCP tool, an rpc, a CLI flag or `--input`, refuses an undeclared field with it, nested ones too, naming its path; a typed client drops such a field instead, as its JSDoc says.
30
+ * - Its message and `issues` name paths and what the schema expects, never a field's value; a path names every key it passes through, a record's keys included, so never key a record by a secret such as an email or a token.
31
+ * - A check's message, or a schema's `message` annotation, is sent as written: never put the input in one.
25
32
  */
26
33
  export declare class InvalidInput extends InvalidInput_base {
27
34
  private readonly "~declared in the action's error, or built in";
28
35
  /**
29
36
  * The `InvalidInput` for input that did not decode, as every surface answers it: the
30
37
  * schema's message, and each of its issues by path, for code that decodes input of its own,
31
- * such as a header or a route parameter. Neither carries a value of the input: decoding
32
- * runs without `reportInput`, so no issue holds one to format. A symbol key is sent as its
33
- * string form, `Symbol(name)`, as JSON has no symbols, which tells it from a string key of
34
- * that name.
38
+ * such as a header or a route parameter:
39
+ * `Schema.decodeUnknownEffect(schema)(value, { errors: "all" }).pipe(Effect.mapError(Action.InvalidInput.fromSchemaError))`.
40
+ * A symbol key is sent as its string form, `Symbol(name)`, as JSON has no symbols, which
41
+ * tells it from a string key of that name.
35
42
  */
36
43
  static readonly fromSchemaError: ({ issue, message }: Schema.SchemaError) => InvalidInput;
37
44
  }
38
45
  declare const Unauthenticated_base: Schema.Class<Unauthenticated, Schema.TaggedStruct<"Unauthenticated", {
39
46
  readonly message: Schema.withConstructorDefault<Schema.String>;
40
47
  }>, import("effect/Cause").YieldableError>;
41
- /** The caller is not authenticated: authentication or an implementation's `authorize` refuses. */
48
+ /**
49
+ * The caller is not authenticated: authentication refuses the credential, or an implementation's
50
+ * `authorize` or a handler fails with it.
51
+ *
52
+ * **Gotchas**
53
+ *
54
+ * - Over HTTP its JSON with a 401: from authentication before the body is read, from a protected action's `authorize` or handler after the input is decoded, each with the scheme's challenge, none for an API key.
55
+ * - Over MCP: from authentication, the same HTTP 401 in place of a JSON-RPC answer, before the arguments are decoded; from a protected tool's `authorize` or handler, the HTTP 401 with the challenge on an endpoint under Bearer, otherwise an `isError: true` tool result in a 200.
56
+ * - Over RPC the typed failure, never a status; on a Toolkit and in a CLI as a declared error.
57
+ * - From another service's client, such as `ActionHttp.client`, one passed on tells a caller whose token is valid to sign in again: map it to an error the action declares, or to a defect.
58
+ */
42
59
  export declare class Unauthenticated extends Unauthenticated_base {
43
60
  private readonly "~declared in the action's error, or built in";
44
61
  }
@@ -47,26 +64,33 @@ declare const Forbidden_base: Schema.Class<Forbidden, Schema.TaggedStruct<"Forbi
47
64
  readonly scopes: Schema.optionalKey<Schema.NonEmptyArray<Schema.String>>;
48
65
  }>, import("effect/Cause").YieldableError>;
49
66
  /**
50
- * The caller may not run this action: an implementation's `authorize` refuses. `scopes`,
51
- * when given, are every OAuth scope the call needs: a refusal naming them is answered with
52
- * the `insufficient_scope` challenge an OAuth client re-authorizes on, so name them only where
53
- * re-authorizing can grant them. What it answers on each surface: `docs/guarantees.md` in this
54
- * package.
67
+ * The caller may not run this action: authentication refuses the credential, an
68
+ * implementation's `authorize` refuses, or a handler fails with it. `scopes`, when given, are every OAuth scope the call needs: a refusal naming
69
+ * them is answered with the `insufficient_scope` challenge an OAuth client re-authorizes on,
70
+ * which only a server publishing discovery can follow up, so name them only where
71
+ * re-authorizing can grant them; with fixed tokens, such as API keys, leave them out
72
+ * (`scopes: []` is a type error).
73
+ *
74
+ * **Gotchas**
75
+ *
76
+ * - Over HTTP its JSON with a 403: from authentication before the body is read, from `authorize` or a handler after the input is decoded; on a protected route under Bearer, one naming `scopes` also carries the challenge.
77
+ * - Over MCP, from authentication the HTTP 403 before the arguments are decoded; from `authorize` or a handler an `isError: true` tool result holding its JSON as text, in a **200**, not a 403; only on an endpoint under Bearer is a protected tool's refusal naming `scopes` the HTTP 403 with the challenge instead.
78
+ * - Over RPC the typed failure, never a status or a challenge; on a Toolkit and in a CLI as a declared error.
79
+ * - From another service's client, such as `ActionHttp.client`, one passed on becomes this server's own refusal: map it to an error the action declares, or to a defect.
55
80
  */
56
81
  export declare class Forbidden extends Forbidden_base {
57
82
  private readonly "~declared in the action's error, or built in";
58
83
  }
59
84
  /**
60
85
  * What authentication or an implementation's `authorize` refuses a caller with, instead of
61
- * running a handler. An error an action declares, such as a rate limit, is not a refusal. As a
62
- * schema, it tells a refusal apart: `Schema.is(Action.Refusal)(error)`.
86
+ * running a handler. As a schema, it tells a refusal apart: `Schema.is(Action.Refusal)(error)`.
63
87
  */
64
88
  export declare const Refusal: Schema.Union<readonly [typeof Unauthenticated, typeof Forbidden]>;
65
89
  /** A refusal: `Unauthenticated | Forbidden`. */
66
90
  export type Refusal = (typeof Refusal)["Type"];
67
91
  /**
68
92
  * The failures every endpoint and tool declares beyond its action's own, and any handler may
69
- * fail with: bad input, and every refusal. No action or binding declares them itself.
93
+ * fail with: bad input, and every refusal. An action or a binding need not declare them; one listed is accepted.
70
94
  */
71
95
  export declare const builtIns: readonly [typeof InvalidInput, typeof Unauthenticated, typeof Forbidden];
72
96
  /** The built-in failures' schemas. */
@@ -75,5 +99,4 @@ export type BuiltIns = (typeof builtIns)[number];
75
99
  export declare const BuiltIn: Schema.Union<readonly [typeof InvalidInput, typeof Unauthenticated, typeof Forbidden]>;
76
100
  /** A built-in failure: `InvalidInput | Refusal`. */
77
101
  export type BuiltIn = (typeof BuiltIn)["Type"];
78
- //#endregion
79
102
  export { Issue };
@@ -1,5 +1,5 @@
1
1
  import { Effect, Predicate, Schema, SchemaIssue } from "effect";
2
- //#region src/contract/errors.ts
2
+ // src/contract/errors.ts
3
3
  const defaultedMessage = (fallback) => Schema.String.pipe(Schema.withConstructorDefault(Effect.succeed(fallback)));
4
4
  const Issue = Schema.Struct({
5
5
  path: Schema.Array(Schema.Union([Schema.String, Schema.Finite])),
@@ -7,10 +7,18 @@ const Issue = Schema.Struct({
7
7
  });
8
8
  const standardIssues = SchemaIssue.makeFormatterStandardSchemaV1();
9
9
  /**
10
- * The request's input does not decode against the action's input. HTTP answers every such
11
- * request with it, its message the schema's own description of what is wrong, and `issues`
12
- * the same description an issue at a time, for a caller that points at a field. A handler
13
- * failing with it for input that decodes but cannot be served may name its own.
10
+ * The request's input does not decode against the action's input: its message is the schema's
11
+ * own description of what is wrong, and `issues` the same an issue at a time, for a caller that
12
+ * points at a field. A handler fails with it, naming `issues` of its own, for input that decodes
13
+ * but cannot be served, instead of declaring a second error for bad input.
14
+ *
15
+ * **Gotchas**
16
+ *
17
+ * - Raised by the library for input that does not decode, before `authorize` and the handler; what each surface answers is on `ActionHttp.layer`, `ActionMcp.layerHttp` and `runStdio`, `ActionRpc.layer`, `ActionToolkit.make` and `ActionCli.command`.
18
+ * - Failed by a handler, it is answered as a declared error on every surface.
19
+ * - What receives encoded input, a route, an MCP tool, an rpc, a CLI flag or `--input`, refuses an undeclared field with it, nested ones too, naming its path; a typed client drops such a field instead, as its JSDoc says.
20
+ * - Its message and `issues` name paths and what the schema expects, never a field's value; a path names every key it passes through, a record's keys included, so never key a record by a secret such as an email or a token.
21
+ * - A check's message, or a schema's `message` annotation, is sent as written: never put the input in one.
14
22
  */
15
23
  var InvalidInput = class InvalidInput extends Schema.TaggedError()("InvalidInput", {
16
24
  message: defaultedMessage("The input does not match the action's input."),
@@ -19,10 +27,10 @@ var InvalidInput = class InvalidInput extends Schema.TaggedError()("InvalidInput
19
27
  /**
20
28
  * The `InvalidInput` for input that did not decode, as every surface answers it: the
21
29
  * schema's message, and each of its issues by path, for code that decodes input of its own,
22
- * such as a header or a route parameter. Neither carries a value of the input: decoding
23
- * runs without `reportInput`, so no issue holds one to format. A symbol key is sent as its
24
- * string form, `Symbol(name)`, as JSON has no symbols, which tells it from a string key of
25
- * that name.
30
+ * such as a header or a route parameter:
31
+ * `Schema.decodeUnknownEffect(schema)(value, { errors: "all" }).pipe(Effect.mapError(Action.InvalidInput.fromSchemaError))`.
32
+ * A symbol key is sent as its string form, `Symbol(name)`, as JSON has no symbols, which
33
+ * tells it from a string key of that name.
26
34
  */
27
35
  static fromSchemaError = ({ issue, message }) => new InvalidInput({
28
36
  message,
@@ -35,16 +43,34 @@ var InvalidInput = class InvalidInput extends Schema.TaggedError()("InvalidInput
35
43
  }))
36
44
  });
37
45
  };
38
- /** The caller is not authenticated: authentication or an implementation's `authorize` refuses. */
46
+ /**
47
+ * The caller is not authenticated: authentication refuses the credential, or an implementation's
48
+ * `authorize` or a handler fails with it.
49
+ *
50
+ * **Gotchas**
51
+ *
52
+ * - Over HTTP its JSON with a 401: from authentication before the body is read, from a protected action's `authorize` or handler after the input is decoded, each with the scheme's challenge, none for an API key.
53
+ * - Over MCP: from authentication, the same HTTP 401 in place of a JSON-RPC answer, before the arguments are decoded; from a protected tool's `authorize` or handler, the HTTP 401 with the challenge on an endpoint under Bearer, otherwise an `isError: true` tool result in a 200.
54
+ * - Over RPC the typed failure, never a status; on a Toolkit and in a CLI as a declared error.
55
+ * - From another service's client, such as `ActionHttp.client`, one passed on tells a caller whose token is valid to sign in again: map it to an error the action declares, or to a defect.
56
+ */
39
57
  var Unauthenticated = class extends Schema.TaggedError()("Unauthenticated", { message: defaultedMessage("Authentication is required.") }, { httpApiStatus: 401 }) {};
40
58
  const scopeToken = /^[\x21\x23-\x5B\x5D-\x7E]+$/;
41
59
  const ScopeToken = Schema.String.check(Schema.isPattern(scopeToken));
42
60
  /**
43
- * The caller may not run this action: an implementation's `authorize` refuses. `scopes`,
44
- * when given, are every OAuth scope the call needs: a refusal naming them is answered with
45
- * the `insufficient_scope` challenge an OAuth client re-authorizes on, so name them only where
46
- * re-authorizing can grant them. What it answers on each surface: `docs/guarantees.md` in this
47
- * package.
61
+ * The caller may not run this action: authentication refuses the credential, an
62
+ * implementation's `authorize` refuses, or a handler fails with it. `scopes`, when given, are every OAuth scope the call needs: a refusal naming
63
+ * them is answered with the `insufficient_scope` challenge an OAuth client re-authorizes on,
64
+ * which only a server publishing discovery can follow up, so name them only where
65
+ * re-authorizing can grant them; with fixed tokens, such as API keys, leave them out
66
+ * (`scopes: []` is a type error).
67
+ *
68
+ * **Gotchas**
69
+ *
70
+ * - Over HTTP its JSON with a 403: from authentication before the body is read, from `authorize` or a handler after the input is decoded; on a protected route under Bearer, one naming `scopes` also carries the challenge.
71
+ * - Over MCP, from authentication the HTTP 403 before the arguments are decoded; from `authorize` or a handler an `isError: true` tool result holding its JSON as text, in a **200**, not a 403; only on an endpoint under Bearer is a protected tool's refusal naming `scopes` the HTTP 403 with the challenge instead.
72
+ * - Over RPC the typed failure, never a status or a challenge; on a Toolkit and in a CLI as a declared error.
73
+ * - From another service's client, such as `ActionHttp.client`, one passed on becomes this server's own refusal: map it to an error the action declares, or to a defect.
48
74
  */
49
75
  var Forbidden = class extends Schema.TaggedError()("Forbidden", {
50
76
  message: defaultedMessage("Not allowed."),
@@ -53,16 +79,14 @@ var Forbidden = class extends Schema.TaggedError()("Forbidden", {
53
79
  const refusals = [Unauthenticated, Forbidden];
54
80
  /**
55
81
  * What authentication or an implementation's `authorize` refuses a caller with, instead of
56
- * running a handler. An error an action declares, such as a rate limit, is not a refusal. As a
57
- * schema, it tells a refusal apart: `Schema.is(Action.Refusal)(error)`.
82
+ * running a handler. As a schema, it tells a refusal apart: `Schema.is(Action.Refusal)(error)`.
58
83
  */
59
84
  const Refusal = Schema.Union(refusals);
60
85
  /**
61
86
  * The failures every endpoint and tool declares beyond its action's own, and any handler may
62
- * fail with: bad input, and every refusal. No action or binding declares them itself.
87
+ * fail with: bad input, and every refusal. An action or a binding need not declare them; one listed is accepted.
63
88
  */
64
89
  const builtIns = [InvalidInput, ...refusals];
65
90
  /** The built-in failures as one schema: `Schema.is(Action.BuiltIn)(error)`. */
66
91
  const BuiltIn = Schema.Union(builtIns);
67
- //#endregion
68
92
  export { BuiltIn, Forbidden, InvalidInput, Refusal, Unauthenticated, builtIns, scopeToken };
@@ -1,7 +1,8 @@
1
- //#region src/contract/hints.ts
1
+ // src/contract/hints.ts
2
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
3
+ * An action's MCP tool hints, MCP's defaults where its `mcp` leaves one out, except that a read
4
+ * is not destructive where MCP's default would say it is: no tool idempotent and every tool
5
+ * open-world. A given value passes through, as MCP
5
6
  * calls `destructiveHint` and `idempotentHint` meaningless on a read-only tool.
6
7
  */
7
8
  const hintsOf = ({ readOnly, mcp }) => ({
@@ -10,5 +11,4 @@ const hintsOf = ({ readOnly, mcp }) => ({
10
11
  idempotentHint: mcp.idempotentHint ?? false,
11
12
  openWorldHint: mcp.openWorldHint ?? true
12
13
  });
13
- //#endregion
14
14
  export { hintsOf };
@@ -2,7 +2,6 @@ import { Anyone } from "./caller.mjs";
2
2
  import { Refusal } from "./errors.mjs";
3
3
  import { Any } from "../Action.mjs";
4
4
  import { Context, Effect, Layer, Scope } from "effect";
5
- //#region src/contract/implementation.d.ts
6
5
  /** Decoded values at the dispatch boundary every surface shares. */
7
6
  export type ErasedValue = Any["input"]["Type"];
8
7
  /**
@@ -28,22 +27,18 @@ interface Memoized<S, E, R> {
28
27
  }
29
28
  /**
30
29
  * Actions bound to their handlers and their authorizer: everything one `Action.implement` call
31
- * produced, one builder serving every surface.
32
- * The private fields make this class nominal: a structurally similar object, including one made by
30
+ * produced, one builder serving every surface. The private fields make this class nominal: a structurally similar object, including one made by
33
31
  * spreading an implementation, is not an implementation. Its type parameters may be written
34
32
  * out, as a package emitting declarations prints them: `A` its actions, `RequestServices` each
35
- * action name's handler's per-request requirements, `EX` and `RX` the failures and services of
33
+ * action name's handler's request-time requirements, `EX` and `RX` the failures and services of
36
34
  * its handlers' builder, `RA` what its authorizer reads per request, and `EAX` and `RAX` the
37
- * failures and services of the authorizer's builder. Public-only selections do not acquire
38
- * authorization. A plain authorization callback builds nothing.
35
+ * failures and services of the authorizer's builder.
39
36
  */
40
37
  export declare class Implementation<A extends Any, RequestServices extends {
41
38
  readonly [name: string]: unknown;
42
39
  }, EX, RX, RA = never, EAX = never, RAX = never> {
43
40
  #private;
44
- /** The contracts this implementation answers. */
45
- readonly actions: ReadonlyArray<A>;
46
- /** Type-only: each action's handler's per-request requirements. */
41
+ /** Type-only: each action's handler's request-time requirements. */
47
42
  readonly "~request": RequestServices;
48
43
  /** Type-only: what building its handlers fails with. */
49
44
  readonly "~buildError": EX;
@@ -55,9 +50,9 @@ export declare class Implementation<A extends Any, RequestServices extends {
55
50
  readonly "~authorizeBuildError": EAX;
56
51
  /** Type-only: what building its authorizer needs. */
57
52
  readonly "~authorizeBuildServices": RAX;
58
- constructor(
59
53
  /** The contracts this implementation answers. */
60
- actions: ReadonlyArray<A>,
54
+ readonly actions: ReadonlyArray<A>;
55
+ constructor(actions: ReadonlyArray<A>,
61
56
  /** Builds each action paired with its handler: its own builder, or the one it shares. */
62
57
  handlers: Memoized<Bound, EX, RX>,
63
58
  /** Builds its authorizer. */
@@ -72,8 +67,7 @@ export declare class Implementation<A extends Any, RequestServices extends {
72
67
  /**
73
68
  * The builders of `app`, its handlers' and its authorizer's, as a layer. Effect memoizes each
74
69
  * by reference within one layer graph, so every surface serving `app` there, and every
75
- * implementation sharing its builder, shares one run. Static, so it stays off the
76
- * public instance type.
70
+ * implementation sharing its builder, shares one run.
77
71
  */
78
72
  static layerOf(app: AnyImplementation): Layer.Layer<never, unknown, unknown>;
79
73
  /**
@@ -192,5 +186,4 @@ export type BuildServices<App, Listed extends Any = OwnActions<App>> = App exten
192
186
  readonly "~buildServices": infer RX;
193
187
  readonly "~authorizeBuildServices": infer RAX;
194
188
  } ? RX | ([Protected<Serving<App, Listed>>] extends [never] ? never : RAX) : never;
195
- //#endregion
196
189
  export {};