@gjermundgaraba/effect-actions 0.19.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 (326) hide show
  1. package/README.md +19 -41
  2. package/dist/Action.d.mts +140 -48
  3. package/dist/Action.mjs +88 -44
  4. package/dist/ActionCli.d.mts +70 -35
  5. package/dist/ActionCli.mjs +53 -34
  6. package/dist/ActionHttp.d.mts +70 -38
  7. package/dist/ActionHttp.mjs +76 -25
  8. package/dist/ActionMcp.d.mts +56 -24
  9. package/dist/ActionMcp.mjs +104 -61
  10. package/dist/ActionRpc.d.mts +222 -57
  11. package/dist/ActionRpc.mjs +301 -49
  12. package/dist/ActionToolkit.d.mts +22 -7
  13. package/dist/ActionToolkit.mjs +37 -14
  14. package/dist/Authentication.d.mts +42 -12
  15. package/dist/Authentication.mjs +38 -32
  16. package/dist/Event.d.mts +370 -0
  17. package/dist/Event.mjs +103 -0
  18. package/dist/Testing.d.mts +1 -3
  19. package/dist/Testing.mjs +2 -3
  20. package/dist/Webhook.d.mts +146 -0
  21. package/dist/Webhook.mjs +72 -0
  22. package/dist/authentication/provider.d.mts +35 -12
  23. package/dist/authentication/provider.mjs +2 -3
  24. package/dist/authentication/refusal.mjs +3 -4
  25. package/dist/cli/commands.d.mts +9 -7
  26. package/dist/cli/commands.mjs +85 -35
  27. package/dist/contract/ast.mjs +5 -3
  28. package/dist/contract/authorizer.d.mts +8 -0
  29. package/dist/contract/authorizer.mjs +20 -0
  30. package/dist/contract/call.d.mts +9 -8
  31. package/dist/contract/call.mjs +49 -8
  32. package/dist/contract/callback.d.mts +7 -0
  33. package/dist/contract/callback.mjs +27 -0
  34. package/dist/contract/caller.d.mts +12 -3
  35. package/dist/contract/caller.mjs +23 -5
  36. package/dist/contract/canonical.mjs +10 -0
  37. package/dist/contract/codec.d.mts +9 -0
  38. package/dist/contract/codec.mjs +9 -0
  39. package/dist/contract/codes.mjs +81 -0
  40. package/dist/contract/declared.d.mts +0 -2
  41. package/dist/contract/declared.mjs +26 -11
  42. package/dist/contract/digest.mjs +6 -0
  43. package/dist/contract/errors.d.mts +9 -5
  44. package/dist/contract/errors.mjs +2 -3
  45. package/dist/contract/feed.d.mts +127 -0
  46. package/dist/contract/feed.mjs +249 -0
  47. package/dist/contract/hints.mjs +1 -2
  48. package/dist/contract/hub.d.mts +94 -0
  49. package/dist/contract/hub.mjs +133 -0
  50. package/dist/contract/idempotency.d.mts +47 -0
  51. package/dist/contract/idempotency.mjs +99 -0
  52. package/dist/contract/implementation.d.mts +73 -51
  53. package/dist/contract/implementation.mjs +94 -60
  54. package/dist/contract/input-check.d.mts +15 -0
  55. package/dist/contract/input-check.mjs +16 -0
  56. package/dist/contract/lease.mjs +28 -0
  57. package/dist/contract/listed.d.mts +4 -0
  58. package/dist/contract/listed.mjs +22 -0
  59. package/dist/contract/log.d.mts +41 -0
  60. package/dist/contract/log.mjs +144 -0
  61. package/dist/contract/media.d.mts +1 -3
  62. package/dist/contract/media.mjs +1 -2
  63. package/dist/contract/names.mjs +15 -6
  64. package/dist/contract/occurrence.d.mts +29 -0
  65. package/dist/contract/occurrence.mjs +45 -0
  66. package/dist/contract/pages.d.mts +13 -0
  67. package/dist/contract/pages.mjs +39 -0
  68. package/dist/contract/progress.d.mts +26 -0
  69. package/dist/contract/progress.mjs +72 -0
  70. package/dist/contract/push.d.mts +20 -0
  71. package/dist/contract/push.mjs +96 -0
  72. package/dist/contract/receiving.d.mts +29 -0
  73. package/dist/contract/receiving.mjs +42 -0
  74. package/dist/contract/recent.mjs +12 -0
  75. package/dist/contract/reported.mjs +16 -0
  76. package/dist/contract/serving.mjs +63 -0
  77. package/dist/contract/status.mjs +15 -7
  78. package/dist/contract/stream.d.mts +34 -0
  79. package/dist/contract/stream.mjs +31 -0
  80. package/dist/contract/subscribe.d.mts +45 -0
  81. package/dist/contract/subscribe.mjs +50 -0
  82. package/dist/contract/transient.mjs +28 -0
  83. package/dist/contract/watch.d.mts +18 -0
  84. package/dist/contract/watch.mjs +105 -0
  85. package/dist/contract/webhooks.d.mts +42 -0
  86. package/dist/contract/webhooks.mjs +53 -0
  87. package/dist/http/client.d.mts +37 -14
  88. package/dist/http/client.mjs +52 -14
  89. package/dist/http/events.d.mts +56 -0
  90. package/dist/http/events.mjs +102 -0
  91. package/dist/http/placed.d.mts +36 -0
  92. package/dist/http/placed.mjs +71 -0
  93. package/dist/http/schema-errors.mjs +2 -3
  94. package/dist/http/streamed.d.mts +21 -0
  95. package/dist/http/streamed.mjs +94 -0
  96. package/dist/http/watch.mjs +50 -0
  97. package/dist/mcp/bridge.mjs +283 -0
  98. package/dist/mcp/client.d.mts +30 -99
  99. package/dist/mcp/client.mjs +60 -134
  100. package/dist/mcp/events.mjs +142 -0
  101. package/dist/mcp/progress.mjs +34 -0
  102. package/dist/mcp/protocol.mjs +40 -15
  103. package/dist/mcp/reply.d.mts +79 -0
  104. package/dist/mcp/reply.mjs +99 -0
  105. package/dist/mcp/request.d.mts +40 -0
  106. package/dist/mcp/request.mjs +51 -0
  107. package/dist/mcp/watch.mjs +115 -0
  108. package/dist/rpc/watch.mjs +39 -0
  109. package/dist/stdio/console.mjs +14 -7
  110. package/dist/testing/memory.d.mts +1 -3
  111. package/dist/testing/memory.mjs +4 -5
  112. package/dist/webhook/address.mjs +47 -0
  113. package/dist/webhook/attempt.mjs +65 -0
  114. package/dist/webhook/receiver.mjs +67 -0
  115. package/dist/webhook/signing.mjs +28 -0
  116. package/dist/webhook/stored.mjs +57 -0
  117. package/dist/webhook/subscriptions.mjs +342 -0
  118. package/docs/Action-failures.md +2 -2
  119. package/docs/Action-streams.md +115 -0
  120. package/docs/Action.md +58 -124
  121. package/docs/ActionCli-failures.md +1 -2
  122. package/docs/ActionCli.md +83 -88
  123. package/docs/ActionHttp-failures.md +3 -3
  124. package/docs/ActionHttp.md +90 -70
  125. package/docs/ActionMcp-failures.md +2 -1
  126. package/docs/ActionMcp.md +120 -92
  127. package/docs/ActionRpc-events.md +126 -0
  128. package/docs/ActionRpc-failures.md +1 -2
  129. package/docs/ActionRpc.md +108 -79
  130. package/docs/ActionToolkit-failures.md +1 -2
  131. package/docs/ActionToolkit.md +33 -90
  132. package/docs/Authentication-advanced.md +57 -61
  133. package/docs/Authentication-failures.md +5 -4
  134. package/docs/Authentication.md +23 -58
  135. package/docs/Event-failures.md +15 -0
  136. package/docs/Event.md +281 -0
  137. package/docs/README.md +51 -82
  138. package/docs/Testing-failures.md +1 -5
  139. package/docs/Testing.md +126 -86
  140. package/docs/Webhook-failures.md +8 -0
  141. package/docs/Webhook.md +319 -0
  142. package/docs/setup-failures.md +2 -3
  143. package/docs/setup.md +32 -53
  144. package/examples/README.md +12 -37
  145. package/examples/assistant/README.md +21 -26
  146. package/examples/assistant/src/agent.ts +66 -24
  147. package/examples/assistant/src/cli.ts +7 -4
  148. package/examples/assistant/src/commands.ts +9 -4
  149. package/examples/assistant/src/identity/Identity.ts +5 -4
  150. package/examples/assistant/src/identity/authorize.ts +13 -5
  151. package/examples/assistant/src/identity/handlers.ts +3 -2
  152. package/examples/assistant/src/identity/permissions.ts +9 -3
  153. package/examples/assistant/src/platform/tasks-file.ts +15 -11
  154. package/examples/assistant/src/scripted.ts +7 -4
  155. package/examples/assistant/src/tasks/Tasks.ts +20 -6
  156. package/examples/assistant/src/tasks/handlers.ts +9 -5
  157. package/examples/assistant/src/tasks/store.ts +38 -11
  158. package/examples/assistant/src/toolkit.ts +11 -7
  159. package/examples/assistant/test/commands.test.ts +53 -27
  160. package/examples/assistant/test/identity/identity.test.ts +27 -11
  161. package/examples/assistant/test/platform/tasks-file.test.ts +39 -23
  162. package/examples/assistant/test/tasks/tasks.test.ts +62 -34
  163. package/examples/assistant/test/toolkit.test.ts +104 -75
  164. package/examples/build-events/README.md +83 -0
  165. package/examples/build-events/src/Api.ts +27 -0
  166. package/examples/build-events/src/Rpc.ts +26 -0
  167. package/examples/build-events/src/builds/Builds.ts +108 -0
  168. package/examples/build-events/src/builds/events.ts +54 -0
  169. package/examples/build-events/src/builds/handlers.ts +58 -0
  170. package/examples/build-events/src/builds/log.ts +30 -0
  171. package/examples/build-events/src/builds/status.ts +28 -0
  172. package/examples/build-events/src/builds/store.ts +130 -0
  173. package/examples/build-events/src/cli.ts +18 -0
  174. package/examples/build-events/src/commands.ts +32 -0
  175. package/examples/build-events/src/deploys/Deploys.ts +44 -0
  176. package/examples/build-events/src/deploys/approvers.ts +55 -0
  177. package/examples/build-events/src/deploys/events.ts +32 -0
  178. package/examples/build-events/src/deploys/handlers.ts +38 -0
  179. package/examples/build-events/src/identity/Identity.ts +29 -0
  180. package/examples/build-events/src/identity/members.ts +7 -0
  181. package/examples/build-events/src/identity/verifier.ts +22 -0
  182. package/examples/build-events/src/notifier/Notices.ts +26 -0
  183. package/examples/build-events/src/notifier/routes.ts +71 -0
  184. package/examples/build-events/src/notifier.ts +48 -0
  185. package/examples/build-events/src/routes.ts +71 -0
  186. package/examples/build-events/src/server.ts +62 -0
  187. package/examples/build-events/test/builds/events.test.ts +68 -0
  188. package/examples/build-events/test/builds/log.test.ts +71 -0
  189. package/examples/build-events/test/builds/progress.test.ts +57 -0
  190. package/examples/build-events/test/builds/status.test.ts +42 -0
  191. package/examples/build-events/test/commands.test.ts +103 -0
  192. package/examples/build-events/test/deploys/events.test.ts +52 -0
  193. package/examples/build-events/test/http.test.ts +95 -0
  194. package/examples/build-events/test/notifier.test.ts +80 -0
  195. package/examples/build-events/test/routes.test.ts +96 -0
  196. package/examples/build-events/test/rpc.test.ts +84 -0
  197. package/examples/build-events/test/support/as.ts +8 -0
  198. package/examples/build-events/test/support/memory.ts +43 -0
  199. package/examples/build-events/test/webhooks.test.ts +143 -0
  200. package/examples/kitchen-sink/README.md +178 -166
  201. package/examples/kitchen-sink/src/Actions.ts +11 -3
  202. package/examples/kitchen-sink/src/Api.ts +12 -6
  203. package/examples/kitchen-sink/src/Rpc.ts +7 -5
  204. package/examples/kitchen-sink/src/admin/Admin.ts +21 -10
  205. package/examples/kitchen-sink/src/admin/authenticate.ts +8 -4
  206. package/examples/kitchen-sink/src/admin/authorize.ts +13 -5
  207. package/examples/kitchen-sink/src/admin/grants.ts +5 -3
  208. package/examples/kitchen-sink/src/admin/handlers.ts +9 -5
  209. package/examples/kitchen-sink/src/agent.ts +14 -6
  210. package/examples/kitchen-sink/src/assistant/Assistant.ts +21 -15
  211. package/examples/kitchen-sink/src/assistant/conversations.ts +14 -9
  212. package/examples/kitchen-sink/src/assistant/demo-model.ts +2 -2
  213. package/examples/kitchen-sink/src/assistant/handlers.ts +42 -22
  214. package/examples/kitchen-sink/src/attachments/Attachments.ts +6 -6
  215. package/examples/kitchen-sink/src/attachments/download.ts +7 -6
  216. package/examples/kitchen-sink/src/attachments/handlers.ts +9 -4
  217. package/examples/kitchen-sink/src/attachments/store.ts +25 -18
  218. package/examples/kitchen-sink/src/browser.ts +10 -4
  219. package/examples/kitchen-sink/src/cli.ts +28 -12
  220. package/examples/kitchen-sink/src/config.ts +1 -1
  221. package/examples/kitchen-sink/src/identity/Identity.ts +24 -10
  222. package/examples/kitchen-sink/src/identity/authenticate.ts +19 -11
  223. package/examples/kitchen-sink/src/identity/authorize.ts +14 -4
  224. package/examples/kitchen-sink/src/identity/directory.ts +16 -7
  225. package/examples/kitchen-sink/src/identity/handlers.ts +1 -0
  226. package/examples/kitchen-sink/src/identity/members.ts +7 -2
  227. package/examples/kitchen-sink/src/identity/quota.ts +23 -20
  228. package/examples/kitchen-sink/src/issues/Issues.ts +91 -15
  229. package/examples/kitchen-sink/src/issues/assistant.ts +4 -2
  230. package/examples/kitchen-sink/src/issues/events.ts +46 -0
  231. package/examples/kitchen-sink/src/issues/export.ts +35 -0
  232. package/examples/kitchen-sink/src/issues/guide.ts +4 -3
  233. package/examples/kitchen-sink/src/issues/handlers.ts +20 -7
  234. package/examples/kitchen-sink/src/issues/store.ts +156 -67
  235. package/examples/kitchen-sink/src/issues/write-limit.ts +44 -18
  236. package/examples/kitchen-sink/src/memory.ts +11 -2
  237. package/examples/kitchen-sink/src/ops.ts +10 -5
  238. package/examples/kitchen-sink/src/request-policy.ts +25 -9
  239. package/examples/kitchen-sink/src/routes.ts +67 -27
  240. package/examples/kitchen-sink/src/sdk.ts +22 -12
  241. package/examples/kitchen-sink/src/seed.ts +15 -9
  242. package/examples/kitchen-sink/src/server.ts +24 -10
  243. package/examples/kitchen-sink/src/stdio.ts +9 -3
  244. package/examples/kitchen-sink/src/workspaces/Workspaces.ts +10 -7
  245. package/examples/kitchen-sink/src/workspaces/handlers.ts +3 -2
  246. package/examples/kitchen-sink/src/workspaces/registry.ts +16 -10
  247. package/examples/kitchen-sink/src/workspaces/tenant.ts +12 -5
  248. package/examples/kitchen-sink/test/admin/admin.test.ts +38 -23
  249. package/examples/kitchen-sink/test/assistant/assistant.test.ts +471 -299
  250. package/examples/kitchen-sink/test/attachments/attachments.test.ts +135 -94
  251. package/examples/kitchen-sink/test/identity/identity.test.ts +110 -82
  252. package/examples/kitchen-sink/test/identity/session.test.ts +65 -43
  253. package/examples/kitchen-sink/test/issues/assistant.test.ts +50 -31
  254. package/examples/kitchen-sink/test/issues/events.test.ts +273 -0
  255. package/examples/kitchen-sink/test/issues/export.test.ts +120 -0
  256. package/examples/kitchen-sink/test/issues/issues.test.ts +105 -75
  257. package/examples/kitchen-sink/test/request-policy.test.ts +130 -33
  258. package/examples/kitchen-sink/test/routes.test.ts +102 -57
  259. package/examples/kitchen-sink/test/support/as.ts +9 -0
  260. package/examples/kitchen-sink/test/support/scripted.ts +6 -5
  261. package/examples/kitchen-sink/test/support/services.ts +17 -0
  262. package/examples/live-rpc/README.md +33 -35
  263. package/examples/live-rpc/src/Rpc.ts +9 -5
  264. package/examples/live-rpc/src/browser.ts +39 -11
  265. package/examples/live-rpc/src/chat/Chat.ts +30 -16
  266. package/examples/live-rpc/src/chat/authorize.ts +2 -1
  267. package/examples/live-rpc/src/chat/events.ts +74 -0
  268. package/examples/live-rpc/src/chat/handlers.ts +13 -5
  269. package/examples/live-rpc/src/chat/room.ts +9 -5
  270. package/examples/live-rpc/src/chat/store.ts +51 -14
  271. package/examples/live-rpc/src/client.ts +22 -5
  272. package/examples/live-rpc/src/identity/Identity.ts +3 -3
  273. package/examples/live-rpc/src/identity/verifier.ts +8 -4
  274. package/examples/live-rpc/src/page.ts +58 -12
  275. package/examples/live-rpc/src/routes.ts +14 -10
  276. package/examples/live-rpc/src/server.ts +15 -5
  277. package/examples/live-rpc/test/browser.test.ts +127 -50
  278. package/examples/live-rpc/test/chat/chat.test.ts +215 -23
  279. package/examples/live-rpc/test/routes.test.ts +130 -46
  280. package/examples/live-rpc/test/server.test.ts +94 -33
  281. package/examples/mcp-tools/README.md +24 -38
  282. package/examples/mcp-tools/src/platform/fake-farm.ts +63 -24
  283. package/examples/mcp-tools/src/platform/print-farm.ts +59 -29
  284. package/examples/mcp-tools/src/printers/Printers.ts +18 -7
  285. package/examples/mcp-tools/src/printers/farm.ts +15 -5
  286. package/examples/mcp-tools/src/printers/handlers.ts +12 -6
  287. package/examples/mcp-tools/src/routes.ts +8 -6
  288. package/examples/mcp-tools/src/server.ts +12 -5
  289. package/examples/mcp-tools/src/stdio.ts +3 -2
  290. package/examples/mcp-tools/test/printers/printers.test.ts +132 -89
  291. package/examples/mcp-tools/test/routes.test.ts +140 -81
  292. package/examples/notes-service/README.md +30 -45
  293. package/examples/notes-service/src/Api.ts +1 -0
  294. package/examples/notes-service/src/browser.ts +15 -14
  295. package/examples/notes-service/src/cli.ts +8 -3
  296. package/examples/notes-service/src/commands.ts +46 -20
  297. package/examples/notes-service/src/identity/Identity.ts +2 -2
  298. package/examples/notes-service/src/identity/authorize.ts +6 -2
  299. package/examples/notes-service/src/identity/handlers.ts +3 -2
  300. package/examples/notes-service/src/identity/users.ts +2 -1
  301. package/examples/notes-service/src/identity/verifier.ts +12 -6
  302. package/examples/notes-service/src/notes/Notes.ts +46 -22
  303. package/examples/notes-service/src/notes/handlers.ts +18 -9
  304. package/examples/notes-service/src/notes/store.ts +70 -43
  305. package/examples/notes-service/src/routes.ts +7 -5
  306. package/examples/notes-service/src/server.ts +12 -5
  307. package/examples/notes-service/src/status/Status.ts +1 -1
  308. package/examples/notes-service/src/status/handlers.ts +5 -3
  309. package/examples/notes-service/src/stdio.ts +7 -3
  310. package/examples/notes-service/src/worker.ts +6 -3
  311. package/examples/notes-service/test/commands.test.ts +164 -91
  312. package/examples/notes-service/test/notes/as.ts +3 -1
  313. package/examples/notes-service/test/notes/idempotency.test.ts +97 -67
  314. package/examples/notes-service/test/notes/in-process.test.ts +26 -15
  315. package/examples/notes-service/test/notes/notes.test.ts +49 -31
  316. package/examples/notes-service/test/routes.test.ts +106 -71
  317. package/examples/notes-service/test/worker.test.ts +21 -13
  318. package/examples/quickstart/README.md +2 -6
  319. package/examples/quickstart/src/Api.ts +1 -0
  320. package/examples/quickstart/src/client.ts +6 -3
  321. package/examples/quickstart/src/greeting/Greeting.ts +1 -1
  322. package/examples/quickstart/src/greeting/handlers.ts +5 -2
  323. package/examples/quickstart/src/routes.ts +3 -2
  324. package/examples/quickstart/src/server.ts +6 -2
  325. package/examples/quickstart/test/routes.test.ts +3 -2
  326. package/package.json +19 -17
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,16 +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
- //#region src/contract/Action.d.ts
8
- type Codec = Schema.Codec<unknown, unknown, never, never>;
9
- type Fields = {
10
- readonly [key: string]: Codec;
11
- };
12
- declare const NoInput: Schema.$Record<Schema.String, Schema.Never>;
13
- 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";
14
16
  /**
15
17
  * How the action's tool presents itself over MCP, in MCP's own names; every field is optional
16
18
  * and none enforces anything. A hint left out is MCP's default, but `destructiveHint`, which is
@@ -70,11 +72,28 @@ export interface Options {
70
72
  */
