@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
@@ -0,0 +1,370 @@
1
+ import { Codec, CodecOf, Fields, NoInput } from "./contract/codec.mjs";
2
+ import { Refusal } from "./contract/errors.mjs";
3
+ import { Gap, OccurrenceSchema } from "./contract/occurrence.mjs";
4
+ import { AnyEventImplementation, EventImplementation } from "./contract/feed.mjs";
5
+ import { Listed, LogStorage } from "./contract/log.mjs";
6
+ import { Events, KeyOptions, MakeEventsOptions, RevisionStorage, layerMemory, makeEvents } from "./contract/hub.mjs";
7
+ import { Protected } from "./contract/implementation.mjs";
8
+ import { Authorizer, Deferred as Deferred$1 } from "./contract/authorizer.mjs";
9
+ import { Options as Options$1 } from "./Action.mjs";
10
+ import { DateTime, Duration, Effect, Schema, Scope, Stream } from "effect";
11
+ declare const TypeId = "~@gjermundgaraba/effect-actions/Event";
12
+ /** What `make` needs to define an event type. */
13
+ export interface Options {
14
+ /**
15
+ * Who may subscribe: `Action.Anyone`, or the identity service a subscriber must have, as an
16
+ * action's `caller` says; the rules of an identity are on `Action.Options.caller`.
17
+ */
18
+ readonly caller: Options$1["caller"];
19
+ /** What happens when the event fires; a model and a CLI user read it beside the event's name. */
20
+ readonly description: string;
21
+ /**
22
+ * The subscription's arguments, a schema or struct fields, as an action's `input` is: they may
23
+ * filter the occurrences, shape them, or configure the subscription. Omit for an event without
24
+ * arguments. Its rules are `Action.Options.input`'s.
25
+ */
26
+ readonly input?: Codec | Fields | undefined;
27
+ /**
28
+ * What each occurrence carries, a schema or struct fields: an object, since MCP sends it as an
29
+ * occurrence's `data`.
30
+ */
31
+ readonly payload: Codec | Fields;
32
+ }
33
+ type OnlyOptions<O> = { readonly [K in Exclude<O extends unknown ? keyof O : never, keyof Options>]: never; };
34
+ type Rules<O> = Options extends O ? unknown : OnlyOptions<O>;
35
+ type InputOf<O> = O extends {
36
+ readonly input?: infer I;
37
+ } ? CodecOf<Extract<Exclude<I, undefined>, Codec | Fields>> | ([undefined] extends [I] ? typeof NoInput : never) : typeof NoInput;
38
+ type PayloadOf<O> = O extends {
39
+ readonly payload: infer P extends Codec | Fields;
40
+ } ? CodecOf<P> : never;
41
+ /**
42
+ * A pure contract for something that happens, bound to its source by `implement`: subscribers
43
+ * receive its occurrences, each carrying a `payload`, filtered and shaped by the subscription's
44
+ * `input`. Its `name`, `input` and `payload` are public: reuse them, as in
45
+ * `Schema.Array(NoteCreated.payload)`.
46
+ */
47
+ export interface Event<Name extends string, Input extends Codec, Payload extends Codec, Caller extends Options["caller"] = Options["caller"]> {
48
+ readonly [TypeId]: typeof TypeId;
49
+ readonly caller: Caller;
50
+ readonly name: Name;
51
+ readonly description: string;
52
+ /** The subscription's arguments as a schema, a `Schema.Struct` where fields were given. */
53
+ readonly input: Input;
54
+ /** What each occurrence carries, as a schema. */
55
+ readonly payload: Payload;
56
+ }
57
+ /** Any event type, with its schemas erased. */
58
+ export type Any = Event<string, Codec, Codec>;
59
+ /** Whether `u` is an event type `make` defined: a binding's list tells events from actions by it. */
60
+ export declare const isEvent: (u: unknown) => u is Any;
61
+ /**
62
+ * Define an event type: what a subscription takes, what each occurrence carries, and who may
63
+ * subscribe. A surface serving it names it by `name`.
64
+ * Guide: `docs/Event.md` in this package; in an application: `examples/build-events/src/builds/`.
65
+ *
66
+ * **Gotchas**
67
+ *
68
+ * - A name is lowercase segments joined by dots, `note.created`: its form is in the message `Invalid event name` throws; it is unique among the events each surface serves, apart from action names.
69
+ * - Surfaces match an implementation to a contract by object identity, never by name, as for actions.
70
+ */
71
+ export declare function make<const Name extends string, const O extends Options>(name: Name, options: O & NoInfer<Rules<O>>): Event<Name, InputOf<O>, PayloadOf<O>, O["caller"]>;
72
+ /**
73
+ * One occurrence as a source produces it: its `data`, and optionally the upstream's own id, which
74
+ * subscribers deduplicate by, and when it happened. Without them, a log source's occurrence
75
+ * takes its cursor as its id and the time it was appended, the same on every read; a check's
76
+ * takes a generated id and the time it is read.
77
+ */
78
+ export interface Emitted<E extends Any> {
79
+ readonly data: E["payload"]["Type"];
80
+ /**
81
+ * A stable id: give the upstream's own, such as a message or delivery id, so a retry or a replay repeats it.
82
+ *
83
+ * **Gotchas**
84
+ *
85
+ * - An id given again for another occurrence is dropped as a duplicate by a `watch` and by `Webhook.layerReceiver`, each keeping its last 1,000: an id from a counter that restarts with the process repeats, so leave it out rather than give one.
86
+ */
87
+ readonly eventId?: string | undefined;
88
+ readonly timestamp?: DateTime.Utc | undefined;
89
+ }
90
+ /**
91
+ * What a check answers for a cursor: the occurrences since it, and the cursor after them.
92
+ *
93
+ * **Gotchas**
94
+ *
95
+ * - A cursor the upstream no longer serves, such as one older than its retention, is answered with `truncated: true` and a fresh cursor: the subscriber then knows it missed occurrences.
96
+ * - A `nextPoll` under a second is read as a second, and one over 5 minutes, `Duration.infinity` included, as 5 minutes, by the server's loop and by a polling client alike.
97
+ */
98
+ export interface Page<E extends Any> {
99
+ readonly events: ReadonlyArray<Emitted<E>>;
100
+ readonly cursor: string;
101
+ readonly truncated?: boolean | undefined;
102
+ /** More occurrences are ready: the subscriber asks again without waiting. */
103
+ readonly hasMore?: boolean | undefined;
104
+ /** How long a subscriber waits before it asks again; defaults to 5 seconds. */
105
+ readonly nextPoll?: Duration.Input | undefined;
106
+ }
107
+ interface Hooked<E extends Any, R> {
108
+ /**
109
+ * What a subscription needs set up upstream while it lasts, such as joining a channel: a scoped
110
+ * Effect, whose finalizer tears it down, run as the subscriber. A stream's lasts as long as the
111
+ * stream, a poll subscription's until `lease` after its last poll, or, watched in process, until
112
+ * the watch ends, and a webhook subscription's until it is unsubscribed or lapses, set up again
113
+ * as `persist` restores it.
114
+ *
115
+ * **Gotchas**
116
+ *
117
+ * - Write it so it may run twice for one subscription: a lease does not outlive the process, so a poll after a restart sets it up again.
118
+ * - A poll subscription's runs in its first poll's context, that request included, held until its lease ends, where a webhook subscription's runs without the request: read what it needs from the request in middleware, which provides it as a service.
119
+ */
120
+ readonly subscription?: ((input: E["input"]["Type"]) => Effect.Effect<void, never, R | Scope.Scope>) | undefined;
121
+ /** How long a poll subscription's `subscription` lasts after its last poll; defaults to 5 minutes. */
122
+ readonly lease?: Duration.Input | undefined;
123
+ /**
124
+ * The key a subscriber of `input` is reached by, run as the subscriber, such as the room it
125
+ * follows: `Events.notify` and `Events.publish` given a key reach the subscribers of that key
126
+ * alone, and those it answers `undefined` for, who follow every key. Without it, every
127
+ * announcement reaches every subscriber.
128
+ *
129
+ * **Gotchas**
130
+ *
131
+ * - An occurrence a log's `emit` gives without a `key` reaches every subscriber, whatever key it follows: give each one its key.
132
+ */
133
+ readonly key?: ((input: E["input"]["Type"]) => string | undefined | Effect.Effect<string | undefined, never, R>) | undefined;
134
+ }
135
+ /**
136
+ * An event type read from an upstream that can be asked what happened since a cursor, such as a
137
+ * change feed or a history API: one function answers every subscriber, whatever the surface.
138
+ *
139
+ * **Gotchas**
140
+ *
141
+ * - An occurrence without `eventId` takes a fresh id at each read, and a page's occurrences before its last are read again from the cursor asked after a reconnect mid-page: give the upstream's ids, or a subscriber receives them again under ids no `watch` drops.
142
+ */
143
+ export interface Check<E extends Any, R = never> extends Hooked<E, R> {
144
+ /**
145
+ * What happened since `cursor`, as the subscriber's `input` selects it, run as the subscriber.
146
+ * A `null` cursor is a subscriber with no position: answer nothing, so it starts after the last
147
+ * occurrence. An event whose occurrence is the whole value, such as a status, is a `Current`.
148
+ */
149
+ readonly check: (input: E["input"]["Type"], cursor: string | null) => Effect.Effect<Page<E>, Refusal, R>;
150
+ readonly log?: never;
151
+ readonly current?: never;
152
+ }
153
+ /**
154
+ * An event type whose occurrence is the whole value, such as a status or a list small enough to
155
+ * send whole: a subscriber receives the value as it is first, then again after each
156
+ * `Events.notify` that reaches it, never a change it missed one by one. The library keeps a
157
+ * revision per key, which each such notify moves, as the cursor and each occurrence's id.
158
+ *
159
+ * **Gotchas**
160
+ *
161
+ * - A notify without a change sends the value again, under a new id: notify after a write that changed it.
162
+ * - The revision is read before `current`, so a value written between them is sent again at the next read, never missed: write the value, then notify.
163
+ */
164
+ export interface Current<E extends Any, R = never> extends Hooked<E, R> {
165
+ /** The value as it is, as the subscriber's `input` selects it, run as the subscriber. */
166
+ readonly current: (input: E["input"]["Type"]) => Effect.Effect<E["payload"]["Type"], Refusal, R>;
167
+ readonly check?: never;
168
+ readonly log?: never;
169
+ }
170
+ /**
171
+ * An event type whose occurrences the library keeps as they are announced, for an upstream with
172
+ * no history of its own: `Events.publish` appends each, and `log.emit`, where given, a stream of
173
+ * them. Each subscriber receives the occurrences its `match` accepts, shaped by its `transform`.
174
+ *
175
+ * **Gotchas**
176
+ *
177
+ * - A cursor is only good where the log that minted it lives, for as long as it keeps its occurrences: after a restart, or once they are dropped, a subscriber resuming from it receives `truncated` and a fresh cursor.
178
+ * - A subscriber reading slower than occurrences arrive falls behind the log like a resumed one: it receives `truncated`, never a backlog held for it alone.
179
+ * - An `emit` stream is consumed from startup, in the server's scope, by one build at a time, until it ends, even past its build's release: a failure ends it and is reported, an end is logged, and the next held build's stream, if any, takes over; the log then holds what was published.
180
+ */
181
+ export interface Log<E extends Any, R = never> extends Hooked<E, R> {
182
+ /** What the log keeps, and the stream feeding it besides `Events.publish`; `{}` for none. */
183
+ readonly log: LogOptions<E>;
184
+ /** Whether a subscriber of `input` receives an occurrence carrying `data`; defaults to every one. */
185
+ readonly match?: ((input: E["input"]["Type"], data: E["payload"]["Type"]) => boolean | Effect.Effect<boolean, never, R>) | undefined;
186
+ /** The data a subscriber of `input` receives in place of `data`, such as one with fields removed. */
187
+ readonly transform?: ((input: E["input"]["Type"], data: E["payload"]["Type"]) => E["payload"]["Type"] | Effect.Effect<E["payload"]["Type"], never, R>) | undefined;
188
+ readonly check?: never;
189
+ readonly current?: never;
190
+ }
191
+ /** What a log source keeps, and what feeds it besides `Events.publish`. */
192
+ export interface LogOptions<E extends Any> {
193
+ /**
194
+ * A stream of occurrences, such as a message bus's, appended as they arrive from startup, each
195
+ * reaching the subscribers of its `key`, as `Events.publish` given one does.
196
+ */
197
+ readonly emit?: Stream.Stream<Emitted<E> & KeyOptions> | undefined;
198
+ /**
199
+ * How long an occurrence is kept for subscribers resuming from a cursor; defaults to 10 minutes.
200
+ * The first build attached to its log sets it, and later builds share that log.
201
+ */
202
+ readonly maxAge?: Duration.Input | undefined;
203
+ /**
204
+ * How many occurrences are kept at most, of every key together; defaults to 10,000.
205
+ *
206
+ * **Gotchas**
207
+ *
208
+ * - A busy key drops a quiet one's occurrences too: a subscriber of the quiet key resuming after them receives a gap, though none of its own was dropped.
209
+ */
210
+ readonly maxEvents?: number | undefined;
211
+ }
212
+ /** Where an event type's occurrences come from: exactly one of `check`, `current` or `log`. */
213
+ export type Source<E extends Any, R = never> = Check<E, R> | Current<E, R> | Log<E, R>;
214
+ /**
215
+ * One occurrence as a subscriber receives it: `eventId`, the source's id or one generated, which
216
+ * a subscriber deduplicates by; `cursor`, where to resume after it, `null` for an event type that
217
+ * cannot be resumed.
218
+ */
219
+ export type Occurrence<E extends Any> = OccurrenceSchema<E>["Type"];
220
+ /** What a subscriber receives: occurrences, and gaps where it missed some. */
221
+ export type Received<E extends Any> = Occurrence<E> | Gap;
222
+ declare const NotServed_base: Schema.Class<NotServed, Schema.TaggedStruct<"NotServed", {}>, import("effect/Cause").YieldableError & {
223
+ readonly brand: unique symbol;
224
+ }>;
225
+ /** The server serves no event type of that name, or no longer does. */
226
+ export declare class NotServed extends NotServed_base {}
227
+ declare const Unsupported_base: Schema.Class<Unsupported, Schema.TaggedStruct<"Unsupported", {
228
+ readonly feature: Schema.String;
229
+ readonly value: Schema.optional<Schema.String>;
230
+ }>, import("effect/Cause").YieldableError & {
231
+ readonly brand: unique symbol;
232
+ }>;
233
+ /**
234
+ * The server serves the event type, but not as asked: `feature` names what it does not offer,
235
+ * such as `deliveryMode`, and `value` which one, such as `push`.
236
+ */
237
+ export declare class Unsupported extends Unsupported_base {}
238
+ /**
239
+ * Why a webhook's callback endpoint failed, in SEP-3415's categories: it refused the connection
240
+ * or was unreachable, timed out, failed TLS, answered a status from 300 to 499 (`http_4xx`, as
241
+ * the categories name no 3xx) or a 5xx, or did not echo the verification challenge.
242
+ */
243
+ export declare const CallbackCategory: Schema.Literals<readonly ["connection_refused", "timeout", "tls_error", "http_4xx", "http_5xx", "challenge_failed"]>;
244
+ export type CallbackCategory = typeof CallbackCategory.Type;
245
+ declare const CallbackFailed_base: Schema.Class<CallbackFailed, Schema.TaggedStruct<"CallbackFailed", {
246
+ readonly category: Schema.Literals<readonly ["connection_refused", "timeout", "tls_error", "http_4xx", "http_5xx", "challenge_failed"]>;
247
+ }>, import("effect/Cause").YieldableError & {
248
+ readonly brand: unique symbol;
249
+ }>;
250
+ /** A webhook subscription's callback endpoint failed its verification, or was unreachable. */
251
+ export declare class CallbackFailed extends CallbackFailed_base {}
252
+ declare const NotSubscribed_base: Schema.Class<NotSubscribed, Schema.TaggedStruct<"NotSubscribed", {}>, import("effect/Cause").YieldableError & {
253
+ readonly brand: unique symbol;
254
+ }>;
255
+ /** No webhook subscription matches the one an unsubscribe names. */
256
+ export declare class NotSubscribed extends NotSubscribed_base {}
257
+ declare const Reason: Schema.Union<readonly [typeof NotServed, typeof Unsupported, typeof CallbackFailed, typeof NotSubscribed]>;
258
+ declare const SubscriptionError_base: Schema.Class<SubscriptionError, Schema.TaggedStruct<"SubscriptionError", {
259
+ readonly event: Schema.String;
260
+ readonly reason: Schema.Union<readonly [typeof NotServed, typeof Unsupported, typeof CallbackFailed, typeof NotSubscribed]>;
261
+ readonly message: Schema.String;
262
+ }>, import("effect/Cause").YieldableError & {
263
+ readonly brand: unique symbol;
264
+ }>;
265
+ /**
266
+ * A subscription that cannot start or go on: `reason` says why, so a subscriber branches on it
267
+ * with `Effect.catchReason("SubscriptionError", "NotServed", ...)`; `message` names the event and
268
+ * the reason. A refusal is `Action.Unauthenticated` or `Action.Forbidden`, and input that does not
269
+ * decode `Action.InvalidInput`, as for an action.
270
+ */
271
+ export declare class SubscriptionError extends SubscriptionError_base {
272
+ /** The error for `event` failing with `reason`, its message built from both. */
273
+ static of(event: string, reason: (typeof Reason)["Type"]): SubscriptionError;
274
+ }
275
+ /**
276
+ * Where a watch keeps its cursor: what it kept under a key, and keeping a new one; a failure `E`
277
+ * of either ends the watch with it.
278
+ */
279
+ export interface CursorStore<E = never> {
280
+ readonly get: (key: string) => Effect.Effect<string | undefined, E>;
281
+ readonly set: (key: string, cursor: string) => Effect.Effect<void, E>;
282
+ }
283
+ declare const WebhookDelivery_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Readonly<A> & {
284
+ readonly _tag: "WebhookDelivery";
285
+ } & import("effect/Pipeable").Pipeable;
286
+ /**
287
+ * A watch delivered by webhook, which `Webhook.delivery` makes: the server POSTs each occurrence
288
+ * to the route `Webhook.layerReceiver` serves in the watching process, reached under `baseUrl`,
289
+ * for as long as the watch is read, each grant asking for `ttl`.
290
+ */
291
+ export declare class WebhookDelivery extends WebhookDelivery_base<{
292
+ /**
293
+ * The https URL the server reaches the watching process at: its `layerReceiver` route's path
294
+ * is appended to it, `https://notifier.example.com` with `/hooks/builds` delivering to
295
+ * `https://notifier.example.com/hooks/builds`.
296
+ */
297
+ readonly baseUrl: string;
298
+ /** The lifetime each subscribe asks for; omitted, the server's longest. */
299
+ readonly ttl?: Duration.Input | undefined;
300
+ }> {}
301
+ /**
302
+ * How a client watches an event type; `E` is what its `resume` store fails with.
303
+ *
304
+ * **Gotchas**
305
+ *
306
+ * - Each run of a watch's stream starts from these options, a retry or a repeat included: to go on where a failed run stopped, give `resume`, which every run reads.
307
+ * - `resume` keeps an occurrence's cursor once the next is pulled, so a consumer stopped while handling one receives it again: deduplicate by `eventId` where that matters.
308
+ */
309
+ export interface WatchOptions<E = never> {
310
+ /** Where to start: a cursor an occurrence or a gap carried; defaults to now. */
311
+ readonly cursor?: string | null | undefined;
312
+ /**
313
+ * `"stream"` holds one subscription open, `"poll"` asks again and again, and a
314
+ * `Webhook.delivery` has the server POST each occurrence to a receiver this process serves,
315
+ * which the watch then requires; left out, a watch streams where its surface can and polls
316
+ * where the server serves the event by poll alone.
317
+ */
318
+ readonly mode?: "stream" | "poll" | WebhookDelivery | undefined;
319
+ /**
320
+ * Where to keep the cursor between watches, such as across restarts: it starts from the cursor
321
+ * kept under `key`, when no `cursor` is given, and keeps each new one there. Effect's
322
+ * `KeyValueStore` is one: `{ store: yield* KeyValueStore.KeyValueStore, key: "builds" }`.
323
+ */
324
+ readonly resume?: {
325
+ readonly store: CursorStore<E>;
326
+ readonly key: string;
327
+ } | undefined;
328
+ }
329
+ /**
330
+ * Authorization for protected event types: it refuses, and fails with nothing else. It runs for
331
+ * every subscription of a protected event type, before its source.
332
+ */
333
+ export type Authorize<E extends Any, R = never> = (event: E) => Effect.Effect<void, Refusal, R>;
334
+ /** Every authenticated subscriber may subscribe to a protected event type. */
335
+ export declare const allowAll: Authorize<Any>;
336
+ type Target = Any | readonly Any[];
337
+ type EventsOf<T extends Target> = T extends readonly Any[] ? T[number] : Extract<T, Any>;
338
+ type TakesNoAuthorize<T extends Target> = [Protected<EventsOf<T>>] extends [never] ? unknown : "A protected event takes { authorize }: your rule, or Event.allowAll";
339
+ type NamesOf<T extends Target> = EventsOf<T>["name"];
340
+ type SourcesOf<T extends Target, R, RequestServices> = T extends Any ? Check<T, R> | Current<T, R> | Log<T, R> : { readonly [K in keyof RequestServices]: K extends NamesOf<T> ? Check<Extract<EventsOf<T>, {
341
+ readonly name: K;
342
+ }>, RequestServices[K]> | Current<Extract<EventsOf<T>, {
343
+ readonly name: K;
344
+ }>, RequestServices[K]> | Log<Extract<EventsOf<T>, {
345
+ readonly name: K;
346
+ }>, RequestServices[K]> : never; };
347
+ type Implemented<T extends Target, RequestServices, R, EX, RX, RA, EAX, RAX> = EventImplementation<EventsOf<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>> | Events, Exclude<RA, Scope.Scope>, Deferred$1<EAX>, Deferred$1<Exclude<RAX, Scope.Scope>>>;
348
+ /**
349
+ * Bind sources to event types, behind authorization: one event type and its source, or a list
350
+ * and a record of sources keyed by event name, given directly or built by an Effect whose
351
+ * services are startup requirements, while what a source yields per subscriber is a request-time
352
+ * requirement. `authorize` is required when the list holds a protected event type. Every surface
353
+ * serving events takes the implementation it returns, beside action implementations in one list.
354
+ *
355
+ * **Gotchas**
356
+ *
357
+ * - The builder runs once per layer graph, however many surfaces serve it, as the first event type a surface serves attaches; a log source's `emit` starts being consumed once its event type is served or watched, and an event type no surface serves starts nothing.
358
+ * - `check`, `current`, `match`, `transform`, `key` and `subscription` run as the subscriber: what they yield, such as the caller, is resolved per subscription.
359
+ * - A source holds exactly one of `check`, `current` or `log`, checked as a surface serving its event type builds, which fails otherwise: a source of an event type no surface serves is not checked.
360
+ * - Serving or watching an event type needs `Event.Events`, which its source attaches to as it builds: provide `Event.layerMemory` once, around the handlers announcing and the surfaces serving.
361
+ * - An occurrence whose data breaks the event's payload is reported, and every surface sends a `Gap` in its place, a poll's page `truncated`: the rest of its page is still delivered.
362
+ * - A read failing with no refusal is reported once, and the subscription reads on from where it reached: a stream a server holds, a webhook's and `Action.client`'s `watch` after 5 seconds, a client's poll, which a server answers with an error, after its reconnect backoff, from 1 second doubling to 30; a refusal ends the subscription.
363
+ */
364
+ 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>(events: T, sources: SourcesOf<T, R, RequestServices> | Effect.Effect<SourcesOf<T, R, RequestServices>, EX, RX>, options: [Protected<EventsOf<T>>] extends [never] ? {
365
+ readonly authorize?: "A public-only target takes no authorize";
366
+ } | undefined : {
367
+ readonly authorize: Authorizer<Protected<EventsOf<T>>, RA, EAX, RAX>;
368
+ }): Implemented<T, RequestServices, R, EX, RX, RA, EAX, RAX>;
369
+ 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>(events: T & TakesNoAuthorize<T>, sources: SourcesOf<T, R, RequestServices> | Effect.Effect<SourcesOf<T, R, RequestServices>, EX, RX>): Implemented<T, RequestServices, R, EX, RX, never, never, never>;
370
+ export { type AnyEventImplementation as AnyImplementation, Events, Gap, type EventImplementation as Implementation, type KeyOptions, type Listed, type LogStorage, type MakeEventsOptions, type RevisionStorage, layerMemory, makeEvents };
package/dist/Event.mjs ADDED
@@ -0,0 +1,103 @@
1
+ import { Anyone, assertCaller } from "./contract/caller.mjs";
2
+ import { assertEventName, assertOnce } from "./contract/names.mjs";
3
+ import { codecOf } from "./contract/codec.mjs";
4
+ import { authorizerOf } from "./contract/authorizer.mjs";
5
+ import { Events, layerMemory, makeEvents } from "./contract/hub.mjs";
6
+ import { Gap } from "./contract/occurrence.mjs";
7
+ import { EventImplementation, sourcesOf } from "./contract/feed.mjs";
8
+ import { Array as Array$1, Data, Effect, Match, Predicate, Schema } from "effect";
9
+ // src/contract/Event.ts
10
+ const TypeId = "~@gjermundgaraba/effect-actions/Event";
11
+ /** Whether `u` is an event type `make` defined: a binding's list tells events from actions by it. */
12
+ const isEvent = (u) => Predicate.hasProperty(u, TypeId);
13
+ function make(name, options) {
14
+ assertEventName(name);
15
+ const { caller } = options;
16
+ assertCaller(caller);
17
+ if (options.payload === void 0) throw new Error(`Missing payload: event "${name}" (declare what each occurrence carries)`);
18
+ return {
19
+ [TypeId]: TypeId,
20
+ caller,
21
+ name,
22
+ description: options.description,
23
+ input: codecOf(options.input ?? {}),
24
+ payload: codecOf(options.payload)
25
+ };
26
+ }
27
+ /** The server serves no event type of that name, or no longer does. */
28
+ var NotServed = class extends Schema.TaggedError()("NotServed", {}) {};
29
+ /**
30
+ * The server serves the event type, but not as asked: `feature` names what it does not offer,
31
+ * such as `deliveryMode`, and `value` which one, such as `push`.
32
+ */
33
+ var Unsupported = class extends Schema.TaggedError()("Unsupported", {
34
+ feature: Schema.String,
35
+ value: Schema.optional(Schema.String)
36
+ }) {};
37
+ /**
38
+ * Why a webhook's callback endpoint failed, in SEP-3415's categories: it refused the connection
39
+ * or was unreachable, timed out, failed TLS, answered a status from 300 to 499 (`http_4xx`, as
40
+ * the categories name no 3xx) or a 5xx, or did not echo the verification challenge.
41
+ */
42
+ const CallbackCategory = Schema.Literals([
43
+ "connection_refused",
44
+ "timeout",
45
+ "tls_error",
46
+ "http_4xx",
47
+ "http_5xx",
48
+ "challenge_failed"
49
+ ]);
50
+ /** A webhook subscription's callback endpoint failed its verification, or was unreachable. */
51
+ var CallbackFailed = class extends Schema.TaggedError()("CallbackFailed", { category: CallbackCategory }) {};
52
+ /** No webhook subscription matches the one an unsubscribe names. */
53
+ var NotSubscribed = class extends Schema.TaggedError()("NotSubscribed", {}) {};
54
+ const Reason = Schema.Union([
55
+ NotServed,
56
+ Unsupported,
57
+ CallbackFailed,
58
+ NotSubscribed
59
+ ]);
60
+ const sentenceOf = Match.typeTags()({
61
+ NotServed: () => "is not served",
62
+ Unsupported: ({ feature, value }) => `is not served with this ${feature}${value === void 0 ? "" : `: ${value}`}`,
63
+ CallbackFailed: ({ category }) => `could not reach its webhook's callback: ${category}`,
64
+ NotSubscribed: () => "has no such webhook subscription"
65
+ });
66
+ /**
67
+ * A subscription that cannot start or go on: `reason` says why, so a subscriber branches on it
68
+ * with `Effect.catchReason("SubscriptionError", "NotServed", ...)`; `message` names the event and
69
+ * the reason. A refusal is `Action.Unauthenticated` or `Action.Forbidden`, and input that does not
70
+ * decode `Action.InvalidInput`, as for an action.
71
+ */
72
+ var SubscriptionError = class SubscriptionError extends Schema.TaggedError()("SubscriptionError", {
73
+ event: Schema.String,
74
+ reason: Reason,
75
+ message: Schema.String
76
+ }, { httpApiStatus: 409 }) {
77
+ /** The error for `event` failing with `reason`, its message built from both. */
78
+ static of(event, reason) {
79
+ return new SubscriptionError({
80
+ event,
81
+ reason,
82
+ message: `Event "${event}" ${sentenceOf(reason)}`
83
+ });
84
+ }
85
+ };
86
+ /**
87
+ * A watch delivered by webhook, which `Webhook.delivery` makes: the server POSTs each occurrence
88
+ * to the route `Webhook.layerReceiver` serves in the watching process, reached under `baseUrl`,
89
+ * for as long as the watch is read, each grant asking for `ttl`.
90
+ */
91
+ var WebhookDelivery = class extends Data.TaggedClass("WebhookDelivery") {};
92
+ /** Every authenticated subscriber may subscribe to a protected event type. */
93
+ const allowAll = () => Effect.void;
94
+ function implement(events, sources, options) {
95
+ const listed = Array$1.ensure(events);
96
+ assertOnce("event", listed);
97
+ const authorizer = authorizerOf({
98
+ singular: "event",
99
+ module: "Event"
100
+ }, listed.some((event) => event.caller !== Anyone), options?.authorize);
101
+ return new EventImplementation(listed, sourcesOf(listed, !Array.isArray(events), sources), authorizer);
102
+ }
103
+ export { CallbackCategory, CallbackFailed, Events, Gap, NotServed, NotSubscribed, SubscriptionError, Unsupported, WebhookDelivery, allowAll, implement, isEvent, layerMemory, make, makeEvents };
@@ -1,7 +1,6 @@
1
1
  import { Served } from "./testing/memory.mjs";
