@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,6 +1,7 @@
1
1
  import { describe, expect, it } from "@effect/vitest";
2
- import { Effect } from "effect";
3
2
  import * as Action from "@gjermundgaraba/effect-actions/Action";
3
+ import { Effect } from "effect";
4
+
4
5
  import { CurrentUser } from "../../src/identity/Identity.js";
5
6
  import { users } from "../../src/identity/users.js";
6
7
  import { notes } from "../../src/notes/handlers.js";
@@ -10,31 +11,41 @@ import { NoteStore } from "../../src/notes/store.js";
10
11
  // What the implementation does, with no transport: `Action.client` runs the same decoding,
11
12
  // `authorize` and handler a server runs. The caller is provided around each call, as a server
12
13
  // provides it per request.
13
- const as = (user: keyof typeof users) => Effect.provideService(CurrentUser, users[user]);
14
+ const as = (user: keyof typeof users) =>
15
+ Effect.provideService(CurrentUser, users[user]);
14
16
 
15
17
  describe("notes, in process", () => {
16
18
  it.effect("keeps each user's notes their own", () =>
17
19
  Effect.gen(function* () {
18
20
  const client = yield* Action.client(notes);
19
- const note = yield* client.createNote({ title: "Plan", body: "Draft" }).pipe(as("ada"));
20
21
 
21
- expect(yield* client.listNotes().pipe(as("ada"))).toEqual({
22
+ const note = yield* client
23
+ .createNote({ title: "Plan", body: "Draft" })
24
+ .pipe(as("ada"));
25
+
26
+ expect(yield* client.listNotes().pipe(as("ada"))).toStrictEqual({
22
27
  notes: [{ id: note.id, title: "Plan" }],
23
28
  });
24
- expect(yield* client.listNotes().pipe(as("grace"))).toEqual({ notes: [] });
29
+ expect(yield* client.listNotes().pipe(as("grace"))).toStrictEqual({
30
+ notes: [],
31
+ });
25
32
 
26
33
  const missing = new NoteNotFound({ id: note.id });
27
34
  const update = { id: note.id, body: "Mine now", revision: note.revision };
28
35
 
29
- expect(yield* Effect.flip(client.getNote({ id: note.id }).pipe(as("grace")))).toEqual(
30
- missing,
31
- );
32
- expect(yield* Effect.flip(client.updateNote(update).pipe(as("grace")))).toEqual(missing);
33
- expect(yield* Effect.flip(client.deleteNote({ id: note.id }).pipe(as("grace")))).toEqual(
34
- missing,
35
- );
36
- expect(yield* client.getNote({ id: note.id }).pipe(as("ada"))).toEqual(note);
37
- }).pipe(Effect.provide(NoteStore.layerMemory)),
36
+ expect(
37
+ yield* Effect.flip(client.getNote({ id: note.id }).pipe(as("grace")))
38
+ ).toStrictEqual(missing);
39
+ expect(
40
+ yield* Effect.flip(client.updateNote(update).pipe(as("grace")))
41
+ ).toStrictEqual(missing);
42
+ expect(
43
+ yield* Effect.flip(client.deleteNote({ id: note.id }).pipe(as("grace")))
44
+ ).toStrictEqual(missing);
45
+ expect(
46
+ yield* client.getNote({ id: note.id }).pipe(as("ada"))
47
+ ).toStrictEqual(note);
48
+ }).pipe(Effect.provide(NoteStore.layerMemory))
38
49
  );
39
50
 
40
51
  it.effect("refuses a write from a caller holding the read scope alone", () =>
@@ -45,6 +56,6 @@ describe("notes, in process", () => {
45
56
 
46
57
  expect(refusal).toBeInstanceOf(Action.Forbidden);
47
58
  expect(refusal.message).toBe("Requires notes:write.");
48
- }).pipe(Effect.provide(NoteStore.layerMemory)),
59
+ }).pipe(Effect.provide(NoteStore.layerMemory))
49
60
  );
50
61
  });
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from "@effect/vitest";
2
- import { Array, Effect, Exit, References } from "effect";
3
2
  import * as Action from "@gjermundgaraba/effect-actions/Action";
3
+ import { Array, Effect, Exit, References } from "effect";
4
+
4
5
  import { notes } from "../../src/notes/handlers.js";
5
6
  import { NoteChanged, NoteNotFound } from "../../src/notes/Notes.js";
