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 +153 -77
- package/dist/cli/index.js +2883 -1014
- package/dist/cli/index.js.map +1 -1
- package/dist/durable-object/index.d.ts +7 -0
- package/dist/durable-object/index.js +244 -0
- package/dist/durable-object/index.js.map +1 -0
- package/dist/durable-object/internal.d.ts +96 -0
- package/dist/durable-object/internal.js +427 -0
- package/dist/durable-object/internal.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +194 -129
- package/dist/index.js.map +1 -1
- package/dist/queue/index.d.ts +3 -3
- package/dist/queue/index.js +194 -129
- package/dist/queue/index.js.map +1 -1
- package/dist/queue/internal.d.ts +1 -1
- package/dist/testing/index.d.ts +21 -1
- package/dist/testing/index.js +342 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/types-C1mNTuC-.d.ts +239 -0
- package/dist/{types-D44i92Zf.d.ts → types-CcW1NyB-.d.ts} +96 -29
- package/package.json +24 -4
package/dist/index.js
CHANGED
|
@@ -62,7 +62,7 @@ async function consumeQueueDefinition(definition, batch, env, executionCtx) {
|
|
|
62
62
|
await consumeMulti(definition.jobs, definition.shared, batch, env, executionCtx);
|
|
63
63
|
}
|
|
64
64
|
async function consumeSingle(config, batch, env, executionCtx) {
|
|
65
|
-
if ("
|
|
65
|
+
if ("batchHandler" in config && config.batchHandler) {
|
|
66
66
|
let ackOrRetryHandled = false;
|
|
67
67
|
const batchCtx = {
|
|
68
68
|
env,
|
|
@@ -81,14 +81,20 @@ async function consumeSingle(config, batch, env, executionCtx) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
|
-
const typedMessages = batch.messages.map((msg) => ({
|
|
85
|
-
data: msg.body,
|
|
86
|
-
id: msg.id,
|
|
87
|
-
timestamp: msg.timestamp,
|
|
88
|
-
attempts: msg.attempts
|
|
89
|
-
}));
|
|
90
84
|
try {
|
|
91
|
-
|
|
85
|
+
const typedMessages = batch.messages.map((msg) => {
|
|
86
|
+
const parsed = config.args.safeParse(msg.body);
|
|
87
|
+
if (!parsed.success) {
|
|
88
|
+
throw new Error(`Queue batch validation failed for ${msg.id}: ${parsed.error.message}`);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
data: parsed.data,
|
|
92
|
+
id: msg.id,
|
|
93
|
+
timestamp: msg.timestamp,
|
|
94
|
+
attempts: msg.attempts
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
await config.batchHandler(batchCtx, typedMessages);
|
|
92
98
|
if (!ackOrRetryHandled) {
|
|
93
99
|
batch.ackAll();
|
|
94
100
|
}
|
|
@@ -113,11 +119,11 @@ async function consumeSingle(config, batch, env, executionCtx) {
|
|
|
113
119
|
}
|
|
114
120
|
};
|
|
115
121
|
try {
|
|
116
|
-
const parsed = config.
|
|
122
|
+
const parsed = config.args.safeParse(message.body);
|
|
117
123
|
if (!parsed.success) {
|
|
118
|
-
throw new Error(`Queue
|
|
124
|
+
throw new Error(`Queue args validation failed: ${parsed.error.message}`);
|
|
119
125
|
}
|
|
120
|
-
await config.
|
|
126
|
+
await config.handler(queueCtx, parsed.data);
|
|
121
127
|
message.ack();
|
|
122
128
|
} catch (error) {
|
|
123
129
|
await callFailureHandler(
|
|
@@ -152,11 +158,11 @@ async function consumeMulti(jobs, shared, batch, env, executionCtx) {
|
|
|
152
158
|
}
|
|
153
159
|
};
|
|
154
160
|
try {
|
|
155
|
-
const parsed = job.
|
|
161
|
+
const parsed = job.args.safeParse(envelope.data);
|
|
156
162
|
if (!parsed.success) {
|
|
157
163
|
throw new Error(`Queue job validation failed for ${jobName}: ${parsed.error.message}`);
|
|
158
164
|
}
|
|
159
|
-
await job.
|
|
165
|
+
await job.handler(queueCtx, parsed.data);
|
|
160
166
|
message.ack();
|
|
161
167
|
} catch (error) {
|
|
162
168
|
await callFailureHandler(
|
|
@@ -170,13 +176,13 @@ async function consumeMulti(jobs, shared, batch, env, executionCtx) {
|
|
|
170
176
|
}
|
|
171
177
|
}
|
|
172
178
|
}
|
|
173
|
-
async function callFailureHandler(handler, context,
|
|
179
|
+
async function callFailureHandler(handler, context, args, rawError) {
|
|
174
180
|
if (!handler) {
|
|
175
181
|
return;
|
|
176
182
|
}
|
|
177
183
|
const error = rawError instanceof Error ? rawError : new Error(String(rawError));
|
|
178
184
|
try {
|
|
179
|
-
await handler(context,
|
|
185
|
+
await handler(context, args, error);
|
|
180
186
|
} catch {
|
|
181
187
|
}
|
|
182
188
|
}
|
|
@@ -192,135 +198,150 @@ var RESERVED_MULTI_JOB_KEYS = /* @__PURE__ */ new Set([
|
|
|
192
198
|
"deliveryDelay",
|
|
193
199
|
"visibilityTimeout",
|
|
194
200
|
"batch",
|
|
195
|
-
"consumer"
|
|
201
|
+
"consumer",
|
|
202
|
+
"args",
|
|
203
|
+
"handler",
|
|
204
|
+
"batchHandler",
|
|
205
|
+
"onFailure",
|
|
206
|
+
"message",
|
|
207
|
+
"process",
|
|
208
|
+
"processBatch"
|
|
196
209
|
]);
|
|
197
210
|
|
|
198
211
|
// src/queue/define-queue.ts
|
|
212
|
+
var LEGACY_SCHEMA_KEY = "message";
|
|
213
|
+
var LEGACY_SINGLE_HANDLER_KEYS = ["process", "processBatch"];
|
|
199
214
|
function defineQueueFactory() {
|
|
200
|
-
function defineQueue(config) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
215
|
+
return function defineQueue(config) {
|
|
216
|
+
assertNoLegacySingleQueueKeys(config);
|
|
217
|
+
const definition = toSingleQueueDefinition(config);
|
|
218
|
+
return createQueueHandle(definition);
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function defineQueuesFactory() {
|
|
222
|
+
return function defineQueues(config) {
|
|
223
|
+
assertNoLegacyMultiQueueKeys(config);
|
|
224
|
+
const definition = toMultiQueueDefinition(config);
|
|
225
|
+
return createQueueHandle(definition);
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function createQueueHandle(definition) {
|
|
229
|
+
let bindingName = null;
|
|
230
|
+
const sendBase = async (ctx, body, options) => {
|
|
231
|
+
if (!bindingName) {
|
|
232
|
+
throw new Error("Queue binding not initialized. Run through better-cf dev/generate/deploy generated entry.");
|
|
233
|
+
}
|
|
234
|
+
const binding = ctx.env[bindingName];
|
|
235
|
+
if (!binding || typeof binding.send !== "function") {
|
|
236
|
+
throw new Error(`Queue binding ${bindingName} not found in env.`);
|
|
237
|
+
}
|
|
238
|
+
await binding.send(body, toCloudflareSendOptions(options));
|
|
239
|
+
};
|
|
240
|
+
const sendBatchBase = async (ctx, messages) => {
|
|
241
|
+
if (!bindingName) {
|
|
242
|
+
throw new Error("Queue binding not initialized. Run through better-cf dev/generate/deploy generated entry.");
|
|
243
|
+
}
|
|
244
|
+
const binding = ctx.env[bindingName];
|
|
245
|
+
if (!binding || typeof binding.sendBatch !== "function") {
|
|
246
|
+
throw new Error(`Queue binding ${bindingName} not found in env.`);
|
|
247
|
+
}
|
|
248
|
+
const payload = messages.map((item) => ({
|
|
249
|
+
body: item.body,
|
|
250
|
+
...toCloudflareSendOptions(item.options)
|
|
251
|
+
}));
|
|
252
|
+
await binding.sendBatch(payload);
|
|
253
|
+
};
|
|
254
|
+
const baseHandle = {
|
|
255
|
+
async send(ctx, data, options) {
|
|
256
|
+
if (definition.kind === "single") {
|
|
257
|
+
await sendBase(ctx, data, options);
|
|
258
|
+
return;
|
|
224
259
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
await binding.sendBatch(payload);
|
|
230
|
-
};
|
|
231
|
-
const baseHandle = {
|
|
232
|
-
async send(ctx, data, options) {
|
|
233
|
-
if (definition.kind === "single") {
|
|
234
|
-
await sendBase(ctx, data, options);
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
await sendBase(ctx, { _job: "__default", data }, options);
|
|
238
|
-
},
|
|
239
|
-
async sendBatch(ctx, messages, options) {
|
|
240
|
-
if (definition.kind === "single") {
|
|
241
|
-
await sendBatchBase(
|
|
242
|
-
ctx,
|
|
243
|
-
messages.map((message) => ({
|
|
244
|
-
body: message.data,
|
|
245
|
-
options: mergeSendOptions(message, options)
|
|
246
|
-
}))
|
|
247
|
-
);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
260
|
+
await sendBase(ctx, { _job: "__default", data }, options);
|
|
261
|
+
},
|
|
262
|
+
async sendBatch(ctx, messages, options) {
|
|
263
|
+
if (definition.kind === "single") {
|
|
250
264
|
await sendBatchBase(
|
|
251
265
|
ctx,
|
|
252
266
|
messages.map((message) => ({
|
|
253
|
-
body:
|
|
267
|
+
body: message.data,
|
|
254
268
|
options: mergeSendOptions(message, options)
|
|
255
269
|
}))
|
|
256
270
|
);
|
|
271
|
+
return;
|
|
257
272
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
async sendBatch(ctx, messages, options) {
|
|
266
|
-
await sendBatchBase(
|
|
267
|
-
ctx,
|
|
268
|
-
messages.map((message) => ({
|
|
269
|
-
body: { _job: jobName, data: message.data },
|
|
270
|
-
options: mergeSendOptions(message, options)
|
|
271
|
-
}))
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
}
|
|
273
|
+
await sendBatchBase(
|
|
274
|
+
ctx,
|
|
275
|
+
messages.map((message) => ({
|
|
276
|
+
body: { _job: "__default", data: message.data },
|
|
277
|
+
options: mergeSendOptions(message, options)
|
|
278
|
+
}))
|
|
279
|
+
);
|
|
276
280
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
bindingName = name;
|
|
284
|
-
},
|
|
285
|
-
getBinding() {
|
|
286
|
-
return bindingName;
|
|
287
|
-
},
|
|
288
|
-
getDefinition() {
|
|
289
|
-
return definition;
|
|
281
|
+
};
|
|
282
|
+
if (definition.kind === "multi") {
|
|
283
|
+
for (const jobName of Object.keys(definition.jobs)) {
|
|
284
|
+
baseHandle[jobName] = {
|
|
285
|
+
async send(ctx, data, options) {
|
|
286
|
+
await sendBase(ctx, { _job: jobName, data }, options);
|
|
290
287
|
},
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
async sendBatch(ctx, messages, options) {
|
|
289
|
+
await sendBatchBase(
|
|
290
|
+
ctx,
|
|
291
|
+
messages.map((message) => ({
|
|
292
|
+
body: { _job: jobName, data: message.data },
|
|
293
|
+
options: mergeSendOptions(message, options)
|
|
294
|
+
}))
|
|
295
|
+
);
|
|
293
296
|
}
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
return baseHandle;
|
|
297
|
-
}
|
|
298
|
-
return defineQueue;
|
|
299
|
-
}
|
|
300
|
-
function toQueueDefinition(config) {
|
|
301
|
-
if (isSingleQueue(config)) {
|
|
302
|
-
if (isPullQueue(config)) {
|
|
303
|
-
if ("process" in config || "processBatch" in config) {
|
|
304
|
-
throw new Error('Queue config with consumer.type="http_pull" cannot define process/processBatch.');
|
|
305
|
-
}
|
|
306
|
-
return {
|
|
307
|
-
kind: "single",
|
|
308
|
-
mode: "pull",
|
|
309
|
-
config
|
|
310
297
|
};
|
|
311
298
|
}
|
|
312
|
-
|
|
313
|
-
|
|
299
|
+
}
|
|
300
|
+
Object.defineProperty(baseHandle, kQueueInternals, {
|
|
301
|
+
enumerable: false,
|
|
302
|
+
configurable: false,
|
|
303
|
+
writable: false,
|
|
304
|
+
value: {
|
|
305
|
+
setBinding(name) {
|
|
306
|
+
bindingName = name;
|
|
307
|
+
},
|
|
308
|
+
getBinding() {
|
|
309
|
+
return bindingName;
|
|
310
|
+
},
|
|
311
|
+
getDefinition() {
|
|
312
|
+
return definition;
|
|
313
|
+
},
|
|
314
|
+
consume(batch, env, executionCtx) {
|
|
315
|
+
return consumeQueueDefinition(definition, batch, env, executionCtx);
|
|
316
|
+
}
|
|
314
317
|
}
|
|
315
|
-
|
|
316
|
-
|
|
318
|
+
});
|
|
319
|
+
return baseHandle;
|
|
320
|
+
}
|
|
321
|
+
function toSingleQueueDefinition(config) {
|
|
322
|
+
if (isPullQueue(config)) {
|
|
323
|
+
if ("handler" in config || "batchHandler" in config) {
|
|
324
|
+
throw new Error('Queue config with consumer.type="http_pull" cannot define handler/batchHandler.');
|
|
317
325
|
}
|
|
318
326
|
return {
|
|
319
327
|
kind: "single",
|
|
320
|
-
mode: "
|
|
328
|
+
mode: "pull",
|
|
321
329
|
config
|
|
322
330
|
};
|
|
323
331
|
}
|
|
332
|
+
if (hasHandler(config) && hasBatchHandler(config)) {
|
|
333
|
+
throw new Error("Queue config cannot define both handler and batchHandler.");
|
|
334
|
+
}
|
|
335
|
+
if (!hasHandler(config) && !hasBatchHandler(config)) {
|
|
336
|
+
throw new Error("Queue config must define one of handler or batchHandler in worker consumer mode.");
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
kind: "single",
|
|
340
|
+
mode: "push",
|
|
341
|
+
config
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function toMultiQueueDefinition(config) {
|
|
324
345
|
const shared = {
|
|
325
346
|
retry: config.retry,
|
|
326
347
|
retryDelay: config.retryDelay,
|
|
@@ -334,6 +355,14 @@ function toQueueDefinition(config) {
|
|
|
334
355
|
if (RESERVED_MULTI_JOB_KEYS.has(key)) {
|
|
335
356
|
continue;
|
|
336
357
|
}
|
|
358
|
+
if (hasLegacyKeys(value)) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`Multi-job queue "${key}" uses legacy keys. Rename message->args and process/processBatch->handler/batchHandler.`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
if (isPlainObject(value) && "batchHandler" in value) {
|
|
364
|
+
throw new Error(`Multi-job queue "${key}" cannot define batchHandler. Use handler per job.`);
|
|
365
|
+
}
|
|
337
366
|
if (isJobConfig(value)) {
|
|
338
367
|
jobs[key] = value;
|
|
339
368
|
}
|
|
@@ -347,23 +376,56 @@ function toQueueDefinition(config) {
|
|
|
347
376
|
shared
|
|
348
377
|
};
|
|
349
378
|
}
|
|
350
|
-
function isSingleQueue(config) {
|
|
351
|
-
return "message" in config;
|
|
352
|
-
}
|
|
353
379
|
function isPullQueue(config) {
|
|
354
380
|
return "consumer" in config && Boolean(config.consumer && config.consumer.type === "http_pull");
|
|
355
381
|
}
|
|
356
|
-
function
|
|
357
|
-
return "
|
|
382
|
+
function hasHandler(config) {
|
|
383
|
+
return "handler" in config && typeof config.handler === "function";
|
|
358
384
|
}
|
|
359
|
-
function
|
|
360
|
-
return "
|
|
385
|
+
function hasBatchHandler(config) {
|
|
386
|
+
return "batchHandler" in config && typeof config.batchHandler === "function";
|
|
361
387
|
}
|
|
362
388
|
function isJobConfig(value) {
|
|
363
389
|
if (!isPlainObject(value)) {
|
|
364
390
|
return false;
|
|
365
391
|
}
|
|
366
|
-
return "
|
|
392
|
+
return "args" in value && "handler" in value;
|
|
393
|
+
}
|
|
394
|
+
function assertNoLegacySingleQueueKeys(config) {
|
|
395
|
+
if (!isPlainObject(config)) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const legacyKeys = [LEGACY_SCHEMA_KEY, ...LEGACY_SINGLE_HANDLER_KEYS].filter((key) => key in config);
|
|
399
|
+
if (legacyKeys.length === 0) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Queue config uses legacy keys (${legacyKeys.join(", ")}). Rename message->args, process->handler, processBatch->batchHandler.`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
function assertNoLegacyMultiQueueKeys(config) {
|
|
407
|
+
const topLevelLegacyKeys = [LEGACY_SCHEMA_KEY, ...LEGACY_SINGLE_HANDLER_KEYS].filter((key) => key in config);
|
|
408
|
+
if (topLevelLegacyKeys.length > 0) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
`Multi-job queue config uses legacy keys (${topLevelLegacyKeys.join(", ")}). Use defineQueues({ job: { args, handler } }).`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
for (const [jobName, value] of Object.entries(config)) {
|
|
414
|
+
if (RESERVED_MULTI_JOB_KEYS.has(jobName) || !isPlainObject(value)) {
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
if (hasLegacyKeys(value)) {
|
|
418
|
+
throw new Error(
|
|
419
|
+
`Multi-job queue "${jobName}" uses legacy keys. Rename message->args and process/processBatch->handler/batchHandler.`
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function hasLegacyKeys(value) {
|
|
425
|
+
if (!isPlainObject(value)) {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
return LEGACY_SCHEMA_KEY in value || LEGACY_SINGLE_HANDLER_KEYS.some((key) => key in value);
|
|
367
429
|
}
|
|
368
430
|
|
|
369
431
|
// src/queue/define-worker.ts
|
|
@@ -390,8 +452,11 @@ function defineWorkerFactory() {
|
|
|
390
452
|
|
|
391
453
|
// src/queue/create-sdk.ts
|
|
392
454
|
function createSDK() {
|
|
455
|
+
const defineQueue = defineQueueFactory();
|
|
456
|
+
const defineQueues = defineQueuesFactory();
|
|
393
457
|
return {
|
|
394
|
-
defineQueue
|
|
458
|
+
defineQueue,
|
|
459
|
+
defineQueues,
|
|
395
460
|
defineWorker: defineWorkerFactory()
|
|
396
461
|
};
|
|
397
462
|
}
|