71
73
  readonly input?: Codec | Fields | undefined;
72
74
  /**
73
- * A schema or struct fields. Omit for an action that returns nothing, `Schema.Void`; what each
74
- * surface answers is on its `layer`, `layerHttp`, `runStdio`, `make` or `command`. A top-level field whose schema is
75
- * `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`.
76
79
  */
77
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;
78
97
  /**
79
98
  * The declared failures: one schema, or a list of them, as `HttpApiEndpoint` takes them;
80
99
  * each keeps its own HTTP status annotation, the third argument of
@@ -91,7 +110,7 @@ export interface Options {
91
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.
92
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.
93
112
  */
94
- readonly error?: Codec | ReadonlyArray<Codec> | undefined;
113
+ readonly error?: Codec | readonly Codec[] | undefined;
95
114
  /**
96
115
  * Whether the action leaves its resource unchanged. Required: an action nobody classified
97
116
  * is the one a reviewer must check. An implementation's `authorize` may read it, and MCP's
@@ -102,25 +121,57 @@ export interface Options {
102
121
  readonly readOnly: boolean;
103
122
  /** How the action's tool presents itself over MCP, and on a native Toolkit's tools. */
104
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;
105
144
  }
106
145
  type UnknownMcp<O> = O extends {
107
146
  readonly mcp?: infer H;
108
147
  } ? Exclude<H extends unknown ? keyof H : never, keyof Mcp> : never;