6
7
  import { NoteStore } from "../../src/notes/store.js";
@@ -13,52 +14,69 @@ describe("notes, in process", () => {
13
14
  it.effect("deletes a note for its owner", () =>
14
15
  Effect.gen(function* () {
15
16
  const client = yield* Action.client(notes);
16
- const note = yield* client.createNote({ title: "Plan", body: "Draft" }).pipe(as("ada"));
17
+
18
+ const note = yield* client
19
+ .createNote({ title: "Plan", body: "Draft" })
20
+ .pipe(as("ada"));
17
21
 
18
22
  yield* client.deleteNote({ id: note.id }).pipe(as("ada"));
19
23
 
20
- expect(yield* Effect.flip(client.getNote({ id: note.id }).pipe(as("ada")))).toEqual(
21
- new NoteNotFound({ id: note.id }),
22
- );
23
- }).pipe(Effect.provide(NoteStore.layerMemory)),
24
+ expect(
25
+ yield* Effect.flip(client.getNote({ id: note.id }).pipe(as("ada")))
26
+ ).toStrictEqual(new NoteNotFound({ id: note.id }));
27
+ }).pipe(Effect.provide(NoteStore.layerMemory))
24
28
  );
25
29
 
26
30
  it.effect("refuses an update of a revision that is no longer current", () =>
27
31
  Effect.gen(function* () {
28
32
  const client = yield* Action.client(notes);
29
- const note = yield* client.createNote({ title: "Plan", body: "Draft" }).pipe(as("ada"));
33
+
34
+ const note = yield* client
35
+ .createNote({ title: "Plan", body: "Draft" })
36
+ .pipe(as("ada"));
37
+
30
38
  const update = { id: note.id, body: "Final", revision: note.revision };
31
39
 
32
- expect(yield* client.updateNote(update).pipe(as("ada"))).toMatchObject({ revision: 2 });
33
- expect(yield* Effect.flip(client.updateNote(update).pipe(as("ada")))).toEqual(
34
- new NoteChanged({ id: note.id, revision: 2 }),
35
- );
36
- }).pipe(Effect.provide(NoteStore.layerMemory)),
40
+ expect(yield* client.updateNote(update).pipe(as("ada"))).toMatchObject({
41
+ revision: 2,
42
+ });
43
+ expect(
44
+ yield* Effect.flip(client.updateNote(update).pipe(as("ada")))
45
+ ).toStrictEqual(new NoteChanged({ id: note.id, revision: 2 }));
46
+ }).pipe(Effect.provide(NoteStore.layerMemory))
37
47
  );
38
48
 
39
- it.effect("lets one of two concurrent updates of the same revision through", () =>
40
- Effect.gen(function* () {
41
- const client = yield* Action.client(notes);
49
+ it.effect(
50
+ "lets one of two concurrent updates of the same revision through",
51
+ () =>
52
+ Effect.gen(function* () {
53
+ const client = yield* Action.client(notes);
42
54
 
43
- // A fiber is set aside after every `steps` steps, and two that run at once then take
44
- // turns. Each count sets the first update aside at another point of its work; under
45
- // three, the two do not finish.
46
- const succeeded = yield* Effect.forEach(Array.range(3, 60), (steps) =>
47
- Effect.gen(function* () {
48
- const note = yield* client.createNote({ title: "Plan", body: "Draft" }).pipe(as("ada"));
55
+ // A fiber is set aside after every `steps` steps, and two that run at once then take
56
+ // turns. Each count sets the first update aside at another point of its work; under
57
+ // three, the two do not finish.
58
+ const succeeded = yield* Effect.forEach(Array.range(3, 60), (steps) =>
59
+ Effect.gen(function* () {
60
+ const note = yield* client
61
+ .createNote({ title: "Plan", body: "Draft" })
62
+ .pipe(as("ada"));
49
63
 
50
- const update = (body: string) =>
51
- Effect.exit(client.updateNote({ id: note.id, body, revision: 1 }).pipe(as("ada")));
64
+ const update = (body: string) =>
65
+ Effect.exit(
66
+ client
67
+ .updateNote({ id: note.id, body, revision: 1 })
68
+ .pipe(as("ada"))
69
+ );
52
70
 
53
- const exits = yield* Effect.all([update("One"), update("Two")], {
54
- concurrency: "unbounded",
55
- }).pipe(Effect.provideService(References.MaxOpsBeforeYield, steps));
71
+ const exits = yield* Effect.all([update("One"), update("Two")], {
72
+ concurrency: "unbounded",
73
+ }).pipe(Effect.provideService(References.MaxOpsBeforeYield, steps));
56
74
 
57
- return exits.filter(Exit.isSuccess).length;
58
- }),
59
- );
75
+ return exits.filter(Exit.isSuccess).length;
76
+ })
77
+ );
60
78
 
61
- expect(succeeded).toEqual(Array.range(3, 60).map(() => 1));
62
- }).pipe(Effect.provide(NoteStore.layerMemory)),
79
+ expect(succeeded).toStrictEqual(Array.range(3, 60).map(() => 1));
80
+ }).pipe(Effect.provide(NoteStore.layerMemory))
63
81
  );
64
82
  });
