better-cf 0.2.2 → 2.0.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/README.md CHANGED
@@ -1,17 +1,15 @@
1
1
  # better-cf
2
2
 
3
- Opinionated Cloudflare Queue SDK + automation CLI designed for modern developer experience.
3
+ Opininated Functional, type-safe Cloudflare SDKs for Durable Objects and Queues.
4
4
 
5
- ## Why better-cf
6
-
7
- `better-cf` keeps Cloudflare Queue primitives but improves day-to-day ergonomics:
5
+ ## Canonical Imports
8
6
 
9
- - typed queue contracts from one place
10
- - less manual entry/config wiring
11
- - structured automation for local dev and codegen
12
- - predictable runtime and testing APIs
7
+ - Primary next-gen surface: `better-cf/durable-object`
8
+ - Legacy queue surface: `better-cf/queue`
9
+ - Testing helpers: `better-cf/testing`
10
+ - CLI binary: `better-cf`
13
11
 
14
- ## Quickstart (Existing Project)
12
+ ## Quickstart
15
13
 
16
14
  ```bash
17
15
  npm i better-cf zod
@@ -20,7 +18,7 @@ npx better-cf init
20
18
  npm run dev
21
19
  ```
22
20
 
23
- ## Quickstart (New Project)
21
+ Create a new worker:
24
22
 
25
23
  ```bash
26
24
  npx better-cf create my-worker
@@ -28,78 +26,132 @@ cd my-worker
28
26
  npm run dev
29
27
  ```
30
28
 
31
- Use a specific package manager during create:
29
+ ## Primary Surface: `better-cf/durable-object`
32
30
 
33
- ```bash
34
- npx better-cf create my-worker --use-pnpm
35
- npx better-cf create my-worker --use-bun
31
+ Use `schema.ts` as the resource registry, keep runtime behavior in sibling files, and call everything through generated `ctx.api`.
32
+
33
+ ```ts
34
+ // better-cf.config.ts
35
+ import { createSDK } from 'better-cf/durable-object';
36
+
37
+ export const sdk = createSDK();
38
+ export const defineWorker = sdk.defineWorker;
36
39
  ```
37
40
 
38
- ## Canonical Imports
41
+ ```ts
42
+ // src/schema.ts
43
+ import { z } from 'zod';
44
+ import { sdk } from '../better-cf.config';
39
45
 
40
- - Queue runtime: `better-cf/queue`
41
- - Testing helpers: `better-cf/testing`
42
- - CLI binary: `better-cf`
46
+ export const room = sdk.defineDurableObject({
47
+ name: 'Room',
48
+ key: z.string(),
49
+ version: 1,
50
+ description: 'Chat room state.'
51
+ });
43
52
 
44
- ## Core Workflow
53
+ export const emailQueue = sdk.defineQueue({
54
+ description: 'Email fanout queue.',
55
+ args: z.object({
56
+ roomId: z.string(),
57
+ to: z.string().email(),
58
+ body: z.string()
59
+ }),
60
+ retry: 3,
61
+ retryDelay: '30s'
62
+ });
63
+ ```
64
+
65
+ ```ts
66
+ // src/room.ts
67
+ import { z } from 'zod';
68
+ import { room } from './schema';
69
+
70
+ export const sendMessage = room.fn({
71
+ description: 'Append a room message.',
72
+ args: z.object({
73
+ body: z.string(),
74
+ author: z.string()
75
+ }),
76
+ returns: z.object({
77
+ ok: z.literal(true)
78
+ }),
79
+ handler: async ({ storage }, args) => {
80
+ const messages = ((await storage.get('messages')) as unknown[] | undefined) ?? [];
81
+ messages.push(args);
82
+ await storage.put('messages', messages);
83
+ return { ok: true };
84
+ }
85
+ });
86
+ ```
45
87
 
46
- `better-cf dev` continuously orchestrates:
88
+ ```ts
89
+ // src/email-queue.ts
90
+ import { emailQueue } from './schema';
91
+
92
+ export const emailQueueConsumer = emailQueue.message({
93
+ description: 'Deliver one email message.',
94
+ handler: async (ctx, job) => {
95
+ await ctx.api.room.sendMessage(job.roomId, {
96
+ body: job.body,
97
+ author: 'system'
98
+ });
99
+ }
100
+ });
101
+ ```
47
102
 
48
- 1. scan queue definitions
49
- 2. validate config
50
- 3. generate `.better-cf/entry.ts` and type files
51
- 4. patch wrangler queue sections
52
- 5. infer env types
53
- 6. run/restart `wrangler dev`
54
- 7. re-run on source/config changes
103
+ ```ts
104
+ // worker.ts
105
+ import { defineWorker } from './better-cf.config';
55
106
 
