@lunora/queue 1.0.0-alpha.96 → 1.0.0-alpha.97

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 CHANGED
@@ -76,16 +76,16 @@ interface QueueProducer<Body = unknown> {
76
76
  }
77
77
  /**
78
78
  * `ctx.queues` — the map of declared queue export names → typed producers.
79
- * Codegen narrows this to the exact export names; the package keeps it open so
80
- * `createQueues` stays schema-agnostic.
79
+ * Keyed by the exact export names `createQueueContext` / `createQueues` are
80
+ * handed, so `queues.jobs` is a `QueueProducer` rather than `QueueProducer |
81
+ * undefined` under `noUncheckedIndexedAccess`. `Name` defaults to `string`
82
+ * (an open map) for schema-agnostic callers.
81
83
  */
82
- interface Queues {
83
- [exportName: string]: QueueProducer;
84
- }
84
+ type Queues<Name extends string = string> = Readonly<Record<Name, QueueProducer>>;
85
85
  /** Options the package-level `createQueues` factory takes. */
86
- interface LunoraQueuesOptions {
86
+ interface LunoraQueuesOptions<Name extends string = string> {
87
87
  /** Map of `lunora/queues.ts` export name → Cloudflare `Queue` producer binding. */
88
- bindings: Record<string, QueueBindingLike>;
88
+ bindings: Record<Name, QueueBindingLike>;
89
89
  }
90
90
  /**
91
91
  * The context handed to a `defineQueue` handler. Decoupled from `@lunora/server`
@@ -139,6 +139,8 @@ interface QueueConsumerTuning {
139
139
  maxBatchSize?: number;
140
140
  /** Max seconds to wait before delivering a partial batch (0–60, default 5). */
141
141
  maxBatchTimeout?: number;
142
+ /** Max concurrent consumer invocations (1–250). Unset lets Cloudflare autoscale. Not emulated by Miniflare in dev. */
143
+ maxConcurrency?: number;
142
144
  /** Retries **after** the initial delivery, before a message is dropped / dead-lettered. Default 3, so up to 4 deliveries in total. */
143
145
  maxRetries?: number;
144
146
  /** Delay in seconds before a failed batch is retried. */
@@ -183,10 +185,8 @@ interface TopicPublisher<Payload = unknown> {
183
185
  /** Publish a batch (at most 100) to every subscription in one send per subscription. */
184
186
  publishBatch: (messages: Iterable<MessageSendRequestLike<Payload>>, options?: QueueSendBatchOptions) => Promise<void>;
185
187
  }
186
- /** `ctx.topics` — declared topic export names → typed publishers. Codegen narrows it to the exact names. */
187
- interface Topics {
188
- [exportName: string]: TopicPublisher;
189
- }
188
+ /** `ctx.topics` — declared topic export names → typed publishers, keyed like {@link Queues}. */
189
+ type Topics<Name extends string = string> = Readonly<Record<Name, TopicPublisher>>;
190
190
  /** The branded result of `defineTopic`, discovered by codegen. Carries no deploy config of its own. */
191
191
  interface TopicDefinition<Payload = unknown> {
192
192
  /**
@@ -219,9 +219,9 @@ interface SubscriptionDefinition<Payload = unknown> extends QueueDefinition<Payl
219
219
  topic: TopicDefinition<Payload>;
220
220
  }
221
221
  /** Wiring info for one declared topic, emitted by codegen into the generated shard. */
222
- interface TopicBindingSpec {
222
+ interface TopicBindingSpec<Name extends string = string> {
223
223
  /** The `lunora/queues.ts` topic export name, e.g. `signups`. */
224
- exportName: string;
224
+ exportName: Name;
225
225
  /** One entry per subscription: its export name and its `Queue` producer binding. */
226
226
  subscriptions: ReadonlyArray<{
227
227
  binding: string;
@@ -229,11 +229,11 @@ interface TopicBindingSpec {
229
229
  }>;
230
230
  }
231
231
  /** Wiring info for one declared queue, emitted by codegen into the generated shard/handler. */
232
- interface QueueBindingSpec {
232
+ interface QueueBindingSpec<Name extends string = string> {
233
233
  /** The Cloudflare `Queue` producer binding name, e.g. `QUEUE_EMAIL`. */
234
234
  binding: string;
235
235
  /** The `lunora/queues.ts` export name, e.g. `emailQueue`. */
236
- exportName: string;
236
+ exportName: Name;
237
237
  /** The stable wrangler queue name, e.g. `email-queue`. */
238
238
  name: string;
239
239
  }
@@ -342,7 +342,9 @@ interface DispatchOptions {
342
342
  * Look up the handler for `batch.queue` and invoke it with a fresh
343
343
  * `QueueRunContext`. Throws a directed error when no push handler is registered
344
344
  * for the delivered queue (a misconfiguration — the consumer was declared
345
- * `pull`, or the queue name drifted from the `defineQueue` export).
345
+ * `pull`, or the queue name drifted from the `defineQueue` export). The
346
+ * registry already carries every per-environment name wrangler.jsonc maps to a
347
+ * declared producer binding (see codegen's `emitQueues`), so the lookup stays exact.
346
348
  */
347
349
  declare const dispatchQueueBatch: (batch: MessageBatchLike, registry: QueueRegistry, options: DispatchOptions) => Promise<void>;
348
350
  /** A Worker `env` projected as a plain record (vars, secrets, and bindings are `unknown`-valued). */
@@ -377,14 +379,14 @@ declare const createQueueCaptureSink: (env: QueueEnv, options?: QueueCaptureOpti
377
379
  * queue named …" error is raised lazily by `ctx.queues.<name>.send(...)` when
378
380
  * the missing queue is actually used.
379
381
  */
380
- declare const createQueueContext: (env: Record<string, unknown>, specs: ReadonlyArray<QueueBindingSpec>) => Queues;
382
+ declare const createQueueContext: <Name extends string>(env: Record<string, unknown>, specs: ReadonlyArray<QueueBindingSpec<Name>>) => Queues<Name>;
381
383
  /**
382
384
  * Build the `ctx.queues` map from `lunora/queues.ts` export name → Cloudflare
383
385
  * `Queue` binding. Each property is a typed {@link QueueProducer}; accessing an
384
386
  * export whose binding is absent throws a directed error naming the declared
385
387
  * queues (raised lazily on first use).
386
388
  */
387
- declare const createQueues: (options: LunoraQueuesOptions) => Queues;
389
+ declare const createQueues: <Name extends string>(options: LunoraQueuesOptions<Name>) => Queues<Name>;
388
390
  /**
389
391
  * The wrangler producer binding name for a queue export: `emailQueue` →
390
392
  * `QUEUE_EMAIL_QUEUE`, `email` → `QUEUE_EMAIL`. The `QUEUE_` prefix namespaces
@@ -467,5 +469,5 @@ declare const defineSubscription: <Payload>(topic: TopicDefinition<Payload>, con
467
469
  * specs. A topic with no subscriptions publishes to nobody; a topic with an
468
470
  * unbound subscription rejects every publish (see {@link unboundPublisher}).
469
471
  */
470
- declare const createTopicContext: (env: Record<string, unknown>, specs: ReadonlyArray<TopicBindingSpec>) => Topics;
472
+ declare const createTopicContext: <Name extends string>(env: Record<string, unknown>, specs: ReadonlyArray<TopicBindingSpec<Name>>) => Topics<Name>;
471
473
  export { type ArgsOf, type CapturedQueueMessage, type FunctionReference, type LunoraQueuesOptions, type QueueBindingSpec, type QueueCaptureOptions, type QueueCaptureSink, type QueueConfig, type QueueConsumerMode, type QueueConsumerTuning, type QueueDefinition, type QueueEnv, type QueueHandler, type DispatchLogger as QueueLogger, type QueueMessage, type QueueMessageBatch, type QueueProducer, type QueueRegistry, type QueueRegistryEntry, type QueueRunContext, type DispatchRunFunction as QueueRunFunction, type Queues, type RunFunctionOptions, type SubscriptionConfig, type SubscriptionDefinition, type TopicBindingSpec, type TopicDefinition, type TopicPublisher, type Topics, createQueueCaptureSink, createQueueContext, createQueueRunContext, createQueues, createTopicContext, defineQueue, defineSubscription, defineTopic, dispatchQueueBatch, isQueueDefinition, queueBindingName, queueDefaultName, shouldCaptureQueue };
package/dist/index.d.ts CHANGED
@@ -76,16 +76,16 @@ interface QueueProducer<Body = unknown> {
76
76
  }
77
77
  /**
78
78
  * `ctx.queues` — the map of declared queue export names → typed producers.
79
- * Codegen narrows this to the exact export names; the package keeps it open so
80
- * `createQueues` stays schema-agnostic.
79
+ * Keyed by the exact export names `createQueueContext` / `createQueues` are
80
+ * handed, so `queues.jobs` is a `QueueProducer` rather than `QueueProducer |
81
+ * undefined` under `noUncheckedIndexedAccess`. `Name` defaults to `string`
82
+ * (an open map) for schema-agnostic callers.
81
83
  */
82
- interface Queues {
83
- [exportName: string]: QueueProducer;
84
- }
84
+ type Queues<Name extends string = string> = Readonly<Record<Name, QueueProducer>>;
85
85
  /** Options the package-level `createQueues` factory takes. */
86
- interface LunoraQueuesOptions {
86
+ interface LunoraQueuesOptions<Name extends string = string> {
87
87
  /** Map of `lunora/queues.ts` export name → Cloudflare `Queue` producer binding. */
88
- bindings: Record<string, QueueBindingLike>;
88
+ bindings: Record<Name, QueueBindingLike>;
89
89
  }
90
90
  /**
91
91
  * The context handed to a `defineQueue` handler. Decoupled from `@lunora/server`
@@ -139,6 +139,8 @@ interface QueueConsumerTuning {
139
139
  maxBatchSize?: number;
140
140
  /** Max seconds to wait before delivering a partial batch (0–60, default 5). */
141
141
  maxBatchTimeout?: number;
142
+ /** Max concurrent consumer invocations (1–250). Unset lets Cloudflare autoscale. Not emulated by Miniflare in dev. */
143
+ maxConcurrency?: number;
142
144
  /** Retries **after** the initial delivery, before a message is dropped / dead-lettered. Default 3, so up to 4 deliveries in total. */
143
145
  maxRetries?: number;
144
146
  /** Delay in seconds before a failed batch is retried. */
@@ -183,10 +185,8 @@ interface TopicPublisher<Payload = unknown> {
183
185
  /** Publish a batch (at most 100) to every subscription in one send per subscription. */
184
186
  publishBatch: (messages: Iterable<MessageSendRequestLike<Payload>>, options?: QueueSendBatchOptions) => Promise<void>;
185
187
  }
186
- /** `ctx.topics` — declared topic export names → typed publishers. Codegen narrows it to the exact names. */
187
- interface Topics {
188
- [exportName: string]: TopicPublisher;
189
- }
188
+ /** `ctx.topics` — declared topic export names → typed publishers, keyed like {@link Queues}. */
189
+ type Topics<Name extends string = string> = Readonly<Record<Name, TopicPublisher>>;
190
190
  /** The branded result of `defineTopic`, discovered by codegen. Carries no deploy config of its own. */
191
191
  interface TopicDefinition<Payload = unknown> {
192
192
  /**
@@ -219,9 +219,9 @@ interface SubscriptionDefinition<Payload = unknown> extends QueueDefinition<Payl
219
219
  topic: TopicDefinition<Payload>;
220
220
  }
221
221
  /** Wiring info for one declared topic, emitted by codegen into the generated shard. */
222
- interface TopicBindingSpec {
222
+ interface TopicBindingSpec<Name extends string = string> {
223
223
  /** The `lunora/queues.ts` topic export name, e.g. `signups`. */
224
- exportName: string;
224
+ exportName: Name;
225
225
  /** One entry per subscription: its export name and its `Queue` producer binding. */
226
226
  subscriptions: ReadonlyArray<{
227
227
  binding: string;
@@ -229,11 +229,11 @@ interface TopicBindingSpec {
229
229
  }>;
230
230
  }
231
231
  /** Wiring info for one declared queue, emitted by codegen into the generated shard/handler. */
232
- interface QueueBindingSpec {
232
+ interface QueueBindingSpec<Name extends string = string> {
233
233
  /** The Cloudflare `Queue` producer binding name, e.g. `QUEUE_EMAIL`. */
234
234
  binding: string;
235
235
  /** The `lunora/queues.ts` export name, e.g. `emailQueue`. */
236
- exportName: string;
236
+ exportName: Name;
237
237
  /** The stable wrangler queue name, e.g. `email-queue`. */
238
238
  name: string;
239
239
  }
@@ -342,7 +342,9 @@ interface DispatchOptions {
342
342
  * Look up the handler for `batch.queue` and invoke it with a fresh
343
343
  * `QueueRunContext`. Throws a directed error when no push handler is registered
344
344
  * for the delivered queue (a misconfiguration — the consumer was declared
345
- * `pull`, or the queue name drifted from the `defineQueue` export).
345
+ * `pull`, or the queue name drifted from the `defineQueue` export). The
346
+ * registry already carries every per-environment name wrangler.jsonc maps to a
347
+ * declared producer binding (see codegen's `emitQueues`), so the lookup stays exact.
346
348
  */
347
349
  declare const dispatchQueueBatch: (batch: MessageBatchLike, registry: QueueRegistry, options: DispatchOptions) => Promise<void>;
348
350
  /** A Worker `env` projected as a plain record (vars, secrets, and bindings are `unknown`-valued). */
@@ -377,14 +379,14 @@ declare const createQueueCaptureSink: (env: QueueEnv, options?: QueueCaptureOpti
377
379
  * queue named …" error is raised lazily by `ctx.queues.<name>.send(...)` when
378
380
  * the missing queue is actually used.
379
381
  */
380
- declare const createQueueContext: (env: Record<string, unknown>, specs: ReadonlyArray<QueueBindingSpec>) => Queues;
382
+ declare const createQueueContext: <Name extends string>(env: Record<string, unknown>, specs: ReadonlyArray<QueueBindingSpec<Name>>) => Queues<Name>;
381
383
  /**
382
384
  * Build the `ctx.queues` map from `lunora/queues.ts` export name → Cloudflare
383
385
  * `Queue` binding. Each property is a typed {@link QueueProducer}; accessing an
384
386
  * export whose binding is absent throws a directed error naming the declared
385
387
  * queues (raised lazily on first use).
386
388
  */
387
- declare const createQueues: (options: LunoraQueuesOptions) => Queues;
389
+ declare const createQueues: <Name extends string>(options: LunoraQueuesOptions<Name>) => Queues<Name>;
388
390
  /**
389
391
  * The wrangler producer binding name for a queue export: `emailQueue` →
390
392
  * `QUEUE_EMAIL_QUEUE`, `email` → `QUEUE_EMAIL`. The `QUEUE_` prefix namespaces
@@ -467,5 +469,5 @@ declare const defineSubscription: <Payload>(topic: TopicDefinition<Payload>, con
467
469
  * specs. A topic with no subscriptions publishes to nobody; a topic with an
468
470
  * unbound subscription rejects every publish (see {@link unboundPublisher}).
469
471
  */
470
- declare const createTopicContext: (env: Record<string, unknown>, specs: ReadonlyArray<TopicBindingSpec>) => Topics;
472
+ declare const createTopicContext: <Name extends string>(env: Record<string, unknown>, specs: ReadonlyArray<TopicBindingSpec<Name>>) => Topics<Name>;
471
473
  export { type ArgsOf, type CapturedQueueMessage, type FunctionReference, type LunoraQueuesOptions, type QueueBindingSpec, type QueueCaptureOptions, type QueueCaptureSink, type QueueConfig, type QueueConsumerMode, type QueueConsumerTuning, type QueueDefinition, type QueueEnv, type QueueHandler, type DispatchLogger as QueueLogger, type QueueMessage, type QueueMessageBatch, type QueueProducer, type QueueRegistry, type QueueRegistryEntry, type QueueRunContext, type DispatchRunFunction as QueueRunFunction, type Queues, type RunFunctionOptions, type SubscriptionConfig, type SubscriptionDefinition, type TopicBindingSpec, type TopicDefinition, type TopicPublisher, type Topics, createQueueCaptureSink, createQueueContext, createQueueRunContext, createQueues, createTopicContext, defineQueue, defineSubscription, defineTopic, dispatchQueueBatch, isQueueDefinition, queueBindingName, queueDefaultName, shouldCaptureQueue };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createQueueCaptureSink as t,shouldCaptureQueue as o}from"./packem_shared/createQueueCaptureSink-CSOPftvj.mjs";import{createQueueContext as i}from"./packem_shared/createQueueContext-BelEY_4M.mjs";import{createQueues as n}from"./packem_shared/createQueues-AS-deVhl.mjs";import{defineQueue as f,isQueueDefinition as c,queueBindingName as x,queueDefaultName as m}from"./packem_shared/defineQueue-TY4-i3nG.mjs";import{dispatchQueueBatch as d}from"./packem_shared/dispatchQueueBatch-L5KE6ias.mjs";import{c as C}from"./packem_shared/run-context-CoNKhXT_.mjs";import{createTopicContext as l,defineSubscription as q,defineTopic as B}from"./packem_shared/createTopicContext-jtRf138F.mjs";export{t as createQueueCaptureSink,i as createQueueContext,C as createQueueRunContext,n as createQueues,l as createTopicContext,f as defineQueue,q as defineSubscription,B as defineTopic,d as dispatchQueueBatch,c as isQueueDefinition,x as queueBindingName,m as queueDefaultName,o as shouldCaptureQueue};
1
+ import{createQueueCaptureSink as t,shouldCaptureQueue as o}from"./packem_shared/createQueueCaptureSink-CSOPftvj.mjs";import{createQueueContext as i}from"./packem_shared/createQueueContext-BelEY_4M.mjs";import{createQueues as n}from"./packem_shared/createQueues-AS-deVhl.mjs";import{defineQueue as f,isQueueDefinition as c,queueBindingName as x,queueDefaultName as m}from"./packem_shared/defineQueue-TY4-i3nG.mjs";import{dispatchQueueBatch as d}from"./packem_shared/dispatchQueueBatch-D030OrmN.mjs";import{c as C}from"./packem_shared/run-context-CoNKhXT_.mjs";import{createTopicContext as l,defineSubscription as q,defineTopic as B}from"./packem_shared/createTopicContext-jtRf138F.mjs";export{t as createQueueCaptureSink,i as createQueueContext,C as createQueueRunContext,n as createQueues,l as createTopicContext,f as defineQueue,q as defineSubscription,B as defineTopic,d as dispatchQueueBatch,c as isQueueDefinition,x as queueBindingName,m as queueDefaultName,o as shouldCaptureQueue};
@@ -0,0 +1 @@
1
+ import{c as h,u as x,f as N,a as q,d as k,o as R}from"./run-context-CoNKhXT_.mjs";import{LunoraError as $,toErrorBody as M}from"@lunora/errors";import{s as A,o as I}from"./requeue-envelope-Ylx5QErd.mjs";const b=e=>typeof e.definition.maxRetries=="number"?e.definition.maxRetries:R,O=e=>{if(e instanceof Date)return e.getTime();const t=typeof e=="number"?e:Number(e);return Number.isFinite(t)?t:0},T=async(e,t)=>{const n=await I(t,e.body).catch(()=>{});return n===void 0?{body:e.body,id:e.id,requeued:!1}:{...n,requeued:!0}},g=e=>{const t=e.LUNORA_ADMIN_TOKEN;return typeof t=="string"&&t.length>0?t:void 0},B=(e,t)=>{let n=0;return(o,i,r)=>(n+=1,e(o,i,{...r,dedupId:`${t}#${String(n)}`,messageId:t}))},L=(e,t,n,o)=>{const i=B(o,t.id);return new Proxy(e,{get:(r,s)=>s==="id"?t.id:s==="body"?t.body:s==="ack"?()=>{n.set(r,"ack"),r.ack()}:s==="retry"?f=>{n.set(r,"retry"),r.retry(f)}:s==="run"?i:Reflect.get(r,s,r)})},j=(e,t,n)=>new Proxy(e,{get:(o,i)=>i==="ackAll"?()=>{n("ack"),o.ackAll()}:i==="retryAll"?r=>{n("retry"),o.retryAll(r)}:i==="messages"?t:Reflect.get(o,i,o)}),S=async(e,t,n)=>{const o=new Map,i=e.messages,r=new Map,s=[];for(const c of i){const u=await T(c,n);r.set(c,u),s.push(L(c,u,o,t))}const l=j(e,s,c=>{for(const u of i)o.has(u)||o.set(u,c)});return{dispositions:o,originals:i,requeued:new Set,views:r,wrappedBatch:l}},m=(e,t)=>e.views.get(t)??{body:t.body,id:t.id,requeued:!1},v=(e,t)=>m(e,t).id,Q=e=>{if(e instanceof Error)return e.message;if(typeof e=="string")return e;if(e!==null&&typeof e=="object")try{return JSON.stringify(e)}catch{return"[unserializable thrown value]"}return String(e)},D=(e,t,n,o,i,r)=>{const s=o?Q(i):void 0,f=b(t),l=o?"error":"ack",c=typeof t.definition.deadLetterQueue=="string"&&t.definition.deadLetterQueue.length>0;return e.originals.map(u=>{const d=u===r,a=e.dispositions.get(u),p=d?"error":a??l,w=typeof u.attempts=="number"?u.attempts:1,y=m(e,u);return{attempts:w,body:y.body,deadLettered:c&&!d&&!e.requeued.has(u)&&p!=="ack"&&w>f,error:p==="error"?s:void 0,exportName:t.exportName,messageId:y.id,outcome:p,queue:n,timestamp:O(u.timestamp)}})},C=(e,t,n)=>{if(!t||!N(n))return;const o=q(n);if(o===void 0)return;const i=e.originals.find(r=>v(e,r)===o);if(!(i===void 0||e.dispositions.has(i)))return i},P=(e,t)=>{t.ack();for(const n of e.originals)n!==t&&!e.dispositions.has(n)&&(e.dispositions.set(n,"retry"),n.retry())},F=async(e,t,n,o,i)=>{const r=g(i),s=q(t),f=e.originals.find(d=>v(e,d)===s&&!e.dispositions.has(d)),l=`@lunora/queue: queue "${o}" (${n.exportName})`,c=n.binding===void 0?void 0:i[n.binding],u=[];for(const d of e.originals)if(!e.dispositions.has(d)){if(e.dispositions.set(d,"retry"),f!==void 0&&d!==f){d.retry();continue}u.push(d)}await Promise.all(u.map(async d=>{const a=m(e,d),p=typeof c?.send!="function"||a.requeued||r===void 0?void 0:async w=>{await c.send(await A(r,a.id,a.body),{contentType:"json",delaySeconds:w})};await k(d,{maxRetries:b(n),requeue:p,where:l})==="requeued"&&e.requeued.add(d)}))},E=async(e,t,n)=>{const o=Object.hasOwn(t,e.queue)?t[e.queue]:void 0;if(o===void 0){const a=Object.keys(t),p=a.length===0?"no push queues are declared":`known push queues: ${a.join(", ")}`;throw new $("INTERNAL",`@lunora/queue: received a batch for queue "${e.queue}" but no push handler is registered (${p}). Likely causes: the queue is declared \`pull\`, a renamed per-environment queue has no wrangler.jsonc producer with the declared binding, or codegen was not rerun.`)}const{handler:i}=o.definition;if(typeof i!="function")throw new TypeError(`@lunora/queue: queue "${e.queue}" (${o.exportName}) has no push handler — it is declared as a pull consumer`);const r=h({env:n.env,exportName:o.exportName,fetchImpl:n.fetchImpl,...n.traceparent===void 0?{}:{traceparent:n.traceparent}}),s=await S(e,r.run,g(n.env));let f=!1,l;try{await i(r,s.wrappedBatch)}catch(a){f=!0,l=a}const c=C(s,f,l);if(c!==void 0){P(s,c);const{body:a,status:p}=M(l,{redactedMessage:"internal error"});console.error(`@lunora/queue: dropped message ${v(s,c)} on queue "${e.queue}" (${o.exportName}) — a dispatch it made failed with a deterministic ${String(p)} (${a.code}: ${a.message}), so it was acked, not retried. Its retries are NOT exhausted and it is not dead-lettered — it will never be redelivered.`)}const u=f&&x(l);u&&await F(s,l,o,e.queue,n.env);const d=f&&c===void 0&&!u;if(n.capture!==void 0)try{await n.capture(D(s,o,e.queue,f,l,c))}catch(a){console.warn("@lunora/queue: capture sink failed (delivery unaffected):",a)}if(d)throw l};export{E as dispatchQueueBatch};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/queue",
3
- "version": "1.0.0-alpha.96",
3
+ "version": "1.0.0-alpha.97",
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",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
48
- "@lunora/platform": "1.0.0-alpha.57"
48
+ "@lunora/platform": "1.0.0-alpha.58"
49
49
  },
50
50
  "engines": {
51
51
  "node": "^22.15.0 || >=24.11.0"
@@ -1 +0,0 @@
1
- import{c as h,u as x,f as N,a as q,d as k,o as R}from"./run-context-CoNKhXT_.mjs";import{LunoraError as $,toErrorBody as M}from"@lunora/errors";import{s as A,o as I}from"./requeue-envelope-Ylx5QErd.mjs";const b=e=>typeof e.definition.maxRetries=="number"?e.definition.maxRetries:R,O=e=>{if(e instanceof Date)return e.getTime();const t=typeof e=="number"?e:Number(e);return Number.isFinite(t)?t:0},T=async(e,t)=>{const o=await I(t,e.body).catch(()=>{});return o===void 0?{body:e.body,id:e.id,requeued:!1}:{...o,requeued:!0}},g=e=>{const t=e.LUNORA_ADMIN_TOKEN;return typeof t=="string"&&t.length>0?t:void 0},B=(e,t)=>{let o=0;return(i,n,r)=>(o+=1,e(i,n,{...r,dedupId:`${t}#${String(o)}`,messageId:t}))},L=(e,t,o,i)=>{const n=B(i,t.id);return new Proxy(e,{get:(r,s)=>s==="id"?t.id:s==="body"?t.body:s==="ack"?()=>{o.set(r,"ack"),r.ack()}:s==="retry"?f=>{o.set(r,"retry"),r.retry(f)}:s==="run"?n:Reflect.get(r,s,r)})},S=(e,t,o)=>new Proxy(e,{get:(i,n)=>n==="ackAll"?()=>{o("ack"),i.ackAll()}:n==="retryAll"?r=>{o("retry"),i.retryAll(r)}:n==="messages"?t:Reflect.get(i,n,i)}),j=async(e,t,o)=>{const i=new Map,n=e.messages,r=new Map,s=[];for(const c of n){const u=await T(c,o);r.set(c,u),s.push(L(c,u,i,t))}const l=S(e,s,c=>{for(const u of n)i.has(u)||i.set(u,c)});return{dispositions:i,originals:n,requeued:new Set,views:r,wrappedBatch:l}},w=(e,t)=>e.views.get(t)??{body:t.body,id:t.id,requeued:!1},v=(e,t)=>w(e,t).id,Q=e=>{if(e instanceof Error)return e.message;if(typeof e=="string")return e;if(e!==null&&typeof e=="object")try{return JSON.stringify(e)}catch{return"[unserializable thrown value]"}return String(e)},D=(e,t,o,i,n,r)=>{const s=i?Q(n):void 0,f=b(t),l=i?"error":"ack",c=typeof t.definition.deadLetterQueue=="string"&&t.definition.deadLetterQueue.length>0;return e.originals.map(u=>{const d=u===r,a=e.dispositions.get(u),p=d?"error":a??l,m=typeof u.attempts=="number"?u.attempts:1,y=w(e,u);return{attempts:m,body:y.body,deadLettered:c&&!d&&!e.requeued.has(u)&&p!=="ack"&&m>f,error:p==="error"?s:void 0,exportName:t.exportName,messageId:y.id,outcome:p,queue:o,timestamp:O(u.timestamp)}})},C=(e,t,o)=>{if(!t||!N(o))return;const i=q(o);if(i===void 0)return;const n=e.originals.find(r=>v(e,r)===i);if(!(n===void 0||e.dispositions.has(n)))return n},P=(e,t)=>{t.ack();for(const o of e.originals)o!==t&&!e.dispositions.has(o)&&(e.dispositions.set(o,"retry"),o.retry())},F=async(e,t,o,i,n)=>{const r=g(n),s=q(t),f=e.originals.find(d=>v(e,d)===s&&!e.dispositions.has(d)),l=`@lunora/queue: queue "${i}" (${o.exportName})`,c=o.binding===void 0?void 0:n[o.binding],u=[];for(const d of e.originals)if(!e.dispositions.has(d)){if(e.dispositions.set(d,"retry"),f!==void 0&&d!==f){d.retry();continue}u.push(d)}await Promise.all(u.map(async d=>{const a=w(e,d),p=typeof c?.send!="function"||a.requeued||r===void 0?void 0:async m=>{await c.send(await A(r,a.id,a.body),{contentType:"json",delaySeconds:m})};await k(d,{maxRetries:b(o),requeue:p,where:l})==="requeued"&&e.requeued.add(d)}))},E=async(e,t,o)=>{const i=Object.hasOwn(t,e.queue)?t[e.queue]:void 0;if(i===void 0){const a=Object.keys(t),p=a.length===0?"no push queues are declared":`known push queues: ${a.join(", ")}`;throw new $("INTERNAL",`@lunora/queue: received a batch for queue "${e.queue}" but no push handler is registered (${p})`)}const{handler:n}=i.definition;if(typeof n!="function")throw new TypeError(`@lunora/queue: queue "${e.queue}" (${i.exportName}) has no push handler — it is declared as a pull consumer`);const r=h({env:o.env,exportName:i.exportName,fetchImpl:o.fetchImpl,...o.traceparent===void 0?{}:{traceparent:o.traceparent}}),s=await j(e,r.run,g(o.env));let f=!1,l;try{await n(r,s.wrappedBatch)}catch(a){f=!0,l=a}const c=C(s,f,l);if(c!==void 0){P(s,c);const{body:a,status:p}=M(l,{redactedMessage:"internal error"});console.error(`@lunora/queue: dropped message ${v(s,c)} on queue "${e.queue}" (${i.exportName}) — a dispatch it made failed with a deterministic ${String(p)} (${a.code}: ${a.message}), so it was acked, not retried. Its retries are NOT exhausted and it is not dead-lettered — it will never be redelivered.`)}const u=f&&x(l);u&&await F(s,l,i,e.queue,o.env);const d=f&&c===void 0&&!u;if(o.capture!==void 0)try{await o.capture(D(s,i,e.queue,f,l,c))}catch(a){console.warn("@lunora/queue: capture sink failed (delivery unaffected):",a)}if(d)throw l};export{E as dispatchQueueBatch};