109
- type KnownMcp<O> = {
148
+ interface KnownMcp<O> {
110
149
  readonly mcp?: { readonly [K in UnknownMcp<O>]: never; };
111
- };
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;
112
162
  type ErrorMembers<G> = G extends ReadonlyArray<infer E> ? E : G;
113
163
  type OnlyOptions<O> = { readonly [K in Exclude<O extends unknown ? keyof O : never, keyof Options>]: never; };
114
- 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>;
115
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;
116
166
  type SchemaOf<O, K extends "input" | "success", Default extends Codec> = CodecOf<Extract<OptionOf<O, K, Default>, Codec | Fields>>;
117
- 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[]>>>;
118
169
  /**
119
170
  * A pure contract: schemas and transport metadata, bound to handlers by `implement`. Its
120
171
  * `name`, `input`, `success`, `error` and `readOnly` are public: reuse them, as in
121
172
  * `Schema.Array(SaveLink.input)`.
122
173
  */
123
- 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> {
124
175
  readonly caller: Caller;
125
176
  readonly name: Name;
126
177
  readonly description: string;
@@ -131,26 +182,33 @@ export interface Action<Name extends string, Input extends Codec, Success extend
131
182
  readonly input: Input;
132
183
  readonly success: Success;
133
184
  /** The declared failures, as a list: one given alone is a list of one. */
134
- 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;
135
188
  readonly readOnly: ReadOnly;
136
189
  /** The `mcp` options as given, none when left out. */
137
190
  readonly mcp: Mcp;
191
+ /** The input field holding the call's idempotency key; `undefined` where none is named. */
192
+ readonly idempotencyKey: Key;
138
193
  }
139
194
  /** Any action, with its schemas erased. */
140
- export type Any = Action<string, Codec, Codec, ReadonlyArray<Codec>>;
195
+ export type Any = Action<string, Codec, Codec, readonly Codec[]>;
141
196
  /**
142
197
  * A list of declared errors, as an action's `error` holds it, and a binding's or an
143
198
  * authentication descriptor's: `<E extends Action.Errors>` constrains one.
144
199
  */
145
- export type Errors = ReadonlyArray<Codec>;
146
- /** Receives decoded input; may fail only with the declared errors and the built-in ones. */
147
- 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>;
148
206
  /**
149
207
  * The handlers of the actions `A`, keyed by name, as `implement` takes them: what a builder
150
208
  * written apart from `implement` returns, `satisfies Action.Handlers<typeof actions>`, so its
151
209
  * handlers are typed from their contracts, as another authorizer of the same handlers needs.
152
210
  */
153
- 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>; };
154
212
  /**
155
213
  * Define an action contract: what it takes and answers, whether it changes anything, and who
156
214
  * may call it. The name is also the route segment, the client method and the tool name.
@@ -163,7 +221,23 @@ export type Handlers<A extends ReadonlyArray<Any>, R = never> = { readonly [Acti
163
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.
164
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.
165
223
  */
