@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
@@ -1,5 +1,7 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
- import { type Note, NoteChanged, NoteNotFound, RequestConflict } from "./Notes.js";
2
+
3
+ import { NoteChanged, NoteNotFound, RequestConflict } from "./Notes.js";
4
+ import type { Note } from "./Notes.js";
3
5
 
4
6
  /**
5
7
  * Where notes live, each owned by one user. Ownership is checked here, in the data access, so no
@@ -8,26 +10,32 @@ import { type Note, NoteChanged, NoteNotFound, RequestConflict } from "./Notes.j
8
10
  export class NoteStore extends Context.Service<
9
11
  NoteStore,
10
12
  {
11
- readonly list: (owner: string) => Effect.Effect<ReadonlyArray<Note>>;
12
- readonly get: (owner: string, id: string) => Effect.Effect<Note, NoteNotFound>;
13
+ readonly list: (owner: string) => Effect.Effect<readonly Note[]>;
14
+ readonly get: (
15
+ owner: string,
16
+ id: string
17
+ ) => Effect.Effect<Note, NoteNotFound>;
13
18
  readonly create: (
14
19
  owner: string,
15
20
  title: string,
16
21
  body: string,
17
- requestId?: string,
22
+ requestId?: string
18
23
  ) => Effect.Effect<Note, RequestConflict>;
19
24
  /** Every note or none: a database does this in one transaction. */
20
25
  readonly createAll: (
21
26
  owner: string,
22
- notes: ReadonlyArray<{ readonly title: string; readonly body: string }>,
23
- ) => Effect.Effect<ReadonlyArray<Note>>;
27
+ notes: ReadonlyArray<{ readonly title: string; readonly body: string }>
28
+ ) => Effect.Effect<readonly Note[]>;
24
29
  readonly update: (
25
30
  owner: string,
26
31
  id: string,
27
32
  body: string,
28
- revision: number,
33
+ revision: number
29
34
  ) => Effect.Effect<Note, NoteNotFound | NoteChanged>;
30
- readonly remove: (owner: string, id: string) => Effect.Effect<void, NoteNotFound>;
35
+ readonly remove: (
36
+ owner: string,
37
+ id: string
38
+ ) => Effect.Effect<void, NoteNotFound>;
31
39
  readonly count: Effect.Effect<number>;
32
40
  }