@@ -1,10 +1,11 @@
1
1
  import { describe, expect, it } from "@effect/vitest";
2
- import { Effect, Layer } from "effect";
3
- import { HttpClient, HttpClientError, HttpClientRequest } from "effect/http";
4
2
  import * as Action from "@gjermundgaraba/effect-actions/Action";
5
3
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
6
4
  import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
7
5
  import * as Testing from "@gjermundgaraba/effect-actions/Testing";
6
+ import { Effect, Layer } from "effect";
7
+ import { HttpClient, HttpClientError, HttpClientRequest } from "effect/http";
8
+
8
9
  import { Http } from "../src/Api.js";
9
10
  import { Notes } from "../src/notes/Notes.js";
10
11
  import { NoteStore } from "../src/notes/store.js";
@@ -22,13 +23,20 @@ const as = (token: string) => ({
22
23
  });
23
24
 
24
25
  describe("the service, over HTTP and MCP", () => {
25
- it.effect("answers the public action without a token and refuses a protected one", () =>
26
- Effect.gen(function* () {
27
- const anyone = yield* ActionHttp.client(Http);
28
-
29
- expect(yield* anyone.getStatus()).toEqual({ service: "notes-service", notes: 0 });
30
- expect(yield* Effect.flip(anyone.whoAmI())).toBeInstanceOf(Action.Unauthenticated);
31
- }).pipe(Effect.provide(served)),
26
+ it.effect(
27
+ "answers the public action without a token and refuses a protected one",
28
+ () =>
29
+ Effect.gen(function* () {
30
+ const anyone = yield* ActionHttp.client(Http);
31
+
32
+ expect(yield* anyone.getStatus()).toStrictEqual({
33
+ service: "notes-service",
34
+ notes: 0,
35
+ });
36
+ expect(yield* Effect.flip(anyone.whoAmI())).toBeInstanceOf(
37
+ Action.Unauthenticated
38
+ );
39
+ }).pipe(Effect.provide(served))
32
40
  );
33
41
 
34
42
  it.effect("refuses a token no user holds", () =>
@@ -39,7 +47,7 @@ describe("the service, over HTTP and MCP", () => {
39
47
 
40
48
  expect(refusal).toBeInstanceOf(Action.Unauthenticated);
41
49
  expect(refusal.message).toBe("Unknown demo token.");
42
- }).pipe(Effect.provide(served)),
50
+ }).pipe(Effect.provide(served))
43
51
  );
44
52
 
45
53
  it.effect("serves the host's own routes beside the actions'", () =>
@@ -49,78 +57,105 @@ describe("the service, over HTTP and MCP", () => {
49
57
 
50
58
  expect(yield* health.text).toBe("ok");
51
59
  expect(yield* openApi.text).toContain('"/api/createNote"');
52
- }).pipe(Effect.provide(served)),
60
+ }).pipe(Effect.provide(served))
53
61
  );
54
62
 