166
- 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
+ };
167
241
  /**
168
242
  * `actions` keyed by name, each its exact contract: `byName(actions).getUser`, its schemas
169
243
  * where a test or a mock reads them, and its types where a helper takes a name,
@@ -171,22 +245,23 @@ export declare function make<const Name extends string, const O extends Options>
171
245
  * `Extract` over the list does not. The list is typed by its members, as every surface types
172
246
  * one. A name held twice is refused, as a surface refuses it.
173
247
  */
174
- export declare function byName<const Actions extends ReadonlyArray<Any>>(actions: Actions): { readonly [A in Actions[number] as A["name"]]: A; };
175
- 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[];
176
250
  type TakesNoAuthorize<T extends Target> = [Protected<ActionsOf<T>>] extends [never] ? unknown : "A protected action takes { authorize }: your rule, or Action.allowAll";
177
- 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>;
178
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;
179
256
  type NoHandler<R> = {
180
257
  readonly "~list": R;
181
258
  };
182
- 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>;
183
260
  type Several<T extends Target, RequestServices> = { readonly [K in keyof RequestServices]: K extends NamesOf<T> ? Handler<Extract<ActionsOf<T>, {
184
261
  readonly name: K;
185
262
  }>, RequestServices[K]> : never; };
186
263
  type HandlersOf<T extends Target, R, RequestServices> = Single<T, R> | Several<T, RequestServices>;