33
41
  >()("notes-service/NoteStore") {
@@ -43,7 +51,8 @@ export class NoteStore extends Context.Service<
43
51
  // Keep the original result even after the note is updated or deleted.
44
52
  const creations = new Map<string, Map<string, Note>>();
45
53
 
46
- const ownedBy = (owner: string) => notes.get(owner) ?? new Map<string, Note>();
54
+ const ownedBy = (owner: string) =>
55
+ notes.get(owner) ?? new Map<string, Note>();
47
56
 
48
57
  const save = (owner: string, note: Note) => {
49
58
  notes.set(owner, ownedBy(owner).set(note.id, note));
@@ -51,67 +60,85 @@ export class NoteStore extends Context.Service<
51
60
  return note;
52
61
  };
53
62
 
54
- const get = Effect.fn("NoteStore.get")(function* (owner: string, id: string) {
63
+ const get = Effect.fn("NoteStore.get")(function* (
64
+ owner: string,
65
+ id: string
66
+ ) {
55
67
  const note = ownedBy(owner).get(id);
56
68
 
57
- if (note === undefined) return yield* new NoteNotFound({ id });
69
+ if (note === undefined) {
70
+ return yield* new NoteNotFound({ id });
71
+ }
58
72
 
59
73
  return note;
60
74
  });
61
75
 
62
76
  return NoteStore.of({
63
- list: (owner) => Effect.sync(() => Array.from(ownedBy(owner).values())),
77
+ list: (owner) => Effect.sync(() => [...ownedBy(owner).values()]),
64
78
  get,
65
- create: Effect.fn("NoteStore.create")(function* (owner, title, body, requestId) {
66
- if (requestId !== undefined) {
67
- const recorded = creations.get(owner)?.get(requestId);
79
+ create: Effect.fn("NoteStore.create")(
80
+ function* (owner, title, body, requestId) {
81
+ if (requestId !== undefined) {
82
+ const recorded = creations.get(owner)?.get(requestId);
68
83
 
69
- if (recorded !== undefined) {
70
- if (recorded.title !== title || recorded.body !== body) {
71
- return yield* new RequestConflict({ requestId });
72
- }
84
+ if (recorded !== undefined) {
85
+ if (recorded.title !== title || recorded.body !== body) {
86
+ return yield* new RequestConflict({ requestId });
87
+ }
73
88
 
74
- return recorded;
89
+ return recorded;
90
+ }
75
91
  }
76
- }
77
92
 
78
- // No yield between lookup, mutation and recording: concurrent calls cannot interleave
79
- // or interruption leave a note without its result.
80
- const note = save(owner, { id: crypto.randomUUID(), title, body, revision: 1 });
93
+ // No yield between lookup, mutation and recording: concurrent calls cannot interleave
94
+ // or interruption leave a note without its result.
95
+ const note = save(owner, {
96
+ id: crypto.randomUUID(),
97
+ title,
98
+ body,
99
+ revision: 1,
100
+ });
101
+
102
+ if (requestId !== undefined) {
103
+ const requests = creations.get(owner) ?? new Map<string, Note>();
104
+ requests.set(requestId, note);
105
+ creations.set(owner, requests);
106
+ }
81
107
 
82
- if (requestId !== undefined) {
83
- const requests = creations.get(owner) ?? new Map<string, Note>();
84
- requests.set(requestId, note);
85
- creations.set(owner, requests);
108
+ return note;
86
109
  }
87
-
88
- return note;
89
- }),
110
+ ),
90
111
  createAll: (owner, all) =>
91
112
  Effect.sync(() =>
92
113
  all.map(({ title, body }) =>
93
- save(owner, { id: crypto.randomUUID(), title, body, revision: 1 }),
94
- ),
114
+ save(owner, { id: crypto.randomUUID(), title, body, revision: 1 })
115
+ )
95
116
  ),
96
117
  // No `yield*` between the read and the write: a fiber may be set aside at one, and two
97
118
  // updates that each read revision 1 before either wrote would both succeed. A database
98
119
  // does this with a conditional update or a transaction.
99
- update: Effect.fn("NoteStore.update")(function* (owner, id, body, revision) {
100
- const current = ownedBy(owner).get(id);
120
+ update: Effect.fn("NoteStore.update")(
121
+ function* (owner, id, body, revision) {
122
+ const current = ownedBy(owner).get(id);
101
123
 
102
- if (current === undefined) return yield* new NoteNotFound({ id });
124
+ if (current === undefined) {
125
+ return yield* new NoteNotFound({ id });
126
+ }
103
127
 
104
- if (current.revision !== revision) {
105
- return yield* new NoteChanged({ id, revision: current.revision });
106
- }
128
+ if (current.revision !== revision) {
129
+ return yield* new NoteChanged({ id, revision: current.revision });
130
+ }
107
131
 
108
- return save(owner, { ...current, body, revision: revision + 1 });
109
- }),
132
+ return save(owner, { ...current, body, revision: revision + 1 });
133
+ }
134
+ ),
110
135
  remove: Effect.fn("NoteStore.remove")(function* (owner, id) {
111
- if (!ownedBy(owner).delete(id)) return yield* new NoteNotFound({ id });
136
+ if (!ownedBy(owner).delete(id)) {
137
+ return yield* new NoteNotFound({ id });
138
+ }
112
139
  }),
113
140
  count: Effect.sync(() =>
114
- Array.from(notes.values()).reduce((total, owned) => total + owned.size, 0),
141
+ [...notes.values()].reduce((total, owned) => total + owned.size, 0)
115
142
  ),
116
143
  });
117
144
  });
@@ -1,8 +1,9 @@
1
+ import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
2
+ import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
1
3
  import { Layer } from "effect";
2
4
  import { HttpRouter, HttpServerResponse } from "effect/http";
3
5
  import { OpenApi } from "effect/http-api";
4
- import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
5
- import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
6
+
6
7
  import { Http } from "./Api.js";
7
8
  import { identity } from "./identity/handlers.js";
8
9
  import { Login } from "./identity/Identity.js";
@@ -25,7 +26,7 @@ const health = HttpRouter.add("GET", "/healthz", HttpServerResponse.text("ok"));
25
26
  const openApi = HttpRouter.add(
26
27
  "GET",
27
28
  "/api/openapi.json",
28
- HttpServerResponse.jsonUnsafe(OpenApi.fromApi(Http.api)),
29
+ HttpServerResponse.jsonUnsafe(OpenApi.fromApi(Http.api))
29
30
  );
30
31
 
31
32
  // What the host serves: the JSON routes, the same actions as MCP tools at `/mcp`, and the host's
@@ -40,12 +41,13 @@ const serving = (requestScoped: boolean) =>
40
41
  ActionMcp.layerHttp(implementations, {
41
42
  name: "notes-service",
42
43
  version: "1.0.0",
43
- instructions: "Notes belong to the signed-in user. Read a note before updating it.",
44
+ instructions:
45
+ "Notes belong to the signed-in user. Read a note before updating it.",
44
46
  authentication: Login,
45
47
  requestScoped,
46
48
  }),
47
49
  health,
48
- openApi,
50
+ openApi
49
51
  ).pipe(Layer.provide(authenticate));
50
52
 
51
53
  export const routes = serving(false);
@@ -1,7 +1,9 @@
1
1
  import { createServer } from "node:http";
2
+
2
3
  import { NodeHttpServer, NodeRuntime } from "@effect/platform-node";
3
4
  import { ByteSize, Config, Effect, Layer } from "effect";
4
5
  import { HttpRouter, HttpServerRequest } from "effect/http";
6
+
5
7
  import { NoteStore } from "./notes/store.js";
6
8
  import { routes } from "./routes.js";
7
9
 
@@ -10,18 +12,23 @@ import { routes } from "./routes.js";
10
12
  const server = Layer.unwrap(
11
13
  Effect.gen(function* () {
12
14
  // The loopback default serves this machine alone; HOST=0.0.0.0 lets others reach it.
13
- const host = yield* Config.String("HOST").pipe(Config.withDefault("127.0.0.1"));
15
+ const host = yield* Config.String("HOST").pipe(
16
+ Config.withDefault("127.0.0.1")
17
+ );
18
+
14
19
  const port = yield* Config.Port("PORT").pipe(Config.withDefault(3000));
15
20
 
16
21
  return HttpRouter.serve(routes).pipe(
17
22
  Layer.provide(NodeHttpServer.layer(createServer, { host, port })),
18
23
  // Request bodies have no size limit unless the host sets one. This covers every route that
19
24
  // reads its body whole.
20
- Layer.provide(Layer.succeed(HttpServerRequest.MaxBodySize, ByteSize.mebibytes(1))),
25
+ Layer.provide(
26
+ Layer.succeed(HttpServerRequest.MaxBodySize, ByteSize.mebibytes(1))
27
+ ),
21
28
  // DEMO ONLY: notes live in this process's memory; a deployment provides a store that lasts.
22
- Layer.provide(NoteStore.layerMemory),
29
+ Layer.provide(NoteStore.layerMemory)
23
30
  );
24
- }),
31
+ })
25
32
  );
