@lunora/queue 1.0.0-alpha.111 → 1.0.0-alpha.113
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 +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/createQueueRunContext-DOgq-SMQ.mjs +1 -0
- package/dist/packem_shared/dispatchQueueBatch-CPejOWBR.mjs +1 -0
- package/dist/packem_shared/{run-context-9CCDiINF.mjs → run-context-D-HWDI09.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/createQueueRunContext-Bkl9aN1l.mjs +0 -1
- package/dist/packem_shared/dispatchQueueBatch-Ch0Yy8TN.mjs +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -96,6 +96,14 @@ interface LunoraQueuesOptions<Name extends string = string> {
|
|
|
96
96
|
interface QueueRunContext {
|
|
97
97
|
/** The worker `env` (bindings + vars). */
|
|
98
98
|
readonly env: Record<string, unknown>;
|
|
99
|
+
/**
|
|
100
|
+
* `ctx.live.publish` — push an event to the SSE subscribers of a
|
|
101
|
+
* `defineLiveStream` on the shard the publish names. Present only when the
|
|
102
|
+
* runtime hands the consumer a publisher (an app that declares a live stream on
|
|
103
|
+
* a target that serves one); absent otherwise. A consumer runs in the Worker, so
|
|
104
|
+
* the publish is forwarded to the shard rather than made in-process.
|
|
105
|
+
*/
|
|
106
|
+
readonly live?: QueueLivePublisher;
|
|
99
107
|
/** Queue-name-prefixed logger. */
|
|
100
108
|
readonly log: DispatchLogger;
|
|
101
109
|
/**
|
|
@@ -107,6 +115,16 @@ interface QueueRunContext {
|
|
|
107
115
|
*/
|
|
108
116
|
readonly run: DispatchRunFunction;
|
|
109
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* The publisher a queue consumer's `ctx.live` is. Structural, so the runtime
|
|
120
|
+
* builds it without importing this package. See {@link QueueRunContext.live}.
|
|
121
|
+
*/
|
|
122
|
+
interface QueueLivePublisher {
|
|
123
|
+
publish: (topic: string, payload: unknown, options: {
|
|
124
|
+
shardKey?: string;
|
|
125
|
+
stream: string;
|
|
126
|
+
}) => Promise<void>;
|
|
127
|
+
}
|
|
110
128
|
/**
|
|
111
129
|
* One delivered message as the push handler sees it: the Cloudflare `Message`
|
|
112
130
|
* plus `run` — a {@link QueueRunContext.run} pinned to THIS message.
|
|
@@ -329,6 +347,8 @@ interface DispatchOptions {
|
|
|
329
347
|
env: Record<string, unknown>;
|
|
330
348
|
/** Injectable fetch for the `ctx.run` dispatcher (tests). */
|
|
331
349
|
fetchImpl?: typeof fetch;
|
|
350
|
+
/** The runtime's shard-forwarding live publisher, handed to the handler as `ctx.live`. */
|
|
351
|
+
live?: QueueLivePublisher;
|
|
332
352
|
/**
|
|
333
353
|
* W3C `traceparent` of the consumer invocation's own trace, supplied by the
|
|
334
354
|
* runtime's `queue()` entry. Forwarded on every `ctx.run` dispatch so the
|
|
@@ -445,6 +465,8 @@ interface RunContextOptions {
|
|
|
445
465
|
env: Record<string, unknown>;
|
|
446
466
|
exportName: string;
|
|
447
467
|
fetchImpl?: typeof fetch;
|
|
468
|
+
/** The runtime's shard-forwarding publisher, surfaced as `ctx.live`. Omitted → `ctx.live` is absent. */
|
|
469
|
+
live?: QueueLivePublisher;
|
|
448
470
|
/** The consumer invocation's `traceparent`, so `ctx.run` joins the queue's trace. */
|
|
449
471
|
traceparent?: string;
|
|
450
472
|
}
|
|
@@ -470,4 +492,4 @@ declare const defineSubscription: <Payload>(topic: TopicDefinition<Payload>, con
|
|
|
470
492
|
* unbound subscription rejects every publish (see {@link unboundPublisher}).
|
|
471
493
|
*/
|
|
472
494
|
declare const createTopicContext: <Name extends string>(env: Record<string, unknown>, specs: ReadonlyArray<TopicBindingSpec<Name>>) => Topics<Name>;
|
|
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 };
|
|
495
|
+
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 QueueLivePublisher, 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
|
@@ -96,6 +96,14 @@ interface LunoraQueuesOptions<Name extends string = string> {
|
|
|
96
96
|
interface QueueRunContext {
|
|
97
97
|
/** The worker `env` (bindings + vars). */
|
|
98
98
|
readonly env: Record<string, unknown>;
|
|
99
|
+
/**
|
|
100
|
+
* `ctx.live.publish` — push an event to the SSE subscribers of a
|
|
101
|
+
* `defineLiveStream` on the shard the publish names. Present only when the
|
|
102
|
+
* runtime hands the consumer a publisher (an app that declares a live stream on
|
|
103
|
+
* a target that serves one); absent otherwise. A consumer runs in the Worker, so
|
|
104
|
+
* the publish is forwarded to the shard rather than made in-process.
|
|
105
|
+
*/
|
|
106
|
+
readonly live?: QueueLivePublisher;
|
|
99
107
|
/** Queue-name-prefixed logger. */
|
|
100
108
|
readonly log: DispatchLogger;
|
|
101
109
|
/**
|
|
@@ -107,6 +115,16 @@ interface QueueRunContext {
|
|
|
107
115
|
*/
|
|
108
116
|
readonly run: DispatchRunFunction;
|
|
109
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* The publisher a queue consumer's `ctx.live` is. Structural, so the runtime
|
|
120
|
+
* builds it without importing this package. See {@link QueueRunContext.live}.
|
|
121
|
+
*/
|
|
122
|
+
interface QueueLivePublisher {
|
|
123
|
+
publish: (topic: string, payload: unknown, options: {
|
|
124
|
+
shardKey?: string;
|
|
125
|
+
stream: string;
|
|
126
|
+
}) => Promise<void>;
|
|
127
|
+
}
|
|
110
128
|
/**
|
|
111
129
|
* One delivered message as the push handler sees it: the Cloudflare `Message`
|
|
112
130
|
* plus `run` — a {@link QueueRunContext.run} pinned to THIS message.
|
|
@@ -329,6 +347,8 @@ interface DispatchOptions {
|
|
|
329
347
|
env: Record<string, unknown>;
|
|
330
348
|
/** Injectable fetch for the `ctx.run` dispatcher (tests). */
|
|
331
349
|
fetchImpl?: typeof fetch;
|
|
350
|
+
/** The runtime's shard-forwarding live publisher, handed to the handler as `ctx.live`. */
|
|
351
|
+
live?: QueueLivePublisher;
|
|
332
352
|
/**
|
|
333
353
|
* W3C `traceparent` of the consumer invocation's own trace, supplied by the
|
|
334
354
|
* runtime's `queue()` entry. Forwarded on every `ctx.run` dispatch so the
|
|
@@ -445,6 +465,8 @@ interface RunContextOptions {
|
|
|
445
465
|
env: Record<string, unknown>;
|
|
446
466
|
exportName: string;
|
|
447
467
|
fetchImpl?: typeof fetch;
|
|
468
|
+
/** The runtime's shard-forwarding publisher, surfaced as `ctx.live`. Omitted → `ctx.live` is absent. */
|
|
469
|
+
live?: QueueLivePublisher;
|
|
448
470
|
/** The consumer invocation's `traceparent`, so `ctx.run` joins the queue's trace. */
|
|
449
471
|
traceparent?: string;
|
|
450
472
|
}
|
|
@@ -470,4 +492,4 @@ declare const defineSubscription: <Payload>(topic: TopicDefinition<Payload>, con
|
|
|
470
492
|
* unbound subscription rejects every publish (see {@link unboundPublisher}).
|
|
471
493
|
*/
|
|
472
494
|
declare const createTopicContext: <Name extends string>(env: Record<string, unknown>, specs: ReadonlyArray<TopicBindingSpec<Name>>) => Topics<Name>;
|
|
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 };
|
|
495
|
+
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 QueueLivePublisher, 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-1r-Qo49i.mjs";import{createQueues as n}from"./packem_shared/createQueues-wa7A7lnf.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-
|
|
1
|
+
import{createQueueCaptureSink as t,shouldCaptureQueue as o}from"./packem_shared/createQueueCaptureSink-CSOPftvj.mjs";import{createQueueContext as i}from"./packem_shared/createQueueContext-1r-Qo49i.mjs";import{createQueues as n}from"./packem_shared/createQueues-wa7A7lnf.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-CPejOWBR.mjs";import{c as C}from"./packem_shared/run-context-D-HWDI09.mjs";import{createTopicContext as l,defineSubscription as q,defineTopic as B}from"./packem_shared/createTopicContext-NB6VdRGc.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 o}from"./run-context-D-HWDI09.mjs";export{o as createQueueRunContext};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as h,e as x,t as N,Z as q,r as k,Q as R}from"./run-context-D-HWDI09.mjs";import{LunoraError as $,toErrorBody as M}from"@lunora/errors";import{s as A,o as I}from"./requeue-envelope-CSyxsn47.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(i,o,r)=>(n+=1,e(i,o,{...r,dedupId:`${t}#${String(n)}`,messageId:t}))},L=(e,t,n,i)=>{const o=B(i,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"?o:Reflect.get(r,s,r)})},j=(e,t,n)=>new Proxy(e,{get:(i,o)=>o==="ackAll"?()=>{n("ack"),i.ackAll()}:o==="retryAll"?r=>{n("retry"),i.retryAll(r)}:o==="messages"?t:Reflect.get(i,o,i)}),Q=async(e,t,n)=>{const i=new Map,o=e.messages,r=new Map,s=[];for(const c of o){const u=await T(c,n);r.set(c,u),s.push(L(c,u,i,t))}const l=j(e,s,c=>{for(const u of o)i.has(u)||i.set(u,c)});return{dispositions:i,originals:o,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,S=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,i,o,r)=>{const s=i?S(o):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,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 i=q(n);if(i===void 0)return;const o=e.originals.find(r=>v(e,r)===i);if(!(o===void 0||e.dispositions.has(o)))return o},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,i,o)=>{const r=g(o),s=q(t),f=e.originals.find(d=>v(e,d)===s&&!e.dispositions.has(d)),l=`@lunora/queue: queue "${i}" (${n.exportName})`,c=n.binding===void 0?void 0:o[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 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}). 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:o}=i.definition;if(typeof o!="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:n.env,exportName:i.exportName,fetchImpl:n.fetchImpl,...n.traceparent===void 0?{}:{traceparent:n.traceparent},live:n.live}),s=await Q(e,r.run,g(n.env));let f=!1,l;try{await o(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,n.env);const d=f&&c===void 0&&!u;if(n.capture!==void 0)try{await n.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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as u,isLunoraError as
|
|
1
|
+
import{LunoraError as u,isLunoraError as $}from"@lunora/errors";import{decodeWire as D,encodeArgsOrThrow as U}from"@lunora/wire";const q=e=>({debug:(t,...r)=>{console.debug(e,t,...r)},error:(t,...r)=>{console.error(e,t,...r)},info:(t,...r)=>{console.info(e,t,...r)},warn:(t,...r)=>{console.warn(e,t,...r)}}),P=9e5,b="DISPATCH_IN_PROGRESS",j="x-lunora-dispatch-declined",C=(e,t,r)=>{if(t===void 0)return{dispose:()=>{},signal:e};const a=new AbortController,o=setTimeout(()=>{a.abort(r())},t);return{dispose:()=>{clearTimeout(o)},signal:a.signal}},c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",I=e=>{let t="",r=0;const a=e.length-2;for(;r<a;r+=3){const n=e[r]<<16|e[r+1]<<8|e[r+2];t+=c.charAt(n>>18&63)+c.charAt(n>>12&63)+c.charAt(n>>6&63)+c.charAt(n&63)}const o=e.length-r;if(o===1){const n=e[r]<<16;t+=c.charAt(n>>18&63)+c.charAt(n>>12&63)}else if(o===2){const n=e[r]<<16|e[r+1]<<8;t+=c.charAt(n>>18&63)+c.charAt(n>>12&63)+c.charAt(n>>6&63)}return t};new TextDecoder;const S=new TextEncoder,N="=",J=e=>{for(let t=0;t<e.length;t+=1)if(e.charCodeAt(t)>255)return!1;return!0},k=e=>I(S.encode(JSON.stringify(e))),M=e=>!e.startsWith(N)&&J(e)?e:`${N}${I(S.encode(e))}`,K="/_lunora/scheduler/dispatch",W=3e4,G=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},T=Symbol("lunoraDispatchFailure"),R=Symbol("lunoraDispatchDecline"),E=Symbol("lunoraDispatchMessageId"),O=(e,t)=>(t!==void 0&&Object.defineProperty(e,E,{value:t}),e),H=(e,t)=>(Object.defineProperty(e,T,{value:!0}),O(e,t)),ee=e=>$(e)?e[E]:void 0,Q=(e,t,r,a)=>{const{status:o}=t;try{const n=JSON.parse(r)?.error;if(typeof n=="object"&&n!==null&&typeof n.code=="string"){const{code:l,data:g,message:d}=n,h=H(new u(l,typeof d=="string"?d:void 0,{data:g,status:o}),a);return t.headers.get(j)==="1"&&Object.defineProperty(h,R,{value:!0}),h}}catch{}return O(new u("INTERNAL",`${e}: function dispatch failed (${String(o)}): ${r}`,{status:o}),a)},B=new Set([400,403,404,422]),F=new Set([b,"DISPATCH_UNAUTHENTICATED"]),_=e=>$(e)&&e[T]===!0,te=e=>_(e)&&B.has(e.status)&&!F.has(e.code),re=e=>_(e)&&e[R]===!0,v=P/1e3,z=3,ne=async(e,t)=>{const r=t.maxRetries??z,a=typeof e.attempts=="number"&&e.attempts>r;let o="";if(a&&t.requeue!==void 0)try{return await t.requeue(v),e.ack(),"requeued"}catch(n){o=` Re-enqueueing a delayed copy failed (${n instanceof Error?n.message:String(n)}).`}return e.retry({delaySeconds:v}),a&&console.error(`${t.where}: message ${e.id} was declined (${b}) on its last delivery (attempt ${String(e.attempts)} of ${String(r+1)}) — its earlier delivery is still running the call.${o} It is retried anyway, but its retries are exhausted, so it will be dead-lettered if the queue has a deadLetterQueue and dropped if not. Raise max_retries so a slow call cannot exhaust it.`),"retried"},X=(e,t,r)=>new u("INTERNAL",`${e}: function dispatch to "${t}" timed out after ${String(r)}ms`,{status:503}),Z=e=>{const{label:t}=e,r=globalThis.fetch,a=e.fetchImpl??(typeof r=="function"?r.bind(globalThis):void 0);return async(o,n,l={})=>{if(typeof a!="function")throw new TypeError(`${t}: no fetch implementation available — pass fetchImpl or run on a platform with global fetch`);const g=()=>e.argsAlreadyEncoded===!0?n??{}:U(t,o.__lunoraRef,n??{}),d=e.env.LUNORA_ORIGIN_URL;if(typeof d!="string"||d.length===0)throw new u("INTERNAL",`${t}: \`LUNORA_ORIGIN_URL\` must be set on the Worker env so a handler can call back into Lunora functions`);const h=e.env.LUNORA_ADMIN_TOKEN;if(typeof h!="string"||h.length===0)throw new u("INTERNAL",`${t}: \`LUNORA_ADMIN_TOKEN\` must be set on the Worker env to authenticate function dispatch`);const L=`${G(d)}${K}`,y={authorization:`Bearer ${h}`,"content-type":"application/json"};e.traceparent!==void 0&&e.traceparent.length>0&&(y.traceparent=e.traceparent),e.identity?.userId!==void 0&&(y["x-lunora-userid"]=M(e.identity.userId)),e.identity?.claims!==void 0&&(y["x-lunora-identity"]=k(e.identity.claims));const m=l.timeoutMs??W,A=C(void 0,m,()=>new DOMException(`dispatch timed out after ${String(m)}ms`,"TimeoutError")),w=i=>{throw i instanceof Error&&i.name==="TimeoutError"?X(t,o.__lunoraRef,m):i};let s;try{try{s=await a(L,{body:JSON.stringify({args:g(),functionPath:o.__lunoraRef,id:l.dedupId,shardKey:l.shardKey}),headers:y,method:"POST",signal:A.signal})}catch(p){return w(p)}if(!s.ok){let p;try{p=await s.text()}catch(x){return w(x)}throw Q(t,s,p,l.messageId)}let i;try{i=await s.text()}catch(p){return w(p)}if(i.length===0)return;let f;try{f=JSON.parse(i)}catch{throw new u("INTERNAL",`${t}: function dispatch returned a non-JSON body (${String(s.status)}): ${i}`,{status:s.status})}if(f===null||typeof f!="object"||Array.isArray(f)||!("result"in f))throw new u("INTERNAL",`${t}: function dispatch returned a JSON body that is not a { result } envelope (${String(s.status)}): ${i}`,{status:s.status});return D(f.result)}finally{A.dispose()}}},oe=e=>({env:e.env,log:q(`[queue:${e.exportName}]`),run:Z({env:e.env,fetchImpl:e.fetchImpl,label:"@lunora/queue",...e.traceparent===void 0?{}:{traceparent:e.traceparent}}),...e.live===void 0?{}:{live:e.live}});export{z as Q,ee as Z,oe as c,re as e,ne as r,te as t};
|
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.113",
|
|
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.
|
|
48
|
+
"@lunora/platform": "1.0.0-alpha.68",
|
|
49
49
|
"@lunora/wire": "1.0.0-alpha.1"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as o}from"./run-context-9CCDiINF.mjs";export{o as createQueueRunContext};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as h,e as x,t as N,Z as q,r as k,Q as R}from"./run-context-9CCDiINF.mjs";import{LunoraError as $,toErrorBody as M}from"@lunora/errors";import{s as A,o as I}from"./requeue-envelope-CSyxsn47.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)}),Q=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,S=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?S(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 Q(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};
|