effect-cf 0.6.0 → 0.7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#20](https://github.com/danieljvdm/effect-cf/pull/20) [`f57a59c`](https://github.com/danieljvdm/effect-cf/commit/f57a59c14d317438348b36ac0341dc921fe72be2) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Tighten binding APIs around Cloudflare RPC and the single-tag KV model. `rpc` now exposes the raw Cloudflare RPC result, while `call` and `scopedCall` resolve and decode definition-backed success values. Durable Object static direct helpers now keep the namespace layer requirement in their effect environment, and the old concrete `Kv.make` / `Kv.Service` constructors have been removed in favor of `Kv.Tag(...).layer({ binding })`.
8
+
9
+ - [#19](https://github.com/danieljvdm/effect-cf/pull/19) [`37b4883`](https://github.com/danieljvdm/effect-cf/commit/37b4883de9790df151ddbb16f2fd432b2d4348b5) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Replace separate binding classes with a single exported tag class API for Queues, Workflows, KV namespaces, Worker service bindings, and Durable Object namespaces. These tags now expose `layer({ binding })` directly, consumers use `const service = yield* Service`, and the old definition `.Binding(...)` / `.binding(...)` / `.Namespace(...)` / `.namespace(...)` helpers have been removed.
10
+
3
11
  ## 0.6.0
4
12
 
5
13
  ### Minor Changes
package/README.md CHANGED
@@ -32,20 +32,19 @@ Runtime creation belongs at Cloudflare entrypoints, not inside binding helpers.
32
32
  - `DurableObjectWebSocket` - WebSocket upgrade helpers for Durable Objects
33
33
  - `Kv` - typed KV namespace helper
34
34
  - `D1` - typed D1 database binding helper with an `@effect/sql-d1` backed SQL layer
35
- - `Queue` / `QueueBinding` - typed Queue producer bindings and consumer handlers
36
- - `Workflow` / `WorkflowBinding` - typed Workflow entrypoints, steps, and starter bindings
35
+ - `Queue` - typed Queue producer/consumer tags plus client and error types
36
+ - `Workflow` - typed Workflow entrypoints, steps, starter clients, and instance types
37
37
  - `Rpc` - Cloudflare RPC type helpers and scoped disposal utilities
38
38
  - `WorkerConfig` - Effect `Config` helpers backed by Cloudflare `env`
39
39
 
40
40
  ## Worker Example
41
41
 
42
42
  ```ts
43
- import { HttpServerResponse } from "effect";
43
+ import { Effect, Layer } from "effect";
44
+ import { HttpServerResponse } from "effect/unstable/http";
44
45
  import { Worker } from "effect-cf";
45
46
 
46
- export default Worker.make({
47
- fetch: () => HttpServerResponse.text("ok"),
48
- });
47
+ export default Worker.make(Layer.empty, Effect.succeed(HttpServerResponse.text("ok")));
49
48
  ```
50
49
 
51
50
  ## Durable Object Example
@@ -70,27 +69,30 @@ export const CounterDurableObject = Counter.make(Layer.empty, {
70
69
  }),
71
70
  },
72
71
  });
72
+
73
+ export const CounterLayer = Counter.layer({ binding: "COUNTER" });
74
+
75
+ export const readCounter = Effect.gen(function* () {
76
+ const counters = yield* Counter;
77
+ return yield* counters.byName("home").get();
78
+ });
73
79
  ```
74
80
 
75
- Define Wrangler bindings and migrations in the consuming application.
81
+ Define Wrangler bindings and migrations in the consuming application. Durable Object namespace bindings are provided with `YourObject.layer({ binding })`, and consumers use `const namespace = yield* YourObject`.
76
82
 
77
83
  ## Queue Example
78
84
 
79
- Queues use the same definition + binding split as Workers and Durable Objects: define the message contract once, use `.binding(...)` or `.Binding(...)` to produce from any runtime with `WorkerEnvironment`, and use `.make(...)` for the consumer Worker entrypoint.
85
+ Queues define the message contract once. The same class is the producer service tag and the consumer Worker definition: use `YourQueue.layer({ binding })` to provide a Cloudflare producer binding from `WorkerEnvironment`, and use `.make(...)` for the consumer Worker entrypoint.
80
86
 
81
87
  ```ts
82
88
  import { Effect, Layer, Schema as S } from "effect";
83
89
  import { Queue } from "effect-cf";
84
90
 