26
33
 
27
- Layer.launch(server).pipe(NodeRuntime.runMain);
34
+ Layer.launch(server).pipe(NodeRuntime.runMain());
@@ -1,5 +1,5 @@
1
- import { Schema } from "effect";
2
1
  import * as Action from "@gjermundgaraba/effect-actions/Action";
2
+ import { Schema } from "effect";
3
3
 
4
4
  // Public: `caller: Action.Anyone`. It answers before anyone has signed in, on every surface.
5
5
  export const GetStatus = Action.make("getStatus", {
@@ -1,5 +1,6 @@
1
- import { Effect } from "effect";
2
1
  import * as Action from "@gjermundgaraba/effect-actions/Action";
2
+ import { Effect } from "effect";
3
+
3
4
  import { NoteStore } from "../notes/store.js";
4
5
  import { GetStatus } from "./Status.js";
5
6
 
@@ -9,6 +10,7 @@ export const status = Action.implement(
9
10
  Effect.gen(function* () {
10
11
  const store = yield* NoteStore;
11
12
 
12
- return () => Effect.map(store.count, (notes) => ({ service: "notes-service", notes }));
13
- }),
13
+ return () =>
14
+ Effect.map(store.count, (notes) => ({ service: "notes-service", notes }));
15
+ })
14
16
  );
@@ -1,7 +1,8 @@
1
1
  import { NodeRuntime, NodeStdio } from "@effect/platform-node";
2
- import { Effect } from "effect";
3
2
  import * as ActionCli from "@gjermundgaraba/effect-actions/ActionCli";
4
3
  import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
4
+ import { Effect } from "effect";
5
+
5
6
  import { identity } from "./identity/handlers.js";
6
7
  import { CurrentUser } from "./identity/Identity.js";
7
8
  import { users } from "./identity/users.js";
@@ -17,11 +18,14 @@ import { status } from "./status/handlers.js";
17
18
  // over stdio, so this host names the caller itself. It is a process of its own with services
18
19
  // of its own. DEMO ONLY: this in-memory store holds its notes, not the running server's. To
19
20
  // share them, both provide a store they both reach, such as a database.
20
- ActionMcp.runStdio([status, notes, identity], { name: "notes-service", version: "1.0.0" }).pipe(
21
+ ActionMcp.runStdio([status, notes, identity], {
22
+ name: "notes-service",
23
+ version: "1.0.0",
24
+ }).pipe(
21
25
  // DEMO ONLY: a real host reads who started it, from its own login or configuration.
22
26
  Effect.provideService(CurrentUser, users.ada),
23
27
  Effect.provide([NodeStdio.layer, NoteStore.layerMemory]),
24
28
  // stdout carries the protocol alone, so logs and failures go to stderr.
25
29
  ActionCli.logToStderr,
26
- NodeRuntime.runMain,
30
+ NodeRuntime.runMain()
27
31
  );
@@ -1,5 +1,6 @@
1
1
  import { Layer } from "effect";
2
2
  import { HttpRouter, HttpServer } from "effect/http";
3
+
3
4
  import { NoteStore } from "./notes/store.js";
4
5
  import { requestScopedRoutes } from "./routes.js";
5
6
 
@@ -13,8 +14,10 @@ const app = HttpRouter.toWebHandler(
13
14
  // DEMO ONLY: each instance of the worker holds notes of its own, lost when it stops. A
14
15
  // deployment provides a store every instance reaches.
15
16
  Layer.provide(NoteStore.layerMemory),
16
- Layer.provide(HttpServer.layerServices),
17
- ),
17
+ Layer.provide(HttpServer.layerServices)
18
+ )
18
19
  );
19
20
 
20
- export default { fetch: (request: Request) => app.handler(request) };
21
+ export default {
22
+ fetch: async (request: Request) => await app.handler(request),
23
+ };