56
- One-shot mode:
107
+ export default defineWorker({
108
+ async fetch(_request, ctx) {
109
+ await ctx.api.room.sendMessage('general', {
110
+ body: 'hello',
111
+ author: 'abhi'
112
+ });
113
+
114
+ await ctx.api.emailQueue.send({
115
+ roomId: 'general',
116
+ to: 'team@example.com',
117
+ body: 'New message'
118
+ });
57
119
 
58
- ```bash
59
- better-cf generate
120
+ return new Response('ok');
121
+ }
122
+ });
60
123
  ```
61
124
 
62
- ## Example Patterns
125
+ What the generator adds:
126
+
127
+ - `.better-cf/entry.ts` with worker, queue, and Durable Object wiring
128
+ - `.better-cf/types.d.ts` with `ctx.api` typing and JSDoc
129
+ - Wrangler queue and `durable_objects` bindings
130
+ - SQLite Durable Object migrations
63
131
 
64
- ### Single queue with typed payload
132
+ ## Legacy Queue Surface
133
+
134
+ `better-cf/queue` remains available for the original inline-consumer model.
65
135
 
66
136
  ```ts
67
137
  import { createSDK } from 'better-cf/queue';
68
138
  import { z } from 'zod';
69
139
 
70
- type Env = { QUEUE_SIGNUP: Queue };
71
- const { defineQueue, defineWorker } = createSDK<Env>();
140
+ const { defineQueue } = createSDK();
72
141
 
73
142
  export const signupQueue = defineQueue({
74
- message: z.object({ email: z.string().email() }),
75
- process: async (ctx, msg) => {
143
+ args: z.object({ email: z.string().email() }),
144
+ handler: async (ctx, msg) => {
76
145
  console.log(ctx.message.id, msg.email);
77
- },
78
- retry: 3,
79
- retryDelay: '30s'
80
- });
81
-
82
- export default defineWorker({
83
- async fetch() {
84
- return new Response('ok');
85
146
  }
86
147
  });
87
148
  ```
88
149
 
89
- ### Batch mode
150
+ Use this surface for existing projects. New projects should prefer `better-cf/durable-object`.
90
151
 
91
- ```ts
92
- const auditQueue = defineQueue({
93
- message: z.object({ action: z.string() }),
94
- batch: { maxSize: 10, timeout: '30s', maxConcurrency: 2 },
95
- processBatch: async (ctx, messages) => {
96
- console.log(messages.length, ctx.batch.queue);
97
- ctx.batch.ackAll();
98
- }
99
- });
100
- ```
152
+ ## Testing
101
153
 
102
- ### Queue unit testing
154
+ Legacy inline queues:
103
155
 
104
156
  ```ts
105
157
  import { testQueue } from 'better-cf/testing';
@@ -112,35 +164,59 @@ const result = await testQueue(signupQueue, {
112
164
  expect(result.acked).toHaveLength(1);
113
165
  ```
114
166
 
115
- ## Comparison with Cloudflare Queue Workflows
167
+ Next-gen external consumers and durable functions:
116
168
 
117
- | Concern | Cloudflare path | better-cf path |
118
- |---|---|---|
119
- | Queue contract shape | Convention/custom runtime checks | `defineQueue({ message: z.object(...) })` |
120
- | Entry + config wiring | Manual exports + Wrangler maintenance | Generated entry + automated Wrangler patching |
121
- | Local dev orchestration | Team-managed scripts | One `better-cf dev` loop |
122
- | Queue test harness | Custom mocks/harnesses | `testQueue` helper |
169
+ ```ts
170
+ import { testDurableFunction, testQueueConsumer } from 'better-cf/testing';
171
+
172
+ await testDurableFunction(sendMessage, {
173
+ env: {},
174
+ args: { body: 'hello', author: 'abhi' }
175
+ });
176
+
177
+ await testQueueConsumer(emailQueue, emailQueueConsumer, {
178
+ env: {},
179
+ api: {
180
+ room: {
181
+ sendMessage: async () => ({ ok: true })
182
+ }
183
+ },
184
+ message: {
185
+ roomId: 'general',
186
+ to: 'team@example.com',
187
+ body: 'hello'
188
+ }
189
+ });
190
+ ```
191
+
192
+ ## Core Workflow
123
193
 
124
- Detailed comparison is being migrated to the separate docs repository.
194
+ `better-cf dev` continuously:
125
195
 
126
- ## Limitations
196
+ 1. scans declarations and external registrations
197
+ 2. validates config and ownership rules
198
+ 3. generates `.better-cf/entry.ts` and type files
199
+ 4. patches Wrangler queues, Durable Objects, and SQLite migrations
200
+ 5. infers env types
201
+ 6. runs or restarts `wrangler dev`
127
202
 
128
- ### Not supported
203
+ One-shot generation:
204
+
205
+ ```bash
206
+ better-cf generate
207
+ ```
129
208
 
130
- - Pull-message runtime abstraction implementation
131
- - Queue metrics/dashboard abstraction
132
- - Dynamic runtime queue declaration
133
- - Unsupported remote queue local-dev parity modes
209
+ ## Scope
134
210
 
135
- ### Known gaps
211
+ The next-gen surface intentionally stays thin over Cloudflare primitives:
136
212
 
137
- - Non-literal config extraction can reduce static mapping fidelity
138
- - Legacy service-worker adapter is compatibility-oriented
139
- - Non-standard worker export patterns beyond documented variants are out of scope
213
+ - typed Durable Object RPC methods, alarms, fetch, init, and WebSocket hibernation hooks
214
+ - typed queue producers and external consumers
215
+ - generated `ctx.api` clients and Wrangler wiring
216
+ - raw escape hatches for native Durable Object namespace methods when needed
140
217
 
141
- Use native Cloudflare APIs directly where the SDK intentionally does not abstract.
218
+ Use native Cloudflare APIs directly when the SDK intentionally does not abstract an edge case yet.
142
219
 
143
220
  ## Docs
144
221
 
145
- Docs are being migrated to a separate Next.js application repository.
146
- During migration, legacy docs source may still exist under `apps/docs`, but it is no longer part of package CI/CD.
222
+ The full docs live under `apps/docs`, including the new Durable Object section, coverage matrix, migration guide, and legacy queue docs.