55
- it.effect("names the scope a caller lacks, so an OAuth client can ask for it", () =>
56
- Effect.gen(function* () {
57
- const reader = yield* ActionHttp.client(Http, as("reader"));
63
+ it.effect(
64
+ "names the scope a caller lacks, so an OAuth client can ask for it",
65
+ () =>
66
+ Effect.gen(function* () {
67
+ const reader = yield* ActionHttp.client(Http, as("reader"));
58
68
 
59
- expect(yield* reader.whoAmI()).toEqual({ id: "reader", scopes: ["notes:read"] });
69
+ expect(yield* reader.whoAmI()).toStrictEqual({
70
+ id: "reader",
71
+ scopes: ["notes:read"],
72
+ });
60
73
 
61
- const refusal = yield* Effect.flip(reader.createNote({ title: "Plan", body: "" }));
74
+ const refusal = yield* Effect.flip(
75
+ reader.createNote({ title: "Plan", body: "" })
76
+ );
62
77
 
63
- expect(refusal).toBeInstanceOf(Action.Forbidden);
64
- expect(refusal).toHaveProperty("scopes", ["notes:write"]);
65
- }).pipe(Effect.provide(served)),
78
+ expect(refusal).toBeInstanceOf(Action.Forbidden);
79
+ expect(refusal).toHaveProperty("scopes", ["notes:write"]);
80
+ }).pipe(Effect.provide(served))
66
81
  );
67
82
 
68
- it.effect("serves one store to both surfaces: a note written over HTTP is read as a tool", () =>
69
- Effect.gen(function* () {
70
- const http = yield* ActionHttp.client(Http, as("ada"));
71
- const mcp = yield* ActionMcp.client([GetStatus, ...Notes], as("ada"));
72
- const note = yield* http.createNote({ title: "Plan", body: "Draft" });
73
-
74
- expect(yield* mcp.getNote({ id: note.id })).toEqual(note);
75
- expect(yield* mcp.getStatus()).toEqual({ service: "notes-service", notes: 1 });
76
- }).pipe(Effect.provide(served)),
83
+ it.effect(
84
+ "serves one store to both surfaces: a note written over HTTP is read as a tool",
85
+ () =>
86
+ Effect.gen(function* () {
87
+ const http = yield* ActionHttp.client(Http, as("ada"));
88
+ const mcp = yield* ActionMcp.client([GetStatus, ...Notes], as("ada"));
89
+ const note = yield* http.createNote({ title: "Plan", body: "Draft" });
90
+
91
+ expect(yield* mcp.getNote({ id: note.id })).toStrictEqual(note);
92
+ expect(yield* mcp.getStatus()).toStrictEqual({
93
+ service: "notes-service",
94
+ notes: 1,
95
+ });
96
+ }).pipe(Effect.provide(served))
77
97
  );
78
98
 
79
- it.effect("lists protected tools to a signed-out MCP client, as one tool is public", () =>
80
- Effect.gen(function* () {
81
- const response = yield* HttpClient.execute(ActionMcp.request("tools/list"));
82
-
83
- expect(response.status).toBe(200);
84
- expect(yield* response.text).toContain('"name":"createNote"');
85
- }).pipe(Effect.provide(served)),
99
+ it.effect(
100
+ "lists protected tools to a signed-out MCP client, as one tool is public",
101
+ () =>
102
+ Effect.gen(function* () {
103
+ const response = yield* HttpClient.execute(
104
+ ActionMcp.request("tools/list")
105
+ );
106
+
107
+ expect(response.status).toBe(200);
108
+ expect(yield* response.text).toContain('"name":"createNote"');
109
+ }).pipe(Effect.provide(served))
86
110
  );
87
111
 
88
- it.effect("retries a committed HTTP write over MCP after its response is lost", () =>
89
- Effect.gen(function* () {
90
- const http = yield* ActionHttp.client(Http, {
91
- transformClient: (client) =>
92
- client.pipe(
93
- as("ada").transformClient,
94
- // The real server finishes the write, but the caller receives no response.
95
- HttpClient.transform((response, request) =>
96
- response.pipe(
97
- Effect.andThen(
98
- Effect.fail(
99
- new HttpClientError.HttpClientError({
100
- reason: new HttpClientError.TransportError({
101
- request,
102
- description: "Response lost after commit",
103
- }),
104
- }),
105
- ),
106
- ),
107
- ),
112
+ it.effect(
113
+ "retries a committed HTTP write over MCP after its response is lost",
114
+ () =>
115
+ Effect.gen(function* () {
116
+ const http = yield* ActionHttp.client(Http, {
117
+ transformClient: (client) =>
118
+ client.pipe(
119
+ as("ada").transformClient,
120
+ // The real server finishes the write, but the caller receives no response.
121
+ HttpClient.transform((response, request) =>
122
+ response.pipe(
123
+ Effect.andThen(
124
+ Effect.fail(
125
+ new HttpClientError.HttpClientError({
126
+ reason: new HttpClientError.TransportError({
127
+ request,
128
+ description: "Response lost after commit",
129
+ }),
130
+ })
131
+ )
132
+ )
133
+ )
134
+ )
108
135
  ),
109
- ),
110
- });
111
-
112
- const mcp = yield* ActionMcp.client([GetStatus, ...Notes], as("ada"));
113
- const input = { requestId: "plan", title: "Plan", body: "Draft" };
114
-
115
- expect(yield* Effect.flip(http.createNote(input))).toBeInstanceOf(
116
- HttpClientError.HttpClientError,
117
- );
118
- expect(yield* mcp.getStatus()).toEqual({ service: "notes-service", notes: 1 });
119
-
120
- const replay = yield* mcp.createNote(input);
121
-
122
- expect(replay).toMatchObject({ title: "Plan", body: "Draft", revision: 1 });
123
- expect(yield* mcp.listNotes()).toEqual({ notes: [{ id: replay.id, title: "Plan" }] });
124
- }).pipe(Effect.provide(served)),
136
+ });
137
+
138
+ const mcp = yield* ActionMcp.client([GetStatus, ...Notes], as("ada"));
139
+ const input = { requestId: "plan", title: "Plan", body: "Draft" };
140
+
141
+ expect(yield* Effect.flip(http.createNote(input))).toBeInstanceOf(
142
+ HttpClientError.HttpClientError
143
+ );
144
+ expect(yield* mcp.getStatus()).toStrictEqual({
145
+ service: "notes-service",
146
+ notes: 1,
147
+ });
148
+
149
+ const replay = yield* mcp.createNote(input);
150
+
151
+ expect(replay).toMatchObject({
152
+ title: "Plan",
153
+ body: "Draft",
154
+ revision: 1,
155
+ });
156
+ expect(yield* mcp.listNotes()).toStrictEqual({
157
+ notes: [{ id: replay.id, title: "Plan" }],
158
+ });
159
+ }).pipe(Effect.provide(served))
125
160
  );
126
161
  });
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, it } from "@effect/vitest";
2
- import { Effect } from "effect";
3
- import { HttpClient, HttpClientRequest } from "effect/http";
4
2
  import * as Action from "@gjermundgaraba/effect-actions/Action";
5
3
  import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
6
4
  import * as Testing from "@gjermundgaraba/effect-actions/Testing";
5
+ import { Effect } from "effect";
6
+ import { HttpClient, HttpClientRequest } from "effect/http";
7
+
7
8
  import { CreateNote, GetNote } from "../src/notes/Notes.js";
8
9
  import { GetStatus } from "../src/status/Status.js";
9
10
  import worker from "../src/worker.js";
@@ -16,19 +17,26 @@ const as = (token: string) => ({
16
17
  });
17
18
 
18
19
  describe("the worker", () => {
19
- it.effect("serves the tools, each request as the caller it authenticates", () =>
20
- Effect.gen(function* () {
21
- const anyone = yield* ActionMcp.client([GetStatus, GetNote]);
22
- const ada = yield* ActionMcp.client([CreateNote, GetNote], as("ada"));
20
+ it.effect(
21
+ "serves the tools, each request as the caller it authenticates",
22
+ () =>
23
+ Effect.gen(function* () {
24
+ const anyone = yield* ActionMcp.client([GetStatus, GetNote]);
25
+ const ada = yield* ActionMcp.client([CreateNote, GetNote], as("ada"));
23
26
 
24
- expect(yield* anyone.getStatus()).toMatchObject({ service: "notes-service" });
27
+ expect(yield* anyone.getStatus()).toMatchObject({
28
+ service: "notes-service",
29
+ });
25
30
 
26
- const note = yield* ada.createNote({ title: "Plan", body: "First draft" });
31
+ const note = yield* ada.createNote({
32
+ title: "Plan",
33
+ body: "First draft",
34
+ });
27
35
 
28
- expect(yield* ada.getNote({ id: note.id })).toEqual(note);
29
- expect(yield* Effect.flip(anyone.getNote({ id: note.id }))).toBeInstanceOf(
30
- Action.Unauthenticated,
31
- );
32
- }).pipe(Effect.provide(served)),
36
+ expect(yield* ada.getNote({ id: note.id })).toStrictEqual(note);
37
+ expect(
38
+ yield* Effect.flip(anyone.getNote({ id: note.id }))
39
+ ).toBeInstanceOf(Action.Unauthenticated);
40
+ }).pipe(Effect.provide(served))
33
41
  );
34
42
  });
@@ -1,14 +1,10 @@
1
1
  # quickstart
2
2
 
3
- The smallest complete program: one action, served as a JSON route and as an MCP tool, called
4
- with its typed client. It is the skeleton of [notes-service](../notes-service/), which adds
5
- authentication, authorization, declared errors, a store, a CLI and a browser client to the same
6
- layout.
3
+ The smallest complete program: one action, served as a JSON route and as an MCP tool, called with its typed client. It is the skeleton of [notes-service](../notes-service/), which adds authentication, authorization, declared errors, a store, a CLI and a browser client to the same layout.
7
4
 
8
5
  ## Run it
9
6
 
10
- From the repository root, after `vp install` (from an installed package, see
11
- [Run one](../README.md#run-one)):
7
+ From the repository root, after `vp install` (from an installed package, see [Run one](../README.md#run-one)):
12
8
 
13
9
  ```sh
14
10
  node --import tsx examples/quickstart/src/server.ts # 127.0.0.1:3000
@@ -1,4 +1,5 @@
1
1
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
2
+
2
3
  import { Greet } from "./greeting/Greeting.js";
3
4
 
4
5
  // The HTTP binding: `POST /api/greet`. The server serves it and the client calls it.
@@ -1,14 +1,17 @@
1
1
  import { NodeRuntime } from "@effect/platform-node";
2
+ import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
2
3
  import { Console, Effect } from "effect";
3
4
  import { FetchHttpClient } from "effect/http";
4
- import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
5
+
5
6
  import { Http } from "./Api.js";
6
7
 
7
8
  // Entrypoint: `node --import tsx examples/quickstart/src/client.ts`, with the server running.
8
9
  // One typed method per action of the binding.
9
10
  // DEMO ONLY: a deployed client reads its `baseUrl` from configuration (kitchen-sink/src/seed.ts).
10
11
  Effect.gen(function* () {
11
- const client = yield* ActionHttp.client(Http, { baseUrl: "http://127.0.0.1:3000" });
12
+ const client = yield* ActionHttp.client(Http, {
13
+ baseUrl: "http://127.0.0.1:3000",
14
+ });
12
15
 
13
16
  yield* Console.log(yield* client.greet({ name: "Ada" }));
14
- }).pipe(Effect.provide(FetchHttpClient.layer), NodeRuntime.runMain);
17
+ }).pipe(Effect.provide(FetchHttpClient.layer), 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
  // A contract: what the action takes and answers, whether it changes anything, and who may
5
5
  // call it. It imports no server code, so any client can import it, a browser page included.
@@ -1,6 +1,9 @@
1
- import { Effect } from "effect";
2
1
  import * as Action from "@gjermundgaraba/effect-actions/Action";
2
+ import { Effect } from "effect";
3
+
3
4
  import { Greet } from "./Greeting.js";
4
5
 
5
6
  // The implementation: the contract bound to its handler. Every surface takes this one value.
6
- export const greeting = Action.implement(Greet, ({ name }) => Effect.succeed(`Hello, ${name}!`));
7
+ export const greeting = Action.implement(Greet, ({ name }) =>
8
+ Effect.succeed(`Hello, ${name}!`)
9
+ );
@@ -1,11 +1,12 @@
1
- import { Layer } from "effect";
2
1
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
3
2
  import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
3
+ import { Layer } from "effect";
4
+
4
5
  import { Http } from "./Api.js";
5
6
  import { greeting } from "./greeting/handlers.js";
6
7
 
7
8
  // One implementation, two surfaces: a JSON route and an MCP tool at `/mcp`.
8
9
  export const routes = Layer.mergeAll(
9
10
  ActionHttp.layer(Http, greeting),
10
- ActionMcp.layerHttp(greeting, { name: "greetings", version: "1.0.0" }),
11
+ ActionMcp.layerHttp(greeting, { name: "greetings", version: "1.0.0" })
11
12
  );
@@ -1,14 +1,18 @@
1
1
  import { createServer } from "node:http";
2
+
2
3
  import { NodeHttpServer, NodeRuntime } from "@effect/platform-node";
3
4
  import { Layer } from "effect";
4
5
  import { HttpRouter } from "effect/http";
6
+
5
7
  import { routes } from "./routes.js";
6
8
 
7
9
  // Entrypoint: `node --import tsx examples/quickstart/src/server.ts`.
8
10
  // DEMO ONLY: a deployed server reads its host and port from config and limits request bodies,
9
11
  // as notes-service/src/server.ts does; `127.0.0.1` serves this machine alone.
10
12
  HttpRouter.serve(routes).pipe(
11
- Layer.provide(NodeHttpServer.layer(createServer, { host: "127.0.0.1", port: 3000 })),
13
+ Layer.provide(
14
+ NodeHttpServer.layer(createServer, { host: "127.0.0.1", port: 3000 })
15
+ ),
12
16
  Layer.launch,
13
- NodeRuntime.runMain,
17
+ NodeRuntime.runMain()
14
18
  );
@@ -1,8 +1,9 @@
1
1
  import { expect, it } from "@effect/vitest";
2
- import { Effect } from "effect";
3
2
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
4
3
  import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
5
4
  import * as Testing from "@gjermundgaraba/effect-actions/Testing";
5
+ import { Effect } from "effect";
6
+
6
7
  import { Http } from "../src/Api.js";
7
8
  import { Greet } from "../src/greeting/Greeting.js";
8
9
  import { routes } from "../src/routes.js";
@@ -15,5 +16,5 @@ it.effect("greets over HTTP and as an MCP tool", () =>
15
16
 
16
17
  expect(yield* http.greet({ name: "Ada" })).toBe("Hello, Ada!");
17
18
  expect(yield* mcp.greet({ name: "Ada" })).toBe("Hello, Ada!");
18
- }).pipe(Effect.provide(Testing.layer(routes))),
19
+ }).pipe(Effect.provide(Testing.layer(routes)))
19
20
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba/effect-actions",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Define Effect action contracts once and project them onto HTTP, MCP, Effect RPC, native Toolkits, and CLIs.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -42,7 +42,9 @@
42
42
  "./ActionRpc": "./dist/ActionRpc.mjs",
43
43
  "./ActionToolkit": "./dist/ActionToolkit.mjs",
44
44
  "./Authentication": "./dist/Authentication.mjs",
45
+ "./Event": "./dist/Event.mjs",
45
46
  "./Testing": "./dist/Testing.mjs",
47
+ "./Webhook": "./dist/Webhook.mjs",
46
48
  "./package.json": "./package.json"
47
49
  },
48
50
  "publishConfig": {
@@ -50,18 +52,22 @@
50
52
  },
51
53
  "devDependencies": {
52
54
  "@effect/platform-node": "4.0.6",
55
+ "@effect/tsgo": "0.57.0",
53
56
  "@effect/vitest": "4.0.6",
54
57
  "@modelcontextprotocol/client": "2.3.1",
55
58
  "@types/node": "26.6.5",
56
59
  "effect": "4.0.6",
60
+ "oxfmt": "0.74.0",
61
+ "oxlint": "1.89.0",
57
62
  "tsx": "4.23.15",
58
63
  "typescript": "7.0.2",
64
+ "ultracite": "8.0.2",
59
65
  "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
60
66
  "vite-plus": "1.4.0",
61
67
  "vitest": "5.0.5"
62
68
  },
63
69
  "peerDependencies": {
64
- "effect": "~4.0.4"
70
+ "effect": "~4.0.5"
65
71
  },
66
72
  "devEngines": {
67
73
  "packageManager": {
@@ -71,7 +77,7 @@
71
77
  }
72
78
  },
73
79
  "engines": {
74
- "node": ">=26"
80
+ "node": ">=26.12.0"
75
81
  },
76
82
  "scripts": {
77
83
  "build": "vp pack",
@@ -79,7 +85,7 @@
79
85
  "test": "vp test",
80
86
  "check": "vp check",
81
87
  "example": "vpx examples/notes-service/src/server.ts",
82
- "test:package": "vp run build && vpx scripts/test-package.ts",
88
+ "test:package": "vp run -r build && vpx scripts/test-package.ts",
83
89
  "docs:sync": "vpx scripts/sync-skill.ts",
84
90
  "ready": "vp check && vp test && vp run test:package"
85
91
  }