@lunora/queue 1.0.0-alpha.7 → 1.0.0-alpha.9
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/dist/index.d.mts +122 -123
- package/dist/index.d.ts +122 -123
- package/dist/index.mjs +2 -2
- package/dist/packem_shared/{createQueueRunContext-wScWyFao.mjs → createQueueRunContext-C8jboCk6.mjs} +8 -1
- package/dist/packem_shared/{dispatchQueueBatch-DbA5vKBs.mjs → dispatchQueueBatch-DSEWhEy8.mjs} +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared types for dispatching a Lunora function back into the worker from a
|
|
3
|
-
* server-initiated context (a workflow body, a queue handler, a scheduled job).
|
|
4
|
-
* Node-safe — no Cloudflare runtime imports — so the consumers stay unit-testable
|
|
5
|
-
* with plain-object doubles.
|
|
6
|
-
*/
|
|
2
|
+
* Shared types for dispatching a Lunora function back into the worker from a
|
|
3
|
+
* server-initiated context (a workflow body, a queue handler, a scheduled job).
|
|
4
|
+
* Node-safe — no Cloudflare runtime imports — so the consumers stay unit-testable
|
|
5
|
+
* with plain-object doubles.
|
|
6
|
+
*/
|
|
7
7
|
/** Opaque generated function reference (`api.foo.bar`), carrying its dispatch id. */
|
|
8
8
|
interface FunctionReference {
|
|
9
9
|
__lunoraRef: string;
|
|
@@ -24,7 +24,6 @@ interface DispatchLogger {
|
|
|
24
24
|
info: (message: unknown, ...rest: unknown[]) => void;
|
|
25
25
|
warn: (message: unknown, ...rest: unknown[]) => void;
|
|
26
26
|
}
|
|
27
|
-
/** Build a {@link DispatchLogger} that prefixes every line with `prefix` (e.g. `[queue:email]`). */
|
|
28
27
|
/** How a queue message body is serialized on the wire (Cloudflare default `"json"`). */
|
|
29
28
|
type QueueContentType = "bytes" | "json" | "text" | "v8";
|
|
30
29
|
/** Options for a single `producer.send(body, options?)`. */
|
|
@@ -46,10 +45,10 @@ interface MessageSendRequestLike<Body = unknown> {
|
|
|
46
45
|
delaySeconds?: number;
|
|
47
46
|
}
|
|
48
47
|
/**
|
|
49
|
-
* Minimal structural projection of workers-types' `Queue<Body>` (the producer
|
|
50
|
-
* binding). The real binding's `send`/`sendBatch` resolve to a metadata object;
|
|
51
|
-
* we widen the return to `Promise<unknown>` so a plain-object fake satisfies it.
|
|
52
|
-
*/
|
|
48
|
+
* Minimal structural projection of workers-types' `Queue<Body>` (the producer
|
|
49
|
+
* binding). The real binding's `send`/`sendBatch` resolve to a metadata object;
|
|
50
|
+
* we widen the return to `Promise<unknown>` so a plain-object fake satisfies it.
|
|
51
|
+
*/
|
|
53
52
|
interface QueueBindingLike<Body = unknown> {
|
|
54
53
|
send: (message: Body, options?: QueueSendOptions) => Promise<unknown>;
|
|
55
54
|
sendBatch: (messages: Iterable<MessageSendRequestLike<Body>>, options?: QueueSendBatchOptions) => Promise<unknown>;
|
|
@@ -80,10 +79,10 @@ interface MessageBatchLike<Body = unknown> {
|
|
|
80
79
|
retryAll: (options?: QueueRetryOptions) => void;
|
|
81
80
|
}
|
|
82
81
|
/**
|
|
83
|
-
* The typed producer bound to `ctx.queues.<name>`. Sending is a side effect, so
|
|
84
|
-
* the generated context exposes this only on `MutationCtx` / `ActionCtx` (never
|
|
85
|
-
* the deterministic `QueryCtx`), mirroring `ctx.scheduler` / `ctx.workflows`.
|
|
86
|
-
*/
|
|
82
|
+
* The typed producer bound to `ctx.queues.<name>`. Sending is a side effect, so
|
|
83
|
+
* the generated context exposes this only on `MutationCtx` / `ActionCtx` (never
|
|
84
|
+
* the deterministic `QueryCtx`), mirroring `ctx.scheduler` / `ctx.workflows`.
|
|
85
|
+
*/
|
|
87
86
|
interface QueueProducer<Body = unknown> {
|
|
88
87
|
/** Enqueue one message. */
|
|
89
88
|
send: (body: Body, options?: QueueSendOptions) => Promise<void>;
|
|
@@ -91,10 +90,10 @@ interface QueueProducer<Body = unknown> {
|
|
|
91
90
|
sendBatch: (messages: Iterable<MessageSendRequestLike<Body>>, options?: QueueSendBatchOptions) => Promise<void>;
|
|
92
91
|
}
|
|
93
92
|
/**
|
|
94
|
-
* `ctx.queues` — the map of declared queue export names → typed producers.
|
|
95
|
-
* Codegen narrows this to the exact export names; the package keeps it open so
|
|
96
|
-
* `createQueues` stays schema-agnostic.
|
|
97
|
-
*/
|
|
93
|
+
* `ctx.queues` — the map of declared queue export names → typed producers.
|
|
94
|
+
* Codegen narrows this to the exact export names; the package keeps it open so
|
|
95
|
+
* `createQueues` stays schema-agnostic.
|
|
96
|
+
*/
|
|
98
97
|
interface Queues {
|
|
99
98
|
[exportName: string]: QueueProducer;
|
|
100
99
|
}
|
|
@@ -104,11 +103,11 @@ interface LunoraQueuesOptions {
|
|
|
104
103
|
bindings: Record<string, QueueBindingLike>;
|
|
105
104
|
}
|
|
106
105
|
/**
|
|
107
|
-
* The context handed to a `defineQueue` handler. Decoupled from `@lunora/server`
|
|
108
|
-
* (like the workflow run context): to touch data, call a Lunora mutation/action
|
|
109
|
-
* via `ctx.run(api.x.y, args)` — the dispatch goes through the same
|
|
110
|
-
* `/_lunora/scheduler/dispatch` path the SchedulerDO and workflows use.
|
|
111
|
-
*/
|
|
106
|
+
* The context handed to a `defineQueue` handler. Decoupled from `@lunora/server`
|
|
107
|
+
* (like the workflow run context): to touch data, call a Lunora mutation/action
|
|
108
|
+
* via `ctx.run(api.x.y, args)` — the dispatch goes through the same
|
|
109
|
+
* `/_lunora/scheduler/dispatch` path the SchedulerDO and workflows use.
|
|
110
|
+
*/
|
|
112
111
|
interface QueueRunContext {
|
|
113
112
|
/** The worker `env` (bindings + vars). */
|
|
114
113
|
readonly env: Record<string, unknown>;
|
|
@@ -137,25 +136,25 @@ interface QueueConsumerTuning {
|
|
|
137
136
|
/** The config object passed to `defineQueue`. */
|
|
138
137
|
interface QueueConfig<Body = unknown> extends QueueConsumerTuning {
|
|
139
138
|
/**
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
* The push-consumer body. Required for `mode: "push"` (the default); omit it
|
|
140
|
+
* for `mode: "pull"`, where an external worker polls the queue over HTTP.
|
|
141
|
+
*/
|
|
143
142
|
handler?: QueueHandler<Body>;
|
|
144
143
|
/** How this queue is consumed. Defaults to `"push"`. */
|
|
145
144
|
mode?: QueueConsumerMode;
|
|
146
145
|
/**
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
* Stable wrangler queue name (`queues.producers[].queue`). Defaults to the
|
|
147
|
+
* kebab-cased export name (`emailQueue` → `email-queue`).
|
|
148
|
+
*/
|
|
150
149
|
name?: string;
|
|
151
150
|
}
|
|
152
151
|
/** The branded result of `defineQueue`, discovered by codegen + config. */
|
|
153
152
|
interface QueueDefinition<Body = unknown> extends QueueConfig<Body> {
|
|
154
153
|
/**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
* Phantom carrier for the message body type, so codegen can type the
|
|
155
|
+
* generated `ctx.queues.<name>` producer as `QueueProducer<Body>` from
|
|
156
|
+
* `typeof <export>`. Never assigned at runtime (type-only).
|
|
157
|
+
*/
|
|
159
158
|
readonly __lunoraBody?: Body;
|
|
160
159
|
/** Runtime brand identifying a `defineQueue` result. */
|
|
161
160
|
isLunoraQueue: true;
|
|
@@ -172,13 +171,13 @@ interface QueueBindingSpec {
|
|
|
172
171
|
/** One declared queue, keyed for batch routing by its stable wrangler name. */
|
|
173
172
|
interface QueueRegistryEntry {
|
|
174
173
|
/**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
* The `defineQueue` result (carries the push handler). The body type is
|
|
175
|
+
* erased to `any` here because the registry is heterogeneous — different
|
|
176
|
+
* queues carry different message bodies, and the handler param is
|
|
177
|
+
* contravariant, so a precise `QueueDefinition<Body>` would not be assignable
|
|
178
|
+
* to a shared `unknown`-bodied slot. Runtime dispatch passes the delivered
|
|
179
|
+
* batch straight through, so the erasure is type-only.
|
|
180
|
+
*/
|
|
182
181
|
definition: QueueDefinition<any>;
|
|
183
182
|
/** The `lunora/queues.ts` export name, for log correlation. */
|
|
184
183
|
exportName: string;
|
|
@@ -188,11 +187,11 @@ type QueueRegistry = Record<string, QueueRegistryEntry>;
|
|
|
188
187
|
/** The disposition a consumer left one message in for a single delivery attempt. */
|
|
189
188
|
type QueueMessageOutcome = "ack" | "error" | "retry";
|
|
190
189
|
/**
|
|
191
|
-
* One consumed message as captured by {@link dispatchQueueBatch} and handed to an
|
|
192
|
-
* {@link QueueCaptureSink}. Structurally matches `@lunora/do`'s
|
|
193
|
-
* `RecordQueueMessageInput` (the reserved `recordQueueMessage` admin RPC payload);
|
|
194
|
-
* the two packages share only this contract, so keep them in sync by hand.
|
|
195
|
-
*/
|
|
190
|
+
* One consumed message as captured by {@link dispatchQueueBatch} and handed to an
|
|
191
|
+
* {@link QueueCaptureSink}. Structurally matches `@lunora/do`'s
|
|
192
|
+
* `RecordQueueMessageInput` (the reserved `recordQueueMessage` admin RPC payload);
|
|
193
|
+
* the two packages share only this contract, so keep them in sync by hand.
|
|
194
|
+
*/
|
|
196
195
|
interface CapturedQueueMessage {
|
|
197
196
|
/** Delivery attempt number for this message (`message.attempts`). */
|
|
198
197
|
attempts: number;
|
|
@@ -214,19 +213,19 @@ interface CapturedQueueMessage {
|
|
|
214
213
|
timestamp: number;
|
|
215
214
|
}
|
|
216
215
|
/**
|
|
217
|
-
* Persists a batch of consumed messages. The codegen worker wires this to POST the
|
|
218
|
-
* batch to the root shard's `recordQueueMessage` admin RPC (the dev queue catcher).
|
|
219
|
-
* Best-effort by contract: {@link dispatchQueueBatch} swallows a rejection so a
|
|
220
|
-
* capture failure never changes delivery semantics.
|
|
221
|
-
*/
|
|
216
|
+
* Persists a batch of consumed messages. The codegen worker wires this to POST the
|
|
217
|
+
* batch to the root shard's `recordQueueMessage` admin RPC (the dev queue catcher).
|
|
218
|
+
* Best-effort by contract: {@link dispatchQueueBatch} swallows a rejection so a
|
|
219
|
+
* capture failure never changes delivery semantics.
|
|
220
|
+
*/
|
|
222
221
|
type QueueCaptureSink = (messages: CapturedQueueMessage[]) => Promise<void> | void;
|
|
223
222
|
interface DispatchOptions {
|
|
224
223
|
/**
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
* Optional capture sink. When set, the batch is instrumented and every
|
|
225
|
+
* message's final disposition is recorded and handed to this sink after the
|
|
226
|
+
* handler runs. Omitted in production unless queue capture is enabled, so a
|
|
227
|
+
* consumer pays no instrumentation cost by default.
|
|
228
|
+
*/
|
|
230
229
|
capture?: QueueCaptureSink;
|
|
231
230
|
/** Worker `env`, forwarded to the queue run context. */
|
|
232
231
|
env: Record<string, unknown>;
|
|
@@ -234,11 +233,11 @@ interface DispatchOptions {
|
|
|
234
233
|
fetchImpl?: typeof fetch;
|
|
235
234
|
}
|
|
236
235
|
/**
|
|
237
|
-
* Look up the handler for `batch.queue` and invoke it with a fresh
|
|
238
|
-
* `QueueRunContext`. Throws a directed error when no push handler is registered
|
|
239
|
-
* for the delivered queue (a misconfiguration — the consumer was declared
|
|
240
|
-
* `pull`, or the queue name drifted from the `defineQueue` export).
|
|
241
|
-
*/
|
|
236
|
+
* Look up the handler for `batch.queue` and invoke it with a fresh
|
|
237
|
+
* `QueueRunContext`. Throws a directed error when no push handler is registered
|
|
238
|
+
* for the delivered queue (a misconfiguration — the consumer was declared
|
|
239
|
+
* `pull`, or the queue name drifted from the `defineQueue` export).
|
|
240
|
+
*/
|
|
242
241
|
declare const dispatchQueueBatch: (batch: MessageBatchLike, registry: QueueRegistry, options: DispatchOptions) => Promise<void>;
|
|
243
242
|
/** A Worker `env` projected as a plain record (vars, secrets, and bindings are `unknown`-valued). */
|
|
244
243
|
type QueueEnv = Record<string, unknown>;
|
|
@@ -250,81 +249,81 @@ interface QueueCaptureOptions {
|
|
|
250
249
|
rootShard?: string;
|
|
251
250
|
}
|
|
252
251
|
/**
|
|
253
|
-
* Whether consumed queue messages should be captured into the studio's log.
|
|
254
|
-
* Explicit `LUNORA_QUEUE_CAPTURE` (`"1"`/`"true"` vs `"0"`/`"false"`) always wins;
|
|
255
|
-
* unset, capture is on only in a development environment. Mirrors
|
|
256
|
-
* `@lunora/mail`'s `shouldCaptureMail` so mail and queue dev capture toggle the
|
|
257
|
-
* same way.
|
|
258
|
-
*/
|
|
252
|
+
* Whether consumed queue messages should be captured into the studio's log.
|
|
253
|
+
* Explicit `LUNORA_QUEUE_CAPTURE` (`"1"`/`"true"` vs `"0"`/`"false"`) always wins;
|
|
254
|
+
* unset, capture is on only in a development environment. Mirrors
|
|
255
|
+
* `@lunora/mail`'s `shouldCaptureMail` so mail and queue dev capture toggle the
|
|
256
|
+
* same way.
|
|
257
|
+
*/
|
|
259
258
|
declare const shouldCaptureQueue: (env: QueueEnv) => boolean;
|
|
260
259
|
/**
|
|
261
|
-
* Build the {@link QueueCaptureSink} that records a processed batch into the
|
|
262
|
-
* studio's root-shard consumed-message log via the reserved `recordQueueMessage`
|
|
263
|
-
* admin RPC. Best-effort by contract: without the `SHARD` binding or
|
|
264
|
-
* `LUNORA_ADMIN_TOKEN` it no-ops, and `dispatchQueueBatch` swallows a
|
|
265
|
-
* rejection, so capture never changes delivery semantics.
|
|
266
|
-
*/
|
|
260
|
+
* Build the {@link QueueCaptureSink} that records a processed batch into the
|
|
261
|
+
* studio's root-shard consumed-message log via the reserved `recordQueueMessage`
|
|
262
|
+
* admin RPC. Best-effort by contract: without the `SHARD` binding or
|
|
263
|
+
* `LUNORA_ADMIN_TOKEN` it no-ops, and `dispatchQueueBatch` swallows a
|
|
264
|
+
* rejection, so capture never changes delivery semantics.
|
|
265
|
+
*/
|
|
267
266
|
declare const createQueueCaptureSink: (env: QueueEnv, options?: QueueCaptureOptions) => QueueCaptureSink;
|
|
268
267
|
/**
|
|
269
|
-
* Build the `ctx.queues` map for a request: resolve every spec's `env[binding]`
|
|
270
|
-
* into the `exportName → Queue binding` map and wrap it in {@link createQueues}.
|
|
271
|
-
* A spec whose binding is absent from `env` is skipped here — the helpful "no
|
|
272
|
-
* queue named …" error is raised lazily by `ctx.queues.<name>.send(...)` when
|
|
273
|
-
* the missing queue is actually used.
|
|
274
|
-
*/
|
|
268
|
+
* Build the `ctx.queues` map for a request: resolve every spec's `env[binding]`
|
|
269
|
+
* into the `exportName → Queue binding` map and wrap it in {@link createQueues}.
|
|
270
|
+
* A spec whose binding is absent from `env` is skipped here — the helpful "no
|
|
271
|
+
* queue named …" error is raised lazily by `ctx.queues.<name>.send(...)` when
|
|
272
|
+
* the missing queue is actually used.
|
|
273
|
+
*/
|
|
275
274
|
declare const createQueueContext: (env: Record<string, unknown>, specs: ReadonlyArray<QueueBindingSpec>) => Queues;
|
|
276
275
|
/**
|
|
277
|
-
* Build the `ctx.queues` map from `lunora/queues.ts` export name → Cloudflare
|
|
278
|
-
* `Queue` binding. Each property is a typed {@link QueueProducer}; accessing an
|
|
279
|
-
* export whose binding is absent throws a directed error naming the declared
|
|
280
|
-
* queues (raised lazily on first use).
|
|
281
|
-
*/
|
|
276
|
+
* Build the `ctx.queues` map from `lunora/queues.ts` export name → Cloudflare
|
|
277
|
+
* `Queue` binding. Each property is a typed {@link QueueProducer}; accessing an
|
|
278
|
+
* export whose binding is absent throws a directed error naming the declared
|
|
279
|
+
* queues (raised lazily on first use).
|
|
280
|
+
*/
|
|
282
281
|
declare const createQueues: (options: LunoraQueuesOptions) => Queues;
|
|
283
282
|
/**
|
|
284
|
-
* The wrangler producer binding name for a queue export: `emailQueue` →
|
|
285
|
-
* `QUEUE_EMAIL_QUEUE`, `email` → `QUEUE_EMAIL`. The `QUEUE_` prefix namespaces
|
|
286
|
-
* these away from `SHARD`/`SESSION`/`SCHEDULER`/`WORKFLOW_*`/`CONTAINER_*` so a
|
|
287
|
-
* queue export can never collide with the built-in bindings.
|
|
288
|
-
*/
|
|
283
|
+
* The wrangler producer binding name for a queue export: `emailQueue` →
|
|
284
|
+
* `QUEUE_EMAIL_QUEUE`, `email` → `QUEUE_EMAIL`. The `QUEUE_` prefix namespaces
|
|
285
|
+
* these away from `SHARD`/`SESSION`/`SCHEDULER`/`WORKFLOW_*`/`CONTAINER_*` so a
|
|
286
|
+
* queue export can never collide with the built-in bindings.
|
|
287
|
+
*/
|
|
289
288
|
declare const queueBindingName: (exportName: string) => string;
|
|
290
289
|
/**
|
|
291
|
-
* The stable queue name wrangler registers (`queues.producers[].queue` and
|
|
292
|
-
* `queues.consumers[].queue`): `emailQueue` → `email-queue`. Used as the
|
|
293
|
-
* deployed queue's identifier when no explicit `name` override is given.
|
|
294
|
-
*/
|
|
290
|
+
* The stable queue name wrangler registers (`queues.producers[].queue` and
|
|
291
|
+
* `queues.consumers[].queue`): `emailQueue` → `email-queue`. Used as the
|
|
292
|
+
* deployed queue's identifier when no explicit `name` override is given.
|
|
293
|
+
*/
|
|
295
294
|
declare const queueDefaultName: (exportName: string) => string;
|
|
296
295
|
/**
|
|
297
|
-
* Declare a Cloudflare Queue deployed alongside the app. Pure validation +
|
|
298
|
-
* branding: codegen discovers the export, emits the typed `ctx.queues.<name>`
|
|
299
|
-
* producer and (for push consumers) the worker `queue()` dispatch; the config
|
|
300
|
-
* layer reconciles the wrangler `queues.producers[]` / `queues.consumers[]`
|
|
301
|
-
* entries from the same definition.
|
|
302
|
-
*
|
|
303
|
-
* ```ts
|
|
304
|
-
* // lunora/queues.ts
|
|
305
|
-
* import { defineQueue } from "@lunora/queue";
|
|
306
|
-
* import { api } from "./_generated/api";
|
|
307
|
-
*
|
|
308
|
-
* export const emailQueue = defineQueue<{ to: string }>({
|
|
309
|
-
* handler: async (ctx, batch) => {
|
|
310
|
-
* for (const message of batch.messages) {
|
|
311
|
-
* await ctx.run(api.email.send, { to: message.body.to });
|
|
312
|
-
* message.ack();
|
|
313
|
-
* }
|
|
314
|
-
* },
|
|
315
|
-
* });
|
|
316
|
-
* ```
|
|
317
|
-
*
|
|
318
|
-
* Enqueue from a mutation or action: `await ctx.queues.emailQueue.send({ to })`.
|
|
319
|
-
*
|
|
320
|
-
* ⚠️ **Privileged dispatch.** A push handler's `ctx.run(...)` calls back into
|
|
321
|
-
* Lunora functions over the admin-authenticated dispatch endpoint (the same
|
|
322
|
-
* trusted path the scheduler and workflows use), so those calls run with the
|
|
323
|
-
* system identity — **end-user RLS is not applied**. Treat a queue handler as
|
|
324
|
-
* trusted server code: validate `message.body` (it may be attacker-influenced if
|
|
325
|
-
* anything user-facing can enqueue) before acting on it, and don't forward an
|
|
326
|
-
* unchecked body straight into a privileged mutation.
|
|
327
|
-
*/
|
|
296
|
+
* Declare a Cloudflare Queue deployed alongside the app. Pure validation +
|
|
297
|
+
* branding: codegen discovers the export, emits the typed `ctx.queues.<name>`
|
|
298
|
+
* producer and (for push consumers) the worker `queue()` dispatch; the config
|
|
299
|
+
* layer reconciles the wrangler `queues.producers[]` / `queues.consumers[]`
|
|
300
|
+
* entries from the same definition.
|
|
301
|
+
*
|
|
302
|
+
* ```ts
|
|
303
|
+
* // lunora/queues.ts
|
|
304
|
+
* import { defineQueue } from "@lunora/queue";
|
|
305
|
+
* import { api } from "./_generated/api";
|
|
306
|
+
*
|
|
307
|
+
* export const emailQueue = defineQueue<{ to: string }>({
|
|
308
|
+
* handler: async (ctx, batch) => {
|
|
309
|
+
* for (const message of batch.messages) {
|
|
310
|
+
* await ctx.run(api.email.send, { to: message.body.to });
|
|
311
|
+
* message.ack();
|
|
312
|
+
* }
|
|
313
|
+
* },
|
|
314
|
+
* });
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* Enqueue from a mutation or action: `await ctx.queues.emailQueue.send({ to })`.
|
|
318
|
+
*
|
|
319
|
+
* ⚠️ **Privileged dispatch.** A push handler's `ctx.run(...)` calls back into
|
|
320
|
+
* Lunora functions over the admin-authenticated dispatch endpoint (the same
|
|
321
|
+
* trusted path the scheduler and workflows use), so those calls run with the
|
|
322
|
+
* system identity — **end-user RLS is not applied**. Treat a queue handler as
|
|
323
|
+
* trusted server code: validate `message.body` (it may be attacker-influenced if
|
|
324
|
+
* anything user-facing can enqueue) before acting on it, and don't forward an
|
|
325
|
+
* unchecked body straight into a privileged mutation.
|
|
326
|
+
*/
|
|
328
327
|
declare const defineQueue: <Body = unknown>(config: QueueConfig<Body>) => QueueDefinition<Body>;
|
|
329
328
|
/** True when a value is a `defineQueue` result (the runtime brand check). */
|
|
330
329
|
declare const isQueueDefinition: (value: unknown) => value is QueueDefinition;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared types for dispatching a Lunora function back into the worker from a
|
|
3
|
-
* server-initiated context (a workflow body, a queue handler, a scheduled job).
|
|
4
|
-
* Node-safe — no Cloudflare runtime imports — so the consumers stay unit-testable
|
|
5
|
-
* with plain-object doubles.
|
|
6
|
-
*/
|
|
2
|
+
* Shared types for dispatching a Lunora function back into the worker from a
|
|
3
|
+
* server-initiated context (a workflow body, a queue handler, a scheduled job).
|
|
4
|
+
* Node-safe — no Cloudflare runtime imports — so the consumers stay unit-testable
|
|
5
|
+
* with plain-object doubles.
|
|
6
|
+
*/
|
|
7
7
|
/** Opaque generated function reference (`api.foo.bar`), carrying its dispatch id. */
|
|
8
8
|
interface FunctionReference {
|
|
9
9
|
__lunoraRef: string;
|
|
@@ -24,7 +24,6 @@ interface DispatchLogger {
|
|
|
24
24
|
info: (message: unknown, ...rest: unknown[]) => void;
|
|
25
25
|
warn: (message: unknown, ...rest: unknown[]) => void;
|
|
26
26
|
}
|
|
27
|
-
/** Build a {@link DispatchLogger} that prefixes every line with `prefix` (e.g. `[queue:email]`). */
|
|
28
27
|
/** How a queue message body is serialized on the wire (Cloudflare default `"json"`). */
|
|
29
28
|
type QueueContentType = "bytes" | "json" | "text" | "v8";
|
|
30
29
|
/** Options for a single `producer.send(body, options?)`. */
|
|
@@ -46,10 +45,10 @@ interface MessageSendRequestLike<Body = unknown> {
|
|
|
46
45
|
delaySeconds?: number;
|
|
47
46
|
}
|
|
48
47
|
/**
|
|
49
|
-
* Minimal structural projection of workers-types' `Queue<Body>` (the producer
|
|
50
|
-
* binding). The real binding's `send`/`sendBatch` resolve to a metadata object;
|
|
51
|
-
* we widen the return to `Promise<unknown>` so a plain-object fake satisfies it.
|
|
52
|
-
*/
|
|
48
|
+
* Minimal structural projection of workers-types' `Queue<Body>` (the producer
|
|
49
|
+
* binding). The real binding's `send`/`sendBatch` resolve to a metadata object;
|
|
50
|
+
* we widen the return to `Promise<unknown>` so a plain-object fake satisfies it.
|
|
51
|
+
*/
|
|
53
52
|
interface QueueBindingLike<Body = unknown> {
|
|
54
53
|
send: (message: Body, options?: QueueSendOptions) => Promise<unknown>;
|
|
55
54
|
sendBatch: (messages: Iterable<MessageSendRequestLike<Body>>, options?: QueueSendBatchOptions) => Promise<unknown>;
|
|
@@ -80,10 +79,10 @@ interface MessageBatchLike<Body = unknown> {
|
|
|
80
79
|
retryAll: (options?: QueueRetryOptions) => void;
|
|
81
80
|
}
|
|
82
81
|
/**
|
|
83
|
-
* The typed producer bound to `ctx.queues.<name>`. Sending is a side effect, so
|
|
84
|
-
* the generated context exposes this only on `MutationCtx` / `ActionCtx` (never
|
|
85
|
-
* the deterministic `QueryCtx`), mirroring `ctx.scheduler` / `ctx.workflows`.
|
|
86
|
-
*/
|
|
82
|
+
* The typed producer bound to `ctx.queues.<name>`. Sending is a side effect, so
|
|
83
|
+
* the generated context exposes this only on `MutationCtx` / `ActionCtx` (never
|
|
84
|
+
* the deterministic `QueryCtx`), mirroring `ctx.scheduler` / `ctx.workflows`.
|
|
85
|
+
*/
|
|
87
86
|
interface QueueProducer<Body = unknown> {
|
|
88
87
|
/** Enqueue one message. */
|
|
89
88
|
send: (body: Body, options?: QueueSendOptions) => Promise<void>;
|
|
@@ -91,10 +90,10 @@ interface QueueProducer<Body = unknown> {
|
|
|
91
90
|
sendBatch: (messages: Iterable<MessageSendRequestLike<Body>>, options?: QueueSendBatchOptions) => Promise<void>;
|
|
92
91
|
}
|
|
93
92
|
/**
|
|
94
|
-
* `ctx.queues` — the map of declared queue export names → typed producers.
|
|
95
|
-
* Codegen narrows this to the exact export names; the package keeps it open so
|
|
96
|
-
* `createQueues` stays schema-agnostic.
|
|
97
|
-
*/
|
|
93
|
+
* `ctx.queues` — the map of declared queue export names → typed producers.
|
|
94
|
+
* Codegen narrows this to the exact export names; the package keeps it open so
|
|
95
|
+
* `createQueues` stays schema-agnostic.
|
|
96
|
+
*/
|
|
98
97
|
interface Queues {
|
|
99
98
|
[exportName: string]: QueueProducer;
|
|
100
99
|
}
|
|
@@ -104,11 +103,11 @@ interface LunoraQueuesOptions {
|
|
|
104
103
|
bindings: Record<string, QueueBindingLike>;
|
|
105
104
|
}
|
|
106
105
|
/**
|
|
107
|
-
* The context handed to a `defineQueue` handler. Decoupled from `@lunora/server`
|
|
108
|
-
* (like the workflow run context): to touch data, call a Lunora mutation/action
|
|
109
|
-
* via `ctx.run(api.x.y, args)` — the dispatch goes through the same
|
|
110
|
-
* `/_lunora/scheduler/dispatch` path the SchedulerDO and workflows use.
|
|
111
|
-
*/
|
|
106
|
+
* The context handed to a `defineQueue` handler. Decoupled from `@lunora/server`
|
|
107
|
+
* (like the workflow run context): to touch data, call a Lunora mutation/action
|
|
108
|
+
* via `ctx.run(api.x.y, args)` — the dispatch goes through the same
|
|
109
|
+
* `/_lunora/scheduler/dispatch` path the SchedulerDO and workflows use.
|
|
110
|
+
*/
|
|
112
111
|
interface QueueRunContext {
|
|
113
112
|
/** The worker `env` (bindings + vars). */
|
|
114
113
|
readonly env: Record<string, unknown>;
|
|
@@ -137,25 +136,25 @@ interface QueueConsumerTuning {
|
|
|
137
136
|
/** The config object passed to `defineQueue`. */
|
|
138
137
|
interface QueueConfig<Body = unknown> extends QueueConsumerTuning {
|
|
139
138
|
/**
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
* The push-consumer body. Required for `mode: "push"` (the default); omit it
|
|
140
|
+
* for `mode: "pull"`, where an external worker polls the queue over HTTP.
|
|
141
|
+
*/
|
|
143
142
|
handler?: QueueHandler<Body>;
|
|
144
143
|
/** How this queue is consumed. Defaults to `"push"`. */
|
|
145
144
|
mode?: QueueConsumerMode;
|
|
146
145
|
/**
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
* Stable wrangler queue name (`queues.producers[].queue`). Defaults to the
|
|
147
|
+
* kebab-cased export name (`emailQueue` → `email-queue`).
|
|
148
|
+
*/
|
|
150
149
|
name?: string;
|
|
151
150
|
}
|
|
152
151
|
/** The branded result of `defineQueue`, discovered by codegen + config. */
|
|
153
152
|
interface QueueDefinition<Body = unknown> extends QueueConfig<Body> {
|
|
154
153
|
/**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
* Phantom carrier for the message body type, so codegen can type the
|
|
155
|
+
* generated `ctx.queues.<name>` producer as `QueueProducer<Body>` from
|
|
156
|
+
* `typeof <export>`. Never assigned at runtime (type-only).
|
|
157
|
+
*/
|
|
159
158
|
readonly __lunoraBody?: Body;
|
|
160
159
|
/** Runtime brand identifying a `defineQueue` result. */
|
|
161
160
|
isLunoraQueue: true;
|
|
@@ -172,13 +171,13 @@ interface QueueBindingSpec {
|
|
|
172
171
|
/** One declared queue, keyed for batch routing by its stable wrangler name. */
|
|
173
172
|
interface QueueRegistryEntry {
|
|
174
173
|
/**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
* The `defineQueue` result (carries the push handler). The body type is
|
|
175
|
+
* erased to `any` here because the registry is heterogeneous — different
|
|
176
|
+
* queues carry different message bodies, and the handler param is
|
|
177
|
+
* contravariant, so a precise `QueueDefinition<Body>` would not be assignable
|
|
178
|
+
* to a shared `unknown`-bodied slot. Runtime dispatch passes the delivered
|
|
179
|
+
* batch straight through, so the erasure is type-only.
|
|
180
|
+
*/
|
|
182
181
|
definition: QueueDefinition<any>;
|
|
183
182
|
/** The `lunora/queues.ts` export name, for log correlation. */
|
|
184
183
|
exportName: string;
|
|
@@ -188,11 +187,11 @@ type QueueRegistry = Record<string, QueueRegistryEntry>;
|
|
|
188
187
|
/** The disposition a consumer left one message in for a single delivery attempt. */
|
|
189
188
|
type QueueMessageOutcome = "ack" | "error" | "retry";
|
|
190
189
|
/**
|
|
191
|
-
* One consumed message as captured by {@link dispatchQueueBatch} and handed to an
|
|
192
|
-
* {@link QueueCaptureSink}. Structurally matches `@lunora/do`'s
|
|
193
|
-
* `RecordQueueMessageInput` (the reserved `recordQueueMessage` admin RPC payload);
|
|
194
|
-
* the two packages share only this contract, so keep them in sync by hand.
|
|
195
|
-
*/
|
|
190
|
+
* One consumed message as captured by {@link dispatchQueueBatch} and handed to an
|
|
191
|
+
* {@link QueueCaptureSink}. Structurally matches `@lunora/do`'s
|
|
192
|
+
* `RecordQueueMessageInput` (the reserved `recordQueueMessage` admin RPC payload);
|
|
193
|
+
* the two packages share only this contract, so keep them in sync by hand.
|
|
194
|
+
*/
|
|
196
195
|
interface CapturedQueueMessage {
|
|
197
196
|
/** Delivery attempt number for this message (`message.attempts`). */
|
|
198
197
|
attempts: number;
|
|
@@ -214,19 +213,19 @@ interface CapturedQueueMessage {
|
|
|
214
213
|
timestamp: number;
|
|
215
214
|
}
|
|
216
215
|
/**
|
|
217
|
-
* Persists a batch of consumed messages. The codegen worker wires this to POST the
|
|
218
|
-
* batch to the root shard's `recordQueueMessage` admin RPC (the dev queue catcher).
|
|
219
|
-
* Best-effort by contract: {@link dispatchQueueBatch} swallows a rejection so a
|
|
220
|
-
* capture failure never changes delivery semantics.
|
|
221
|
-
*/
|
|
216
|
+
* Persists a batch of consumed messages. The codegen worker wires this to POST the
|
|
217
|
+
* batch to the root shard's `recordQueueMessage` admin RPC (the dev queue catcher).
|
|
218
|
+
* Best-effort by contract: {@link dispatchQueueBatch} swallows a rejection so a
|
|
219
|
+
* capture failure never changes delivery semantics.
|
|
220
|
+
*/
|
|
222
221
|
type QueueCaptureSink = (messages: CapturedQueueMessage[]) => Promise<void> | void;
|
|
223
222
|
interface DispatchOptions {
|
|
224
223
|
/**
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
* Optional capture sink. When set, the batch is instrumented and every
|
|
225
|
+
* message's final disposition is recorded and handed to this sink after the
|
|
226
|
+
* handler runs. Omitted in production unless queue capture is enabled, so a
|
|
227
|
+
* consumer pays no instrumentation cost by default.
|
|
228
|
+
*/
|
|
230
229
|
capture?: QueueCaptureSink;
|
|
231
230
|
/** Worker `env`, forwarded to the queue run context. */
|
|
232
231
|
env: Record<string, unknown>;
|
|
@@ -234,11 +233,11 @@ interface DispatchOptions {
|
|
|
234
233
|
fetchImpl?: typeof fetch;
|
|
235
234
|
}
|
|
236
235
|
/**
|
|
237
|
-
* Look up the handler for `batch.queue` and invoke it with a fresh
|
|
238
|
-
* `QueueRunContext`. Throws a directed error when no push handler is registered
|
|
239
|
-
* for the delivered queue (a misconfiguration — the consumer was declared
|
|
240
|
-
* `pull`, or the queue name drifted from the `defineQueue` export).
|
|
241
|
-
*/
|
|
236
|
+
* Look up the handler for `batch.queue` and invoke it with a fresh
|
|
237
|
+
* `QueueRunContext`. Throws a directed error when no push handler is registered
|
|
238
|
+
* for the delivered queue (a misconfiguration — the consumer was declared
|
|
239
|
+
* `pull`, or the queue name drifted from the `defineQueue` export).
|
|
240
|
+
*/
|
|
242
241
|
declare const dispatchQueueBatch: (batch: MessageBatchLike, registry: QueueRegistry, options: DispatchOptions) => Promise<void>;
|
|
243
242
|
/** A Worker `env` projected as a plain record (vars, secrets, and bindings are `unknown`-valued). */
|
|
244
243
|
type QueueEnv = Record<string, unknown>;
|
|
@@ -250,81 +249,81 @@ interface QueueCaptureOptions {
|
|
|
250
249
|
rootShard?: string;
|
|
251
250
|
}
|
|
252
251
|
/**
|
|
253
|
-
* Whether consumed queue messages should be captured into the studio's log.
|
|
254
|
-
* Explicit `LUNORA_QUEUE_CAPTURE` (`"1"`/`"true"` vs `"0"`/`"false"`) always wins;
|
|
255
|
-
* unset, capture is on only in a development environment. Mirrors
|
|
256
|
-
* `@lunora/mail`'s `shouldCaptureMail` so mail and queue dev capture toggle the
|
|
257
|
-
* same way.
|
|
258
|
-
*/
|
|
252
|
+
* Whether consumed queue messages should be captured into the studio's log.
|
|
253
|
+
* Explicit `LUNORA_QUEUE_CAPTURE` (`"1"`/`"true"` vs `"0"`/`"false"`) always wins;
|
|
254
|
+
* unset, capture is on only in a development environment. Mirrors
|
|
255
|
+
* `@lunora/mail`'s `shouldCaptureMail` so mail and queue dev capture toggle the
|
|
256
|
+
* same way.
|
|
257
|
+
*/
|
|
259
258
|
declare const shouldCaptureQueue: (env: QueueEnv) => boolean;
|
|
260
259
|
/**
|
|
261
|
-
* Build the {@link QueueCaptureSink} that records a processed batch into the
|
|
262
|
-
* studio's root-shard consumed-message log via the reserved `recordQueueMessage`
|
|
263
|
-
* admin RPC. Best-effort by contract: without the `SHARD` binding or
|
|
264
|
-
* `LUNORA_ADMIN_TOKEN` it no-ops, and `dispatchQueueBatch` swallows a
|
|
265
|
-
* rejection, so capture never changes delivery semantics.
|
|
266
|
-
*/
|
|
260
|
+
* Build the {@link QueueCaptureSink} that records a processed batch into the
|
|
261
|
+
* studio's root-shard consumed-message log via the reserved `recordQueueMessage`
|
|
262
|
+
* admin RPC. Best-effort by contract: without the `SHARD` binding or
|
|
263
|
+
* `LUNORA_ADMIN_TOKEN` it no-ops, and `dispatchQueueBatch` swallows a
|
|
264
|
+
* rejection, so capture never changes delivery semantics.
|
|
265
|
+
*/
|
|
267
266
|
declare const createQueueCaptureSink: (env: QueueEnv, options?: QueueCaptureOptions) => QueueCaptureSink;
|
|
268
267
|
/**
|
|
269
|
-
* Build the `ctx.queues` map for a request: resolve every spec's `env[binding]`
|
|
270
|
-
* into the `exportName → Queue binding` map and wrap it in {@link createQueues}.
|
|
271
|
-
* A spec whose binding is absent from `env` is skipped here — the helpful "no
|
|
272
|
-
* queue named …" error is raised lazily by `ctx.queues.<name>.send(...)` when
|
|
273
|
-
* the missing queue is actually used.
|
|
274
|
-
*/
|
|
268
|
+
* Build the `ctx.queues` map for a request: resolve every spec's `env[binding]`
|
|
269
|
+
* into the `exportName → Queue binding` map and wrap it in {@link createQueues}.
|
|
270
|
+
* A spec whose binding is absent from `env` is skipped here — the helpful "no
|
|
271
|
+
* queue named …" error is raised lazily by `ctx.queues.<name>.send(...)` when
|
|
272
|
+
* the missing queue is actually used.
|
|
273
|
+
*/
|
|
275
274
|
declare const createQueueContext: (env: Record<string, unknown>, specs: ReadonlyArray<QueueBindingSpec>) => Queues;
|
|
276
275
|
/**
|
|
277
|
-
* Build the `ctx.queues` map from `lunora/queues.ts` export name → Cloudflare
|
|
278
|
-
* `Queue` binding. Each property is a typed {@link QueueProducer}; accessing an
|
|
279
|
-
* export whose binding is absent throws a directed error naming the declared
|
|
280
|
-
* queues (raised lazily on first use).
|
|
281
|
-
*/
|
|
276
|
+
* Build the `ctx.queues` map from `lunora/queues.ts` export name → Cloudflare
|
|
277
|
+
* `Queue` binding. Each property is a typed {@link QueueProducer}; accessing an
|
|
278
|
+
* export whose binding is absent throws a directed error naming the declared
|
|
279
|
+
* queues (raised lazily on first use).
|
|
280
|
+
*/
|
|
282
281
|
declare const createQueues: (options: LunoraQueuesOptions) => Queues;
|
|
283
282
|
/**
|
|
284
|
-
* The wrangler producer binding name for a queue export: `emailQueue` →
|
|
285
|
-
* `QUEUE_EMAIL_QUEUE`, `email` → `QUEUE_EMAIL`. The `QUEUE_` prefix namespaces
|
|
286
|
-
* these away from `SHARD`/`SESSION`/`SCHEDULER`/`WORKFLOW_*`/`CONTAINER_*` so a
|
|
287
|
-
* queue export can never collide with the built-in bindings.
|
|
288
|
-
*/
|
|
283
|
+
* The wrangler producer binding name for a queue export: `emailQueue` →
|
|
284
|
+
* `QUEUE_EMAIL_QUEUE`, `email` → `QUEUE_EMAIL`. The `QUEUE_` prefix namespaces
|
|
285
|
+
* these away from `SHARD`/`SESSION`/`SCHEDULER`/`WORKFLOW_*`/`CONTAINER_*` so a
|
|
286
|
+
* queue export can never collide with the built-in bindings.
|
|
287
|
+
*/
|
|
289
288
|
declare const queueBindingName: (exportName: string) => string;
|
|
290
289
|
/**
|
|
291
|
-
* The stable queue name wrangler registers (`queues.producers[].queue` and
|
|
292
|
-
* `queues.consumers[].queue`): `emailQueue` → `email-queue`. Used as the
|
|
293
|
-
* deployed queue's identifier when no explicit `name` override is given.
|
|
294
|
-
*/
|
|
290
|
+
* The stable queue name wrangler registers (`queues.producers[].queue` and
|
|
291
|
+
* `queues.consumers[].queue`): `emailQueue` → `email-queue`. Used as the
|
|
292
|
+
* deployed queue's identifier when no explicit `name` override is given.
|
|
293
|
+
*/
|
|
295
294
|
declare const queueDefaultName: (exportName: string) => string;
|
|
296
295
|
/**
|
|
297
|
-
* Declare a Cloudflare Queue deployed alongside the app. Pure validation +
|
|
298
|
-
* branding: codegen discovers the export, emits the typed `ctx.queues.<name>`
|
|
299
|
-
* producer and (for push consumers) the worker `queue()` dispatch; the config
|
|
300
|
-
* layer reconciles the wrangler `queues.producers[]` / `queues.consumers[]`
|
|
301
|
-
* entries from the same definition.
|
|
302
|
-
*
|
|
303
|
-
* ```ts
|
|
304
|
-
* // lunora/queues.ts
|
|
305
|
-
* import { defineQueue } from "@lunora/queue";
|
|
306
|
-
* import { api } from "./_generated/api";
|
|
307
|
-
*
|
|
308
|
-
* export const emailQueue = defineQueue<{ to: string }>({
|
|
309
|
-
* handler: async (ctx, batch) => {
|
|
310
|
-
* for (const message of batch.messages) {
|
|
311
|
-
* await ctx.run(api.email.send, { to: message.body.to });
|
|
312
|
-
* message.ack();
|
|
313
|
-
* }
|
|
314
|
-
* },
|
|
315
|
-
* });
|
|
316
|
-
* ```
|
|
317
|
-
*
|
|
318
|
-
* Enqueue from a mutation or action: `await ctx.queues.emailQueue.send({ to })`.
|
|
319
|
-
*
|
|
320
|
-
* ⚠️ **Privileged dispatch.** A push handler's `ctx.run(...)` calls back into
|
|
321
|
-
* Lunora functions over the admin-authenticated dispatch endpoint (the same
|
|
322
|
-
* trusted path the scheduler and workflows use), so those calls run with the
|
|
323
|
-
* system identity — **end-user RLS is not applied**. Treat a queue handler as
|
|
324
|
-
* trusted server code: validate `message.body` (it may be attacker-influenced if
|
|
325
|
-
* anything user-facing can enqueue) before acting on it, and don't forward an
|
|
326
|
-
* unchecked body straight into a privileged mutation.
|
|
327
|
-
*/
|
|
296
|
+
* Declare a Cloudflare Queue deployed alongside the app. Pure validation +
|
|
297
|
+
* branding: codegen discovers the export, emits the typed `ctx.queues.<name>`
|
|
298
|
+
* producer and (for push consumers) the worker `queue()` dispatch; the config
|
|
299
|
+
* layer reconciles the wrangler `queues.producers[]` / `queues.consumers[]`
|
|
300
|
+
* entries from the same definition.
|
|
301
|
+
*
|
|
302
|
+
* ```ts
|
|
303
|
+
* // lunora/queues.ts
|
|
304
|
+
* import { defineQueue } from "@lunora/queue";
|
|
305
|
+
* import { api } from "./_generated/api";
|
|
306
|
+
*
|
|
307
|
+
* export const emailQueue = defineQueue<{ to: string }>({
|
|
308
|
+
* handler: async (ctx, batch) => {
|
|
309
|
+
* for (const message of batch.messages) {
|
|
310
|
+
* await ctx.run(api.email.send, { to: message.body.to });
|
|
311
|
+
* message.ack();
|
|
312
|
+
* }
|
|
313
|
+
* },
|
|
314
|
+
* });
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* Enqueue from a mutation or action: `await ctx.queues.emailQueue.send({ to })`.
|
|
318
|
+
*
|
|
319
|
+
* ⚠️ **Privileged dispatch.** A push handler's `ctx.run(...)` calls back into
|
|
320
|
+
* Lunora functions over the admin-authenticated dispatch endpoint (the same
|
|
321
|
+
* trusted path the scheduler and workflows use), so those calls run with the
|
|
322
|
+
* system identity — **end-user RLS is not applied**. Treat a queue handler as
|
|
323
|
+
* trusted server code: validate `message.body` (it may be attacker-influenced if
|
|
324
|
+
* anything user-facing can enqueue) before acting on it, and don't forward an
|
|
325
|
+
* unchecked body straight into a privileged mutation.
|
|
326
|
+
*/
|
|
328
327
|
declare const defineQueue: <Body = unknown>(config: QueueConfig<Body>) => QueueDefinition<Body>;
|
|
329
328
|
/** True when a value is a `defineQueue` result (the runtime brand check). */
|
|
330
329
|
declare const isQueueDefinition: (value: unknown) => value is QueueDefinition;
|
package/dist/index.mjs
CHANGED
|
@@ -2,5 +2,5 @@ export { createQueueCaptureSink, shouldCaptureQueue } from './packem_shared/crea
|
|
|
2
2
|
export { createQueueContext } from './packem_shared/createQueueContext-D0XCdCsd.mjs';
|
|
3
3
|
export { default as createQueues } from './packem_shared/createQueues-14-vSICK.mjs';
|
|
4
4
|
export { defineQueue, isQueueDefinition, queueBindingName, queueDefaultName } from './packem_shared/defineQueue-D40gREfg.mjs';
|
|
5
|
-
export { dispatchQueueBatch } from './packem_shared/dispatchQueueBatch-
|
|
6
|
-
export { createQueueRunContext } from './packem_shared/createQueueRunContext-
|
|
5
|
+
export { dispatchQueueBatch } from './packem_shared/dispatchQueueBatch-DSEWhEy8.mjs';
|
|
6
|
+
export { createQueueRunContext } from './packem_shared/createQueueRunContext-C8jboCk6.mjs';
|
package/dist/packem_shared/{createQueueRunContext-wScWyFao.mjs → createQueueRunContext-C8jboCk6.mjs}
RENAMED
|
@@ -54,9 +54,16 @@ const createDispatchRunner = (options) => {
|
|
|
54
54
|
throw new LunoraError("INTERNAL", `${label}: \`LUNORA_ADMIN_TOKEN\` must be set on the Worker env to authenticate function dispatch`);
|
|
55
55
|
}
|
|
56
56
|
const url = `${trimTrailingSlashes(origin)}${SCHEDULER_DISPATCH_PATH}`;
|
|
57
|
+
const headers = { authorization: `Bearer ${token}`, "content-type": "application/json" };
|
|
58
|
+
if (options.identity?.userId !== void 0) {
|
|
59
|
+
headers["x-lunora-userid"] = options.identity.userId;
|
|
60
|
+
}
|
|
61
|
+
if (options.identity?.claims !== void 0) {
|
|
62
|
+
headers["x-lunora-identity"] = JSON.stringify(options.identity.claims);
|
|
63
|
+
}
|
|
57
64
|
const response = await fetchImpl(url, {
|
|
58
65
|
body: JSON.stringify({ args: args ?? {}, functionPath: function_.__lunoraRef, shardKey: runOptions.shardKey }),
|
|
59
|
-
headers
|
|
66
|
+
headers,
|
|
60
67
|
method: "POST"
|
|
61
68
|
});
|
|
62
69
|
if (!response.ok) {
|
package/dist/packem_shared/{dispatchQueueBatch-DbA5vKBs.mjs → dispatchQueueBatch-DSEWhEy8.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
import { createQueueRunContext } from './createQueueRunContext-
|
|
2
|
+
import { createQueueRunContext } from './createQueueRunContext-C8jboCk6.mjs';
|
|
3
3
|
|
|
4
4
|
const DEFAULT_MAX_RETRIES = 3;
|
|
5
5
|
const timestampToMs = (value) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/queue",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.9",
|
|
4
4
|
"description": "Cloudflare Queues for Lunora: defineQueue producers + consumers, the ctx.queues surface, and the generated queue() worker handler",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"background-jobs",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
47
|
+
"@lunora/errors": "1.0.0-alpha.6"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": "^22.15.0 || >=24.11.0"
|