@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
@@ -1,10 +1,17 @@
1
1
  import { Anyone } from "./contract/caller.mjs";
2
2
  import { BuiltIn, BuiltIns } from "./contract/errors.mjs";
3
- import { BuildError, Holding, Known, Member, MiddlewareOf, Protected, Selected, Served, Serving } from "./contract/implementation.mjs";
4
- import { Any, Errors } from "./Action.mjs";
5
- import { Any as Any$1, DescriptorOf, Identity, Matching, PublicOnly, RemoteRequest, Required, ServedProvider, ServerBuildServices, VerifierError } from "./authentication/provider.mjs";
3
+ import { EventBuildError, EventBuildServices, EventServedRequest, HoldingEvents, ServingEvents } from "./contract/feed.mjs";
4
+ import { Any } from "./Event.mjs";
5
+ import { BuildError, Holding, Known, Member, MiddlewareOf, Selected, SelectedEvents, Served } from "./contract/implementation.mjs";
6
+ import { Any as Any$1, Errors } from "./Action.mjs";
6
7
  import { Certain, ErrorsOf } from "./contract/declared.mjs";
7
- import { AnyHttp, Client, MethodError, Options } from "./http/client.mjs";
8
+ import { Any as Any$2, DescriptorOf, LayerIdentity, Matching, PublicOnly, RemoteRequest, Required, ServedProvider, ServerBuildServices, VerifierError } from "./authentication/provider.mjs";
9
+ import { Only } from "./contract/listed.mjs";
10
+ import { SubscriptionOf } from "./contract/push.mjs";
11
+ import { pageOf, sentOf } from "./http/events.mjs";
12
+ import { AnyHttp, Client, MethodError, Options, WatchError } from "./http/client.mjs";
13
+ import { SuccessOf } from "./http/streamed.mjs";
14
+ import { Placed, placed } from "./http/placed.mjs";
8
15
  import { Context, Effect, FileSystem, Layer, Path, Schema, Types } from "effect";
9
16
  import { Etag, HttpClient, HttpClientError, HttpPlatform, HttpRouter } from "effect/http";
10
17
  import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware } from "effect/http-api";
@@ -27,29 +34,32 @@ type Through<M extends Middleware, R> = number extends M["length"] ? R | HttpApi
27
34
  type ServerOnly<M extends Middleware, E extends Errors> = [Exclude<HttpApiMiddleware.Error<IdOf<M[number]>>, Extract<Certain<E>, Errors[number]>["Type"] | BuiltIn> | HttpApiMiddleware.MiddlewareClient<IdOf<M[number]>>] extends [never] ? unknown : {
28
35
  readonly "Layer middleware fails only with the binding's errors and needs no client": never;
29
36
  };
30
- type HttpLayer<H extends AnyHttp, Apps extends Served, M extends Middleware, A extends Any = H["actions"][number]> = Layer.Layer<never, BuildError<Holding<Member<Apps>, A>, A>, ServerBuildServices<Holding<Member<Apps>, A>, A> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", LayerRequest<Member<Apps>, A, M>> | ServedProvider<Member<Apps>, A, H extends {
37
+ type HttpLayer<H extends AnyHttp, Apps extends Served, M extends Middleware, A extends Any$1 = H["actions"][number], E extends Any = H["events"][number]> = Layer.Layer<never, BuildError<Holding<Member<Apps>, A>, A> | EventBuildError<HoldingEvents<Member<Apps>, E>, E>, ServerBuildServices<Holding<Member<Apps>, A>, A> | EventBuildServices<HoldingEvents<Member<Apps>, E>, E> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", LayerRequest<Member<Apps>, A, E, M>> | ServedProvider<Member<Apps>, A, H extends {
31
38
  readonly authentication: infer D;
32
- } ? D : never> | IdOf<M[number]> | Etag.Generator | FileSystem.FileSystem | HttpPlatform.HttpPlatform | Path.Path>;
39
+ } ? D : never, ServingEvents<Member<Apps>, E>> | IdOf<M[number]> | Etag.Generator | FileSystem.FileSystem | HttpPlatform.HttpPlatform | Path.Path>;
33
40
  /** `ActionHttp.layer`'s options. */