85
- class AvatarJobs extends Queue.Tag<AvatarJobs>()("AvatarJobs", {
91
+ class AvatarQueue extends Queue.Tag<AvatarQueue>()("AvatarQueue", {
86
92
  message: S.Struct({ userId: S.String, imageKey: S.String }),
87
93
  }) {}
88
94
 
89
- export const AvatarQueue = AvatarJobs.binding("AvatarQueue", {
90
- binding: "AVATAR_QUEUE",
91
- });
92
-
93
- export const AvatarQueueConsumer = AvatarJobs.make(Layer.empty, {
95
+ export const AvatarQueueConsumer = AvatarQueue.make(Layer.empty, {
94
96
  queue: (batch) =>
95
97
  Effect.gen(function* () {
96
98
  for (const message of batch.messages) {
@@ -100,6 +102,8 @@ export const AvatarQueueConsumer = AvatarJobs.make(Layer.empty, {
100
102
  }),
101
103
  });
102
104
 
105
+ export const AvatarQueueLayer = AvatarQueue.layer({ binding: "AVATAR_QUEUE" });
106
+
103
107
  export const enqueueAvatar = (userId: string, imageKey: string) =>
104
108
  Effect.gen(function* () {
105
109
  const queue = yield* AvatarQueue;
@@ -107,7 +111,7 @@ export const enqueueAvatar = (userId: string, imageKey: string) =>
107
111
  });
108
112
  ```
109
113
 
110
- Producers should usually use `const queue = yield* AvatarQueue` and then call `queue.send(...)`, `queue.sendBatch(...)`, or `queue.metrics()`. The static `AvatarQueue.send(...)` helpers remain available for compatibility and concise one-off calls.
114
+ Producers should usually use `const queue = yield* AvatarQueue` and then call `queue.send(...)`, `queue.sendBatch(...)`, or `queue.metrics()`. The static `AvatarQueue.send(...)` helpers remain available for concise one-off calls.
111
115
 
112
116
  Queue handlers run inline failures through Cloudflare's normal retry path. If background work scheduled with `WorkerContext.waitUntil(...)` should also make the batch retry, use `WorkerContext.waitUntilPropagating(...)` or `waitUntil(..., { mode: "propagate" })`; the default `waitUntil` mode observes and logs failures without rejecting the native `waitUntil` promise.
113
117
 
@@ -136,12 +140,10 @@ export const ExportWorkflowEntrypoint = ExportWorkflow.make(Layer.empty, {
136
140
  }),
137
141
  });
138
142
 
139
- export const ExportWorkflowBinding = ExportWorkflow.binding("ExportWorkflow", {
140
- binding: "EXPORT_WORKFLOW",
141
- });
143
+ export const ExportWorkflowLayer = ExportWorkflow.layer({ binding: "EXPORT_WORKFLOW" });
142
144
  ```
143
145
 
144
- Use `ExportWorkflowBinding.create(...)`, `createBatch(...)`, and `get(...)` to start and inspect instances.
146
+ Provide `ExportWorkflow.layer({ binding: "EXPORT_WORKFLOW" })`, then use `const workflow = yield* ExportWorkflow` or the static `ExportWorkflow.create(...)`, `createBatch(...)`, and `get(...)` helpers to start and inspect instances.
145
147
 
146
148
  In definition-backed workflows, the `payload` argument is the typed decoded payload and is the source of truth. `Workflow.WorkflowEvent.payload` is also re-provided decoded for convenience; `Workflow.WorkflowEvent.raw.payload` remains the native Cloudflare event payload.
147
149
 
@@ -193,24 +195,23 @@ export const RoomLive = DurableObject.make(layer, {
193
195
 
194
196
  Schema-backed attachments can rehydrate hibernated sockets:
195
197
 
196
- ```ts
197
- const restored =
198
- yield *
199
- Attachments.rehydrate({
200
- tag: "room:general",
201
- onInvalid: "ignore-and-close",
202
- });
198
+ ```text
199
+ const restored = yield* Attachments.rehydrate({
200
+ tag: "room:general",
201
+ onInvalid: "ignore-and-close",
202
+ });
203
203
 
204
204
  for (const { socket, attachment } of restored) {
205
- yield * socket.send(`restored:${attachment.id}`).pipe(Effect.ignore);
205
+ yield* socket.send(`restored:${attachment.id}`).pipe(Effect.ignore);
206
206
  }
207
207
  ```
208
208
 
209
209
  Worker-to-Durable-Object forwarding should stay native so WebSocket upgrade responses are preserved:
210
210
 
211
- ```ts
211
+ ```text
212
212
  if (Worker.isWebSocketUpgrade(request)) {
213
- return yield * ChatRooms.byName(roomId).fetch(request);
213
+ const rooms = yield* ChatRoom;
214
+ return yield* rooms.byName(roomId).fetch(request);
214
215
  }
215
216
  ```
216
217