2
2
  import { Layer } from "effect";
3
3
  import { HttpClient, HttpRouter } from "effect/http";
4
- //#region src/testing/Testing.d.ts
5
4
  /**
6
5
  * The native `HttpClient`, answered by `handler` instead of the network: a web handler the test
7
6
  * serves and disposes of, which other tests and raw web `Request`s may share, such as
@@ -29,5 +28,4 @@ export declare function layer(handler: (request: Request) => Promise<Response>):
29
28
  * - It never requires `FileSystem`, `Path`, `HttpPlatform` or `Etag.Generator`: one provided around it is the routes', and `HttpServer.layerServices`' defaults stand in for the rest, whose `FileSystem` is a no-op, so a file the routes read is `NotFound` until `NodeFileSystem.layer` is provided around it.
30
29
  * - It answers HTTP requests and cannot upgrade a WebSocket: give an rpc layer `RpcServer.layerProtocolHttp` in the test, and its client `RpcClient.layerProtocolHttp` over this layer.
31
30
  */
32
- export declare function layer<A, E, R>(routes: Layer.Layer<A, E, R>): Layer.Layer<HttpClient.HttpClient, E, Exclude<HttpRouter.Request.Without<R> | HttpRouter.Request.Only<"Requires", R> | HttpRouter.Request.Only<"GlobalRequires", R>, Served>>;
33
- //#endregion
31
+ export declare function layer<A, E, R>(routes: Layer.Layer<A, E, R>): Layer.Layer<HttpClient.HttpClient, E, Exclude<HttpRouter.Request.Without<R> | HttpRouter.Request.Only<"Requires", R> | HttpRouter.Request.Only<"GlobalRequires", R>, Served>>;
package/dist/Testing.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import { clientOf } from "./testing/memory.mjs";
2
2
  import { Cause, Console, Context, Effect, ErrorReporter, FileSystem, Layer, Option, Path } from "effect";
3
3
  import { Etag, HttpEffect, HttpPlatform, HttpRouter } from "effect/http";
4
- //#region src/testing/Testing.ts
4
+ // src/testing/Testing.ts
5
+ const isReported = (cause) => cause.reasons.some((reason) => Cause.isFailReason(reason) && !ErrorReporter.isIgnored(reason.error) || Cause.isDieReason(reason) && !ErrorReporter.isIgnored(reason.defect));
5
6
  function layer(routes) {
6
7
  if (!Layer.isLayer(routes)) return clientOf(routes);
7
8
  return Layer.unwrap(Effect.gen(function* () {
@@ -12,6 +13,4 @@ function layer(routes) {
12
13
  return clientOf(HttpEffect.toWebHandlerWith(context)(Effect.tapCause(app, (cause) => isReported(cause) ? Effect.logError(cause).pipe(Effect.provideService(Console.Console, globalThis.console)) : Effect.void)));
13
14
  }));
14
15
  }
15
- const isReported = (cause) => cause.reasons.some((reason) => Cause.isFailReason(reason) && !ErrorReporter.isIgnored(reason.error) || Cause.isDieReason(reason) && !ErrorReporter.isIgnored(reason.defect));
16
- //#endregion
17
16
  export { layer };