34
- export interface LayerOptions<M extends Middleware = [], A extends Any = never> {
41
+ export interface LayerOptions<M extends Middleware = [], C extends Any$1 | Any = never> {
35
42
  /**
36
- * The actions it serves, among the binding's actions the implementations hold: `[GetUser]`.
37
- * Defaults to every one of them.
43
+ * The actions and event types it serves, among the binding's the implementations hold:
44
+ * `[GetUser, UserRenamed]`, exactly those. Defaults to every one of them.
45
+ *
46
+ * **Gotchas**
47
+ *
48
+ * - A list naming actions alone serves no event type: each event route answers 404 until the event types are listed beside them; two layers of one binding given lists that overlap fail at startup with Effect's duplicate-route message.
38
49
  */
39
- readonly actions?: ReadonlyArray<A> | undefined;
50
+ readonly actions?: readonly C[] | undefined;
40
51
  /**
41
52
  * Native endpoint middleware every served route runs, the first innermost: inside the
42
53
  * authentication of a protected route, outside its decoding, so it also sees invalid input.
43
54
  *
44
55
  * **Gotchas**
45
56
  *
46
- * - A middleware requiring the identity needs a layer serving only protected actions: serve the public ones from a second layer without it.
57
+ * - A middleware requiring the identity needs a layer serving only protected actions and event types: serve the public ones from a second layer without it.
58
+ * - On a stream answering an action declaring `progress`, or one answering a stream, a middleware sees the call end at its first event, while its handler runs on, and never sees a later failure; what it provides lasts the handler.
47
59
  */
48
60
  readonly middleware?: M;
49
61
  }
50
- type LayerRequest<App, A extends Any, M extends Middleware> = Exclude<Through<M, RemoteRequest<App, A>>, HttpRouter.Provided | ([Extract<Serving<App, A>, {
51
- readonly caller: typeof Anyone;
52
- }>] extends [never] ? Identity<Protected<Serving<App, A>>> : never)>;
62
+ type LayerRequest<App, A extends Any$1, E extends Any, M extends Middleware> = Exclude<Through<M, RemoteRequest<App, A> | EventServedRequest<App, E>>, HttpRouter.Provided | LayerIdentity<App, A, E>>;
53
63
  /** Contract-level configuration shared by servers and clients. */
54
64
  interface Options$1<E extends Errors = Errors> {
55
65
  /**
@@ -77,20 +87,24 @@ interface Options$1<E extends Errors = Errors> {
77
87
  *
78
88
  * - One descriptor per binding: actions of a second identity take a second binding with its own `prefix`, and a public action may be in both.
79
89
  */
80
- readonly authentication?: Any$1;
90
+ readonly authentication?: Any$2;
81
91
  }
82
- type Endpoint<A extends Any, E extends Errors, D> = A extends Any ? HttpApiEndpoint.HttpApiEndpoint<A["name"], "POST", `/${string}`, never, never, Schema.toCodecJson<A["input"]>, never, Schema.toCodecJson<A["success"]>, Schema.toCodecJson<A["error"][number] | E[number] | VerifierError<A, D> | BuiltIns>, never> : never;
83
- type Api<Actions extends ReadonlyArray<Any>, E extends Errors, D> = HttpApi.HttpApi<"actions", HttpApiGroup.HttpApiGroup<string, Endpoint<Actions[number], E, D>, true>>;
92
+ type EndpointError<A extends Any$1, E extends Errors, D> = Schema.toCodecJson<A["error"][number] | E[number] | VerifierError<A, D> | BuiltIns>;
93
+ type Endpoint<A extends Any$1, E extends Errors, D> = A extends Any$1 ? HttpApiEndpoint.HttpApiEndpoint<A["name"], "POST", `/${string}`, never, never, Schema.toCodecJson<A["input"]>, never, SuccessOf<A, EndpointError<A, E, D>>, EndpointError<A, E, D>> : never;
94
+ type EventEndpoint<V extends Any, E extends Errors, D> = V extends Any ? HttpApiEndpoint.HttpApiEndpoint<V["name"], "POST", `/${string}`, never, never, Schema.toCodecJson<ReturnType<typeof SubscriptionOf<V>>>, never, Schema.toCodecJson<ReturnType<typeof pageOf<V>>> | ReturnType<typeof sentOf<V>>, Schema.toCodecJson<E[number] | VerifierError<V, D> | BuiltIns>> : never;
95
+ type Api<Actions extends readonly Any$1[], E extends Errors, D, Events extends readonly Any[]> = HttpApi.HttpApi<"actions", HttpApiGroup.HttpApiGroup<string, Endpoint<Actions[number], E, D> | EventEndpoint<Events[number], E, D>, true>>;
84
96
  /**
85
- * An HTTP binding: actions, the errors every endpoint declares, and where they are mounted.
86
- * Plain data, so a copy of it, or one made by another installed copy of this package,
87
- * serves the same; `layer` serves it and `client` calls it.
97
+ * An HTTP binding: actions and event types, the errors every endpoint declares, and where they
98
+ * are mounted. Plain data, so a copy of it, or one made by another installed copy of this
99
+ * package, serves the same; `layer` serves it and `client` calls it.
88
100
  */
89
- export interface Binding<Actions extends ReadonlyArray<Any>, E extends Errors = [], D extends Any$1 | undefined = undefined> {
101
+ export interface Binding<Actions extends readonly Any$1[], E extends Errors = [], D extends Any$2 | undefined = undefined, Events extends readonly Any[] = readonly []> {
90
102
  /** Its protected actions' authentication descriptor, or `undefined` without one. */
91
103
  readonly authentication: D;
92
104
  /** The exact actions bound to this binding. */
93
105
  readonly actions: Actions;
106
+ /** The exact event types bound to this binding, each at `POST <prefix>/events/<name>`. */
107
+ readonly events: Events;
94
108
  /** The errors every endpoint declares besides its action's own, as a list. */
95
109
  readonly error: E;
96
110
  /** Where its routes mount: `/api` by default, `/` at the root, without a trailing slash. */
@@ -107,24 +121,30 @@ export interface Binding<Actions extends ReadonlyArray<Any>, E extends Errors =
107
121
  * - Combined into one document, `HttpApi.make("app").addHttpApi(A.api).addHttpApi(B.api)`, a shared action name makes `OpenApi.fromApi` throw `Duplicate OpenAPI operationId`, and a shared prefix silently drops one binding's actions from the document and a native client: otherwise document each alone.
108
122
  * - A binding holding no protected action documents no security scheme, so Swagger shows no Authorize button.
109
123
  */
110
- readonly api: Api<Actions, E, D>;
124
+ readonly api: Api<Actions, E, D, Events>;
111
125
  }
112
126
  /**
113
127
  * Bind actions once, for servers and clients alike, each at `POST <prefix>/<action>`, a
114
- * `readOnly` one too. Every endpoint declares its action's errors, the binding's `error`, and
115
- * the built-in `InvalidInput`, `Unauthenticated` and `Forbidden`, so clients decode each as a
116
- * typed failure; a protected one also declares what its descriptor's verifier fails with.
128
+ * `readOnly` one too, and event types, each at `POST <prefix>/events/<name>`. Every endpoint
129
+ * declares its action's errors, the binding's `error`, and the built-in `InvalidInput`,
130
+ * `Unauthenticated` and `Forbidden`, so clients decode each as a typed failure; a protected one
131
+ * also declares what its descriptor's verifier fails with.
117
132
  * Guide: `docs/ActionHttp.md` in this package; in an application: `examples/notes-service/src/Api.ts`.
133
+ *
134
+ * **Gotchas**
135
+ *
136
+ * - An action and an event type of one name, or an event type listed twice, throw when `make` is called: they would share a client's method.
137
+ * - A protected event type, where `authentication` has `principal`, is bound at `POST <prefix>/events/<name>/subscribe` and `/unsubscribe` too, for `Webhook.layer`'s delivery, and OpenAPI lists both: a binding cannot know whether its server provides that layer, and without it they answer a 409 `Event.SubscriptionError`, reason `Unsupported`.
118
138
  */
119
- export declare function make<const Actions extends ReadonlyArray<Any & {
139
+ export declare function make<const Contracts extends ReadonlyArray<(Any$1 | Any) & {
120
140
  readonly caller: typeof Anyone;
121
- }>>(actions: Actions): Binding<Actions, [], undefined>;
122
- export declare function make<const Actions extends ReadonlyArray<Any>, const O extends Options$1 = {}>(actions: Actions, options: O & Required<Actions[number]> & Matching<Actions[number], NoInfer<DescriptorOf<O>>> & NoInfer<Known<O, Options$1>>): Binding<Actions, ErrorsOf<O>, DescriptorOf<O>>;
123
- export declare function make<const Actions extends ReadonlyArray<Any>>(actions: Actions & PublicOnly<Actions>): Binding<Actions, [], undefined>;
141
+ }>>(contracts: Contracts): Binding<Only<Contracts, Any$1>, [], undefined, Only<Contracts, Any>>;
142
+ export declare function make<const Contracts extends ReadonlyArray<Any$1 | Any>, const O extends Options$1 = {}>(contracts: Contracts, options: O & Required<Contracts[number]> & Matching<Contracts[number], NoInfer<DescriptorOf<O>>> & NoInfer<Known<O, Options$1>>): Binding<Only<Contracts, Any$1>, ErrorsOf<O>, DescriptorOf<O>, Only<Contracts, Any>>;
143
+ export declare function make<const Contracts extends ReadonlyArray<Any$1 | Any>>(contracts: Contracts & PublicOnly<Contracts>): Binding<Only<Contracts, Any$1>, [], undefined, Only<Contracts, Any>>;
124
144
  /**
125
- * Serve the binding's actions among `implementations` in one layer, or those its `actions` lists,
126
- * authenticating a protected action's request before decoding it, then running its
127
- * implementation's `authorize` before each handler. An implementation's actions the binding
145
+ * Serve the binding's actions and event types among `implementations` in one layer, or those its
146
+ * `actions` lists, authenticating a protected action's request before decoding it, then running
147
+ * its implementation's `authorize` before each handler. An implementation's actions the binding
128
148
  * leaves out have no route here, one holding none of those served is not built, and a non-empty
129
149
  * list holding none of the binding's actions is refused when `layer` is called. Several layers
130
150
  * may serve one binding, each with its own `actions` and `middleware` covering its routes only.
@@ -138,7 +158,10 @@ export declare function make<const Actions extends ReadonlyArray<Any>>(actions:
138
158
  *
139
159
  * **Gotchas**
140
160
  *
141
- * - A success is its JSON as the body; an action without `success` answers an empty 200, no body and no content type, so a hand-written client must not parse it. One without `input` still takes the body `{}` typed as JSON: no body is a 400.
161
+ * - A success is its JSON as the body; an action without `success` answers an empty 200, no body and no content type, so a hand-written client must not parse it, or an empty 204 where it declares `progress`. One without `input` still takes the body `{}` typed as JSON: no body is a 400.
162
+ * - An action declaring `progress` answers a request whose `Accept` names `text/event-stream`, whatever else it names, with an SSE stream, listed in OpenAPI: a `progress` event for each value, its data the value's JSON, then one `final` event holding the answer's JSON, `null` for none; without that `Accept` it answers as any action, its progress dropped.
163
+ * - An action answering a stream, `Action.Stream`, answers an SSE stream alone, whatever `Accept` says: an `element` event for each element, its data the element's JSON, and a `progress` event for each value where it declares `progress`; a stream of none answers a 200 with no event. A failure before the first event answers its status; one after ends the stream as Effect's stream failure event, a defect as `Internal server error`.
164
+ * - The stream starts with the call's first event, so a failure before it, a refusal included, answers as it would without the stream; a declared failure after it ends the stream with Effect's `effect/http-api/stream/failure` event, reported as `Info` as without the stream, and a defect or an answer that does not encode with one saying `Internal server error`, its cause logged and reported.
142
165
  * - A body that is not JSON is a 400 `InvalidInput`; a request with no `Content-Type` a 415 `Unsupported content-type: none`, one with another type Effect's own 415; each after authentication on a protected route.
143
166
  * - A defect, an undeclared failure, or a success or an error that does not encode answers an empty 500: the cause goes to the log and to the call's `ErrorReporter`s, never to the body.
144
167
  * - A path no layer serves, or a method other than `POST` on a route, is a 404.
@@ -147,9 +170,15 @@ export declare function make<const Actions extends ReadonlyArray<Any>>(actions:
147
170
  * - `HttpRouter.provideRequest(layer)` builds its layer once, at startup, so it never provides an identity or a tenant.
148
171
  * - A request body is read whole, however large, unless the host provides `HttpServerRequest.MaxBodySize` to `HttpRouter.serve`, limiting every route that reads its body whole, MCP endpoints included; a body a route reads as a stream is not limited. On Node a request over it is an empty 413 from Effect 4.0.6, and before it gets no response, its connection closed; `HttpRouter.toWebHandler` ignores it, so limit bodies in the platform serving the handler.
149
172
  * - An `ErrorReporter` the server runs with is given, as `Info`, a handler's declared failure and an input that does not decode, never a refusal or a refused credential: give an error class `override readonly [ErrorReporter.ignore] = true` to keep it out, and drop `Info` for a built-in error, which takes no mark.
173
+ * - An event's route takes `{ input, cursor }`, a `cursor` omitted or `null` reading from now, and answers a page, `{ events, cursor, truncated, hasMore, nextPollMs }`, or, to a request accepting `text/event-stream`, an SSE stream of `Checked`, `Occurrence`, `Gap` and `Failed` data, a `Checked` every 30 seconds; the stream carries no `id:`, so `Last-Event-ID` resumes nothing: resume from a cursor in the body.
174
+ * - A subscription is refused as an action's call is, before its stream starts, with the route's 401 or 403; a refusal later ends the stream with Effect's `effect/http-api/stream/failure` event.
175
+ * - A source's failure no refusal explains is logged and reported once, sent as a bare `Failed`, and read again from where it reached after 5 seconds, before the stream's first `Checked` too; a poll's is a 500, which `client`'s `watch` reconnects after.
176
+ * - A stream is read only as fast as its client reads it: a slow reader of a log source falls behind its log, and receives a `Gap`.
177
+ * - A protected event with a `subscription` hook needs `principal` on its descriptor to key poll subscribers: without it the layer fails to build with a message naming the event.
178
+ * - On a host with no lasting process, such as `HttpRouter.toWebHandler` on a serverless platform, serve check sources alone, without a `subscription` hook or `Webhook.layer`: nothing refuses the others, and under `Event.layerMemory` each instance keeps its own log of a log source, whose cursors another reads as a `Gap`, and its own leases and webhook subscriptions, lost as it ends.
150
179
  */
151
180
  export declare function layer<const H extends AnyHttp, const Apps extends Served>(binding: H, implementations: Apps): HttpLayer<H, Apps, []>;
152
- export declare function layer<const H extends AnyHttp, const Apps extends Served, const O extends LayerOptions<Middleware, H["actions"][number]>>(binding: H, implementations: Apps, options: O & ServerOnly<MiddlewareOf<O, Middleware>, H["error"]> & NoInfer<Known<O, LayerOptions<Middleware>>>): HttpLayer<H, Apps, MiddlewareOf<O, Middleware>, Selected<O, H["actions"][number]>>;
181
+ export declare function layer<const H extends AnyHttp, const Apps extends Served, const O extends LayerOptions<Middleware, H["actions"][number] | H["events"][number]>>(binding: H, implementations: Apps, options: O & ServerOnly<MiddlewareOf<O, Middleware>, H["error"]> & NoInfer<Known<O, LayerOptions<Middleware>>>): HttpLayer<H, Apps, MiddlewareOf<O, Middleware>, Selected<O, H["actions"][number]>, SelectedEvents<O, H["events"][number]>>;
153
182
  /**
154
183
  * Effect's native `HttpApiClient` for a binding, one method per action taking the
155
184
  * action's input directly: `client.greet({ name })`, the argument omitted when `{}` is a valid
@@ -162,6 +191,11 @@ export declare function layer<const H extends AnyHttp, const Apps extends Served
162
191
  *
163
192
  * - An input that does not encode fails with `InvalidInput`, and nothing is sent; undeclared input fields, which TypeScript lets through on a wider value, are dropped as it encodes, since the server would refuse them.
164
193
  * - It interprets no status and retries nothing: a failed write may have happened, and only a declared error says what the server did.
194
+ * - `watch(event, input, options)` holds the event's SSE stream open, or polls its pages with `mode: "poll"`, as `Action.client`'s; a watch of an event the server does not serve, its route a 404, fails with `Event.SubscriptionError`, reason `NotServed`.
195
+ * - A watch reconnects after the transport fails, a status such as 503, or 60 seconds without an answer or a frame, a second later and doubling to 30 seconds, each reconnect logged as a warning; a declared error, a refusal included, ends it.
196
+ * - A watch's stream ends at an SSE `retry:` field, which `ActionHttp.layer` never sends, as Effect's client decodes it, and the watch connects again: a server or proxy sending one as each stream starts delivers nothing.
197
+ * - A call of an action declaring `progress` reads the route's event stream, as every request accepts it, and its answer from the `final` event; the server sends the values whether or not the call gave `onProgress`, and without one they are dropped.
198
+ * - A call of an action answering a stream returns a `Stream` of its elements, failing as the route's stream fails, with `Sse.SseError` or `Sse.Retry` too; its `onProgress` gets each `progress` event, which the server sends whether or not the call gave one.
165
199
  */
166
200
  export declare function client<const H extends AnyHttp>(binding: H, options?: Options): Effect.Effect<Client<H>, never, HttpClient.HttpClient>;
167
201
  export declare function client<const H extends AnyHttp, E, R>(binding: H, options: Options<E, R>): Effect.Effect<Client<H, E, R>, never, HttpClient.HttpClient>;
@@ -173,4 +207,4 @@ export declare function client<const H extends AnyHttp, E, R>(binding: H, option
173
207
  */
174
208
  export declare function fetchClient<const H extends AnyHttp>(binding: H, options?: FetchClientOptions): Client<H>;
175
209
  export declare function fetchClient<const H extends AnyHttp, E, R>(binding: H, options: FetchClientOptions<E, R>): Client<H, E, R>;
176
- export { type AnyHttp as Any, type Client, type Options as ClientOptions, type MethodError, Options$1 as Options };
210
+ export { type AnyHttp as Any, type Client, type Options as ClientOptions, type MethodError, Options$1 as Options, type Placed, type WatchError, placed };
@@ -1,11 +1,19 @@
1
1
  import { Anyone } from "./contract/caller.mjs";
2
2
  import { declared } from "./contract/status.mjs";
3
- import { bindingErrors, bindingOf, projectedErrors } from "./contract/declared.mjs";
4
- import { acquire, provideHandlers, servingOf } from "./contract/implementation.mjs";
5
- import { providerOf } from "./authentication/provider.mjs";
3
+ import { bindingErrors, bindingOf, eventErrors, projectedErrors } from "./contract/declared.mjs";
4
+ import { elementOf } from "./contract/stream.mjs";
5
+ import { acquire } from "./contract/implementation.mjs";
6
6
  import { inputParseOptions } from "./contract/call.mjs";
7
+ import { acquireFeeds, unkeyedHook } from "./contract/feed.mjs";
8
+ import { offersWebhook, servedWebhooks } from "./contract/webhooks.mjs";
9
+ import { providerOf } from "./authentication/provider.mjs";
10
+ import { listed } from "./contract/listed.mjs";
11
+ import { provideServed, servingOf } from "./contract/serving.mjs";
12
+ import { answeredWithProgress, answeringElements, answeringWithProgress, elementStreamOf, progressStreamOf } from "./http/streamed.mjs";
7
13
  import { methods } from "./http/client.mjs";
14
+ import { answering, eventEndpointOf, servedSent, webhookAnswers, webhookEndpointsOf } from "./http/events.mjs";
8
15
  import { SchemaErrors, schemaErrors } from "./http/schema-errors.mjs";
16
+ import { placed } from "./http/placed.mjs";
9
17
  import { Context, Effect, Layer, Scope } from "effect";
10
18
  import { FetchHttpClient, HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
11
19
  import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, OpenApi } from "effect/http-api";
@@ -18,24 +26,45 @@ const apiOf = (group, endpoints) => {
18
26
  const [first, ...rest] = endpoints;
19
27
  return HttpApi.make("actions").annotate(HttpApi.ParseOptions, { errors: "all" }).add(first === void 0 ? empty : empty.add(first, ...rest));
20
28
  };
29
+ const successOf = (action, errors) => {
30
+ const element = elementOf(action.success);
31
+ if (element !== void 0) return elementStreamOf(element, action.progress, errors);
32
+ return action.progress === void 0 ? action.success : [answeredWithProgress(action.success), progressStreamOf(action.progress, action.success, errors)];
33
+ };
21
34
  const endpointOf = (action, path, errors) => HttpApiEndpoint.post(action.name, path, {
22
35
  payload: action.input.pipe(HttpApiSchema.asJson()),
23
- success: action.success,
36
+ success: successOf(action, errors),
24
37
  error: errors.map(declared)
25
38
  });
26
- const withinLayerMiddleware = (endpoint, middleware = []) => middleware.reduce((inner, key) => inner.middleware(key), endpoint);
27
- function make(actions, options = {}) {
28
- const bound = bindingOf("ActionHttp binding", actions, options);
39
+ const answerOf = (action, run) => {
40
+ const element = elementOf(action.success);
41
+ if (element !== void 0) return answeringElements(element, run);
42
+ return action.progress === void 0 ? run : answeringWithProgress(action.success, run);
43
+ };
44
+ const withinLayerMiddleware = (endpoint, middleware = []) => {
45
+ let within = endpoint;
46
+ for (const key of middleware) within = within.middleware(key);
47
+ return within;
48
+ };
49
+ function make(contracts, options = {}) {
50
+ const { actions, events } = listed(contracts);
51
+ const bound = bindingOf("ActionHttp binding", actions, events, options);
29
52
  const mount = mountSegments(options.prefix);
30
53
  const security = bound.authentication?.["~middleware"];
31
- const endpoints = actions.map((action) => {
32
- const isPublic = security === void 0 || action.caller === Anyone;
33
- const endpoint = endpointOf(action, absolutePath([...mount, action.name]), bindingErrors(action, bound)).annotate(OpenApi.Description, action.description);
34
- return isPublic ? endpoint : endpoint.middleware(security);
35
- });
54
+ const secured = (endpoint, { caller }) => security === void 0 || caller === Anyone ? endpoint : endpoint.middleware(security);
55
+ const endpoints = [...actions.map((action) => secured(endpointOf(action, absolutePath([...mount, action.name]), bindingErrors(action, bound)).annotate(OpenApi.Description, action.description), action)), ...events.flatMap((event) => {
56
+ const path = absolutePath([
57
+ ...mount,
58
+ "events",
59
+ event.name
60
+ ]);
61
+ const errors = eventErrors(event, bound);
62
+ return [eventEndpointOf(event, path, errors), ...offersWebhook(event, bound.authentication) ? webhookEndpointsOf(event, path, errors) : []].map((endpoint) => secured(endpoint, event));
63
+ })];
36
64
  const api = apiOf(groupName(mount), endpoints);
37
65
  return {
38
66
  actions,
67
+ events,
39
68
  ...bound,
40
69
  prefix: absolutePath(mount),
41
70
  api
@@ -50,31 +79,54 @@ var StepUp = class extends HttpApiMiddleware.Service()("effect-actions/ActionHtt
50
79
  var JsonContentType = class extends HttpApiMiddleware.Service()("effect-actions/ActionHttp/JsonContentType") {};
51
80
  const jsonContentType = () => Layer.succeed(JsonContentType, (route) => Effect.flatMap(HttpServerRequest.HttpServerRequest, (request) => request.headers["content-type"] === void 0 ? Effect.succeed(HttpServerResponse.text("Unsupported content-type: none", { status: 415 })) : route));
52
81
  function layer(binding, served, options = {}) {
53
- const { apps, actions, authentication } = servingOf("HTTP binding", binding, served, options);
82
+ const selected = servingOf("HTTP binding", binding, served, options);
83
+ const { apps, actions, events, eventTypes, authentication } = selected;
54
84
  const mount = mountSegments(binding.prefix);
55
85
  const name = groupName(mount);
56
- const api = apiOf(name, actions.map((action) => {
57
- const endpoint = withinLayerMiddleware(endpointOf(action, absolutePath([...mount, action.name]), projectedErrors(action, binding.error)).middleware(JsonContentType).middleware(SchemaErrors), options.middleware);
58
- return action.caller === Anyone || authentication === void 0 ? endpoint : endpoint.middleware(StepUp).middleware(authentication["~middleware"]);
59
- })).annotate(HttpApi.PayloadParseOptions, inputParseOptions);
86
+ const routed = (endpoint, { caller }) => {
87
+ const within = withinLayerMiddleware(endpoint.middleware(JsonContentType).middleware(SchemaErrors), options.middleware);
88
+ return caller === Anyone || authentication === void 0 ? within : within.middleware(StepUp).middleware(authentication["~middleware"]);
89
+ };
90
+ const api = apiOf(name, [...actions.map((action) => routed(endpointOf(action, absolutePath([...mount, action.name]), projectedErrors(action, binding.error)), action)), ...eventTypes.flatMap((event) => {
91
+ const path = absolutePath([
92
+ ...mount,
93
+ "events",
94
+ event.name
95
+ ]);
96
+ const errors = eventErrors(event, { error: binding.error });
97
+ return [eventEndpointOf(event, path, errors, servedSent), ...offersWebhook(event, authentication) ? webhookEndpointsOf(event, path, errors) : []].map((endpoint) => routed(endpoint, event));
98
+ })]).annotate(HttpApi.PayloadParseOptions, inputParseOptions);
60
99
  const handlers = Layer.unwrap(Effect.gen(function* () {
61
- const bound = yield* acquire(apps);
100
+ const bound = yield* acquire(apps, authentication);
101
+ const feeds = yield* acquireFeeds(events);
102
+ const refusal = unkeyedHook("ActionHttp", feeds, authentication?.principal !== void 0);
103
+ if (refusal !== void 0) return yield* Effect.die(new Error(refusal));
104
+ const { webhooks, offered } = yield* servedWebhooks(feeds, authentication);
62
105
  const security = authentication === void 0 ? Layer.empty : yield* Effect.map(providerOf(authentication), (provider) => Layer.mergeAll(Layer.succeed(authentication["~middleware"], provider.middleware), Layer.succeed(StepUp, provider.stepUp)));
63
106
  const hostProvidedMiddleware = yield* Effect.forEach(options.middleware ?? [], (key) => Effect.map(Effect.service(key), (service) => Layer.succeed(key, service)));
64
- const byName = Object.fromEntries(bound.map(([action, run]) => [action.name, (request) => run(request.payload)]));
107
+ const webhookAnswered = offered.map((feed) => [feed.event.name, webhookAnswers(feed, authentication, webhooks)]);
108
+ const byName = {
109
+ ...Object.fromEntries(bound.map(([action, run]) => {
110
+ const answer = answerOf(action, run);
111
+ return [action.name, (request) => answer(request.payload)];
112
+ })),
113
+ ...Object.fromEntries(feeds.map((feed) => [feed.event.name, answering(feed, authentication)])),
114
+ ...Object.fromEntries(webhookAnswered.map(([eventName, { subscribe }]) => [`${eventName}/subscribe`, subscribe])),
115
+ ...Object.fromEntries(webhookAnswered.map(([eventName, { unsubscribe }]) => [`${eventName}/unsubscribe`, unsubscribe]))
116
+ };
65
117
  return HttpApiBuilder.group(api, name, (builder) => builder.handleAll(byName)).pipe(Layer.provide([
66
- schemaErrors(),
118
+ schemaErrors,
67
119
  jsonContentType(),
68
120
  security,
69
121
  ...hostProvidedMiddleware
70
122
  ]), buildInEmptyContext);
71
- })).pipe(provideHandlers(apps));
123
+ })).pipe(provideServed(selected));
72
124
  return HttpApiBuilder.layer(api).pipe(Layer.provide(handlers), Layer.provide(startupBeneathRequest()));
73
125
  }
74
126
  function client(binding, options) {
75
127
  return methods(binding, options);
76
128
  }
77
129
  function fetchClient(binding, { fetch, ...options } = {}) {
78
- return methods(binding, options).pipe(Effect.provide(FetchHttpClient.layer), Effect.provideService(FetchHttpClient.Fetch, fetch ?? ((input, init) => globalThis.fetch(input, init))), Effect.runSync);
130
+ return methods(binding, options).pipe(Effect.provide(FetchHttpClient.layer), Effect.provideService(FetchHttpClient.Fetch, fetch ?? (async (input, init) => await globalThis.fetch(input, init))), Effect.runSync);
79
131
  }
80
- export { client, fetchClient, layer, make };
132
+ export { client, fetchClient, layer, make, placed };
@@ -1,21 +1,30 @@
1
- import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Protected, SelectedOf, Selection, Served, ServedRequest } from "./contract/implementation.mjs";
2
- import { Any } from "./Action.mjs";
3
- import { Any as Any$1, Matching, ProviderOf, RemoteRequest, Required, ServerBuildServices } from "./authentication/provider.mjs";
4
- import { Client, ClientOptions, McpCallError, Params, RequestOptions, client, request } from "./mcp/client.mjs";
1
+ import { EventBuildError, EventBuildServices, EventOf, EventRequestOf, EventServedRequest, HoldingEvents } from "./contract/feed.mjs";
2
+ import { Any } from "./Event.mjs";
3
+ import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Protected, SelectedEventsOf, SelectedOf, Selection, Served, ServedRequest } from "./contract/implementation.mjs";
4
+ import { Any as Any$1 } from "./Action.mjs";
5
+ import { Any as Any$2, Matching, ProviderOf, RemoteRequest, Required, ServerBuildServices } from "./authentication/provider.mjs";
6
+ import { McpCallError } from "./mcp/reply.mjs";
7
+ import { Client, ClientOptions, client } from "./mcp/client.mjs";
8
+ import { Params, RequestOptions, request } from "./mcp/request.mjs";
5
9
  import { Cause, Effect, Layer, Stdio } from "effect";
6
- import { McpSchema, McpServer } from "effect/ai";
7
10
  import { HttpRouter } from "effect/http";
11
+ import { McpSchema, McpServer } from "effect/ai";
8
12
  /**
9
13
  * An MCP server, over HTTP or stdio: every native `McpServer.layerStdio` option but `protocols`,
10
14
  * the server information, `instructions` and `extensions` among them, the actions `A` that are
11
- * its tools, and the native features it serves beside them.
15
+ * its tools and the event types `A` it serves, and the native features it serves beside them.
12
16
  */
13
- export interface Options<E = never, R = never, A extends Any = Any> extends Omit<Parameters<typeof McpServer.layerStdio>[0], "protocols"> {
17
+ export interface Options<E = never, R = never, A extends Any$1 | Any = Any$1 | Any> extends Omit<Parameters<typeof McpServer.layerStdio>[0], "protocols"> {
14
18
  /**
15
- * The actions that are tools, among the implementations' actions: `[GetUser, RenameUser]`.
16
- * Each keeps its implementation's authorization and builder. Defaults to every action of them.
19
+ * The actions that are tools and the event types it serves, among the implementations':
20
+ * `[GetUser, RenameUser, UserRenamed]`, exactly those. Each keeps its implementation's
21
+ * authorization and builder. Defaults to every action and event type of them.
22
+ *
23
+ * **Gotchas**
24
+ *
25
+ * - A list naming actions alone serves no event type: the events extension is not declared, and `events/*` answer `-32601`, until the event types are listed beside them.
17
26
  */
18
- readonly actions?: ReadonlyArray<A> | undefined;
27
+ readonly actions?: readonly A[] | undefined;
19
28
  /**
20
29
  * Native MCP features served beside the actions' tools, such as `McpServer.resource`,
21
30
  * `McpServer.prompt` and `McpServer.toolkit` layers merged into one. They register on this
@@ -35,7 +44,7 @@ export interface Options<E = never, R = never, A extends Any = Any> extends Omit
35
44
  * `McpServer.layerHttp` option, `allowedOrigins` among them, except `protocols` and
36
45
  * `allowSessionTermination`, which are fixed.
37
46
  */
38
- export interface LayerHttpOptions<E = never, R = never, A extends Any = Any> extends Options<E, R, A>, Omit<Parameters<typeof McpServer.layerHttp>[0], "protocols" | "path" | "allowSessionTermination"> {
47
+ export interface LayerHttpOptions<E = never, R = never, A extends Any$1 | Any = Any$1 | Any> extends Options<E, R, A>, Omit<Parameters<typeof McpServer.layerHttp>[0], "protocols" | "path" | "allowSessionTermination"> {
39
48
  /** The endpoint's route; defaults to `/mcp`. */
40
49
  readonly path?: HttpRouter.PathInput;
41
50
  /**
@@ -53,12 +62,16 @@ export interface LayerHttpOptions<E = never, R = never, A extends Any = Any> ext
53
62
  * - Every prompt and resource request needs a credential, listings included: to offer them signed out, serve them on an endpoint without protected tools.
54
63
  * - A handler that sends a notification starts the response, so a refusal after it is an `isError: true` tool result, not the 401 or 403 a client re-authorizes on: refuse in `authorize`.
55
64
  */
56
- readonly authentication?: Any$1;
65
+ readonly authentication?: Any$2;
57
66
  /**
58
67
  * Whether MCP 2026-07-28 clients may subscribe, holding a stream open for list changes and
59
- * resource updates. Defaults to whether `features` are given: an endpoint of actions alone
60
- * registers its tools once, before any client connects, and serves no resource. Pass `true`
61
- * where a handler changes the server through `McpServer.McpServer`.
68
+ * resource updates. Defaults to whether `features` or event implementations are given: an
69
+ * endpoint of actions alone registers its tools once, before any client connects, and serves no
70
+ * resource. Pass `true` where a handler changes the server through `McpServer.McpServer`.
71
+ *
72
+ * **Gotchas**
73
+ *
74
+ * - `false` also serves events by poll alone: Effect's server pushes no notification without subscriptions, so `events/list` lists `["poll"]` and `events/stream` answers `-32026`.
62
75
  */
63
76
  readonly allowSubscriptions?: boolean | undefined;
64
77
  /**
@@ -74,7 +87,13 @@ export interface LayerHttpOptions<E = never, R = never, A extends Any = Any> ext
74
87
  type ToolRequestContext<R> = Exclude<R, McpSchema.McpRequestContext | McpServer.McpServer>;
75
88
  type HttpToolRequestContext<R> = Exclude<ToolRequestContext<R>, HttpRouter.Provided>;
76
89
  type Features<R> = Exclude<R, McpServer.McpServer>;
77
- type Inferring<D, A extends Any> = [D] extends [undefined] ? [Protected<A>] extends [never] ? {
90
+ /** The actions and event types an endpoint of `Apps` given the options `O` serves. */
91
+ type Contracts<Apps extends Served, O> = SelectedOf<O, Apps> | SelectedEventsOf<O, Apps>;
92
+ /** What building an endpoint of `Apps` given the options `O` may fail with. */
93
+ type ServerError<Apps extends Served, O> = BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | EventBuildError<HoldingEvents<Member<Apps>, SelectedEventsOf<O, Apps>>, SelectedEventsOf<O, Apps>> | Cause.IllegalArgumentError;
94
+ /** What building the event sources of an endpoint of `Apps` given the options `O` needs. */
95
+ type EventServices<Apps extends Served, O> = EventBuildServices<HoldingEvents<Member<Apps>, SelectedEventsOf<O, Apps>>, SelectedEventsOf<O, Apps>>;
96
+ type Inferring<D, A extends Pick<Any$1, "caller">> = [D] extends [undefined] ? [Protected<A>] extends [never] ? {
78
97
  readonly authentication?: undefined;
79
98
  } : unknown : {
80
99
  readonly authentication?: D;
@@ -85,21 +104,36 @@ type Inferring<D, A extends Any> = [D] extends [undefined] ? [Protected<A>] exte
85
104
  * revision opens a session with `initialize`, which holds what it sent there and no identity,
86
105
  * and lasts until that client ends it with a DELETE, answered 204, or the process stops. Protected
87
106
  * tools need `authentication`; for a serverless host, see `requestScoped`. In a tool call, what
88
- * middleware provides per request wins over what the endpoint was built with.
89
- * Guide: `docs/ActionMcp.md` in this package; in an application: `examples/mcp-tools/`.
107
+ * middleware provides per request wins over what the endpoint was built with. The event
108
+ * types among `implementations`, or those `actions` lists, are served as SEP-3415 events,
109
+ * `events/list`, `events/poll` and `events/stream`, and, where `Webhook.layer` is provided, `events/subscribe`
110
+ * and `events/unsubscribe`: experimental, as that extension is a draft, pinned to its PR head
111
+ * 2ec39b9c49c583411b73cb460dea5f52b5c541a6.
112
+ * Guide: `docs/ActionMcp.md` in this package; in an application: `examples/mcp-tools/`, and
113
+ * `examples/build-events/` for events.
90
114
  *
91
115
  * **Gotchas**
92
116
  *
93
117
  * - A success is `isError: false` with `structuredContent` its JSON and the same JSON as one text block, both `null` for an action without `success`; an `Action.Image` field is left out of `structuredContent` and its text, its block following them, and a success of images alone has no `structuredContent`.
94
118
  * - A declared error is an `isError: true` tool result in a 200 holding its JSON as one text block, with no `structuredContent`; a refusal is answered as `Action.Unauthenticated` and `Action.Forbidden` say.
119
+ * - An action answering a stream, `Action.Stream`, answers its elements collected into one array once the stream ends, a success of that array.
120
+ * - An action declaring `progress` sends each value as `notifications/progress` on the `progressToken` its call's `_meta` carries, before the result: `progress` counts from 1, `message` is the value's JSON and `_meta["effect-actions/progress"]` the value; a call without a token gets none.
95
121
  * - Arguments that do not decode never reach the handler: an `isError: true` result in a 200, text `Invalid parameters for tool '<name>': …`, or for a 2025-06-18 client a JSON-RPC error.
96
122
  * - A defect, an undeclared failure, or a success or an error that does not encode is `isError: true`, text `Tool execution failed due to an internal server error.`: the cause goes to the log and to the call's `ErrorReporter`s, never to the client.
97
123
  * - An unknown tool is a JSON-RPC error `-32602` in a 200; a body that is not JSON a JSON-RPC error `-32700` in a 200; a `Content-Type` missing or not JSON an empty 415; another HTTP method an empty 405 with `Allow: POST, DELETE`, `Allow: POST` where `requestScoped`, or the 401 of an endpoint with authentication when signed out.
98
124
  * - A 2025-11-25 or 2025-06-18 client gets `structuredContent` only for a success that is an object: any other success arrives as its text block alone, a string unquoted.
99
125
  * - A client asking for an earlier revision, such as 2025-03-26, is offered 2025-11-25, so a client speaking only that earlier one cannot connect.
100
126
  * - A request body is read whole, however large, and a 2025 session holds what its `initialize` sent until it ends, so an oversized `initialize` costs memory for that long: limit bodies as `ActionHttp.layer` says.
127
+ * - An event type named starting with `mcp.`, a prefix MCP keeps for its own events, is refused as the endpoint is made, with `MCP event name starts with "mcp."`.
128
+ * - Events are 2026-07-28's alone: a 2025 session gets `-32601` for `events/*`, and its `initialize` advertises no events extension.
129
+ * - With `requestScoped`, `events/stream` answers `-32026`, no server outliving a request to push, so events are served by poll, and by webhook where `Webhook.layer` is provided; their sources, logs, `subscription` hooks' leases and webhook subscriptions are built once with the endpoint, as without it.
130
+ * - On a host with no lasting process, such as a serverless platform, serve check sources alone, without a `subscription` hook: nothing refuses the others, and under `Event.layerMemory` each instance keeps its own log of a log source, whose cursors another reads as a gap, and its own leases, lost as it ends.
131
+ * - A protected event with a `subscription` hook needs `principal` on `authentication`, or the endpoint is refused at startup: a poll's hook is held per principal between requests.
132
+ * - A push stream holds its request open while it is read, a heartbeat every 30 seconds: a host's request timeout ends it, and `client`'s `watch` reconnects from its cursor.
133
+ * - A poll's `maxEvents` caps a log source's page, at most 100, and is not honored for a check source, whose page is answered whole since only its last cursor resumes; `maxAgeMs` is accepted and not honored, a log source keeping what its `log.maxAge` and `log.maxEvents` say.
134
+ * - A push stream's failure that no refusal explains, such as an upstream's outage, is `notifications/events/error`, its cause logged and reported, and the stream resumes from where it reached after 5 seconds; a refusal ends it with `notifications/events/terminated`. One before `active`, or a poll's, is a JSON-RPC `-32603`, which `client`'s `watch` reconnects after, as after a 5xx.
101
135
  */
102
- export declare function layerHttp<const Apps extends Served, const O extends Selection<ActionOf<Member<Apps>>> = {}, E = never, R = never, const D extends Any$1 | undefined = undefined>(implementations: Apps, options: LayerHttpOptions<E, R, ActionOf<Member<Apps>>> & O & NoInfer<Known<O, LayerHttpOptions<E, R, Any>>> & NoInfer<Required<SelectedOf<O, Apps>>> & Matching<NoInfer<SelectedOf<O, Apps>>, NoInfer<D>> & Inferring<D, NoInfer<SelectedOf<O, Apps>>>): Layer.Layer<never, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | Cause.IllegalArgumentError | E, ServerBuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", HttpToolRequestContext<RemoteRequest<Member<Apps>, SelectedOf<O, Apps>>>> | ProviderOf<D> | Features<R>>;
136
+ export declare function layerHttp<const Apps extends Served, const O extends Selection<ActionOf<Member<Apps>> | EventOf<Member<Apps>>> = {}, E = never, R = never, const D extends Any$2 | undefined = undefined>(implementations: Apps, options: LayerHttpOptions<E, R, ActionOf<Member<Apps>> | EventOf<Member<Apps>>> & O & NoInfer<Known<O, LayerHttpOptions<E, R>>> & NoInfer<Required<Contracts<Apps, O>>> & Matching<NoInfer<Contracts<Apps, O>>, NoInfer<D>> & Inferring<D, NoInfer<Contracts<Apps, O>>>): Layer.Layer<never, ServerError<Apps, O> | E, ServerBuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | EventServices<Apps, O> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", HttpToolRequestContext<RemoteRequest<Member<Apps>, SelectedOf<O, Apps>> | EventServedRequest<Member<Apps>, SelectedEventsOf<O, Apps>>>> | ProviderOf<D> | Features<R>>;
103
137
  /**
104
138
  * Serve MCP tools through newline-delimited JSON-RPC on standard I/O, speaking MCP
105
139
  * 2026-07-28, 2025-11-25 or 2025-06-18, as the host negotiates: the whole
@@ -108,13 +142,13 @@ export declare function layerHttp<const Apps extends Served, const O extends Sel
108
142
  * go to stderr, since stdout carries the protocol. The host supplies the `Stdio` service, and
109
143
  * provides the identity around `runStdio`; arguments are tool input only and never establish
110
144
  * request identity or authority, and each implementation's `authorize` runs for its protected
111
- * actions. Its tool results, and how it answers each revision and an earlier one asked for, are
112
- * `layerHttp`'s.
145
+ * actions and events. Its tool results, its events, and how it answers each revision and an
146
+ * earlier one asked for, are `layerHttp`'s.
113
147
  *
114
148
  * **Gotchas**
115
149
  *
116
150
  * - A line that is not JSON goes unanswered, with no `-32700` as over HTTP, and the lines after it are still answered.
117
151
  * - The layers provided around it log to stdout, among the protocol's lines: apply `ActionCli.logToStderr` to the program, last, as its JSDoc says.
118
152
  */
119
- export declare function runStdio<const Apps extends Served, const O extends Selection<ActionOf<Member<Apps>>> = {}, E = never, R = never>(implementations: Apps, options: Options<E, R, ActionOf<Member<Apps>>> & O & NoInfer<Known<O, Options<E, R, Any>>>): Effect.Effect<void, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | Cause.IllegalArgumentError | E, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | Stdio.Stdio | ToolRequestContext<ServedRequest<Member<Apps>, SelectedOf<O, Apps>>> | Features<R>>;
153
+ export declare function runStdio<const Apps extends Served, const O extends Selection<ActionOf<Member<Apps>> | EventOf<Member<Apps>>> = {}, E = never, R = never>(implementations: Apps, options: Options<E, R, ActionOf<Member<Apps>> | EventOf<Member<Apps>>> & O & NoInfer<Known<O, Options<E, R>>>): Effect.Effect<void, ServerError<Apps, O> | E, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | EventServices<Apps, O> | Stdio.Stdio | ToolRequestContext<ServedRequest<Member<Apps>, SelectedOf<O, Apps>> | EventRequestOf<Member<Apps>, SelectedEventsOf<O, Apps>>> | Features<R>>;
120
154
  export { type Client, type ClientOptions, McpCallError, type Params, type RequestOptions, client, request };