187
- type Deferred<T> = [T][T extends unknown ? 0 : never];
188
- 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>;
189
- 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>>>;
190
265
  /** Every authenticated caller may call a protected action. */
191
266
  export declare const allowAll: Authorize<Any>;
192
267
  /**
@@ -207,12 +282,12 @@ export declare const allowAll: Authorize<Any>;
207
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.
208
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`.
209
284
  */
210
- 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] ? {
211
286
  readonly authorize?: "A public-only target takes no authorize";
212
287
  } | undefined : {
213
288
  readonly authorize: Authorizer<Protected<ActionsOf<T>>, RA, EAX, RAX>;
214
289
  }): Implemented<T, RequestServices, R, EX, RX, RA, EAX, RAX>;
215
- 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>;
216
291
  /**
217
292
  * The builders of `implementations`, as one layer providing nothing. Surfaces build them
218
293
  * themselves, once per layer graph, but routes under `HttpRouter.serve` or `Testing.layer`
@@ -225,26 +300,43 @@ export declare function implement<const T extends Target, RequestServices extend
225
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.
226
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.
227
302
  */
228
- export declare function layer<const Apps extends Served>(implementations: Apps): Layer.Layer<never, BuildError<Member<Apps>>, BuildServices<Member<Apps>>>;
229
- 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>>;
230
305
  /**
231
- * 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
232
308
  * `client` gives, each method owing per call what its handler and `authorize` read.
233
309
  */
234
- 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>>; };
235
- /** What `client` takes: the actions to call. */
236
- 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> {
237
322
  /**
238
- * The actions to call, among the implementations': `[GetUser]`. Defaults to every action of
239
- * 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.
240
330
  */
241
- readonly actions?: ReadonlyArray<A> | undefined;
331
+ readonly actions?: readonly A[] | undefined;
242
332
  }
243
333
  /**
244
334
  * Call implementations in process: one method per action, taking its input directly as
245
335
  * `ActionHttp.client`'s methods do, `client.renameUser({ id, name })`, so moving between the
246
- * two changes the line acquiring it. A call runs as a remote one does, its input and its answer
247
- * 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
248
340
  * their own, failing with the action's errors and the built-in ones. Acquiring it builds the
249
341
  * implementations' builders into the layer graph it is acquired in, as a layer does, and its
250
342
  * scope holds them: acquire it where builders live.
@@ -257,7 +349,7 @@ export interface ClientOptions<A extends Any> {
257
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.
258
350
  * - It drops undeclared input fields as it encodes, and fails with `InvalidInput`, running nothing, for an input that does not encode.
259
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.
260
353
  */
261
- 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>;
262
- //#endregion
263
- 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 };