@gjermundgaraba/effect-actions 0.20.0 → 0.21.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 (321) hide show
  1. package/README.md +19 -41
  2. package/dist/Action.d.mts +140 -46
  3. package/dist/Action.mjs +87 -42
  4. package/dist/ActionCli.d.mts +69 -33
  5. package/dist/ActionCli.mjs +51 -32
  6. package/dist/ActionHttp.d.mts +69 -35
  7. package/dist/ActionHttp.mjs +75 -23
  8. package/dist/ActionMcp.d.mts +56 -22
  9. package/dist/ActionMcp.mjs +94 -69
  10. package/dist/ActionRpc.d.mts +222 -55
  11. package/dist/ActionRpc.mjs +299 -46
  12. package/dist/ActionToolkit.d.mts +22 -5
  13. package/dist/ActionToolkit.mjs +36 -12
  14. package/dist/Authentication.d.mts +42 -10
  15. package/dist/Authentication.mjs +37 -30
  16. package/dist/Event.d.mts +370 -0
  17. package/dist/Event.mjs +103 -0
  18. package/dist/Testing.mjs +1 -1
  19. package/dist/Webhook.d.mts +146 -0
  20. package/dist/Webhook.mjs +72 -0
  21. package/dist/authentication/provider.d.mts +35 -10
  22. package/dist/authentication/provider.mjs +1 -1
  23. package/dist/authentication/refusal.mjs +2 -2
  24. package/dist/cli/commands.d.mts +9 -5
  25. package/dist/cli/commands.mjs +84 -33
  26. package/dist/contract/ast.mjs +4 -1
  27. package/dist/contract/authorizer.d.mts +8 -0
  28. package/dist/contract/authorizer.mjs +20 -0
  29. package/dist/contract/call.d.mts +9 -6
  30. package/dist/contract/call.mjs +48 -6
  31. package/dist/contract/callback.d.mts +7 -0
  32. package/dist/contract/callback.mjs +27 -0
  33. package/dist/contract/caller.d.mts +13 -2
  34. package/dist/contract/caller.mjs +22 -3
  35. package/dist/contract/canonical.mjs +10 -0
  36. package/dist/contract/codec.d.mts +9 -0
  37. package/dist/contract/codec.mjs +9 -0
  38. package/dist/contract/codes.mjs +81 -0
  39. package/dist/contract/declared.mjs +25 -9
  40. package/dist/contract/digest.mjs +6 -0
  41. package/dist/contract/errors.d.mts +9 -3
  42. package/dist/contract/errors.mjs +1 -1
  43. package/dist/contract/feed.d.mts +127 -0
  44. package/dist/contract/feed.mjs +249 -0
  45. package/dist/contract/hub.d.mts +94 -0
  46. package/dist/contract/hub.mjs +133 -0
  47. package/dist/contract/idempotency.d.mts +47 -0
  48. package/dist/contract/idempotency.mjs +99 -0
  49. package/dist/contract/implementation.d.mts +73 -47
  50. package/dist/contract/implementation.mjs +92 -58
  51. package/dist/contract/input-check.d.mts +15 -0
  52. package/dist/contract/input-check.mjs +16 -0
  53. package/dist/contract/lease.mjs +28 -0
  54. package/dist/contract/listed.d.mts +4 -0
  55. package/dist/contract/listed.mjs +22 -0
  56. package/dist/contract/log.d.mts +41 -0
  57. package/dist/contract/log.mjs +144 -0
  58. package/dist/contract/names.mjs +14 -4
  59. package/dist/contract/occurrence.d.mts +29 -0
  60. package/dist/contract/occurrence.mjs +45 -0
  61. package/dist/contract/pages.d.mts +13 -0
  62. package/dist/contract/pages.mjs +39 -0
  63. package/dist/contract/progress.d.mts +26 -0
  64. package/dist/contract/progress.mjs +72 -0
  65. package/dist/contract/push.d.mts +20 -0
  66. package/dist/contract/push.mjs +96 -0
  67. package/dist/contract/receiving.d.mts +29 -0
  68. package/dist/contract/receiving.mjs +42 -0
  69. package/dist/contract/recent.mjs +12 -0
  70. package/dist/contract/reported.mjs +16 -0
  71. package/dist/contract/serving.mjs +63 -0
  72. package/dist/contract/status.mjs +14 -5
  73. package/dist/contract/stream.d.mts +34 -0
  74. package/dist/contract/stream.mjs +31 -0
  75. package/dist/contract/subscribe.d.mts +45 -0
  76. package/dist/contract/subscribe.mjs +50 -0
  77. package/dist/contract/transient.mjs +28 -0
  78. package/dist/contract/watch.d.mts +18 -0
  79. package/dist/contract/watch.mjs +105 -0
  80. package/dist/contract/webhooks.d.mts +42 -0
  81. package/dist/contract/webhooks.mjs +53 -0
  82. package/dist/http/client.d.mts +37 -12
  83. package/dist/http/client.mjs +51 -12
  84. package/dist/http/events.d.mts +56 -0
  85. package/dist/http/events.mjs +102 -0
  86. package/dist/http/placed.d.mts +36 -0
  87. package/dist/http/placed.mjs +71 -0
  88. package/dist/http/schema-errors.mjs +1 -1
  89. package/dist/http/streamed.d.mts +21 -0
  90. package/dist/http/streamed.mjs +94 -0
  91. package/dist/http/watch.mjs +50 -0
  92. package/dist/mcp/bridge.mjs +283 -0
  93. package/dist/mcp/client.d.mts +30 -97
  94. package/dist/mcp/client.mjs +59 -132
  95. package/dist/mcp/events.mjs +142 -0
  96. package/dist/mcp/progress.mjs +34 -0
  97. package/dist/mcp/protocol.mjs +39 -13
  98. package/dist/mcp/reply.d.mts +79 -0
  99. package/dist/mcp/reply.mjs +99 -0
  100. package/dist/mcp/request.d.mts +40 -0
  101. package/dist/mcp/request.mjs +51 -0
  102. package/dist/mcp/watch.mjs +115 -0
  103. package/dist/rpc/watch.mjs +39 -0
  104. package/dist/stdio/console.mjs +13 -5
  105. package/dist/testing/memory.d.mts +1 -1
  106. package/dist/testing/memory.mjs +3 -3
  107. package/dist/webhook/address.mjs +47 -0
  108. package/dist/webhook/attempt.mjs +65 -0
  109. package/dist/webhook/receiver.mjs +67 -0
  110. package/dist/webhook/signing.mjs +28 -0
  111. package/dist/webhook/stored.mjs +57 -0
  112. package/dist/webhook/subscriptions.mjs +342 -0
  113. package/docs/Action-failures.md +2 -2
  114. package/docs/Action-streams.md +115 -0
  115. package/docs/Action.md +58 -124
  116. package/docs/ActionCli-failures.md +1 -2
  117. package/docs/ActionCli.md +83 -88
  118. package/docs/ActionHttp-failures.md +3 -3
  119. package/docs/ActionHttp.md +90 -70
  120. package/docs/ActionMcp-failures.md +2 -1
  121. package/docs/ActionMcp.md +120 -92
  122. package/docs/ActionRpc-events.md +126 -0
  123. package/docs/ActionRpc-failures.md +1 -2
  124. package/docs/ActionRpc.md +108 -79
  125. package/docs/ActionToolkit-failures.md +1 -2
  126. package/docs/ActionToolkit.md +33 -90
  127. package/docs/Authentication-advanced.md +52 -60
  128. package/docs/Authentication-failures.md +5 -4
  129. package/docs/Authentication.md +23 -58
  130. package/docs/Event-failures.md +15 -0
  131. package/docs/Event.md +281 -0
  132. package/docs/README.md +51 -82
  133. package/docs/Testing-failures.md +1 -5
  134. package/docs/Testing.md +126 -86
  135. package/docs/Webhook-failures.md +8 -0
  136. package/docs/Webhook.md +319 -0
  137. package/docs/setup-failures.md +2 -3
  138. package/docs/setup.md +32 -52
  139. package/examples/README.md +12 -37
  140. package/examples/assistant/README.md +21 -26
  141. package/examples/assistant/src/agent.ts +66 -24
  142. package/examples/assistant/src/cli.ts +7 -4
  143. package/examples/assistant/src/commands.ts +9 -4
  144. package/examples/assistant/src/identity/Identity.ts +5 -4
  145. package/examples/assistant/src/identity/authorize.ts +13 -5
  146. package/examples/assistant/src/identity/handlers.ts +3 -2
  147. package/examples/assistant/src/identity/permissions.ts +9 -3
  148. package/examples/assistant/src/platform/tasks-file.ts +15 -11
  149. package/examples/assistant/src/scripted.ts +7 -4
  150. package/examples/assistant/src/tasks/Tasks.ts +20 -6
  151. package/examples/assistant/src/tasks/handlers.ts +9 -5
  152. package/examples/assistant/src/tasks/store.ts +38 -11
  153. package/examples/assistant/src/toolkit.ts +11 -7
  154. package/examples/assistant/test/commands.test.ts +53 -27
  155. package/examples/assistant/test/identity/identity.test.ts +27 -11
  156. package/examples/assistant/test/platform/tasks-file.test.ts +39 -23
  157. package/examples/assistant/test/tasks/tasks.test.ts +62 -34
  158. package/examples/assistant/test/toolkit.test.ts +104 -75
  159. package/examples/build-events/README.md +83 -0
  160. package/examples/build-events/src/Api.ts +27 -0
  161. package/examples/build-events/src/Rpc.ts +26 -0
  162. package/examples/build-events/src/builds/Builds.ts +108 -0
  163. package/examples/build-events/src/builds/events.ts +54 -0
  164. package/examples/build-events/src/builds/handlers.ts +58 -0
  165. package/examples/build-events/src/builds/log.ts +30 -0
  166. package/examples/build-events/src/builds/status.ts +28 -0
  167. package/examples/build-events/src/builds/store.ts +130 -0
  168. package/examples/build-events/src/cli.ts +18 -0
  169. package/examples/build-events/src/commands.ts +32 -0
  170. package/examples/build-events/src/deploys/Deploys.ts +44 -0
  171. package/examples/build-events/src/deploys/approvers.ts +55 -0
  172. package/examples/build-events/src/deploys/events.ts +32 -0
  173. package/examples/build-events/src/deploys/handlers.ts +38 -0
  174. package/examples/build-events/src/identity/Identity.ts +29 -0
  175. package/examples/build-events/src/identity/members.ts +7 -0
  176. package/examples/build-events/src/identity/verifier.ts +22 -0
  177. package/examples/build-events/src/notifier/Notices.ts +26 -0
  178. package/examples/build-events/src/notifier/routes.ts +71 -0
  179. package/examples/build-events/src/notifier.ts +48 -0
  180. package/examples/build-events/src/routes.ts +71 -0
  181. package/examples/build-events/src/server.ts +62 -0
  182. package/examples/build-events/test/builds/events.test.ts +68 -0
  183. package/examples/build-events/test/builds/log.test.ts +71 -0
  184. package/examples/build-events/test/builds/progress.test.ts +57 -0
  185. package/examples/build-events/test/builds/status.test.ts +42 -0
  186. package/examples/build-events/test/commands.test.ts +103 -0
  187. package/examples/build-events/test/deploys/events.test.ts +52 -0
  188. package/examples/build-events/test/http.test.ts +95 -0
  189. package/examples/build-events/test/notifier.test.ts +80 -0
  190. package/examples/build-events/test/routes.test.ts +96 -0
  191. package/examples/build-events/test/rpc.test.ts +84 -0
  192. package/examples/build-events/test/support/as.ts +8 -0
  193. package/examples/build-events/test/support/memory.ts +43 -0
  194. package/examples/build-events/test/webhooks.test.ts +143 -0
  195. package/examples/kitchen-sink/README.md +178 -166
  196. package/examples/kitchen-sink/src/Actions.ts +11 -3
  197. package/examples/kitchen-sink/src/Api.ts +12 -6
  198. package/examples/kitchen-sink/src/Rpc.ts +7 -5
  199. package/examples/kitchen-sink/src/admin/Admin.ts +21 -10
  200. package/examples/kitchen-sink/src/admin/authenticate.ts +8 -4
  201. package/examples/kitchen-sink/src/admin/authorize.ts +13 -5
  202. package/examples/kitchen-sink/src/admin/grants.ts +5 -3
  203. package/examples/kitchen-sink/src/admin/handlers.ts +9 -5
  204. package/examples/kitchen-sink/src/agent.ts +14 -6
  205. package/examples/kitchen-sink/src/assistant/Assistant.ts +21 -15
  206. package/examples/kitchen-sink/src/assistant/conversations.ts +14 -9
  207. package/examples/kitchen-sink/src/assistant/demo-model.ts +2 -2
  208. package/examples/kitchen-sink/src/assistant/handlers.ts +42 -22
  209. package/examples/kitchen-sink/src/attachments/Attachments.ts +6 -6
  210. package/examples/kitchen-sink/src/attachments/download.ts +7 -6
  211. package/examples/kitchen-sink/src/attachments/handlers.ts +9 -4
  212. package/examples/kitchen-sink/src/attachments/store.ts +25 -18
  213. package/examples/kitchen-sink/src/browser.ts +10 -4
  214. package/examples/kitchen-sink/src/cli.ts +28 -12
  215. package/examples/kitchen-sink/src/config.ts +1 -1
  216. package/examples/kitchen-sink/src/identity/Identity.ts +24 -10
  217. package/examples/kitchen-sink/src/identity/authenticate.ts +19 -11
  218. package/examples/kitchen-sink/src/identity/authorize.ts +14 -4
  219. package/examples/kitchen-sink/src/identity/directory.ts +16 -7
  220. package/examples/kitchen-sink/src/identity/handlers.ts +1 -0
  221. package/examples/kitchen-sink/src/identity/members.ts +7 -2
  222. package/examples/kitchen-sink/src/identity/quota.ts +23 -20
  223. package/examples/kitchen-sink/src/issues/Issues.ts +91 -15
  224. package/examples/kitchen-sink/src/issues/assistant.ts +4 -2
  225. package/examples/kitchen-sink/src/issues/events.ts +46 -0
  226. package/examples/kitchen-sink/src/issues/export.ts +35 -0
  227. package/examples/kitchen-sink/src/issues/guide.ts +4 -3
  228. package/examples/kitchen-sink/src/issues/handlers.ts +20 -7
  229. package/examples/kitchen-sink/src/issues/store.ts +156 -67
  230. package/examples/kitchen-sink/src/issues/write-limit.ts +44 -18
  231. package/examples/kitchen-sink/src/memory.ts +11 -2
  232. package/examples/kitchen-sink/src/ops.ts +10 -5
  233. package/examples/kitchen-sink/src/request-policy.ts +21 -9
  234. package/examples/kitchen-sink/src/routes.ts +67 -27
  235. package/examples/kitchen-sink/src/sdk.ts +22 -12
  236. package/examples/kitchen-sink/src/seed.ts +15 -9
  237. package/examples/kitchen-sink/src/server.ts +24 -10
  238. package/examples/kitchen-sink/src/stdio.ts +9 -3
  239. package/examples/kitchen-sink/src/workspaces/Workspaces.ts +10 -7
  240. package/examples/kitchen-sink/src/workspaces/handlers.ts +3 -2
  241. package/examples/kitchen-sink/src/workspaces/registry.ts +16 -10
  242. package/examples/kitchen-sink/src/workspaces/tenant.ts +12 -5
  243. package/examples/kitchen-sink/test/admin/admin.test.ts +38 -23
  244. package/examples/kitchen-sink/test/assistant/assistant.test.ts +471 -299
  245. package/examples/kitchen-sink/test/attachments/attachments.test.ts +135 -94
  246. package/examples/kitchen-sink/test/identity/identity.test.ts +110 -82
  247. package/examples/kitchen-sink/test/identity/session.test.ts +65 -43
  248. package/examples/kitchen-sink/test/issues/assistant.test.ts +50 -31
  249. package/examples/kitchen-sink/test/issues/events.test.ts +273 -0
  250. package/examples/kitchen-sink/test/issues/export.test.ts +120 -0
  251. package/examples/kitchen-sink/test/issues/issues.test.ts +105 -75
  252. package/examples/kitchen-sink/test/request-policy.test.ts +108 -47
  253. package/examples/kitchen-sink/test/routes.test.ts +102 -57
  254. package/examples/kitchen-sink/test/support/as.ts +9 -0
  255. package/examples/kitchen-sink/test/support/scripted.ts +6 -5
  256. package/examples/kitchen-sink/test/support/services.ts +17 -0
  257. package/examples/live-rpc/README.md +33 -35
  258. package/examples/live-rpc/src/Rpc.ts +9 -5
  259. package/examples/live-rpc/src/browser.ts +39 -11
  260. package/examples/live-rpc/src/chat/Chat.ts +30 -16
  261. package/examples/live-rpc/src/chat/authorize.ts +2 -1
  262. package/examples/live-rpc/src/chat/events.ts +74 -0
  263. package/examples/live-rpc/src/chat/handlers.ts +13 -5
  264. package/examples/live-rpc/src/chat/room.ts +9 -5
  265. package/examples/live-rpc/src/chat/store.ts +51 -14
  266. package/examples/live-rpc/src/client.ts +22 -5
  267. package/examples/live-rpc/src/identity/Identity.ts +3 -3
  268. package/examples/live-rpc/src/identity/verifier.ts +8 -4
  269. package/examples/live-rpc/src/page.ts +58 -12
  270. package/examples/live-rpc/src/routes.ts +14 -10
  271. package/examples/live-rpc/src/server.ts +15 -5
  272. package/examples/live-rpc/test/browser.test.ts +127 -50
  273. package/examples/live-rpc/test/chat/chat.test.ts +215 -23
  274. package/examples/live-rpc/test/routes.test.ts +130 -46
  275. package/examples/live-rpc/test/server.test.ts +94 -33
  276. package/examples/mcp-tools/README.md +24 -38
  277. package/examples/mcp-tools/src/platform/fake-farm.ts +63 -24
  278. package/examples/mcp-tools/src/platform/print-farm.ts +59 -29
  279. package/examples/mcp-tools/src/printers/Printers.ts +18 -7
  280. package/examples/mcp-tools/src/printers/farm.ts +15 -5
  281. package/examples/mcp-tools/src/printers/handlers.ts +12 -6
  282. package/examples/mcp-tools/src/routes.ts +8 -6
  283. package/examples/mcp-tools/src/server.ts +12 -5
  284. package/examples/mcp-tools/src/stdio.ts +3 -2
  285. package/examples/mcp-tools/test/printers/printers.test.ts +132 -89
  286. package/examples/mcp-tools/test/routes.test.ts +140 -81
  287. package/examples/notes-service/README.md +30 -45
  288. package/examples/notes-service/src/Api.ts +1 -0
  289. package/examples/notes-service/src/browser.ts +15 -14
  290. package/examples/notes-service/src/cli.ts +8 -3
  291. package/examples/notes-service/src/commands.ts +46 -20
  292. package/examples/notes-service/src/identity/Identity.ts +2 -2
  293. package/examples/notes-service/src/identity/authorize.ts +6 -2
  294. package/examples/notes-service/src/identity/handlers.ts +3 -2
  295. package/examples/notes-service/src/identity/users.ts +2 -1
  296. package/examples/notes-service/src/identity/verifier.ts +12 -6
  297. package/examples/notes-service/src/notes/Notes.ts +46 -22
  298. package/examples/notes-service/src/notes/handlers.ts +18 -9
  299. package/examples/notes-service/src/notes/store.ts +70 -43
  300. package/examples/notes-service/src/routes.ts +7 -5
  301. package/examples/notes-service/src/server.ts +12 -5
  302. package/examples/notes-service/src/status/Status.ts +1 -1
  303. package/examples/notes-service/src/status/handlers.ts +5 -3
  304. package/examples/notes-service/src/stdio.ts +7 -3
  305. package/examples/notes-service/src/worker.ts +6 -3
  306. package/examples/notes-service/test/commands.test.ts +164 -91
  307. package/examples/notes-service/test/notes/as.ts +3 -1
  308. package/examples/notes-service/test/notes/idempotency.test.ts +97 -67
  309. package/examples/notes-service/test/notes/in-process.test.ts +26 -15
  310. package/examples/notes-service/test/notes/notes.test.ts +49 -31
  311. package/examples/notes-service/test/routes.test.ts +106 -71
  312. package/examples/notes-service/test/worker.test.ts +21 -13
  313. package/examples/quickstart/README.md +2 -6
  314. package/examples/quickstart/src/Api.ts +1 -0
  315. package/examples/quickstart/src/client.ts +6 -3
  316. package/examples/quickstart/src/greeting/Greeting.ts +1 -1
  317. package/examples/quickstart/src/greeting/handlers.ts +5 -2
  318. package/examples/quickstart/src/routes.ts +3 -2
  319. package/examples/quickstart/src/server.ts +6 -2
  320. package/examples/quickstart/test/routes.test.ts +3 -2
  321. package/package.json +10 -4
package/README.md CHANGED
@@ -1,12 +1,8 @@
1
1
  # effect-actions
2
2
 
3
- Define an Effect action once. Serve it over HTTP, MCP and Effect RPC, hand it to a model as a
4
- native Toolkit, and run it from a CLI. Same schemas, same handler, Effect's own servers and clients
5
- underneath.
3
+ Define an Effect action once. Serve it over HTTP, MCP and Effect RPC, hand it to a model as a native Toolkit, and run it from a CLI. Same schemas, same handler, Effect's own servers and clients underneath. Events go the same way: watched over HTTP, MCP, Effect RPC and the CLI, or delivered by webhook. A companion package, [effect-actions-cloudflare](packages/cloudflare/), serves the HTTP, MCP and RPC routes, events and webhooks included, from Cloudflare Durable Objects.
6
4
 
7
- **If you are a coding agent**, start at [docs/README.md](docs/README.md), not here. It routes
8
- every task, protecting an action and limiting request bodies included, to its page and its
9
- example.
5
+ **If you are a coding agent**, start at [docs/README.md](docs/README.md), not here. It routes every task, protecting an action and limiting request bodies included, to its page and its example.
10
6
 
11
7
  ## Looks like this
12
8
 
@@ -25,7 +21,9 @@ const Greet = Action.make("greet", {
25
21
  An implementation binds it to its handler:
26
22
 
27
23
  ```ts
28
- const greeting = Action.implement(Greet, ({ name }) => Effect.succeed(`Hello, ${name}!`));
24
+ const greeting = Action.implement(Greet, ({ name }) =>
25
+ Effect.succeed(`Hello, ${name}!`)
26
+ );
29
27
  ```
30
28
 
31
29
  A binding gives it a route, for the server to serve and a client to call:
@@ -39,14 +37,11 @@ And the same implementation is served on every surface the host wants:
39
37
  ```ts
40
38
  const routes = Layer.mergeAll(
41
39
  ActionHttp.layer(Http, greeting),
42
- ActionMcp.layerHttp(greeting, { name: "greetings", version: "1.0.0" }),
40
+ ActionMcp.layerHttp(greeting, { name: "greetings", version: "1.0.0" })
43
41
  );
44
42
  ```
45
43
 
46
- Serve `routes` with Effect's `HttpRouter.serve` and a platform server, as
47
- [examples/quickstart](examples/quickstart) does, file by file, and you have
48
- `POST /api/greet` and an MCP tool named `greet` at `/mcp`. The same `greeting`
49
- implementation is also:
44
+ Serve `routes` with Effect's `HttpRouter.serve` and a platform server, as [examples/quickstart](examples/quickstart) does, file by file, and you have `POST /api/greet` and an MCP tool named `greet` at `/mcp`. The same `greeting` implementation is also:
50
45
 
51
46
  ```ts
52
47
  const { toolkit, layer } = ActionToolkit.make(greeting); // native Effect AI Toolkit and its handler layer
@@ -81,41 +76,30 @@ Effect.gen(function* () {
81
76
  ## Install
82
77
 
83
78
  ```sh
84
- pnpm add @gjermundgaraba/effect-actions effect@~4.0.4
79
+ pnpm add @gjermundgaraba/effect-actions effect@~4.0.5
85
80
  ```
86
81
 
87
- It needs TypeScript 7 or newer and is ESM only
88
- ([docs/setup.md](docs/setup.md#install)). Add `@effect/platform-node@~4.0.4` to serve or run a CLI from Node, and `@types/node` for its `node:` imports. Every module is a
89
- subpath import (`.../Action`, `.../ActionHttp`, ...); there is no package root, so a
90
- contracts-only bundle never loads a server.
82
+ It needs TypeScript 7 or newer and is ESM only ([docs/setup.md](docs/setup.md#install)). Add `@effect/platform-node@~4.0.5` to serve or run a CLI from Node, and `@types/node` for its `node:` imports. Every module is a subpath import (`.../Action`, `.../ActionHttp`, ...); there is no package root, so a contracts-only bundle never loads a server.
91
83
 
92
84
  ## Examples
93
85
 
94
- [examples/](examples/README.md) holds complete applications, each a directory you can run, with
95
- its own tests, laid out the way we recommend for an application: a folder per domain, the
96
- contract in one file a browser can import, one entrypoint per way of running it. They are
97
- type-checked and tested with the library and ship in the package.
86
+ [examples/](examples/README.md) holds complete applications, each a directory you can run, with its own tests, laid out the way we recommend for an application: a folder per domain, the contract in one file a browser can import, one entrypoint per way of running it. They are type-checked and tested with the library and ship in the package.
98
87
 
99
88
  - [quickstart](examples/quickstart/): the program above.
100
- - [notes-service](examples/notes-service/): a service with bearer authentication, a JSON API
101
- and MCP tools from one implementation, a CLI and a browser client. Most applications are this.
89
+ - [notes-service](examples/notes-service/): a service with bearer authentication, a JSON API and MCP tools from one implementation, a CLI and a browser client. Most applications are this.
102
90
  - [mcp-tools](examples/mcp-tools/): an MCP server whose tools wrap an upstream HTTP API.
103
- - [assistant](examples/assistant/): no server, the same implementations as a model's tools
104
- and as local commands.
91
+ - [assistant](examples/assistant/): no server, the same implementations as a model's tools and as local commands.
105
92
  - [live-rpc](examples/live-rpc/): Effect RPC over a WebSocket.
106
- - [kitchen-sink](examples/kitchen-sink/): every module and nearly every option in one
107
- application, with an index from each to the file that shows it.
93
+ - [build-events](examples/build-events/): events watched over MCP, HTTP, RPC, a command line and webhooks, and actions reporting progress and answering a stream.
94
+ - [kitchen-sink](examples/kitchen-sink/): every module and nearly every option in one application, with an index from each to the file that shows it.
108
95
 
109
96
  ## Docs
110
97
 
111
- [docs/](docs/README.md) is written for coding agents: one short page per module, organized by
112
- task, each linking to the example files that show the task working. Start at
113
- [docs/README.md](docs/README.md).
98
+ [docs/](docs/README.md) is written for coding agents: one short page per module, organized by task, each linking to the example files that show the task working. Start at [docs/README.md](docs/README.md).
114
99
 
115
100
  ## For agents
116
101
 
117
- The `effect-actions` skill puts the docs in front of a coding agent. With the
118
- [skills CLI](https://github.com/vercel-labs/skills), for any agent it supports:
102
+ The `effect-actions` skill puts the docs in front of a coding agent. With the [skills CLI](https://github.com/vercel-labs/skills), for any agent it supports:
119
103
 
120
104
  ```sh
121
105
  npx skills add gjermundgaraba/effect-actions --skill effect-actions
@@ -135,16 +119,11 @@ codex plugin marketplace add gjermundgaraba/effect-actions
135
119
  codex plugin add effect-actions@effect-actions-marketplace
136
120
  ```
137
121
 
138
- The skill is generated from `docs/` on the main branch, which can be ahead of the latest
139
- release. Its links to the examples are paths into the installed package,
140
- `node_modules/@gjermundgaraba/effect-actions/examples`, which an agent reads without a network;
141
- the pages of the installed version are beside them, in
142
- `node_modules/@gjermundgaraba/effect-actions/docs`.
122
+ The skill is generated from `docs/` on the main branch, which can be ahead of the latest release. Its links to the examples are paths into the installed package, `node_modules/@gjermundgaraba/effect-actions/examples`, which an agent reads without a network; the pages of the installed version are beside them, in `node_modules/@gjermundgaraba/effect-actions/docs`.
143
123
 
144
124
  ## Status
145
125
 
146
- The `effect` peer range is in [docs/setup.md](docs/setup.md#install). Actions are unary JSON over HTTP, MCP and Effect RPC, whose protocols speak JSON alone: no streaming or uploads.
147
- Token verification and the authorization rule belong to the application; the library supplies the authentication descriptor and provider, runs the rule, and answers the refusals. See [what the library leaves to you](docs/README.md#what-the-library-leaves-to-you).
126
+ The `effect` peer range is in [docs/setup.md](docs/setup.md#install). Actions are unary JSON over HTTP, MCP and Effect RPC, whose protocols speak JSON alone: no streaming or uploads. Token verification and the authorization rule belong to the application; the library supplies the authentication descriptor and provider, runs the rule, and answers the refusals. See [what the library leaves to you](docs/README.md#what-the-library-leaves-to-you).
148
127
 
149
128
  ## Acknowledgements
150
129
 
@@ -152,5 +131,4 @@ A few ideas (like the native Toolkit and CLI projections) are inspired by the ex
152
131
 
153
132
  ## Contributing
154
133
 
155
- The rules for changing the library are in
156
- [AGENTS.md](https://github.com/gjermundgaraba/effect-actions/blob/main/AGENTS.md). MIT licensed.
134
+ The rules for changing the library are in [AGENTS.md](https://github.com/gjermundgaraba/effect-actions/blob/main/AGENTS.md). MIT licensed.
package/dist/Action.d.mts CHANGED
@@ -1,15 +1,18 @@
1
+ import { Codec, CodecOf, Fields, NoInput } from "./contract/codec.mjs";
1
2
  import { Anyone } from "./contract/caller.mjs";
2
3
  import { BuiltIn, Forbidden, InvalidInput, Issue, Refusal, Unauthenticated } from "./contract/errors.mjs";
3
- import { ActionOf, AnyImplementation, Authorize, BuildError, BuildServices, Holding, Implementation, Known, Member, Offered, OptionalUnless, Protected, RequestOf, SelectedOf, Served, Serving } from "./contract/implementation.mjs";
4
+ import { EventBuildError, EventBuildServices, EventOf, EventRequestOf, HoldingEvents, ServingEvents } from "./contract/feed.mjs";
5
+ import { Any as Any$1, Received, SubscriptionError } from "./Event.mjs";
6
+ import { ReportingOf } from "./contract/progress.mjs";
7
+ import { ActionOf, AnyImplementation, Authorize, BuildError, BuildServices, Holding, Implementation, Known, Member, Offered, OptionalUnless, Protected, RequestOf, SelectedEventsOf, SelectedOf, Served, Serving } from "./contract/implementation.mjs";
8
+ import { Authorizer, Deferred as Deferred$1 } from "./contract/authorizer.mjs";
4
9
  import { Call } from "./contract/call.mjs";
10
+ import { Idempotency, layerIdempotency } from "./contract/idempotency.mjs";
11
+ import { Answer, Stream, StreamOf } from "./contract/stream.mjs";
12
+ import { WatchArgs } from "./contract/watch.mjs";
5
13
  import { Image } from "./contract/media.mjs";
6
- import { Context, Effect, Layer, Schema, Scope } from "effect";
7
- type Codec = Schema.Codec<unknown, unknown, never, never>;
8
- type Fields = {
9
- readonly [key: string]: Codec;
10
- };
11
- declare const NoInput: Schema.$Record<Schema.String, Schema.Never>;
12
- type CodecOf<S extends Codec | Fields> = S extends Codec ? S : S extends Schema.Struct.Fields ? keyof S extends never ? typeof NoInput : Extract<Schema.Struct<S>, Codec> : never;
14
+ import { InputCheck } from "./contract/input-check.mjs";
15
+ import { Context, Effect, Layer, Schema, Scope, Stream as Stream$1 } from "effect";
13
16
  /**
14
17
  * How the action's tool presents itself over MCP, in MCP's own names; every field is optional
15
18
  * and none enforces anything. A hint left out is MCP's default, but `destructiveHint`, which is
@@ -69,11 +72,28 @@ export interface Options {
69
72
  */
70
73
  readonly input?: Codec | Fields | undefined;
71
74
  /**
72
- * A schema or struct fields. Omit for an action that returns nothing, `Schema.Void`; what each
73
- * surface answers is on its `layer`, `layerHttp`, `runStdio`, `make` or `command`. A top-level field whose schema is
74
- * `Action.Image` is a media field: see `Action.Image`.
75
+ * A schema or struct fields, or `Action.Stream(element)` for an action answering a stream of
76
+ * elements. Omit for an action that returns nothing, `Schema.Void`; what each surface answers
77
+ * is on its `layer`, `layerHttp`, `runStdio`, `make` or `command`. A top-level field whose
78
+ * schema is `Action.Image` is a media field: see `Action.Image`.
75
79
  */
76
80
  readonly success?: Codec | Fields | undefined;
81
+ /**
82
+ * A schema or struct fields: what the handler reports while it runs, through its second
83
+ * argument's `progress`, before it answers. Omit for an action that reports nothing; the
84
+ * handler then takes its input alone. A client's call takes `onProgress` after the input;
85
+ * how each surface carries progress is on `ActionHttp.layer`, `ActionRpc.make`,
86
+ * `ActionMcp.layerHttp`, `ActionToolkit.make` and `ActionCli.command`.
87
+ *
88
+ * **Gotchas**
89
+ *
90
+ * - Progress is a snapshot to show, not an event to count: values a caller falls behind on are dropped, the oldest first, while the answer never is.
91
+ * - A value breaking the schema is the call's defect, as an answer breaking `success` is.
92
+ * - A caller's `onProgress` that fails or dies is logged as a warning, and the call goes on and answers: the handler is never interrupted for it.
93
+ * - In process, `Action.client`, the toolkit and a local command hand `onProgress` each value as reported, checked by the schema and never encoded: a value with no JSON form reaches them, and is the call's defect on a surface sending JSON.
94
+ * - A value the caller's `progress` schema cannot decode, where client and server differ, is logged and dropped by MCP's client; RPC's and HTTP's clients decode it with the response, so the call fails with a `SchemaError`, `onProgress` given or not.
95
+ */
96
+ readonly progress?: Codec | Fields | undefined;
77
97
  /**
78
98
  * The declared failures: one schema, or a list of them, as `HttpApiEndpoint` takes them;
79
99
  * each keeps its own HTTP status annotation, the third argument of
@@ -90,7 +110,7 @@ export interface Options {
90
110
  * - An entry that is a `Schema.suspend` whose thunk reads a `const` declared further down throws a `ReferenceError` at `make`, which reads every `_tag` when called: declare the error first.
91
111
  * - A failure the schemas reject, undeclared or breaking a check, is the call's defect on every surface, beside the `SchemaError` saying why; a class error built with `disableChecks` is accepted and fails to encode instead.
92
112
  */
93
- readonly error?: Codec | ReadonlyArray<Codec> | undefined;
113
+ readonly error?: Codec | readonly Codec[] | undefined;
94
114
  /**
95
115
  * Whether the action leaves its resource unchanged. Required: an action nobody classified
96
116
  * is the one a reviewer must check. An implementation's `authorize` may read it, and MCP's
@@ -101,25 +121,57 @@ export interface Options {
101
121
  readonly readOnly: boolean;
102
122
  /** How the action's tool presents itself over MCP, and on a native Toolkit's tools. */
103
123
  readonly mcp?: Mcp;
124
+ /**
125
+ * The input field, a string, holding the call's idempotency key: a call repeated with the key
126
+ * and the same input is answered as the first was, its handler run once, on every surface, as
127
+ * the key travels with the input. The caller makes the key at random for the call it means,
128
+ * and sends it again on each retry; a call without it runs as any other. What is answered
129
+ * again is kept by `Action.Idempotency`, which an implementation of the action requires at
130
+ * startup: provide `Action.layerIdempotency`, which a Durable Object's `make` and `handlers`
131
+ * provide over its storage.
132
+ *
133
+ * **Gotchas**
134
+ *
135
+ * - Kept are an answer and a failure the action declares or a built-in one; a defect, an interruption and a declared failure whose status is 408, 429, 500, 502, 503 or 504 are not, and the call runs again when retried: at least once, not exactly once.
136
+ * - A repeat with the key and another input fails with `InvalidInput`, naming the key: the key stands for one call, and a record's keys in another order are the same input.
137
+ * - A repeat while the first runs waits for its answer, in the same server, traced as an `Idempotency.wait` span; two servers sharing a store run both, the later answer kept.
138
+ * - A repeat answers without the progress the first reported.
139
+ * - An answer kept before a deployment changed `success` or an error schema, so that it no longer decodes, is ignored: the call runs again.
140
+ * - An action answering `Action.Stream` throws at `make`: an answer is kept whole.
141
+ * - A key is each caller's own only where the surface's authentication has `principal`: without one, as over `Action.client`, `ActionToolkit`, the CLI or a public action, another caller sending it with the same input is given the answer, as a model filling the field in may, so make it at random, never from the input.
142
+ */
143
+ readonly idempotencyKey?: string | undefined;
104
144
  }
105
145
  type UnknownMcp<O> = O extends {
106
146
  readonly mcp?: infer H;
107
147
  } ? Exclude<H extends unknown ? keyof H : never, keyof Mcp> : never;
108
- type KnownMcp<O> = {
148
+ interface KnownMcp<O> {
109
149
  readonly mcp?: { readonly [K in UnknownMcp<O>]: never; };
110
- };
150
+ }
151
+ type StringFields<I> = { readonly [F in keyof I]-?: I[F] extends string | undefined ? F : never; }[keyof I] & string;
152
+ type KnownKey<O> = O extends {
153
+ readonly idempotencyKey: infer K extends string;
154
+ } ? K extends StringFields<SchemaOf<O, "input", typeof NoInput>["Type"]> ? unknown : {
155
+ readonly idempotencyKey: K & {
156
+ readonly "~": "idempotencyKey names a string field of the input";
157
+ };
158
+ } : unknown;
159
+ type KeyOf<O> = O extends {
160
+ readonly idempotencyKey: infer K extends string;
161
+ } ? K : undefined;
111
162
  type ErrorMembers<G> = G extends ReadonlyArray<infer E> ? E : G;
112
163
  type OnlyOptions<O> = { readonly [K in Exclude<O extends unknown ? keyof O : never, keyof Options>]: never; };
113
- type Rules<O> = Options extends O ? unknown : OnlyOptions<O> & KnownMcp<O>;
164
+ type Rules<O> = Options extends O ? unknown : OnlyOptions<O> & KnownMcp<O> & KnownKey<O>;
114
165
  type OptionOf<O, K extends keyof Options, Default> = O extends unknown ? K extends keyof O ? Exclude<O[K], undefined> | ({} extends Pick<O, K> ? Default : undefined extends O[K] ? Default : never) : Default : never;
115
166
  type SchemaOf<O, K extends "input" | "success", Default extends Codec> = CodecOf<Extract<OptionOf<O, K, Default>, Codec | Fields>>;
116
- type ErrorSchemasOf<O> = ReadonlyArray<ErrorMembers<Extract<OptionOf<O, "error", []>, Codec | ReadonlyArray<Codec>>>>;
167
+ type ProgressOf<O> = [Extract<OptionOf<O, "progress", never>, Codec | Fields>] extends [never] ? undefined : CodecOf<Extract<OptionOf<O, "progress", never>, Codec | Fields>>;
168
+ type ErrorSchemasOf<O> = ReadonlyArray<ErrorMembers<Extract<OptionOf<O, "error", []>, Codec | readonly Codec[]>>>;
117
169
  /**
118
170
  * A pure contract: schemas and transport metadata, bound to handlers by `implement`. Its
119
171
  * `name`, `input`, `success`, `error` and `readOnly` are public: reuse them, as in
120
172
  * `Schema.Array(SaveLink.input)`.
121
173
  */
122
- export interface Action<Name extends string, Input extends Codec, Success extends Codec, Errors extends ReadonlyArray<Codec>, ReadOnly extends boolean = boolean, Caller extends Options["caller"] = Options["caller"]> {
174
+ export interface Action<Name extends string, Input extends Codec, Success extends Codec, ErrorSchemas extends readonly Codec[], ReadOnly extends boolean = boolean, Caller extends Options["caller"] = Options["caller"], Progress extends Codec | undefined = Codec | undefined, Key extends string | undefined = string | undefined> {
123
175
  readonly caller: Caller;
124
176
  readonly name: Name;
125
177
  readonly description: string;
@@ -130,26 +182,33 @@ export interface Action<Name extends string, Input extends Codec, Success extend
130
182
  readonly input: Input;
131
183
  readonly success: Success;
132
184
  /** The declared failures, as a list: one given alone is a list of one. */
133
- readonly error: Errors;
185
+ readonly error: ErrorSchemas;
186
+ /** What the handler reports while it runs, as a schema; `undefined` where none is declared. */
187
+ readonly progress: Progress;
134
188
  readonly readOnly: ReadOnly;
135
189
  /** The `mcp` options as given, none when left out. */
136
190
  readonly mcp: Mcp;
191
+ /** The input field holding the call's idempotency key; `undefined` where none is named. */
192
+ readonly idempotencyKey: Key;
137
193
  }
138
194
  /** Any action, with its schemas erased. */
139
- export type Any = Action<string, Codec, Codec, ReadonlyArray<Codec>>;
195
+ export type Any = Action<string, Codec, Codec, readonly Codec[]>;
140
196
  /**
141
197
  * A list of declared errors, as an action's `error` holds it, and a binding's or an
142
198
  * authentication descriptor's: `<E extends Action.Errors>` constrains one.
143
199
  */
144
- export type Errors = ReadonlyArray<Codec>;
145
- /** Receives decoded input; may fail only with the declared errors and the built-in ones. */
146
- export type Handler<A extends Any, R = never> = (input: A["input"]["Type"]) => Effect.Effect<A["success"]["Type"], A["error"][number]["Type"] | BuiltIn, R>;
200
+ export type Errors = readonly Codec[];
201
+ /**
202
+ * Receives decoded input, and what it reports progress by where `A` declares `progress`; may
203
+ * fail only with the declared errors and the built-in ones.
204
+ */
205
+ export type Handler<A extends Any, R = never> = (input: A["input"]["Type"], ...reporting: ReportingOf<A>) => Answer<A["success"], A["error"][number]["Type"] | BuiltIn, R>;
147
206
  /**
148
207
  * The handlers of the actions `A`, keyed by name, as `implement` takes them: what a builder
149
208
  * written apart from `implement` returns, `satisfies Action.Handlers<typeof actions>`, so its
150
209
  * handlers are typed from their contracts, as another authorizer of the same handlers needs.
151
210
  */
152
- export type Handlers<A extends ReadonlyArray<Any>, R = never> = { readonly [Action in A[number] as Action["name"]]: Handler<Action, R>; };
211
+ export type Handlers<A extends readonly Any[], R = never> = { readonly [Each in A[number] as Each["name"]]: Handler<Each, R>; };
153
212
  /**
154
213
  * Define an action contract: what it takes and answers, whether it changes anything, and who
155
214
  * may call it. The name is also the route segment, the client method and the tool name.
@@ -162,7 +221,23 @@ export type Handlers<A extends ReadonlyArray<Any>, R = never> = { readonly [Acti
162
221
  * - Surfaces match an implementation to a contract by object identity, never by name: implement, bind and list the one exported value. A list narrowed at run time is still typed as holding every member.
163
222
  * - In a helper whose type parameter stands for `mcp`, `make` refuses the option, since it cannot check the hints: type the parameter `Action.Mcp`, or spread a constant.
164
223
  */
165
- export declare function make<const Name extends string, const O extends Options>(name: Name, options: O & NoInfer<Rules<O>>): Action<Name, SchemaOf<O, "input", typeof NoInput>, SchemaOf<O, "success", typeof Schema.Void>, ErrorSchemasOf<O>, O["readOnly"], O["caller"]>;
224
+ export declare function make<const Name extends string, const O extends Options>(name: Name, options: O & NoInfer<Rules<O>>): Action<Name, SchemaOf<O, "input", typeof NoInput>, SchemaOf<O, "success", typeof Schema.Void>, ErrorSchemasOf<O>, O["readOnly"], O["caller"], ProgressOf<O>, KeyOf<O>>;
225
+ /**
226
+ * `action` with the errors `more` declared after its own, as if `make` had been given them in
227
+ * `error`: its handler may fail with them, and every surface answers and every client decodes
228
+ * them. For errors a module adds to the actions it serves, such as a proxy's or a store's,
229
+ * whose contracts are written elsewhere: `pipe(action, Action.withErrors([Unavailable]))`, or
230
+ * `Action.withErrors(action, [Unavailable])`.
231
+ *
232
+ * **Gotchas**
233
+ *
234
+ * - It returns a new contract with the same name: implement, bind and list the value it returns, since surfaces match a contract by identity; the original and the copy in one surface are one name held twice.
235
+ * - A `_tag` that `more` shares with the action's own errors or a built-in one throws as `make` throws for one `error` list: reuse the one schema value, which is declared once.
236
+ */
237
+ export declare const withErrors: {
238
+ <const More extends Errors>(more: More): <Name extends string, Input extends Codec, Success extends Codec, ErrorSchemas extends Errors, ReadOnly extends boolean, Caller extends Options["caller"], Progress extends Codec | undefined, Key extends string | undefined>(action: Action<Name, Input, Success, ErrorSchemas, ReadOnly, Caller, Progress, Key>) => Action<Name, Input, Success, readonly [...ErrorSchemas, ...More], ReadOnly, Caller, Progress, Key>;
239
+ <Name extends string, Input extends Codec, Success extends Codec, ErrorSchemas extends Errors, ReadOnly extends boolean, Caller extends Options["caller"], Progress extends Codec | undefined, Key extends string | undefined, const More extends Errors>(action: Action<Name, Input, Success, ErrorSchemas, ReadOnly, Caller, Progress, Key>, more: More): Action<Name, Input, Success, readonly [...ErrorSchemas, ...More], ReadOnly, Caller, Progress, Key>;
240
+ };
166
241
  /**
167
242
  * `actions` keyed by name, each its exact contract: `byName(actions).getUser`, its schemas
168
243
  * where a test or a mock reads them, and its types where a helper takes a name,
@@ -170,22 +245,23 @@ export declare function make<const Name extends string, const O extends Options>
170
245
  * `Extract` over the list does not. The list is typed by its members, as every surface types
171
246
  * one. A name held twice is refused, as a surface refuses it.
172
247
  */
173
- export declare function byName<const Actions extends ReadonlyArray<Any>>(actions: Actions): { readonly [A in Actions[number] as A["name"]]: A; };
174
- type Target = Any | ReadonlyArray<Any>;
248
+ export declare function byName<const Actions extends readonly Any[]>(actions: Actions): { readonly [A in Actions[number] as A["name"]]: A; };
249
+ type Target = Any | readonly Any[];
175
250
  type TakesNoAuthorize<T extends Target> = [Protected<ActionsOf<T>>] extends [never] ? unknown : "A protected action takes { authorize }: your rule, or Action.allowAll";
176
- type ActionsOf<T extends Target> = T extends ReadonlyArray<Any> ? T[number] : Extract<T, Any>;
251
+ type ActionsOf<T extends Target> = T extends readonly Any[] ? T[number] : Extract<T, Any>;
177
252
  type NamesOf<T extends Target> = ActionsOf<T>["name"];
253
+ type KeepsAnswers<T extends Target> = [Extract<ActionsOf<T>, {
254
+ readonly idempotencyKey: string;
255
+ }>] extends [never] ? never : Idempotency;
178
256
  type NoHandler<R> = {
179
257
  readonly "~list": R;
180
258
  };
181
- type Single<T extends Target, R> = T extends Any ? (input: T["input"]["Type"]) => Effect.Effect<T["success"]["Type"], T["error"][number]["Type"] | BuiltIn, R> : NoHandler<R>;
259
+ type Single<T extends Target, R> = T extends Any ? (input: T["input"]["Type"], ...reporting: ReportingOf<T>) => Answer<T["success"], T["error"][number]["Type"] | BuiltIn, R> : NoHandler<R>;
182
260
  type Several<T extends Target, RequestServices> = { readonly [K in keyof RequestServices]: K extends NamesOf<T> ? Handler<Extract<ActionsOf<T>, {
183
261
  readonly name: K;
184
262
  }>, RequestServices[K]> : never; };
185
263
  type HandlersOf<T extends Target, R, RequestServices> = Single<T, R> | Several<T, RequestServices>;
186
- type Deferred<T> = [T][T extends unknown ? 0 : never];
187
- type Authorizer<A extends Any, RA, EAX, RAX> = ((action: A) => Effect.Effect<void, Refusal, RA>) | Effect.Effect<(action: A) => Effect.Effect<void, Refusal, RA>, EAX, RAX>;
188
- type Implemented<T extends Target, RequestServices, R, EX, RX, RA, EAX, RAX> = Implementation<ActionsOf<T>, { readonly [K in NamesOf<T>]: Exclude<T extends ReadonlyArray<Any> ? RequestServices[K & keyof RequestServices] : R, Scope.Scope>; }, Deferred<EX>, Deferred<Exclude<RX, Scope.Scope>>, Exclude<RA, Scope.Scope>, Deferred<EAX>, Deferred<Exclude<RAX, Scope.Scope>>>;
264
+ type Implemented<T extends Target, RequestServices, R, EX, RX, RA, EAX, RAX> = Implementation<ActionsOf<T>, { readonly [K in NamesOf<T>]: Exclude<T extends readonly Any[] ? RequestServices[K & keyof RequestServices] : R, Scope.Scope>; }, Deferred$1<EX>, Deferred$1<Exclude<RX, Scope.Scope> | KeepsAnswers<T>>, Exclude<RA, Scope.Scope>, Deferred$1<EAX>, Deferred$1<Exclude<RAX, Scope.Scope>>>;
189
265
  /** Every authenticated caller may call a protected action. */
190
266
  export declare const allowAll: Authorize<Any>;
191
267
  /**
@@ -206,12 +282,12 @@ export declare const allowAll: Authorize<Any>;
206
282
  * - What a plain `authorize` yields is a request-time requirement of every surface; to read a store once, make `authorize` an Effect that yields it and returns the function.
207
283
  * - An authorizer fails only with `Unauthenticated` or `Forbidden`; a limit callers should see, such as a rate limit, is the handler's, declared in the action's `error`.
208
284
  */
209
- export declare function implement<const T extends Target, RequestServices extends (T extends ReadonlyArray<Any> ? { readonly [K in NamesOf<T>]: unknown; } : never), R = never, EX = never, RX = never, RA = never, EAX = never, RAX = never>(actions: T, handlers: HandlersOf<T, R, RequestServices> | Effect.Effect<HandlersOf<T, R, RequestServices>, EX, RX>, options: [Protected<ActionsOf<T>>] extends [never] ? {
285
+ export declare function implement<const T extends Target, RequestServices extends (T extends readonly Any[] ? { readonly [K in NamesOf<T>]: unknown; } : never), R = never, EX = never, RX = never, RA = never, EAX = never, RAX = never>(actions: T, handlers: HandlersOf<T, R, RequestServices> | Effect.Effect<HandlersOf<T, R, RequestServices>, EX, RX>, options: [Protected<ActionsOf<T>>] extends [never] ? {
210
286
  readonly authorize?: "A public-only target takes no authorize";
211
287
  } | undefined : {
212
288
  readonly authorize: Authorizer<Protected<ActionsOf<T>>, RA, EAX, RAX>;
213
289
  }): Implemented<T, RequestServices, R, EX, RX, RA, EAX, RAX>;
214
- export declare function implement<const T extends Target, RequestServices extends (T extends ReadonlyArray<Any> ? { readonly [K in NamesOf<T>]: unknown; } : never), R = never, EX = never, RX = never>(actions: T & TakesNoAuthorize<T>, handlers: HandlersOf<T, R, RequestServices> | Effect.Effect<HandlersOf<T, R, RequestServices>, EX, RX>): Implemented<T, RequestServices, R, EX, RX, never, never, never>;
290
+ export declare function implement<const T extends Target, RequestServices extends (T extends readonly Any[] ? { readonly [K in NamesOf<T>]: unknown; } : never), R = never, EX = never, RX = never>(actions: T & TakesNoAuthorize<T>, handlers: HandlersOf<T, R, RequestServices> | Effect.Effect<HandlersOf<T, R, RequestServices>, EX, RX>): Implemented<T, RequestServices, R, EX, RX, never, never, never>;
215
291
  /**
216
292
  * The builders of `implementations`, as one layer providing nothing. Surfaces build them
217
293
  * themselves, once per layer graph, but routes under `HttpRouter.serve` or `Testing.layer`
@@ -224,26 +300,43 @@ export declare function implement<const T extends Target, RequestServices extend
224
300
  * - A job merged beside `HttpRouter.serve` with no `Action.layer` above both shares the server's build only when its own side builds first, which depends on timing: put the job inside the layer the server serves, or provide this layer above both.
225
301
  * - Do not wrap a server in `Layer.fresh`: two servers already share no build. It is for two surfaces in one graph that must each have a build of their own; provide what they share, authentication included, outside it.
226
302
  */
227
- export declare function layer<const Apps extends Served>(implementations: Apps): Layer.Layer<never, BuildError<Member<Apps>>, BuildServices<Member<Apps>>>;
228
- type Method<A extends Any, R> = Call<A, Effect.Effect<A["success"]["Type"], A["error"][number]["Type"] | BuiltIn, R>>;
303
+ export declare function layer<const Apps extends Served>(implementations: Apps): Layer.Layer<never, BuildError<Member<Apps>> | EventBuildError<Member<Apps>>, BuildServices<Member<Apps>> | EventBuildServices<Member<Apps>>>;
304
+ type Method<A extends Any, R> = Call<A, Answer<A["success"], A["error"][number]["Type"] | BuiltIn, R>>;
229
305
  /**
230
- * Every action of the implementations `Apps`, one or a list, as `client.<action>(input)`: what
306
+ * Every action of the implementations `Apps`, one or a list, as `client.<action>(input)`, or
307
+ * those of `Listed`, and `watch` for their event types, or those of `ListedEvents`: what
231
308
  * `client` gives, each method owing per call what its handler and `authorize` read.
232
309
  */
233
- export type Client<Apps extends Served, Listed extends Any = Extract<ActionOf<Member<Apps>>, Any>> = { readonly [A in Serving<Member<Apps>, Listed> as A["name"]]: Method<A, RequestOf<Member<Apps>, A>>; };
234
- /** What `client` takes: the actions to call. */
235
- export interface ClientOptions<A extends Any> {
310
+ export type Client<Apps extends Served, Listed extends Any = Extract<ActionOf<Member<Apps>>, Any>, ListedEvents extends Any$1 = EventOf<Member<Apps>>> = { readonly [A in Serving<Member<Apps>, Listed> as A["name"]]: Method<A, RequestOf<Member<Apps>, A>>; } & Watching<Member<Apps>, ListedEvents>;
311
+ /**
312
+ * Watch one of the event types of the implementations `App`, or of those among `Listed`: its
313
+ * occurrences, and the gaps where occurrences were missed, owing per subscription what its
314
+ * source and `authorize` read.
315
+ */
316
+ export type Watch<App, Listed extends Any$1 = EventOf<App>> = <E extends ServingEvents<App, Listed>, SE = never>(event: E, ...args: WatchArgs<E, SE>) => Stream$1.Stream<Received<E>, SubscriptionError | BuiltIn | SE, EventRequestOf<App, E>>;
317
+ type Watching<App, Listed extends Any$1> = [ServingEvents<App, Listed>] extends [never] ? unknown : {
318
+ readonly watch: Watch<App, Listed>;
319
+ };
320
+ /** What `client` takes: the actions to call and the event types to watch. */
321
+ export interface ClientOptions<A extends Any | Any$1> {
236
322
  /**
237
- * The actions to call, among the implementations': `[GetUser]`. Defaults to every action of
238
- * them. Options whose `actions` may be absent are typed as every action, as they may run.
323
+ * The actions to call and the event types to watch, among the implementations':
324
+ * `[GetUser, UserRenamed]`, exactly those. Defaults to every one of them. Options whose
325
+ * `actions` may be absent are typed as every one, as they may run.
326
+ *
327
+ * **Gotchas**
328
+ *
329
+ * - A list naming actions alone watches no event type: the client has no `watch` until the event types are listed beside them.
239
330
  */
240
- readonly actions?: ReadonlyArray<A> | undefined;
331
+ readonly actions?: readonly A[] | undefined;
241
332
  }
242
333
  /**
243
334
  * Call implementations in process: one method per action, taking its input directly as
244
335
  * `ActionHttp.client`'s methods do, `client.renameUser({ id, name })`, so moving between the
245
- * two changes the line acquiring it. A call runs as a remote one does, its input and its answer
246
- * through their JSON codecs, the implementation's `authorize` and the handler in a scope of
336
+ * two changes the line acquiring it; given event implementations, `watch(event, input, options)`
337
+ * too, for their event types or those `actions` lists, as every client of a surface serving
338
+ * events has it. A call runs as a remote one does, its input and its answer through their JSON
339
+ * codecs, the implementation's `authorize` and the handler in a scope of
247
340
  * their own, failing with the action's errors and the built-in ones. Acquiring it builds the
248
341
  * implementations' builders into the layer graph it is acquired in, as a layer does, and its
249
342
  * scope holds them: acquire it where builders live.
@@ -256,6 +349,7 @@ export interface ClientOptions<A extends Any> {
256
349
  * - Every implemented action has a method, whether or not a surface serves it; `actions` lists the ones to call, and an implementation holding none of them is left unbuilt.
257
350
  * - It drops undeclared input fields as it encodes, and fails with `InvalidInput`, running nothing, for an input that does not encode.
258
351
  * - A builder acquiring a client of its own implementation is refused by the compiler, TS7022, and with the types bypassed never completes: call the services both use instead.
352
+ * - `watch` runs as the caller around the stream, authorized when it starts.
259
353
  */
260
- 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>;
261
- export { type AnyImplementation, Anyone, type Authorize, type BuildError, type BuildServices, BuiltIn, Forbidden, Image, type Implementation, InvalidInput, type Issue, Refusal, Unauthenticated };
354
+ export declare function client<const Apps extends Served, const O extends ClientOptions<ActionOf<Member<Apps>> | EventOf<Member<Apps>>> = {}>(implementations: Apps, ...options: OptionalUnless<O, ClientOptions<ActionOf<Member<Apps>> | EventOf<Member<Apps>>> & O & NoInfer<Known<O, ClientOptions<Any>>>>): Effect.Effect<Client<Apps, Offered<O, ActionOf<Member<Apps>>>, Offered<O, EventOf<Member<Apps>>>>, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | EventBuildError<HoldingEvents<Member<Apps>, SelectedEventsOf<O, Apps>>, SelectedEventsOf<O, Apps>>, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | EventBuildServices<HoldingEvents<Member<Apps>, SelectedEventsOf<O, Apps>>, SelectedEventsOf<O, Apps>> | Scope.Scope>;
355
+ export { type AnyImplementation, Anyone, type Authorize, type BuildError, type BuildServices, BuiltIn, Forbidden, Idempotency, Image, type Implementation, InputCheck, InvalidInput, type Issue, Refusal, Stream, type StreamOf, Unauthenticated, layerIdempotency };