@stacksjs/queue 0.70.371 → 0.70.375

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.
Files changed (2) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var{defineProperty:h1,getOwnPropertyNames:iL,getOwnPropertyDescriptor:cL}=Object,dL=Object.prototype.hasOwnProperty;function nL(L){return this[L]}var sL=(L)=>{var $=(B4??=new WeakMap).get(L),V;if($)return $;if($=h1({},"__esModule",{value:!0}),L&&typeof L==="object"||typeof L==="function"){for(var Y of iL(L))if(!dL.call($,Y))h1($,Y,{get:nL.bind(L,Y),enumerable:!(V=cL(L,Y))||V.enumerable})}return B4.set(L,$),$},B4;var rL=(L)=>L;function tL(L,$){this[L]=rL.bind(null,$)}var p=(L,$)=>{for(var V in $)h1(L,V,{get:$[V],enumerable:!0,configurable:!0,set:tL.bind($,V)})};var v=(L,$)=>()=>(L&&($=L(L=0)),$);var H=import.meta.require;function y(L,$,V,Y){return{jobName:L,payload:$,options:V,envelopeVersion:1,dispatchedAt:new Date().toISOString(),...Y?{traceId:Y}:{}}}function j(L){try{return JSON.stringify(L)}catch($){throw aL(L,$)}}function _1(L){j(L)}function aL(L,$){if(eL($)){let G=null;try{G=g1(L,"",new Set)}catch{}let U=G?`\`${G.path}\` is a BigInt (${G.value}n)`:"it holds a BigInt";return Error(`[queue] Job "${L.jobName}" was not dispatched: ${U}, `+"and job envelopes travel as JSON, which cannot represent one. Convert it where you dispatch \u2014 "+"`String(value)` keeps every digit, `Number(value)` is exact below 2^53 \u2014 and convert it back inside "+"the job handler. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.",{cause:$})}if(L0($))return Error(`[queue] Job "${L.jobName}" was not dispatched: its payload contains a circular reference, which JSON cannot represent. Send the ids of the things the payload points at rather than the `+`objects themselves \u2014 that also keeps the job replayable once those objects have changed. ${"See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts."}`,{cause:$});let Y=$ instanceof Error?$.message:String($);return Error(`[queue] Job "${L.jobName}" was not dispatched: serializing its payload threw (${Y}). `+"JSON rejects only two things on its own and this was neither, so the throw came from the payload \u2014 "+"a `toJSON()` or a property getter. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.",{cause:$})}function eL(L){return L instanceof TypeError&&/bigint/i.test(L.message)}function L0(L){return L instanceof TypeError&&/circular|cyclic/i.test(L.message)}function g1(L,$,V){if(typeof L==="bigint")return{path:$||"envelope",value:L};if(!L||typeof L!=="object")return null;if(V.has(L))return null;if(V.add(L),Array.isArray(L)){for(let Y=0;Y<L.length;Y++){let G=g1(L[Y],`${$}[${Y}]`,V);if(G)return G}return null}for(let[Y,G]of Object.entries(L)){let U=g1(G,$?`${$}.${Y}`:Y,V);if(U)return U}return null}function T4(L,$){if(p1.has(L))return;p1.add(L),console.warn($)}function $0(){p1.clear()}function T1(L){let $;if(typeof L==="string")try{$=JSON.parse(L)}catch{return{ok:!1,reason:"malformed",detail:"not valid JSON"}}else if(L&&typeof L==="object")$=L;else return{ok:!1,reason:"malformed",detail:`expected string or object, got ${typeof L}`};if($.envelopeVersion===1){if(typeof $.jobName!=="string")return{ok:!1,reason:"missing-job-name"};return{ok:!0,envelope:{jobName:$.jobName,payload:$.payload,options:$.options??void 0,envelopeVersion:1,dispatchedAt:typeof $.dispatchedAt==="string"?$.dispatchedAt:new Date(0).toISOString(),...typeof $.traceId==="string"&&$.traceId?{traceId:$.traceId}:{}},source:"v1"}}if(typeof $.envelopeVersion==="number"&&$.envelopeVersion>1)return{ok:!1,reason:"unknown-version",detail:`envelopeVersion=${$.envelopeVersion}, this worker speaks v1`};if(typeof $.jobName==="string")return T4("v0-implicit","[queue/envelope] Processing pre-#1884 job envelope without envelopeVersion. In-flight jobs from before the upgrade will continue to work; new dispatches use the v1 shape automatically."),{ok:!0,envelope:{jobName:$.jobName,payload:$.payload,options:$.options??void 0,envelopeVersion:1,dispatchedAt:typeof $.dispatchedAt==="string"?$.dispatchedAt:new Date(0).toISOString(),...typeof $.traceId==="string"&&$.traceId?{traceId:$.traceId}:{}},source:"v0-implicit"};if(typeof $.job==="string")return T4("laravel-legacy","[queue/envelope] Processing Laravel-legacy job envelope (`{ job, data }` shape). "+"Will continue to process but the queue table contains migration-era rows \u2014 "+"consider flushing once they drain."),{ok:!0,envelope:{jobName:$.job.replace(/^App\\+Jobs\\+/,"").replace(/^.*\\/,""),payload:$.data,options:void 0,envelopeVersion:1,dispatchedAt:new Date(0).toISOString()},source:"laravel-legacy"};return{ok:!1,reason:"missing-job-name"}}var oL=1,p1;var a=v(()=>{p1=new Set});var i1={};p(i1,{runJob:()=>R4,restore:()=>l1,isFaked:()=>J4,getFakeQueue:()=>N4,fake:()=>u1,expectJobToFail:()=>C4,createQueueTester:()=>S4,QueueTester:()=>E1});class E4{dispatchedJobs=[];pushedJobs=[];processedJobs=[];failedJobs=[];dispatch(L,$,V={}){this.dispatchedJobs.push({name:L,data:$,options:V,dispatchedAt:new Date,queue:V.queue||"default"})}push(L,$,V={}){this.pushedJobs.push({name:L,data:$,options:V,dispatchedAt:new Date,queue:V.queue||"default"})}dispatched(L){if(L)return this.dispatchedJobs.filter(($)=>$.name===L);return[...this.dispatchedJobs]}pushed(L){if(L)return this.pushedJobs.filter(($)=>$.name===L);return[...this.pushedJobs]}assertDispatched(L,$){let V=this.dispatched(L);if(V.length===0)throw Error(`Expected job "${L}" to be dispatched, but it was not.`);if($){if(V.filter($).length===0)throw Error(`Expected job "${L}" to be dispatched matching the callback, but no matching jobs were found.`)}}assertNotDispatched(L){let $=this.dispatched(L);if($.length>0)throw Error(`Expected job "${L}" to not be dispatched, but it was dispatched ${$.length} time(s).`)}assertDispatchedTimes(L,$){let V=this.dispatched(L);if(V.length!==$)throw Error(`Expected job "${L}" to be dispatched ${$} time(s), but it was dispatched ${V.length} time(s).`)}assertNothingDispatched(){if(this.dispatchedJobs.length>0){let L=[...new Set(this.dispatchedJobs.map(($)=>$.name))].join(", ");throw Error(`Expected no jobs to be dispatched, but found: ${L}`)}}assertPushed(L,$){let V=this.pushed(L);if(V.length===0)throw Error(`Expected job "${L}" to be pushed, but it was not.`);if($){if(V.filter($).length===0)throw Error(`Expected job "${L}" to be pushed matching the callback, but no matching jobs were found.`)}}assertPushedWithDelay(L,$){if(this.pushed(L).filter((G)=>G.options.delay===$).length===0)throw Error(`Expected job "${L}" to be pushed with delay ${$}ms, but no matching jobs were found.`)}assertPushedOn(L,$){if(this.pushed($).filter((Y)=>Y.queue===L).length===0)throw Error(`Expected job "${$}" to be pushed on queue "${L}", but it was not.`)}async processJob(L,$){let V=this.dispatchedJobs.find((Y)=>Y.name===L);if(!V)throw Error(`No dispatched job found with name "${L}"`);try{await $(V.data),this.processedJobs.push(V)}catch(Y){throw this.failedJobs.push({job:V,error:Y}),Y}}processed(L){if(L)return this.processedJobs.filter(($)=>$.name===L);return[...this.processedJobs]}failed(L){if(L)return this.failedJobs.filter(($)=>$.job.name===L);return[...this.failedJobs]}reset(){this.dispatchedJobs=[],this.pushedJobs=[],this.processedJobs=[],this.failedJobs=[]}}function u1(){return Z1=new E4,Z1}function N4(){return Z1}function J4(){return Z1!==null}function l1(){Z1=null}class E1{queue;constructor(){this.queue=u1()}dispatch(L,$,V={}){return this.queue.dispatch(L,$,V),this}push(L,$,V={}){return this.queue.push(L,$,V),this}assertDispatched(L,$){return this.queue.assertDispatched(L,$),this}assertNotDispatched(L){return this.queue.assertNotDispatched(L),this}assertDispatchedTimes(L,$){return this.queue.assertDispatchedTimes(L,$),this}assertNothingDispatched(){return this.queue.assertNothingDispatched(),this}dispatched(L){return this.queue.dispatched(L)}reset(){return this.queue.reset(),this}cleanup(){l1()}}function S4(){return new E1}async function R4(L,$){return await L.handle($)}async function C4(L,$,V){try{throw await L.handle($),Error("Expected job to fail, but it succeeded")}catch(Y){if(Y.message==="Expected job to fail, but it succeeded")throw Y;if(V){let G=Y.message;if(typeof V==="string"){if(!G.includes(V))throw Error(`Expected error to contain "${V}", got "${G}"`)}else if(!V.test(G))throw Error(`Expected error to match ${V}, got "${G}"`)}return Y}}var Z1=null;var O1={};p(O1,{setQueueManager:()=>_0,getQueueManager:()=>W0,dispatchSync:()=>X0,dispatchAfter:()=>K0,dispatch:()=>U0,createRedisDispatcher:()=>Z0,chain:()=>G0,batch:()=>Y0,StacksQueueManager:()=>q4,RedisQueue:()=>e,RedisJob:()=>k4,QueueManager:()=>z0});import{Queue as V0,batch as Y0,chain as G0,dispatch as U0,dispatchAfter as K0,dispatchSync as X0,getQueueManager as W0,QueueManager as z0,setQueueManager as _0}from"@stacksjs/bun-queue";import{log as c1}from"@stacksjs/logging";class e{queue;config;isProcessing=!1;constructor(L,$){this.config=$,this.queue=new V0(L,{driver:"redis",prefix:$.prefix,redis:$.redis?{url:$.redis.url||this.buildRedisUrl($.redis)}:void 0,defaultJobOptions:$.defaultJobOptions?{delay:$.defaultJobOptions.delay,attempts:$.defaultJobOptions.attempts,backoff:$.defaultJobOptions.backoff,removeOnComplete:$.defaultJobOptions.removeOnComplete,removeOnFail:$.defaultJobOptions.removeOnFail,priority:$.defaultJobOptions.priority,lifo:$.defaultJobOptions.lifo,timeout:$.defaultJobOptions.timeout,jobId:$.defaultJobOptions.jobId,dependsOn:$.defaultJobOptions.dependsOn,keepJobs:$.defaultJobOptions.keepJobs,deadLetter:$.defaultJobOptions.deadLetter}:void 0,limiter:$.limiter,metrics:$.metrics,stalledJobCheckInterval:$.stalledJobCheckInterval,maxStalledJobRetries:$.maxStalledJobRetries,distributedLock:$.distributedLock,defaultDeadLetterOptions:$.defaultDeadLetterOptions,horizontalScaling:$.horizontalScaling,logLevel:$.logLevel}),c1.debug(`Redis queue "${L}" initialized`)}buildRedisUrl(L){let $=L.host||"localhost",V=L.port||6379,Y=L.password?`:${encodeURIComponent(L.password)}@`:"",G=Number.isFinite(L.db)&&L.db>=0?L.db:0;return`redis://${Y}${$}:${V}/${G}`}async add(L,$){let V={delay:$?.delay?$.delay*1000:void 0,attempts:$?.maxTries,priority:$?.priority,timeout:$?.timeout?$.timeout*1000:void 0,backoff:Array.isArray($?.backoff)?$.backoff.map((Y)=>(Number(Y)||1)*1000):$?.backoff};return this.queue.add(L,V)}process(L,$){if(this.isProcessing){c1.warn("Queue is already processing");return}this.isProcessing=!0,this.queue.process(L,$),c1.info(`Started processing queue with concurrency ${L}`)}async getJob(L){return this.queue.getJob(L)}async getJobs(L){return this.queue.getJobs(L)}async getJobCounts(){return this.queue.getJobCounts()}async removeJob(L){return this.queue.removeJob(L)}async pause(){return this.queue.pause()}async resume(){return this.queue.resume()}async empty(){return this.queue.empty()}async close(){return this.isProcessing=!1,this.queue.close()}async getMetrics(){return this.queue.getMetrics()}async ping(){return this.queue.ping()}async scheduleCron(L){return this.queue.scheduleCron({cronExpression:L.cron,data:L.data,timezone:L.tz,jobId:L.name})}async unscheduleCron(L){return this.queue.unscheduleCron(L)}async getDeadLetterJobs(){return this.queue.getDeadLetterJobs()}async republishDeadLetterJob(L){return this.queue.republishDeadLetterJob(L)}async clearDeadLetterQueue(){return this.queue.clearDeadLetterQueue()}async bulkRemove(L){return this.queue.bulkRemove(L)}async getClusterInfo(){return this.queue.getClusterInfo()}isLeader(){return this.queue.isLeader()}getQueue(){return this.queue}on(L,$){this.queue.events.on(L,$)}}class q4{config;queues=new Map;defaultConnection="default";constructor(L){this.config=L;for(let[$,V]of Object.entries(L))if(V.driver==="redis")this.queues.set($,new e($,V))}queue(L){let $=L||this.defaultConnection,V=this.queues.get($);if(!V){let Y=this.config[$];if(!Y)throw Error(`Queue "${$}" not configured`);V=new e($,Y),this.queues.set($,V)}return V}setDefaultConnection(L){this.defaultConnection=L}async closeAll(){let L=Array.from(this.queues.values()).map(($)=>$.close());await Promise.all(L),this.queues.clear()}}function Z0(L,$){let V=new e(L,$);return async(Y,G)=>{return V.add(Y,G)}}class k4{data;options={};queue;constructor(L,$,V){this.data=V;this.queue=new e(L,$)}async dispatch(){await this.queue.add(this.data,this.options)}async dispatchNow(){await this.queue.add(this.data,{...this.options,immediate:!0})}delay(L){return this.options.delay=L,this}afterResponse(){return this.options.afterResponse=!0,this}chain(L){return this.options.chainedJobs=L,this}onQueue(L){return this.options.queue=L,this}priority(L){return this.options.priority=L,this}tries(L){return this.options.maxTries=L,this}timeout(L){return this.options.timeout=L,this}backoff(L){return this.options.backoff=L,this}}var F1=()=>{};function B(L){let $=L,V=$?.errno;if(typeof V==="string"&&V.toUpperCase()==="42P01")return!0;if(typeof V==="number"&&V===1146)return!0;if((typeof $?.code==="string"?$.code.toUpperCase():"")==="42P01")return!0;let G=$?.message??"";return G.includes("no such table")||G.includes("doesn't exist")||G.includes("does not exist")}var m4={};p(m4,{retryDeadLetterJob:()=>f4,purgeDeadLetterJobs:()=>j4,moveToDeadLetter:()=>J1,listDeadLetterJobs:()=>w4});import{db as L1}from"@stacksjs/database";function N1(){if(y4)return;y4=!0,console.warn("[queue/dlq] dead_letter_jobs table missing \u2014 DLQ disabled. "+"Run migrations to enable poison-message isolation.")}async function J1(L,$,V=1){let Y=new Date().toISOString().slice(0,19).replace("T"," ");try{return await L1.insertInto("dead_letter_jobs").values({uuid:L.uuid??crypto.randomUUID(),connection:L.connection??"database",queue:L.queue??"default",payload:L.payload??"{}",exception:L.exception??"unknown",reason:$,total_failures:V,first_failed_at:L.failed_at??Y,last_failed_at:Y,dead_lettered_at:Y}).execute(),!0}catch(G){if(B(G))return N1(),!1;throw G}}async function w4(L={}){try{let $=L1.selectFrom("dead_letter_jobs").selectAll();if(L.queue)$=$.where("queue","=",L.queue);if(L.reason)$=$.where("reason","=",L.reason);if(L.sinceCutoffMs){let Y=new Date(L.sinceCutoffMs).toISOString().slice(0,19).replace("T"," ");$=$.where("dead_lettered_at",">=",Y)}if(L.limit&&L.limit>0)$=$.limit(L.limit);return await $.execute()??[]}catch($){if(B($))return N1(),[];throw $}}async function f4(L){try{let $=await L1.selectFrom("dead_letter_jobs").where("id","=",L).selectAll().executeTakeFirst();if(!$)return!1;let V=Math.floor(Date.now()/1000);return await L1.insertInto("jobs").values({queue:$.queue,payload:$.payload,attempts:0,reserved_at:null,available_at:V,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),await L1.deleteFrom("dead_letter_jobs").where("id","=",L).execute(),!0}catch($){if(B($))return N1(),!1;throw $}}async function j4(L=30){try{let $=new Date(Date.now()-L*24*60*60*1000).toISOString().slice(0,19).replace("T"," "),V=await L1.deleteFrom("dead_letter_jobs").where("dead_lettered_at","<",$).execute();return Number(V?.numDeletedRows??V?.[0]?.numDeletedRows??V?.affectedRows??0)}catch($){if(B($))return N1(),0;throw $}}var y4=!1;var S1=()=>{};function d1(){if(h4)return;h4=!0,console.warn("[queue/idempotency] job_idempotency table missing \u2014 idempotency keys are accepted but NOT enforced. "+"Run migrations to enable dedup.")}async function F0(L){try{let{db:$}=await import("@stacksjs/database"),V=await $.selectFrom("job_idempotency").where("idempotency_key","=",L).select(["idempotency_key"]).executeTakeFirst();return Boolean(V)}catch($){if(B($))return d1(),!1;throw $}}async function H0(L,$,V){try{let{db:Y}=await import("@stacksjs/database");await Y.insertInto("job_idempotency").values({idempotency_key:L,job_name:$,queue:V??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}catch(Y){if(B(Y)){d1();return}let G=Y?.message??"";if(G.includes("UNIQUE constraint")||G.includes("Duplicate entry"))return;throw Y}}async function n1(L,$,V){try{let{db:Y}=await import("@stacksjs/database");return await Y.insertInto("job_idempotency").values({idempotency_key:L,job_name:$,queue:V??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),"claimed"}catch(Y){if(B(Y))return d1(),"unenforced";let G=Y?.message??"";if(G.includes("UNIQUE constraint")||G.includes("Duplicate entry"))return"duplicate";throw Y}}async function s1(L){try{let{db:$}=await import("@stacksjs/database");await $.deleteFrom("job_idempotency").where("idempotency_key","=",L).execute()}catch{}}var h4=!1;var r1=()=>{};var i4={};p(i4,{unquarantineJob:()=>u4,recordFailureForPoison:()=>p4,quarantineJob:()=>b4,listQuarantined:()=>l4,isQuarantined:()=>R1,hashPayload:()=>Q1});import{createHash as Q0}from"crypto";import{db as R}from"@stacksjs/database";function H1(){if(g4)return;g4=!0,console.warn("[queue/poison] job_quarantine table missing \u2014 poison detection disabled. "+"Run migrations to enable.")}function Q1(L){let $;try{$=typeof L==="string"?L:JSON.stringify(L??null)}catch{$=String(L)}return Q0("sha256").update($).digest("hex").slice(0,32)}async function p4(L,$,V={}){let Y=V.maxFailures??5,G=V.windowMinutes??60,U=Q1($),K=new Date,z=K.toISOString().slice(0,19).replace("T"," ");try{let X=await R.selectFrom("job_quarantine").where("job_name","=",L).where("payload_hash","=",U).selectAll().executeTakeFirst();if(!X)return await R.insertInto("job_quarantine").values({job_name:L,payload_hash:U,failure_count:1,window_start:z,quarantined_at:null}).execute(),!1;if(X.quarantined_at)return!0;let W=Date.parse(X.window_start.replace(" ","T")+"Z"),_=K.getTime()-W,A=G*60*1000;if(Number.isFinite(W)&&_>A)return await R.updateTable("job_quarantine").set({failure_count:1,window_start:z}).where("id","=",X.id).execute(),!1;let x=X.failure_count+1;if(x>=Y)return await R.updateTable("job_quarantine").set({failure_count:x,quarantined_at:z}).where("id","=",X.id).execute(),!0;return await R.updateTable("job_quarantine").set({failure_count:x}).where("id","=",X.id).execute(),!1}catch(X){if(B(X))return H1(),!1;throw X}}async function R1(L,$){let V=Q1($);try{let Y=await R.selectFrom("job_quarantine").where("job_name","=",L).where("payload_hash","in",[V,"*"]).whereNotNull("quarantined_at").select(["id"]).executeTakeFirst();return Boolean(Y)}catch(Y){if(B(Y))return H1(),!1;throw Y}}async function b4(L,$){let V=$===void 0?"*":Q1($),Y=new Date().toISOString().slice(0,19).replace("T"," ");try{let G=await R.selectFrom("job_quarantine").where("job_name","=",L).where("payload_hash","=",V).select(["id"]).executeTakeFirst();if(G)await R.updateTable("job_quarantine").set({quarantined_at:Y}).where("id","=",G.id).execute();else await R.insertInto("job_quarantine").values({job_name:L,payload_hash:V,failure_count:0,window_start:Y,quarantined_at:Y}).execute()}catch(G){if(B(G)){H1();return}throw G}}async function u4(L){try{await R.deleteFrom("job_quarantine").where("job_name","=",L).execute()}catch($){if(B($)){H1();return}throw $}}async function l4(){try{return await R.selectFrom("job_quarantine").whereNotNull("quarantined_at").selectAll().execute()??[]}catch(L){if(B(L))return H1(),[];throw L}}var g4=!1;var C1=()=>{};function c(L){let $=L?.numUpdatedRows;if($===null||$===void 0)return 0;if(typeof $==="object")return Number($.changes??0);return Number($)}function A0(L,$){let V=y(L,$.payload||{},{queue:$.queue,tries:$.maxTries,timeout:$.timeout,backoff:Array.isArray($.backoff)?$.backoff:void 0});return{queue:$.queue||"default",payload:j(V),attempts:0,available_at:D0($.delay||0),created_at:new Date().toISOString().slice(0,19).replace("T"," ")}}async function t1(L,$){let V=A0(L,$),{db:Y}=await import("@stacksjs/database");await Y.insertInto("jobs").values(V).execute()}function D0(L){let $=Date.now();return Math.floor($/1000+L)}var q1=v(()=>{a()});var b={};p(b,{withEvents:()=>d4,onQueueEvent:()=>c4,getWorkerTracker:()=>A1,getQueueEvents:()=>$1,getGlobalMetrics:()=>v1,emitQueueEvent:()=>d,QueueMetrics:()=>I1,QueueEvents:()=>k1,OnQueueEvent:()=>n4});import{log as m}from"@stacksjs/logging";class k1{handlers=new Map;wildcardHandlers=new Set;listenerSubscriptions=new WeakMap;reclaim=new FinalizationRegistry((L)=>{L()});on(L,$){if(!this.handlers.has(L))this.handlers.set(L,new Set);return this.handlers.get(L).add($),()=>{this.handlers.get(L)?.delete($)}}subscribeListener(L,$,V){let Y=new WeakRef(L),G=(X)=>{let W=Y.deref();if(W===void 0){U();return}return V.call(W,X)},U=this.on($,G),K=()=>{U();let X=Y.deref();if(X===void 0)return;let W=this.listenerSubscriptions.get(X);if(!W)return;if(W.delete(K),W.size===0)this.listenerSubscriptions.delete(X),this.reclaim.unregister(X)},z=this.listenerSubscriptions.get(L);if(!z)z=new Set,this.listenerSubscriptions.set(L,z);return z.add(K),this.reclaim.register(L,U,L),K}unsubscribeListener(L){let $=this.listenerSubscriptions.get(L);if(!$)return 0;let V=Array.from($);for(let Y of V)Y();return this.listenerSubscriptions.delete(L),this.reclaim.unregister(L),V.length}listenerCount(L){if(L==="*")return this.wildcardHandlers.size;return this.handlers.get(L)?.size??0}onAny(L){return this.wildcardHandlers.add(L),()=>{this.wildcardHandlers.delete(L)}}once(L,$){let V=async(Y)=>{this.handlers.get(L)?.delete(V),await $(Y)};return this.on(L,V)}async emit(L,$){let V={...$,timestamp:Date.now()};this.logEvent(L,V);let Y=this.handlers.get(L);if(Y)for(let G of Y)try{await G(V)}catch(U){m.error(`Error in queue event handler for ${L}:`,U)}for(let G of this.wildcardHandlers)try{await G(L,V)}catch(U){m.error("Error in wildcard queue event handler:",U)}}logEvent(L,$){let V=$.jobId?`[${$.jobId}]`:"",Y=$.queueName?`on ${$.queueName}`:"";switch(L){case"job:added":m.debug(`Job added ${V} ${Y}`);break;case"job:processing":m.debug(`Job processing ${V} ${Y}`);break;case"job:completed":m.info(`Job completed ${V} ${Y} in ${$.duration}ms`);break;case"job:failed":m.error(`Job failed ${V} ${Y}:`,$.error);break;case"job:retrying":m.warn(`Job retrying ${V} ${Y} (attempt ${$.attemptsMade})`);break;case"job:stalled":m.warn(`Job stalled ${V} ${Y}`);break;case"queue:error":m.error(`Queue error ${Y}:`,$.error);break}}off(L){this.handlers.delete(L)}removeAllListeners(){this.handlers.clear(),this.wildcardHandlers.clear(),this.listenerSubscriptions=new WeakMap}}function $1(){if(!o1)o1=new k1;return o1}function c4(L,$){let V=$1();if(L==="*")return V.onAny($);return V.on(L,$)}function d(L,$){return $1().emit(L,$)}function d4(L,$){return async(...V)=>{let Y=V[0]?.id||"unknown",G=Date.now();await d("job:processing",{jobId:Y,queueName:L,data:V[0]?.data});try{let U=await $(...V);return await d("job:completed",{jobId:Y,queueName:L,result:U,duration:Date.now()-G}),U}catch(U){throw await d("job:failed",{jobId:Y,queueName:L,error:U,duration:Date.now()-G}),U}}}function n4(L){return function($,V){if(typeof V!=="object"||V===null)throw TypeError(`@OnQueueEvent('${L}') requires standard (TC39) decorators. Legacy decorators ('experimentalDecorators: true') give a method decorator no construction-time hook, so the handler cannot be bound to an instance.`);if(V.kind!=="method")throw TypeError(`@OnQueueEvent('${L}') can only decorate a class method, but it was applied to a ${V.kind}${V.name===void 0?"":` ('${String(V.name)}')`}. Move the handler into a method, or call onQueueEvent('${L}', handler) directly.`);if(V.static)throw TypeError(`@OnQueueEvent('${L}') cannot decorate the static method '${String(V.name)}': a static method has no instance, so it would subscribe at class-definition time and stay subscribed for the life of the process, even if the class is never used. Use an instance method, or subscribe explicitly with onQueueEvent('${L}', MyClass.${String(V.name)}).`);return V.addInitializer(function(){$1().subscribeListener(this,L,$)}),$}}class I1{jobCounts={added:0,completed:0,failed:0,processing:0};completions=[];errors=[];unsubscribe=[];constructor(){this.setupListeners()}setupListeners(){let L=$1();this.unsubscribe.push(L.on("job:added",()=>{this.jobCounts.added++}),L.on("job:processing",()=>{this.jobCounts.processing++}),L.on("job:completed",($)=>{this.jobCounts.completed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1);let V=$.duration||0;if(this.completions.push({timestamp:Date.now(),duration:V}),this.completions.length>1000)this.completions.shift()}),L.on("job:failed",($)=>{if(this.jobCounts.failed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1),$.error){if(this.errors.push({error:$.error,timestamp:Date.now()}),this.errors.length>100)this.errors.shift()}}))}getThroughputPerMinute(){let L=Date.now()-60000;return this.completions.filter((V)=>V.timestamp>=L).length}getAverageProcessingTime(){let L=Date.now()-60000,$=this.completions.filter((V)=>V.timestamp>=L);if($.length===0)return 0;return $.reduce((V,Y)=>V+Y.duration,0)/$.length}getMetrics(){return{counts:{...this.jobCounts},averageDuration:this.getAverageProcessingTime(),recentErrors:[...this.errors],throughputPerMinute:this.getThroughputPerMinute()}}reset(){this.jobCounts={added:0,completed:0,failed:0,processing:0},this.completions=[],this.errors=[]}stop(){this.unsubscribe.forEach((L)=>L()),this.unsubscribe=[]}}function v1(){if(!a1)a1=new I1;return a1}class s4{workers=new Map;register(L,$){this.workers.set(L,{id:L,status:"idle",queue:$,processedCount:0,failedCount:0,lastActivityAt:new Date().toISOString(),startedAt:new Date().toISOString()})}markActive(L){let $=this.workers.get(L);if($)$.status="active",$.lastActivityAt=new Date().toISOString()}markIdle(L){let $=this.workers.get(L);if($)$.status="idle",$.lastActivityAt=new Date().toISOString()}recordCompletion(L){let $=this.workers.get(L);if($)$.processedCount++,$.lastActivityAt=new Date().toISOString()}recordFailure(L){let $=this.workers.get(L);if($)$.failedCount++,$.lastActivityAt=new Date().toISOString()}unregister(L){let $=this.workers.get(L);if($)$.status="stopped"}getAll(){return Array.from(this.workers.values())}clear(){this.workers.clear()}}function A1(){return x0}var o1=null,a1=null,x0;var k=v(()=>{x0=new s4});var l={};p(l,{recordBatchJobFailure:()=>U4,recordBatchJobCompletion:()=>G4,isBatchCancelled:()=>UL,getBatchCallbacks:()=>D1,batchRecordToHash:()=>$L,batchRecordFromHash:()=>Y4,batchCounterIncrements:()=>r4,PendingBatch:()=>w1,DispatchedBatch:()=>u,Batch:()=>e1});var{RedisClient:P0}=globalThis.Bun;import{log as T}from"@stacksjs/logging";import{env as M0}from"@stacksjs/env";function h(){return M0.QUEUE_DRIVER||"sync"}class w1{jobs;options={thenCallbacks:[],catchCallbacks:[],finallyCallbacks:[],progressCallbacks:[]};constructor(L){this.jobs=L.map(($)=>("job"in $)?$:{job:$})}name(L){return this.options.name=L,this}onQueue(L){return this.options.queue=L,this}allowFailures(){return this.options.allowFailures=!0,this}then(L){return this.options.thenCallbacks.push(L),this}catch(L){return this.options.catchCallbacks.push(L),this}finally(L){return this.options.finallyCallbacks.push(L),this}progress(L){return this.options.progressCallbacks.push(L),this}thenHandler(L){return this.options.thenHandler=L,this}catchHandler(L){return this.options.catchHandler=L,this}finallyHandler(L){return this.options.finallyHandler=L,this}async dispatch(){let L=crypto.randomUUID(),$=this.jobs.length;if($===0)throw Error("Cannot dispatch an empty batch");let V=h();await T0({id:L,name:this.options.name||"",total_jobs:$,pending_jobs:$,failed_jobs:0,failed_job_ids:"[]",options:JSON.stringify({queue:this.options.queue,allowFailures:this.options.allowFailures||!1}),cancelled_at:null,created_at:new Date().toISOString().slice(0,19).replace("T"," "),finished_at:null,then_handler:this.options.thenHandler?JSON.stringify(this.options.thenHandler):null,catch_handler:this.options.catchHandler?JSON.stringify(this.options.catchHandler):null,finally_handler:this.options.finallyHandler?JSON.stringify(this.options.finallyHandler):null}),B0(L,this.options);try{let{emitQueueEvent:Y}=await Promise.resolve().then(() => (k(),b));await Y("batch:added",{jobId:L,data:{name:this.options.name,totalJobs:$}})}catch{}for(let Y=0;Y<this.jobs.length;Y++){let G=this.jobs[Y];if(!G)continue;let{job:U,payload:K}=G,z={...K,_batchId:L,_batchIndex:Y};if(this.options.queue&&!U.queue)U.queue=this.options.queue;if(V==="sync")try{await U.dispatchNow(z),await G4(L)}catch(X){await U4(L,`${L}:${Y}`,X)}else await U.dispatch(z)}return T.info(`[Batch] Dispatched batch "${this.options.name||L}" with ${$} jobs`),new u(L)}getJobs(){return[...this.jobs]}getOptions(){return this.options}}class u{id;constructor(L){this.id=L}async fresh(){return n(this.id)}async getName(){return(await this.fresh())?.name||""}async totalJobs(){return(await this.fresh())?.total_jobs||0}async pendingJobs(){return(await this.fresh())?.pending_jobs||0}async failedJobs(){return(await this.fresh())?.failed_jobs||0}async completedJobs(){let L=await this.fresh();if(!L)return 0;return L.total_jobs-L.pending_jobs}async progress(){let L=await this.fresh();if(!L||L.total_jobs===0)return 0;let $=L.total_jobs-L.pending_jobs;return Math.round($/L.total_jobs*100)}async finished(){return(await this.fresh())?.finished_at!==null}async cancelled(){return(await this.fresh())?.cancelled_at!==null}async hasFailures(){return((await this.fresh())?.failed_jobs||0)>0}async failedJobIds(){let L=await this.fresh();if(!L)return[];try{return JSON.parse(L.failed_job_ids||"[]")}catch{return[]}}async cancel(){if(h()==="redis")await y0(this.id);else await q0(this.id);T.info(`[Batch] Cancelled batch ${this.id}`);let $=D1(this.id);if($)for(let V of $.finallyCallbacks)try{await V(this)}catch(Y){T.error(`[Batch] Error in finally callback for batch ${this.id}:`,Y)}}async add(L){let $=await this.fresh();if(!$)throw Error(`Batch ${this.id} not found`);if($.cancelled_at)throw Error(`Batch ${this.id} has been cancelled`);if($.finished_at)throw Error(`Batch ${this.id} has already finished`);let V=L.map((U)=>("job"in U)?U:{job:U});if(!await J0(this.id,V.length))throw Error(`Batch ${this.id} was cancelled, finished or deleted before the jobs could be added`);let G=JSON.parse($.options||"{}");for(let U=0;U<V.length;U++){let K=V[U];if(!K)continue;let{job:z,payload:X}=K,W={...X,_batchId:this.id,_batchIndex:$.total_jobs+U};if(G.queue&&!z.queue)z.queue=G.queue;await z.dispatch(W)}T.info(`[Batch] Added ${V.length} jobs to batch ${this.id}`)}async delete(){await S0(this.id),y1(this.id)}}class e1{static create(L){return new w1(L)}static async find(L){if(!await n(L))return null;return new u(L)}static async all(){return(await E0()).map(($)=>new u($.id))}static async prune(L=24){return R0(L)}}function B0(L,$){L4.set(L,$)}function D1(L){return L4.get(L)}function y1(L){L4.delete(L)}async function T0(L){if(h()==="redis")await I0(L);else await t4(L)}async function n(L){if(h()==="redis")return v0(L);return o4(L)}async function E0(){if(h()==="redis")return VL();return a4()}async function N0(L,$){if(h()==="redis")await YL(L,$);else await $4(L,$)}async function J0(L,$){if($===0)return!0;if(h()==="redis")try{let U=await U1(),K=`${G1}${L}`;return await U.hincrby(K,"total_jobs",$),await U.hincrby(K,"pending_jobs",$),U.close(),!0}catch{}let{db:V,sql:Y}=await import("@stacksjs/database"),G=await V.updateTable("job_batches").set(r4(Y,$)).where("id","=",L).whereNull("cancelled_at").whereNull("finished_at").executeTakeFirst();return c(G)>0}function r4(L,$){return{total_jobs:L`total_jobs + ${$}`,pending_jobs:L`pending_jobs + ${$}`}}async function S0(L){if(h()==="redis")await GL(L);else await e4(L)}async function R0(L){if(h()==="redis")return w0(L);return LL(L)}function C0(L){return!!(L.then_handler||L.catch_handler||L.finally_handler)}async function t4(L){let{db:$}=await import("@stacksjs/database"),V={id:L.id,name:L.name,total_jobs:L.total_jobs,pending_jobs:L.pending_jobs,failed_jobs:L.failed_jobs,failed_job_ids:L.failed_job_ids,options:L.options,cancelled_at:L.cancelled_at,created_at:L.created_at,finished_at:L.finished_at};if(!C0(L)){await $.insertInto("job_batches").values(V).execute();return}try{await $.insertInto("job_batches").values({...V,then_handler:L.then_handler??null,catch_handler:L.catch_handler??null,finally_handler:L.finally_handler??null}).execute()}catch(Y){T.warn(`[Batch] Could not persist terminal handlers for batch ${L.id}: ${Y?.message}. The job_batches table predates the then_handler/catch_handler/finally_handler columns; recreate it to enable handlers that survive a worker restart.`),await $.insertInto("job_batches").values(V).execute()}}async function o4(L){let{db:$}=await import("@stacksjs/database");return await $.selectFrom("job_batches").where("id","=",L).selectAll().executeTakeFirst()||null}async function a4(){let{db:L}=await import("@stacksjs/database");return await L.selectFrom("job_batches").selectAll().orderBy("created_at","desc").execute()}async function $4(L,$){let{db:V}=await import("@stacksjs/database");await V.updateTable("job_batches").set($).where("id","=",L).execute()}async function e4(L){let{db:$}=await import("@stacksjs/database");await $.deleteFrom("job_batches").where("id","=",L).execute()}async function q0(L){await $4(L,{cancelled_at:new Date().toISOString().slice(0,19).replace("T"," "),finished_at:new Date().toISOString().slice(0,19).replace("T"," ")})}async function LL(L){let{db:$}=await import("@stacksjs/database"),V=new Date(Date.now()-L*60*60*1000).toISOString().slice(0,19).replace("T"," "),Y=await $.deleteFrom("job_batches").whereNotNull("finished_at").where("finished_at","<",V).executeTakeFirst();return Number(Y?.numDeletedRows??0)}async function k0(){let{queue:L}=await import("@stacksjs/config"),$=L?.connections?.redis?.redis;if($?.url)return $.url;let V=$?.password?`:${encodeURIComponent($.password)}@`:"",Y=$?.db?`/${$.db}`:"";return`redis://${V}${$?.host||"localhost"}:${$?.port||6379}${Y}`}async function U1(){let L=new P0(await k0());return await L.connect(),L}function $L(L){return{id:L.id,name:L.name,total_jobs:String(L.total_jobs),pending_jobs:String(L.pending_jobs),failed_jobs:String(L.failed_jobs),failed_job_ids:L.failed_job_ids,options:L.options,cancelled_at:L.cancelled_at||"",created_at:L.created_at,finished_at:L.finished_at||"",then_handler:L.then_handler||"",catch_handler:L.catch_handler||"",finally_handler:L.finally_handler||""}}async function I0(L){try{let $=await U1();await $.hset(`${G1}${L.id}`,$L(L)),await $.sadd(V4,L.id),$.close()}catch{await t4(L)}}function Y4(L){if(!L?.id)return null;return{id:L.id,name:L.name??"",total_jobs:Number(L.total_jobs),pending_jobs:Number(L.pending_jobs),failed_jobs:Number(L.failed_jobs),failed_job_ids:L.failed_job_ids??"",options:L.options??"",cancelled_at:L.cancelled_at||null,created_at:L.created_at??"",finished_at:L.finished_at||null,then_handler:L.then_handler||null,catch_handler:L.catch_handler||null,finally_handler:L.finally_handler||null}}async function v0(L){try{let $=await U1(),V=`${G1}${L}`,Y=await $.hgetall(V);return $.close(),Y4(Y)}catch{return o4(L)}}async function VL(){try{let L=await U1(),$=await L.smembers(V4),V=[];for(let Y of $){let G=Y4(await L.hgetall(`${G1}${Y}`));if(G)V.push(G)}return L.close(),V}catch{return a4()}}async function YL(L,$){try{let V=await U1(),Y=`${G1}${L}`,G={};for(let[U,K]of Object.entries($))G[U]=K===null?"":String(K);await V.hset(Y,G),V.close()}catch{await $4(L,$)}}async function GL(L){try{let $=await U1();await $.del(`${G1}${L}`),await $.srem(V4,L),$.close()}catch{await e4(L)}}async function y0(L){let $=new Date().toISOString().slice(0,19).replace("T"," ");await YL(L,{cancelled_at:$,finished_at:$})}async function w0(L){try{let $=await VL(),V=Date.now()-L*60*60*1000,Y=0;for(let G of $)if(G.finished_at){if(new Date(G.finished_at).getTime()<V)await GL(G.id),Y++}return Y}catch{return LL(L)}}function V1(L){if(!L)return null;try{let $=JSON.parse(L);if($&&($.kind==="job"||$.kind==="module"))return $;return T.warn(`[Batch] handler JSON has unknown kind '${$?.kind}' \u2014 skipping`),null}catch($){return T.warn(`[Batch] failed to parse persistent handler: ${$.message}`),null}}async function Y1(L,$){try{if(L.kind==="job"){let{Jobs:G}=await Promise.resolve().then(() => (x1(),f1));await G.dispatch(L.name,{...L.payload??{},_batchId:$});return}let V=await import(L.module).catch((G)=>{return T.warn(`[Batch] persistent handler module not found: ${L.module} (${G.message})`),null});if(!V)return;let Y=V[L.export];if(typeof Y!=="function"){T.warn(`[Batch] persistent handler export '${L.export}' is not a function on ${L.module}`);return}await Y(L.payload,$)}catch(V){T.error(`[Batch] persistent handler threw for batch ${$}:`,V)}}async function G4(L){let{db:$,sql:V}=await import("@stacksjs/database");await $.updateTable("job_batches").set({pending_jobs:V`GREATEST(pending_jobs - 1, 0)`}).where("id","=",L).where("pending_jobs",">",0).execute();let Y=new Date().toISOString().slice(0,19).replace("T"," "),G=await $.updateTable("job_batches").set({finished_at:Y}).where("id","=",L).where("pending_jobs","=",0).whereNull("finished_at").executeTakeFirst(),U=c(G)>0,K=D1(L),z=new u(L);if(K)for(let X of K.progressCallbacks)try{await X(z)}catch(W){T.error(`[Batch] Error in progress callback for batch ${L}:`,W)}if(U){try{let{emitQueueEvent:O}=await Promise.resolve().then(() => (k(),b));await O("batch:completed",{jobId:L})}catch{}let X=await n(L);if(!X){y1(L),T.info(`[Batch] Batch ${L} finished (record vanished)`);return}let W=JSON.parse(X.options||"{}"),A=!((X.failed_jobs||0)>0)||W.allowFailures;if(K){if(A)for(let O of K.thenCallbacks)try{await O(z)}catch(Z){T.error(`[Batch] Error in then callback for batch ${L}:`,Z)}for(let O of K.finallyCallbacks)try{await O(z)}catch(Z){T.error(`[Batch] Error in finally callback for batch ${L}:`,Z)}y1(L)}if(A){let O=V1(X.then_handler);if(O)await Y1(O,L)}else{let O=V1(X.catch_handler);if(O)await Y1(O,L)}let x=V1(X.finally_handler);if(x)await Y1(x,L);T.info(`[Batch] Batch ${L} finished`)}}async function U4(L,$,V){let{db:Y,sql:G}=await import("@stacksjs/database"),U=await n(L);if(!U)return;let K=JSON.parse(U.options||"{}");await Y.updateTable("job_batches").set({pending_jobs:G`GREATEST(pending_jobs - 1, 0)`,failed_jobs:G`failed_jobs + 1`}).where("id","=",L).where("pending_jobs",">",0).execute();try{let Z=await n(L);if(Z){let F=[];try{F=JSON.parse(Z.failed_job_ids||"[]")}catch{F=[]}F.push($),await N0(L,{failed_job_ids:JSON.stringify(F)})}}catch{}let z=new u(L),X=D1(L);if(X)for(let Z of X.catchCallbacks)try{await Z(z,V)}catch(F){T.error(`[Batch] Error in catch callback for batch ${L}:`,F)}try{let{emitQueueEvent:Z}=await Promise.resolve().then(() => (k(),b));await Z("batch:failed",{jobId:L,error:V})}catch{}let W=new Date().toISOString().slice(0,19).replace("T"," "),_=Y.updateTable("job_batches").set(K.allowFailures?{finished_at:W}:{finished_at:W,cancelled_at:W}).where("id","=",L).whereNull("finished_at");if(K.allowFailures)_=_.where("pending_jobs","=",0);let A=await _.executeTakeFirst();if(c(A)===0)return;let x=!!K.allowFailures;if(X){if(x)for(let Z of X.thenCallbacks)try{await Z(z)}catch(F){T.error(`[Batch] Error in then callback for batch ${L}:`,F)}for(let Z of X.finallyCallbacks)try{await Z(z)}catch(F){T.error(`[Batch] Error in finally callback for batch ${L}:`,F)}y1(L)}let O=await n(L);if(O){if(x){let F=V1(O.then_handler);if(F)await Y1(F,L)}else{let F=V1(O.catch_handler);if(F)await Y1(F,L)}let Z=V1(O.finally_handler);if(Z)await Y1(Z,L)}T.info(`[Batch] Batch ${L} finished with failure(s)`)}async function UL(L){return(await n(L))?.cancelled_at!==null}var L4,G1="stacks:batch:",V4="stacks:batches";var g=v(()=>{q1();L4=new Map});var f1={};p(f1,{runJob:()=>j1,resolveJobFile:()=>ZL,jobBatch:()=>_L,job:()=>WL,Jobs:()=>zL});import{appPath as f0,frameworkPath as j0}from"@stacksjs/path";import{env as m0}from"@stacksjs/env";import{enqueueAfterCommit as h0,isInTransaction as g0}from"@stacksjs/database";function p0(){return m0.QUEUE_DRIVER||"sync"}function b0(){if(KL)return;KL=!0,console.warn("[queue] .afterCommit() was called outside of `db.transaction(...)`. Dispatching immediately. Wrap the call in a transaction or drop .afterCommit() to silence this message.")}class w{name;payload;options={};txMode="auto";constructor(L,$){this.name=L;this.payload=$}onQueue(L){return this.options.queue=L,this}delay(L){return this.options.delay=L,this}tries(L){return this.options.tries=L,this}timeout(L){return this.options.timeout=L,this}backoff(L){return this.options.backoff=L,this}withContext(L){return this.options.context=L,this}withIdempotencyKey(L){return this.options.idempotencyKey=L,this}afterCommit(){return this.txMode="after",this}withoutCommit(){return this.txMode="immediate",this}async dispatch(){let{isFaked:L,getFakeQueue:$}=await Promise.resolve().then(() => i1);if(L()){$()?.dispatch(this.name,this.payload,this.options);return}if(this.txMode!=="immediate"){if(g0()){let Y=this.runDispatchPipeline.bind(this);if(h0(async()=>{await Y()}))return}else if(this.txMode==="after")b0()}await this.runDispatchPipeline()}async runDispatchPipeline(){let L=!1;if(this.options.idempotencyKey){let $=await n1(this.options.idempotencyKey,this.name,this.options.queue);if($==="duplicate")return;L=$==="claimed"}try{if(await R1(this.name,this.payload)){let V=y(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});if(await J1({queue:this.options.queue||"default",payload:j(V),exception:`quarantined: ${this.name}`},"poison-detected"))return}let $=p0();if($==="database")await this.dispatchToDatabase();else if($==="redis")await this.dispatchToRedis();else if($==="sync")await j1(this.name,{payload:this.payload,context:this.options.context,traceId:await XL()});else if($==="sqs"||$==="memory"||$==="beanstalkd")throw Error(`[queue] Driver "${$}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);else throw Error(`[queue] Unknown QUEUE_DRIVER "${$}". Allowed values: redis, database, sync.`)}catch($){if(L&&this.options.idempotencyKey)await s1(this.options.idempotencyKey);throw $}}async dispatchIf(L){if(L)await this.dispatch()}async dispatchUnless(L){if(!L)await this.dispatch()}async dispatchToDatabase(){let L=Math.floor(Date.now()/1000),$=this.options.delay?L+this.options.delay:L,V=y(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff},await XL()),Y=j(V),{db:G}=await import("@stacksjs/database");await G.insertInto("jobs").values({queue:this.options.queue||"default",payload:Y,attempts:0,reserved_at:null,available_at:$,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(){let L=y(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});_1(L);let{RedisQueue:$}=await Promise.resolve().then(() => (F1(),O1)),{queue:V}=await import("@stacksjs/config"),Y=V?.connections?.redis;if(!Y)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new $(this.options.queue||"default",Y).add(L,{delay:this.options.delay,maxTries:this.options.tries,timeout:this.options.timeout,backoff:this.options.backoff})}async dispatchNow(){await j1(this.name,{payload:this.payload,context:this.options.context})}}function WL(L,$){return new w(L,$)}function _L(L){let{PendingBatch:$}=(g(),sL(l));return new $(L)}async function ZL(L){let $=[f0(`Jobs/${L}.ts`),j0(`defaults/app/Jobs/${L}.ts`)];try{let V=import.meta.resolve("@stacksjs/defaults/package.json"),Y=new URL(".",V).pathname;$.push(`${Y}app/Jobs/${L}.ts`)}catch{}for(let V of $)if(await Bun.file(V).exists())return V;return null}async function j1(L,$={}){let{withTraceId:V}=await import("@stacksjs/router"),Y=$.traceId??`job:${L}:${Math.random().toString(36).slice(2,10)}`;await V(Y,async()=>{let G=await ZL(L);if(!G)throw Error(`Job ${L} not found. Looked in app/Jobs/${L}.ts and the framework defaults (storage/framework/defaults/app/Jobs, @stacksjs/defaults).`);let K=(await import(G)).default;if(!K)throw Error(`Job ${L} does not export a default`);if(typeof K.handle==="function")await K.handle($.payload);else if(typeof K.action==="string"){let{runAction:z}=await import("@stacksjs/actions");await z(K.action)}else if(typeof K.action==="function")await K.action();else if(typeof K==="function")await K($.payload,$.context);else throw Error(`Job ${L} does not have a valid handler`)})}async function XL(){try{let{getTraceId:L}=await import("@stacksjs/router");return L()}catch{return}}var KL=!1,zL;var x1=v(()=>{S1();a();r1();C1();zL={make(L,$){return new w(L,$)},async dispatch(L,$){await new w(L,$).dispatch()},async dispatchIf(L,$,V){if(L)await new w($,V).dispatch()},async dispatchUnless(L,$,V){if(!L)await new w($,V).dispatch()},async dispatchNow(L,$){await new w(L,$).dispatchNow()},dispatchAfter(L,$,V){return new w($,V).delay(L)},async dispatchOnce(L,$,V){await new w($,V).withIdempotencyKey(L).dispatch()},async dispatchAfterCommit(L,$){await new w(L,$).afterCommit().dispatch()}}});var m1={};p(m1,{resumeQueue:()=>DL,recordCircuitSuccess:()=>HL,recordCircuitFailure:()=>QL,pauseQueue:()=>AL,listCircuitState:()=>xL,isCircuitOpen:()=>FL});import{db as S}from"@stacksjs/database";function s(){if(OL)return;OL=!0,console.warn("[queue/circuit-breaker] queue_circuit_state table missing \u2014 circuit breaker disabled. "+"Run migrations to enable.")}async function X4(L,$){try{let V=await S.selectFrom("queue_circuit_state").where("queue_name","=",L).selectAll().executeTakeFirst();if(V)return V;return await S.insertInto("queue_circuit_state").values({queue_name:L,success_count:0,failure_count:0,window_start:$,paused_at:null,resume_at:null}).execute(),{queue_name:L,success_count:0,failure_count:0,window_start:$,paused_at:null,resume_at:null}}catch(V){if(B(V))return s(),null;let Y=V?.message??"";if(Y.includes("UNIQUE constraint")||Y.includes("Duplicate entry"))return await S.selectFrom("queue_circuit_state").where("queue_name","=",L).selectAll().executeTakeFirst();throw V}}async function FL(L){try{let $=await S.selectFrom("queue_circuit_state").where("queue_name","=",L).select(["paused_at","resume_at"]).executeTakeFirst();if(!$||!$.paused_at)return!1;if($.resume_at){let V=Date.parse($.resume_at.replace(" ","T")+"Z");if(Number.isFinite(V)&&Date.now()>=V)return await S.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",L).execute(),!1}return!0}catch($){if(B($))return s(),!1;throw $}}async function HL(L,$={}){let V=$.windowSeconds??300,Y=new Date().toISOString().slice(0,19).replace("T"," "),G=await X4(L,Y);if(!G)return;try{if(K4(G.window_start,V)){await S.updateTable("queue_circuit_state").set({success_count:1,failure_count:0,window_start:Y}).where("queue_name","=",L).execute();return}await S.updateTable("queue_circuit_state").set({success_count:G.success_count+1}).where("queue_name","=",L).execute()}catch(U){if(B(U)){s();return}throw U}}async function QL(L,$={}){let V=$.failureRateThreshold??0.5,Y=$.windowSeconds??300,G=$.pauseSeconds??300,U=$.minObservations??10,K=new Date,z=K.toISOString().slice(0,19).replace("T"," "),X=await X4(L,z);if(!X)return!1;if(X.paused_at)return!1;try{let{success_count:W,failure_count:_}=X;if(K4(X.window_start,Y))W=0,_=0;_+=1;let A=W+_,x=A===0?0:_/A;if(A>=U&&x>=V){let Z=new Date(K.getTime()+G*1000).toISOString().slice(0,19).replace("T"," ");return await S.updateTable("queue_circuit_state").set({success_count:W,failure_count:_,window_start:z,paused_at:z,resume_at:Z}).where("queue_name","=",L).execute(),!0}return await S.updateTable("queue_circuit_state").set({success_count:W,failure_count:_,window_start:K4(X.window_start,Y)?z:X.window_start}).where("queue_name","=",L).execute(),!1}catch(W){if(B(W))return s(),!1;throw W}}function K4(L,$){if(!L)return!0;let V=Date.parse(L.replace(" ","T")+"Z");if(!Number.isFinite(V))return!0;return Date.now()-V>$*1000}async function AL(L,$=300){let V=new Date,Y=V.toISOString().slice(0,19).replace("T"," "),G=new Date(V.getTime()+$*1000).toISOString().slice(0,19).replace("T"," ");await X4(L,Y);try{await S.updateTable("queue_circuit_state").set({paused_at:Y,resume_at:G}).where("queue_name","=",L).execute()}catch(U){if(B(U)){s();return}throw U}}async function DL(L){try{await S.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",L).execute()}catch($){if(B($)){s();return}throw $}}async function xL(){try{return await S.selectFrom("queue_circuit_state").selectAll().execute()??[]}catch(L){if(B(L))return s(),[];throw L}}var OL=!1;var P1=()=>{};a();import{env as O0}from"@stacksjs/env";function I4(){return O0.QUEUE_DRIVER||"sync"}class v4{name;description;action;handle;queue;rate;tries;timeout;backoff;backoffConfig;enabled;constructor(L){this.name=L.name,this.description=L.description,this.handle=L.handle,this.queue=L.queue,this.rate=L.rate,this.action=L.action,this.tries=L.tries,this.timeout=L.timeout,this.backoff=L.backoff,this.backoffConfig=L.backoffConfig,this.enabled=L.enabled}async dispatch(L){let{isFaked:$,getFakeQueue:V}=await Promise.resolve().then(() => i1);if($()){V()?.dispatch(this.name||"UnknownJob",L,{queue:this.queue,tries:this.tries,timeout:this.timeout});return}let Y=I4();if(Y==="sync")return this.dispatchNow(L);if(Y==="redis")return this.dispatchToRedis(L);if(Y==="database")return this.dispatchToDatabase(L);if(Y==="sqs"||Y==="memory"||Y==="beanstalkd")throw Error(`[queue] Driver "${Y}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);throw Error(`[queue] Unknown QUEUE_DRIVER "${Y}". Allowed values: redis, database, sync.`)}async dispatchIf(L,$){if(L)return this.dispatch($)}async dispatchUnless(L,$){if(!L)return this.dispatch($)}async dispatchAfter(L,$){let V=I4();if(V==="redis")return this.dispatchToRedis($,{delay:L});if(V==="database")return this.dispatchToDatabase($,{delay:L});return await new Promise((Y)=>setTimeout(Y,L*1000)),await this.dispatchNow($)}async dispatchNow(L){if(typeof this.handle==="function")await this.handle(L);else if(typeof this.action==="string"){let{runAction:$}=await import("@stacksjs/actions");await $(this.action)}else if(typeof this.action==="function")await this.action();else throw Error(`Job ${this.name} does not have a valid handler`)}async dispatchToDatabase(L,$){let V=Math.floor(Date.now()/1000),Y=$?.delay?V+$.delay:V,G=y(this.name??this.constructor.name,L,{queue:this.queue??"default",tries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0}),U=j(G),{db:K}=await import("@stacksjs/database");await K.insertInto("jobs").values({queue:this.queue||"default",payload:U,attempts:0,reserved_at:null,available_at:Y,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(L,$){let V=y(this.name??this.constructor.name,L,{queue:this.queue??"default",tries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0});_1(V);let{RedisQueue:Y}=await Promise.resolve().then(() => (F1(),O1)),{queue:G}=await import("@stacksjs/config"),U=G?.connections?.redis;if(!U)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new Y(this.queue||"default",U).add(V,{delay:$?.delay,maxTries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0})}}x1();r1();S1();C1();P1();a();var W4=(L)=>`__job_progress__:${L}`,z4=(L)=>`__job_cancel__:${L}`;function u0(L){return Number.isFinite(L)?Math.max(0,Math.min(100,L)):0}async function l0(L,$,V){let{cache:Y}=await import("@stacksjs/cache");await Y.set(W4(L),{percent:u0($),message:V,updatedAt:Date.now()},3600)}async function i0(L){let{cache:$}=await import("@stacksjs/cache");return await $.get(W4(L))??null}async function c0(L){let{cache:$}=await import("@stacksjs/cache");await $.set(z4(L),1,3600)}async function d0(L){let{cache:$}=await import("@stacksjs/cache");return Boolean(await $.get(z4(L)))}async function n0(L){let{cache:$}=await import("@stacksjs/cache");await Promise.all([$.del(W4(L)),$.del(z4(L))])}import{log as K1}from"@stacksjs/logging";import*as PL from"@stacksjs/path";class ML{jobs=new Map;initialized=!1;register(L){this.jobs.set(L.name,L),K1.debug(`Registered job: ${L.name} (${L.type})`)}get(L){return this.jobs.get(L)}all(){return Array.from(this.jobs.values())}byQueue(L){return this.all().filter(($)=>$.config.queue===L)}scheduled(){return this.all().filter((L)=>L.config.rate||L.config.schedule)}has(L){return this.jobs.has(L)}clear(){this.jobs.clear(),this.initialized=!1}setInitialized(L){this.initialized=L}isInitialized(){return this.initialized}}var r=new ML;async function _4(L){let $=L||PL.userJobsPath(),V=[];try{let Y=new Bun.Glob("**/*.{ts,js}"),G={cwd:$,onlyFiles:!0,absolute:!0};for await(let U of Y.scan(G)){if(U.includes(".test.")||U.includes(".spec.")||U.endsWith("index.ts")||U.endsWith("index.js"))continue;try{let K=await s0(U);if(K)V.push(K),r.register(K)}catch(K){K1.warn(`Failed to load job from ${U}: ${K.message}`)}}return r.setInitialized(!0),K1.info(`Discovered ${V.length} jobs from ${$}`),V}catch(Y){return K1.error(`Failed to discover jobs: ${Y.message}`),[]}}async function s0(L){try{let $=await import(L),V=L.split("/").pop()?.replace(/\.(ts|js)$/,"")||"UnknownJob";if($.default&&typeof $.default==="function"){let Y=$.default;if(typeof Y.handle==="function"||typeof Y.prototype?.handle==="function"){let G=Y.config||{};return{name:G.name||V,path:L,config:{name:G.name||V,description:G.description,queue:G.queue||"default",tries:G.retries||3,timeout:G.timeout,withoutOverlapping:G.withoutOverlapping,schedule:G.schedule,retryAfter:G.retryAfter},type:"class",module:Y}}}if($.default&&typeof $.default==="object"){let Y=$.default;if(typeof Y.handle==="function"||typeof Y.action==="string")return{name:Y.name||V,path:L,config:{name:Y.name||V,description:Y.description,queue:Y.queue||"default",tries:Y.tries||3,backoff:Y.backoff,rate:Y.rate,timeout:Y.timeout||Y.timeOut,backoffConfig:Y.backoffConfig},type:"function",module:Y}}return null}catch($){return K1.debug(`Could not load job from ${L}: ${$.message}`),null}}function r0(L){return r.get(L)}function t0(){return r.all()}function Z4(){return r.scheduled()}async function o0(L,$){let V=r.get(L);if(!V)throw Error(`Job "${L}" not found. Did you run discoverJobs()?`);try{if(V.type==="class"){if(typeof V.module.handle==="function")return await V.module.handle($);return await new V.module().handle($)}else{if(typeof V.module.handle==="function")return await V.module.handle($);throw Error(`Job "${L}" does not have a handle method`)}}catch(Y){throw K1.error(`Failed to execute job "${L}": ${Y.message}`),Y}}function a0(L){let $=L.config;return{name:$.name,queue:$.queue,tries:$.tries,backoff:$.backoff,timeout:$.timeout,backoffConfig:$.backoffConfig,rate:$.rate}}import{log as E}from"@stacksjs/logging";k();import{log as TL}from"@stacksjs/logging";var BL=!1;async function EL(){if(BL)return!0;try{let{db:L}=await import("@stacksjs/database");return await L.unsafe("CREATE TABLE IF NOT EXISTS scheduled_job_runs (job_name VARCHAR(255) PRIMARY KEY, last_run_at VARCHAR(64) NOT NULL)").execute(),BL=!0,!0}catch(L){return TL.debug(`[scheduler] run-marker persistence unavailable, using in-memory lastRun: ${L instanceof Error?L.message:String(L)}`),!1}}async function NL(L){if(!await EL())return null;try{let{db:$}=await import("@stacksjs/database"),V=await $.selectFrom("scheduled_job_runs").where("job_name","=",L).select(["last_run_at"]).executeTakeFirst();if(!V?.last_run_at)return null;let Y=new Date(V.last_run_at);return Number.isNaN(Y.getTime())?null:Y}catch{return null}}async function JL(L,$){if(!await EL())return;try{let{db:V}=await import("@stacksjs/database"),Y=$.toISOString();await V.deleteFrom("scheduled_job_runs").where("job_name","=",L).execute(),await V.insertInto("scheduled_job_runs").values({job_name:L,last_run_at:Y}).execute()}catch{}}function e0(L){return`%"jobName":"${L.replace(/[\\%_]/g,(V)=>`\\${V}`)}"%`}async function SL(L){try{let{db:$}=await import("@stacksjs/database"),V=await $.unsafe("SELECT 1 AS present FROM jobs WHERE payload LIKE ? ESCAPE '\\' LIMIT 1",[e0(L)]).execute(),Y=Array.isArray(V)?V:V?.rows??[];return Array.isArray(Y)&&Y.length>0}catch($){return TL.debug(`[scheduler] overlap check unavailable, dispatching anyway: ${$ instanceof Error?$.message:String($)}`),!1}}q1();var IL={checkInterval:60000,preventOverlapping:!0},P={isRunning:!1,isShuttingDown:!1,checkInterval:null,jobs:new Map,config:{...IL}},RL=new Set;function L9(L,$){if(RL.has(L))return;RL.add(L),E.warn(`[scheduler] Cron expression "${L}" specifies seconds="${$}" but the scheduler `+"ticks at minute granularity \u2014 the seconds field is being ignored. Use a 5-field expression "+"to avoid this warning, or wait for sub-minute scheduling support.")}var CL=!1,qL=new Map;function $9(L){let $=qL.get(L);if(!$)$=new Intl.DateTimeFormat("en-US",{timeZone:L,hour12:!1,month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",weekday:"short"}),qL.set(L,$);return $}function O4(L,$){if($&&$!=="local"&&$!=="system")try{let V=$9($).formatToParts(L),Y=(K)=>V.find((z)=>z.type===K)?.value??"",G={Sun:0,Mon:1,Tue:2,Wed:3,Thu:4,Fri:5,Sat:6},U=Number(Y("hour"));if(U===24)U=0;return{minute:Number(Y("minute")),hour:U,day:Number(Y("day")),month:Number(Y("month")),dayOfWeek:G[Y("weekday")]??L.getDay()}}catch{if(!CL)CL=!0,E.warn(`[scheduler] Invalid timezone "${$}"; falling back to system local time.`)}return{minute:L.getMinutes(),hour:L.getHours(),day:L.getDate(),month:L.getMonth()+1,dayOfWeek:L.getDay()}}function V9(L,$,V){let Y=O4(new Date,V),G=Y.minute,U=Y.hour,K=Y.day,z=Y.month,X=Y.dayOfWeek;if($){let F=O4($,V);if(F.minute===G&&F.hour===U&&F.day===K)return!1}let W=L.trim().split(/\s+/);if(W.length===6){let F=W[0];if(F&&F!=="0"&&F!=="*")L9(L,F);W=W.slice(1)}if(W.length<5)return E.warn(`Invalid cron expression: ${L}`),!1;let[_,A,x,O,Z]=W;return f(_,G,0,59)&&f(A,U,0,23)&&f(x,K,1,31)&&f(O,z,1,12)&&f(Z,X,0,6)}function f(L,$,V,Y){if(L==="*")return!0;if(L.includes(","))return L.split(",").map((K)=>Number.parseInt(K.trim(),10)).includes($);if(L.includes("-")){let[U,K]=L.split("-").map((z)=>Number.parseInt(z.trim(),10));return $>=U&&$<=K}if(L.includes("/")){let[U,K]=L.split("/"),z=Number.parseInt(K,10);if(U==="*")return $%z===0;if(U.includes("-")){let[X,W]=U.split("-").map((_)=>Number.parseInt(_.trim(),10));return $>=X&&$<=W&&($-X)%z===0}}let G=Number.parseInt(L,10);return!Number.isNaN(G)&&$===G}function vL(L){let V={"@yearly":"0 0 1 1 *","@annually":"0 0 1 1 *","@monthly":"0 0 1 * *","@weekly":"0 0 * * 0","@daily":"0 0 * * *","@midnight":"0 0 * * *","@hourly":"0 * * * *"}[L.toLowerCase()];if(V)return V;let Y=L.match(/^Every\.(\w+)$/i);if(Y&&Y[1]!==void 0){let U=Y[1].toLowerCase();return{second:"* * * * *",fiveseconds:"* * * * *",tenseconds:"* * * * *",thirtyseconds:"* * * * *",minute:"* * * * *",fiveminutes:"*/5 * * * *",tenminutes:"*/10 * * * *",fifteenminutes:"*/15 * * * *",thirtyminutes:"*/30 * * * *",hour:"0 * * * *",twohours:"0 */2 * * *",sixhours:"0 */6 * * *",twelvehours:"0 */12 * * *",day:"0 0 * * *",week:"0 0 * * 0",month:"0 0 1 * *"}[U]||null}let G=L.split(/\s+/).length;if(G>=5&&G<=6)return L;return null}function yL(L,$){let V=L.trim().split(/\s+/),Y=V.length===6?V.slice(1):V;if(Y.length<5)return null;let[G,U,K,z,X]=Y,W=new Date;W.setSeconds(0,0);let _=527040;for(let A=1;A<=_;A++){let x=new Date(W.getTime()+A*60000),O=O4(x,$);if(f(G,O.minute,0,59)&&f(U,O.hour,0,23)&&f(K,O.day,1,31)&&f(z,O.month,1,12)&&f(X,O.dayOfWeek,0,6))return x}return null}async function Y9(L={}){if(P.isRunning){E.warn("Scheduler is already running");return}P.config={...IL,...L},P.isRunning=!0,P.isShuttingDown=!1,await _4();let $=Z4();for(let G of $){let U=G.config.rate||G.config.schedule;if(U){let K=vL(U);if(K){let z=await NL(G.name);P.jobs.set(G.name,{job:G,lastRun:z,nextRun:yL(K,P.config.timezone),isRunning:!1}),E.info(`Registered scheduled job: ${G.name} (${K})`)}else E.warn(`Invalid schedule for job ${G.name}: ${U}`)}}if(P.jobs.size===0){E.info("No scheduled jobs found");return}E.info(`Scheduler started with ${P.jobs.size} job(s)`),process.on("SIGINT",()=>F4()),process.on("SIGTERM",()=>F4());let V=!1,Y=()=>{if(P.isShuttingDown)return;let G=P.config.checkInterval,U=G-Date.now()%G;P.checkInterval=setTimeout(()=>{if(!P.isShuttingDown&&!V)V=!0,kL().catch((K)=>E.error("Scheduler check failed:",K)).finally(()=>{V=!1});Y()},U),P.checkInterval?.unref?.()};Y(),await kL()}async function kL(){for(let[L,$]of P.jobs){let V=$.job.config.rate||$.job.config.schedule;if(!V)continue;let Y=vL(V);if(!Y)continue;if(V9(Y,$.lastRun,P.config.timezone)){if((P.config.preventOverlapping||$.job.config.withoutOverlapping)&&await SL(L)){E.debug(`Skipping ${L}: previous execution still running`);continue}if($.isRunning){E.debug(`Skipping ${L}: a dispatch for it is already in flight`);continue}try{$.isRunning=!0,$.lastRun=new Date,$.nextRun=yL(Y,P.config.timezone),await JL(L,$.lastRun),E.info(`Dispatching scheduled job: ${L}`),await d("job:added",{jobId:`scheduled-${L}-${Date.now()}`,queueName:$.job.config.queue||"default",jobName:L}),await t1(L,{queue:$.job.config.queue||"default",payload:{},maxTries:$.job.config.tries||3,timeout:$.job.config.timeout||60}),$.isRunning=!1,E.info(`Scheduled job ${L} dispatched to queue`)}catch(U){$.isRunning=!1,E.error(`Failed to dispatch scheduled job ${L}:`,U)}}}}async function F4(){if(!P.isRunning)return;if(E.info("Stopping scheduler..."),P.isShuttingDown=!0,P.checkInterval)clearTimeout(P.checkInterval),P.checkInterval=null;P.isRunning=!1,P.jobs.clear(),E.info("Scheduler stopped")}function G9(){return{isRunning:P.isRunning,jobCount:P.jobs.size,jobs:Array.from(P.jobs.entries()).map(([L,$])=>({name:L,schedule:$.job.config.rate||$.job.config.schedule,lastRun:$.lastRun,nextRun:$.nextRun,isRunning:$.isRunning}))}}function U9(){return P.isRunning}function K9(){return new Map(P.jobs)}async function X9(L){let $=P.jobs.get(L);if(!$)throw Error(`Scheduled job "${L}" not found`);E.info(`Manually triggering scheduled job: ${L}`),await t1(L,{queue:$.job.config.queue||"default",payload:{},maxTries:$.job.config.tries||3,timeout:$.job.config.timeout||60})}k();k();import{log as W9}from"@stacksjs/logging";var z9={maxPendingWarning:1000,maxPendingCritical:5000,maxFailedWarning:10,maxFailedCritical:100,maxJobAgeWarning:3600,maxJobAgeCritical:86400,maxErrorRateWarning:0.1,maxErrorRateCritical:0.5};async function H4(L={}){let $={...z9,...L},V=[],Y=new Date,G=Math.floor(Y.getTime()/1000);try{let{db:U}=await import("@stacksjs/database"),K=await U.selectFrom("jobs").selectAll().execute(),z=await U.selectFrom("failed_jobs").selectAll().execute(),X=new Map;for(let Q of K){let M=Q.queue||"default";if(!X.has(M))X.set(M,{pending:0,processing:0,delayed:0});let J=X.get(M);if(Q.reserved_at)J.processing++;else if(Q.available_at&&Q.available_at>G)J.delayed++;else if(J.pending++,Q.created_at){let q=typeof Q.created_at==="number"?Q.created_at:Math.floor(new Date(Q.created_at).getTime()/1000),M4=G-q;if(!J.oldestAge||M4>J.oldestAge)J.oldestAge=M4}}let W=new Map;for(let Q of z){let M=Q.queue||"default";W.set(M,(W.get(M)||0)+1)}let _=[],A=new Set([...X.keys(),...W.keys()]),x=$.queues?[...A].filter((Q)=>$.queues.includes(Q)):[...A],O=0,Z=0,F=0,I=0;for(let Q of x){let M=X.get(Q)||{pending:0,processing:0,delayed:0},J=W.get(Q)||0;O+=M.pending,Z+=M.processing,F+=M.delayed,I+=J;let q="healthy";if(M.pending>=$.maxPendingCritical)q="unhealthy",V.push({level:"critical",message:`Queue "${Q}" has ${M.pending} pending jobs (threshold: ${$.maxPendingCritical})`,queue:Q,timestamp:Y.toISOString()});else if(M.pending>=$.maxPendingWarning)q="degraded",V.push({level:"warning",message:`Queue "${Q}" has ${M.pending} pending jobs (threshold: ${$.maxPendingWarning})`,queue:Q,timestamp:Y.toISOString()});if(J>=$.maxFailedCritical)q="unhealthy",V.push({level:"critical",message:`Queue "${Q}" has ${J} failed jobs (threshold: ${$.maxFailedCritical})`,queue:Q,timestamp:Y.toISOString()});else if(J>=$.maxFailedWarning){if(q==="healthy")q="degraded";V.push({level:"warning",message:`Queue "${Q}" has ${J} failed jobs (threshold: ${$.maxFailedWarning})`,queue:Q,timestamp:Y.toISOString()})}if(M.oldestAge){if(M.oldestAge>=$.maxJobAgeCritical)q="unhealthy",V.push({level:"critical",message:`Queue "${Q}" has a job waiting for ${Math.floor(M.oldestAge/3600)} hours`,queue:Q,timestamp:Y.toISOString()});else if(M.oldestAge>=$.maxJobAgeWarning){if(q==="healthy")q="degraded";V.push({level:"warning",message:`Queue "${Q}" has a job waiting for ${Math.floor(M.oldestAge/60)} minutes`,queue:Q,timestamp:Y.toISOString()})}}_.push({name:Q,status:q,pending:M.pending,processing:M.processing,delayed:M.delayed,failed:J,oldestJobAge:M.oldestAge})}let x4=v1().getMetrics(),bL=x4.throughputPerMinute,uL=x4.averageDuration,P4=O+Z+F+I,z1=P4>0?I/P4:0,o="healthy";if(_.some((Q)=>Q.status==="unhealthy"))o="unhealthy";else if(_.some((Q)=>Q.status==="degraded"))o="degraded";if(z1>=$.maxErrorRateCritical)o="unhealthy",V.push({level:"critical",message:`Overall error rate is ${(z1*100).toFixed(1)}% (threshold: ${$.maxErrorRateCritical*100}%)`,timestamp:Y.toISOString()});else if(z1>=$.maxErrorRateWarning){if(o==="healthy")o="degraded";V.push({level:"warning",message:`Overall error rate is ${(z1*100).toFixed(1)}% (threshold: ${$.maxErrorRateWarning*100}%)`,timestamp:Y.toISOString()})}let lL=A1().getAll().map((Q)=>({id:Q.id,status:Q.status,queue:Q.queue,processedCount:Q.processedCount,failedCount:Q.failedCount,lastActivityAt:Q.lastActivityAt}));return{status:o,timestamp:Y.toISOString(),queues:_,workers:lL,metrics:{totalPending:O,totalProcessing:Z,totalDelayed:F,totalFailed:I,throughputPerMinute:bL,averageProcessingTime:uL,errorRate:z1},alerts:V}}catch(U){W9.error("Failed to perform queue health check:",U);let z=A1().getAll().map((X)=>({id:X.id,status:X.status,queue:X.queue,processedCount:X.processedCount,failedCount:X.failedCount,lastActivityAt:X.lastActivityAt}));return{status:"unhealthy",timestamp:Y.toISOString(),queues:[],workers:z,metrics:{totalPending:0,totalProcessing:0,totalDelayed:0,totalFailed:0,throughputPerMinute:0,averageProcessingTime:0,errorRate:0},alerts:[{level:"critical",message:`Health check failed: ${U.message}`,timestamp:Y.toISOString()}]}}}function _9(L={}){return async($)=>{let V=await H4(L),Y=V.status==="healthy"?200:V.status==="degraded"?207:503;return new Response(JSON.stringify(V,null,2),{status:Y,headers:{"Content-Type":"application/json","Cache-Control":"no-store"}})}}async function Z9(L={}){return(await H4(L)).status==="healthy"}import{log as C}from"@stacksjs/logging";var O9={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function M1(L){return String(L).replace(/[&<>"']/g,($)=>O9[$]??$)}class Q4{config;notificationCount=0;lastResetTime=Date.now();pendingBatch=[];batchTimeout=null;activeFlush=null;constructor(L){this.config=L}async notify(L){if(this.config.filter&&!this.config.filter(L))return;if(this.config.rateLimit){let $=Date.now();if($-this.lastResetTime>3600000)this.notificationCount=0,this.lastResetTime=$;if(this.notificationCount>=this.config.rateLimit){C.debug("Rate limit reached for failed job notifications");return}}if(this.config.batch){if(this.pendingBatch.push(L),!this.batchTimeout)this.batchTimeout=setTimeout(()=>{this.batchTimeout=null,this.activeFlush=this.flushBatch().catch(($)=>C.error("Failed to flush notification batch:",$)).finally(()=>{this.activeFlush=null})},this.config.batchInterval||60000),this.batchTimeout.unref?.();return}await this.sendNotifications([L])}async flushBatch(){if(this.pendingBatch.length===0)return;let L=[...this.pendingBatch];if(this.pendingBatch=[],this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;await this.sendNotifications(L)}async shutdown(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.activeFlush)await this.activeFlush.catch(()=>{});await this.flushBatch().catch((L)=>C.error("Failed to flush notification batch on shutdown:",L))}async sendNotifications(L){let $=[];for(let V of this.config.channels)switch(V){case"email":if(this.config.email)$.push(this.sendEmail(L));break;case"slack":if(this.config.slack)$.push(this.sendSlack(L));break;case"discord":if(this.config.discord)$.push(this.sendDiscord(L));break;case"webhook":if(this.config.webhook)$.push(this.sendWebhook(L));break;case"log":this.logFailures(L);break}try{await Promise.all($)}catch(V){C.error("Failed to send job failure notifications:",V)}finally{this.notificationCount+=L.length}}async sendEmail(L){let $=this.config.email,V=$.subject||`[Queue] ${L.length} Job(s) Failed`,Y=this.formatEmailBody(L);try{let{mail:G}=await import("@stacksjs/email");await G.send({to:Array.isArray($.to)?$.to:[$.to],from:$.from,subject:V,html:Y})}catch(G){C.error("Failed to send email notification:",G)}}formatEmailBody(L){let $=L.map((V)=>`
2
+ var{defineProperty:h1,getOwnPropertyNames:cL,getOwnPropertyDescriptor:dL}=Object,nL=Object.prototype.hasOwnProperty;function sL(L){return this[L]}var rL=(L)=>{var $=(B4??=new WeakMap).get(L),V;if($)return $;if($=h1({},"__esModule",{value:!0}),L&&typeof L==="object"||typeof L==="function"){for(var Y of cL(L))if(!nL.call($,Y))h1($,Y,{get:sL.bind(L,Y),enumerable:!(V=dL(L,Y))||V.enumerable})}return B4.set(L,$),$},B4;var tL=(L)=>L;function oL(L,$){this[L]=tL.bind(null,$)}var p=(L,$)=>{for(var V in $)h1(L,V,{get:$[V],enumerable:!0,configurable:!0,set:oL.bind($,V)})};var v=(L,$)=>()=>(L&&($=L(L=0)),$);var Q=import.meta.require;function y(L,$,V,Y){return{jobName:L,payload:$,options:V,envelopeVersion:1,dispatchedAt:new Date().toISOString(),...Y?{traceId:Y}:{}}}function j(L){try{return JSON.stringify(L)}catch($){throw eL(L,$)}}function _1(L){j(L)}function eL(L,$){if(L0($)){let G=null;try{G=g1(L,"",new Set)}catch{}let U=G?`\`${G.path}\` is a BigInt (${G.value}n)`:"it holds a BigInt";return Error(`[queue] Job "${L.jobName}" was not dispatched: ${U}, `+"and job envelopes travel as JSON, which cannot represent one. Convert it where you dispatch \u2014 "+"`String(value)` keeps every digit, `Number(value)` is exact below 2^53 \u2014 and convert it back inside "+"the job handler. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.",{cause:$})}if($0($))return Error(`[queue] Job "${L.jobName}" was not dispatched: its payload contains a circular reference, which JSON cannot represent. Send the ids of the things the payload points at rather than the `+`objects themselves \u2014 that also keeps the job replayable once those objects have changed. ${"See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts."}`,{cause:$});let Y=$ instanceof Error?$.message:String($);return Error(`[queue] Job "${L.jobName}" was not dispatched: serializing its payload threw (${Y}). `+"JSON rejects only two things on its own and this was neither, so the throw came from the payload \u2014 "+"a `toJSON()` or a property getter. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.",{cause:$})}function L0(L){return L instanceof TypeError&&/bigint/i.test(L.message)}function $0(L){return L instanceof TypeError&&/circular|cyclic/i.test(L.message)}function g1(L,$,V){if(typeof L==="bigint")return{path:$||"envelope",value:L};if(!L||typeof L!=="object")return null;if(V.has(L))return null;if(V.add(L),Array.isArray(L)){for(let Y=0;Y<L.length;Y++){let G=g1(L[Y],`${$}[${Y}]`,V);if(G)return G}return null}for(let[Y,G]of Object.entries(L)){let U=g1(G,$?`${$}.${Y}`:Y,V);if(U)return U}return null}function T4(L,$){if(p1.has(L))return;p1.add(L),console.warn($)}function V0(){p1.clear()}function T1(L){let $;if(typeof L==="string")try{$=JSON.parse(L)}catch{return{ok:!1,reason:"malformed",detail:"not valid JSON"}}else if(L&&typeof L==="object")$=L;else return{ok:!1,reason:"malformed",detail:`expected string or object, got ${typeof L}`};if($.envelopeVersion===1){if(typeof $.jobName!=="string")return{ok:!1,reason:"missing-job-name"};return{ok:!0,envelope:{jobName:$.jobName,payload:$.payload,options:$.options??void 0,envelopeVersion:1,dispatchedAt:typeof $.dispatchedAt==="string"?$.dispatchedAt:new Date(0).toISOString(),...typeof $.traceId==="string"&&$.traceId?{traceId:$.traceId}:{}},source:"v1"}}if(typeof $.envelopeVersion==="number"&&$.envelopeVersion>1)return{ok:!1,reason:"unknown-version",detail:`envelopeVersion=${$.envelopeVersion}, this worker speaks v1`};if(typeof $.jobName==="string")return T4("v0-implicit","[queue/envelope] Processing pre-#1884 job envelope without envelopeVersion. In-flight jobs from before the upgrade will continue to work; new dispatches use the v1 shape automatically."),{ok:!0,envelope:{jobName:$.jobName,payload:$.payload,options:$.options??void 0,envelopeVersion:1,dispatchedAt:typeof $.dispatchedAt==="string"?$.dispatchedAt:new Date(0).toISOString(),...typeof $.traceId==="string"&&$.traceId?{traceId:$.traceId}:{}},source:"v0-implicit"};if(typeof $.job==="string")return T4("laravel-legacy","[queue/envelope] Processing Laravel-legacy job envelope (`{ job, data }` shape). "+"Will continue to process but the queue table contains migration-era rows \u2014 "+"consider flushing once they drain."),{ok:!0,envelope:{jobName:$.job.replace(/^App\\+Jobs\\+/,"").replace(/^.*\\/,""),payload:$.data,options:void 0,envelopeVersion:1,dispatchedAt:new Date(0).toISOString()},source:"laravel-legacy"};return{ok:!1,reason:"missing-job-name"}}var aL=1,p1;var a=v(()=>{p1=new Set});var i1={};p(i1,{runJob:()=>R4,restore:()=>l1,isFaked:()=>J4,getFakeQueue:()=>N4,fake:()=>u1,expectJobToFail:()=>C4,createQueueTester:()=>S4,QueueTester:()=>E1});class E4{dispatchedJobs=[];pushedJobs=[];processedJobs=[];failedJobs=[];dispatch(L,$,V={}){this.dispatchedJobs.push({name:L,data:$,options:V,dispatchedAt:new Date,queue:V.queue||"default"})}push(L,$,V={}){this.pushedJobs.push({name:L,data:$,options:V,dispatchedAt:new Date,queue:V.queue||"default"})}dispatched(L){if(L)return this.dispatchedJobs.filter(($)=>$.name===L);return[...this.dispatchedJobs]}pushed(L){if(L)return this.pushedJobs.filter(($)=>$.name===L);return[...this.pushedJobs]}assertDispatched(L,$){let V=this.dispatched(L);if(V.length===0)throw Error(`Expected job "${L}" to be dispatched, but it was not.`);if($){if(V.filter($).length===0)throw Error(`Expected job "${L}" to be dispatched matching the callback, but no matching jobs were found.`)}}assertNotDispatched(L){let $=this.dispatched(L);if($.length>0)throw Error(`Expected job "${L}" to not be dispatched, but it was dispatched ${$.length} time(s).`)}assertDispatchedTimes(L,$){let V=this.dispatched(L);if(V.length!==$)throw Error(`Expected job "${L}" to be dispatched ${$} time(s), but it was dispatched ${V.length} time(s).`)}assertNothingDispatched(){if(this.dispatchedJobs.length>0){let L=[...new Set(this.dispatchedJobs.map(($)=>$.name))].join(", ");throw Error(`Expected no jobs to be dispatched, but found: ${L}`)}}assertPushed(L,$){let V=this.pushed(L);if(V.length===0)throw Error(`Expected job "${L}" to be pushed, but it was not.`);if($){if(V.filter($).length===0)throw Error(`Expected job "${L}" to be pushed matching the callback, but no matching jobs were found.`)}}assertPushedWithDelay(L,$){if(this.pushed(L).filter((G)=>G.options.delay===$).length===0)throw Error(`Expected job "${L}" to be pushed with delay ${$}ms, but no matching jobs were found.`)}assertPushedOn(L,$){if(this.pushed($).filter((Y)=>Y.queue===L).length===0)throw Error(`Expected job "${$}" to be pushed on queue "${L}", but it was not.`)}async processJob(L,$){let V=this.dispatchedJobs.find((Y)=>Y.name===L);if(!V)throw Error(`No dispatched job found with name "${L}"`);try{await $(V.data),this.processedJobs.push(V)}catch(Y){throw this.failedJobs.push({job:V,error:Y}),Y}}processed(L){if(L)return this.processedJobs.filter(($)=>$.name===L);return[...this.processedJobs]}failed(L){if(L)return this.failedJobs.filter(($)=>$.job.name===L);return[...this.failedJobs]}reset(){this.dispatchedJobs=[],this.pushedJobs=[],this.processedJobs=[],this.failedJobs=[]}}function u1(){return Z1=new E4,Z1}function N4(){return Z1}function J4(){return Z1!==null}function l1(){Z1=null}class E1{queue;constructor(){this.queue=u1()}dispatch(L,$,V={}){return this.queue.dispatch(L,$,V),this}push(L,$,V={}){return this.queue.push(L,$,V),this}assertDispatched(L,$){return this.queue.assertDispatched(L,$),this}assertNotDispatched(L){return this.queue.assertNotDispatched(L),this}assertDispatchedTimes(L,$){return this.queue.assertDispatchedTimes(L,$),this}assertNothingDispatched(){return this.queue.assertNothingDispatched(),this}dispatched(L){return this.queue.dispatched(L)}reset(){return this.queue.reset(),this}cleanup(){l1()}}function S4(){return new E1}async function R4(L,$){return await L.handle($)}async function C4(L,$,V){try{throw await L.handle($),Error("Expected job to fail, but it succeeded")}catch(Y){if(Y.message==="Expected job to fail, but it succeeded")throw Y;if(V){let G=Y.message;if(typeof V==="string"){if(!G.includes(V))throw Error(`Expected error to contain "${V}", got "${G}"`)}else if(!V.test(G))throw Error(`Expected error to match ${V}, got "${G}"`)}return Y}}var Z1=null;var O1={};p(O1,{setQueueManager:()=>Z0,getQueueManager:()=>W0,dispatchSync:()=>z0,dispatchAfter:()=>X0,dispatch:()=>K0,createRedisDispatcher:()=>O0,chain:()=>U0,batch:()=>G0,StacksQueueManager:()=>q4,RedisQueue:()=>e,RedisJob:()=>k4,QueueManager:()=>_0});import{Queue as Y0,batch as G0,chain as U0,dispatch as K0,dispatchAfter as X0,dispatchSync as z0,getQueueManager as W0,QueueManager as _0,setQueueManager as Z0}from"@stacksjs/bun-queue";import{log as c1}from"@stacksjs/logging";class e{queue;config;isProcessing=!1;constructor(L,$){this.config=$,this.queue=new Y0(L,{driver:"redis",prefix:$.prefix,redis:$.redis?{url:$.redis.url||this.buildRedisUrl($.redis)}:void 0,defaultJobOptions:$.defaultJobOptions?{delay:$.defaultJobOptions.delay,attempts:$.defaultJobOptions.attempts,backoff:$.defaultJobOptions.backoff,removeOnComplete:$.defaultJobOptions.removeOnComplete,removeOnFail:$.defaultJobOptions.removeOnFail,priority:$.defaultJobOptions.priority,lifo:$.defaultJobOptions.lifo,timeout:$.defaultJobOptions.timeout,jobId:$.defaultJobOptions.jobId,dependsOn:$.defaultJobOptions.dependsOn,keepJobs:$.defaultJobOptions.keepJobs,deadLetter:$.defaultJobOptions.deadLetter}:void 0,limiter:$.limiter,metrics:$.metrics,stalledJobCheckInterval:$.stalledJobCheckInterval,maxStalledJobRetries:$.maxStalledJobRetries,distributedLock:$.distributedLock,defaultDeadLetterOptions:$.defaultDeadLetterOptions,horizontalScaling:$.horizontalScaling,logLevel:$.logLevel}),c1.debug(`Redis queue "${L}" initialized`)}buildRedisUrl(L){let $=L.host||"localhost",V=L.port||6379,Y=L.password?`:${encodeURIComponent(L.password)}@`:"",G=Number.isFinite(L.db)&&L.db>=0?L.db:0;return`redis://${Y}${$}:${V}/${G}`}async add(L,$){let V={delay:$?.delay?$.delay*1000:void 0,attempts:$?.maxTries,priority:$?.priority,timeout:$?.timeout?$.timeout*1000:void 0,backoff:Array.isArray($?.backoff)?$.backoff.map((Y)=>(Number(Y)||1)*1000):$?.backoff};return this.queue.add(L,V)}process(L,$){if(this.isProcessing){c1.warn("Queue is already processing");return}this.isProcessing=!0,this.queue.process(L,$),c1.info(`Started processing queue with concurrency ${L}`)}async getJob(L){return this.queue.getJob(L)}async getJobs(L){return this.queue.getJobs(L)}async getJobCounts(){return this.queue.getJobCounts()}async removeJob(L){return this.queue.removeJob(L)}async pause(){return this.queue.pause()}async resume(){return this.queue.resume()}async empty(){return this.queue.empty()}async close(){return this.isProcessing=!1,this.queue.close()}async getMetrics(){return this.queue.getMetrics()}async ping(){return this.queue.ping()}async scheduleCron(L){return this.queue.scheduleCron({cronExpression:L.cron,data:L.data,timezone:L.tz,jobId:L.name})}async unscheduleCron(L){return this.queue.unscheduleCron(L)}async getDeadLetterJobs(){return this.queue.getDeadLetterJobs()}async republishDeadLetterJob(L){return this.queue.republishDeadLetterJob(L)}async clearDeadLetterQueue(){return this.queue.clearDeadLetterQueue()}async bulkRemove(L){return this.queue.bulkRemove(L)}async getClusterInfo(){return this.queue.getClusterInfo()}isLeader(){return this.queue.isLeader()}getQueue(){return this.queue}on(L,$){this.queue.events.on(L,$)}}class q4{config;queues=new Map;defaultConnection="default";constructor(L){this.config=L;for(let[$,V]of Object.entries(L))if(V.driver==="redis")this.queues.set($,new e($,V))}queue(L){let $=L||this.defaultConnection,V=this.queues.get($);if(!V){let Y=this.config[$];if(!Y)throw Error(`Queue "${$}" not configured`);V=new e($,Y),this.queues.set($,V)}return V}setDefaultConnection(L){this.defaultConnection=L}async closeAll(){let L=Array.from(this.queues.values()).map(($)=>$.close());await Promise.all(L),this.queues.clear()}}function O0(L,$){let V=new e(L,$);return async(Y,G)=>{return V.add(Y,G)}}class k4{data;options={};queue;constructor(L,$,V){this.data=V;this.queue=new e(L,$)}async dispatch(){await this.queue.add(this.data,this.options)}async dispatchNow(){await this.queue.add(this.data,{...this.options,immediate:!0})}delay(L){return this.options.delay=L,this}afterResponse(){return this.options.afterResponse=!0,this}chain(L){return this.options.chainedJobs=L,this}onQueue(L){return this.options.queue=L,this}priority(L){return this.options.priority=L,this}tries(L){return this.options.maxTries=L,this}timeout(L){return this.options.timeout=L,this}backoff(L){return this.options.backoff=L,this}}var F1=()=>{};function B(L){let $=L,V=$?.errno;if(typeof V==="string"&&V.toUpperCase()==="42P01")return!0;if(typeof V==="number"&&V===1146)return!0;if((typeof $?.code==="string"?$.code.toUpperCase():"")==="42P01")return!0;let G=$?.message??"";return G.includes("no such table")||G.includes("doesn't exist")||G.includes("does not exist")}var m4={};p(m4,{retryDeadLetterJob:()=>f4,purgeDeadLetterJobs:()=>j4,moveToDeadLetter:()=>J1,listDeadLetterJobs:()=>w4});import{db as L1}from"@stacksjs/database";function N1(){if(y4)return;y4=!0,console.warn("[queue/dlq] dead_letter_jobs table missing \u2014 DLQ disabled. "+"Run migrations to enable poison-message isolation.")}async function J1(L,$,V=1){let Y=new Date().toISOString().slice(0,19).replace("T"," ");try{return await L1.insertInto("dead_letter_jobs").values({uuid:L.uuid??crypto.randomUUID(),connection:L.connection??"database",queue:L.queue??"default",payload:L.payload??"{}",exception:L.exception??"unknown",reason:$,total_failures:V,first_failed_at:L.failed_at??Y,last_failed_at:Y,dead_lettered_at:Y}).execute(),!0}catch(G){if(B(G))return N1(),!1;throw G}}async function w4(L={}){try{let $=L1.selectFrom("dead_letter_jobs").selectAll();if(L.queue)$=$.where("queue","=",L.queue);if(L.reason)$=$.where("reason","=",L.reason);if(L.sinceCutoffMs){let Y=new Date(L.sinceCutoffMs).toISOString().slice(0,19).replace("T"," ");$=$.where("dead_lettered_at",">=",Y)}if(L.limit&&L.limit>0)$=$.limit(L.limit);return await $.execute()??[]}catch($){if(B($))return N1(),[];throw $}}async function f4(L){try{let $=await L1.selectFrom("dead_letter_jobs").where("id","=",L).selectAll().executeTakeFirst();if(!$)return!1;let V=Math.floor(Date.now()/1000);return await L1.insertInto("jobs").values({queue:$.queue,payload:$.payload,attempts:0,reserved_at:null,available_at:V,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),await L1.deleteFrom("dead_letter_jobs").where("id","=",L).execute(),!0}catch($){if(B($))return N1(),!1;throw $}}async function j4(L=30){try{let $=new Date(Date.now()-L*24*60*60*1000).toISOString().slice(0,19).replace("T"," "),V=await L1.deleteFrom("dead_letter_jobs").where("dead_lettered_at","<",$).execute();return Number(V?.numDeletedRows??V?.[0]?.numDeletedRows??V?.affectedRows??0)}catch($){if(B($))return N1(),0;throw $}}var y4=!1;var S1=()=>{};function d1(){if(h4)return;h4=!0,console.warn("[queue/idempotency] job_idempotency table missing \u2014 idempotency keys are accepted but NOT enforced. "+"Run migrations to enable dedup.")}async function Q0(L){try{let{db:$}=await import("@stacksjs/database"),V=await $.selectFrom("job_idempotency").where("idempotency_key","=",L).select(["idempotency_key"]).executeTakeFirst();return Boolean(V)}catch($){if(B($))return d1(),!1;throw $}}async function H0(L,$,V){try{let{db:Y}=await import("@stacksjs/database");await Y.insertInto("job_idempotency").values({idempotency_key:L,job_name:$,queue:V??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}catch(Y){if(B(Y)){d1();return}let G=Y?.message??"";if(G.includes("UNIQUE constraint")||G.includes("Duplicate entry"))return;throw Y}}async function n1(L,$,V){try{let{db:Y}=await import("@stacksjs/database");return await Y.insertInto("job_idempotency").values({idempotency_key:L,job_name:$,queue:V??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),"claimed"}catch(Y){if(B(Y))return d1(),"unenforced";let G=Y?.message??"";if(G.includes("UNIQUE constraint")||G.includes("Duplicate entry"))return"duplicate";throw Y}}async function s1(L){try{let{db:$}=await import("@stacksjs/database");await $.deleteFrom("job_idempotency").where("idempotency_key","=",L).execute()}catch{}}var h4=!1;var r1=()=>{};var i4={};p(i4,{unquarantineJob:()=>u4,recordFailureForPoison:()=>p4,quarantineJob:()=>b4,listQuarantined:()=>l4,isQuarantined:()=>R1,hashPayload:()=>H1});import{createHash as A0}from"crypto";import{db as R}from"@stacksjs/database";function Q1(){if(g4)return;g4=!0,console.warn("[queue/poison] job_quarantine table missing \u2014 poison detection disabled. "+"Run migrations to enable.")}function H1(L){let $;try{$=typeof L==="string"?L:JSON.stringify(L??null)}catch{$=String(L)}return A0("sha256").update($).digest("hex").slice(0,32)}async function p4(L,$,V={}){let Y=V.maxFailures??5,G=V.windowMinutes??60,U=H1($),K=new Date,W=K.toISOString().slice(0,19).replace("T"," ");try{let X=await R.selectFrom("job_quarantine").where("job_name","=",L).where("payload_hash","=",U).selectAll().executeTakeFirst();if(!X)return await R.insertInto("job_quarantine").values({job_name:L,payload_hash:U,failure_count:1,window_start:W,quarantined_at:null}).execute(),!1;if(X.quarantined_at)return!0;let z=Date.parse(X.window_start.replace(" ","T")+"Z"),_=K.getTime()-z,A=G*60*1000;if(Number.isFinite(z)&&_>A)return await R.updateTable("job_quarantine").set({failure_count:1,window_start:W}).where("id","=",X.id).execute(),!1;let x=X.failure_count+1;if(x>=Y)return await R.updateTable("job_quarantine").set({failure_count:x,quarantined_at:W}).where("id","=",X.id).execute(),!0;return await R.updateTable("job_quarantine").set({failure_count:x}).where("id","=",X.id).execute(),!1}catch(X){if(B(X))return Q1(),!1;throw X}}async function R1(L,$){let V=H1($);try{let Y=await R.selectFrom("job_quarantine").where("job_name","=",L).where("payload_hash","in",[V,"*"]).whereNotNull("quarantined_at").select(["id"]).executeTakeFirst();return Boolean(Y)}catch(Y){if(B(Y))return Q1(),!1;throw Y}}async function b4(L,$){let V=$===void 0?"*":H1($),Y=new Date().toISOString().slice(0,19).replace("T"," ");try{let G=await R.selectFrom("job_quarantine").where("job_name","=",L).where("payload_hash","=",V).select(["id"]).executeTakeFirst();if(G)await R.updateTable("job_quarantine").set({quarantined_at:Y}).where("id","=",G.id).execute();else await R.insertInto("job_quarantine").values({job_name:L,payload_hash:V,failure_count:0,window_start:Y,quarantined_at:Y}).execute()}catch(G){if(B(G)){Q1();return}throw G}}async function u4(L){try{await R.deleteFrom("job_quarantine").where("job_name","=",L).execute()}catch($){if(B($)){Q1();return}throw $}}async function l4(){try{return await R.selectFrom("job_quarantine").whereNotNull("quarantined_at").selectAll().execute()??[]}catch(L){if(B(L))return Q1(),[];throw L}}var g4=!1;var C1=()=>{};function c(L){let $=L?.numUpdatedRows;if($===null||$===void 0)return 0;if(typeof $==="object")return Number($.changes??0);return Number($)}function D0(L,$){let V=y(L,$.payload||{},{queue:$.queue,tries:$.maxTries,timeout:$.timeout,backoff:Array.isArray($.backoff)?$.backoff:void 0});return{queue:$.queue||"default",payload:j(V),attempts:0,available_at:x0($.delay||0),created_at:new Date().toISOString().slice(0,19).replace("T"," ")}}async function t1(L,$){let V=D0(L,$),{db:Y}=await import("@stacksjs/database");await Y.insertInto("jobs").values(V).execute()}function x0(L){let $=Date.now();return Math.floor($/1000+L)}var q1=v(()=>{a()});var b={};p(b,{withEvents:()=>d4,onQueueEvent:()=>c4,getWorkerTracker:()=>A1,getQueueEvents:()=>$1,getGlobalMetrics:()=>v1,emitQueueEvent:()=>d,QueueMetrics:()=>I1,QueueEvents:()=>k1,OnQueueEvent:()=>n4});import{log as m}from"@stacksjs/logging";class k1{handlers=new Map;wildcardHandlers=new Set;listenerSubscriptions=new WeakMap;reclaim=new FinalizationRegistry((L)=>{L()});on(L,$){if(!this.handlers.has(L))this.handlers.set(L,new Set);return this.handlers.get(L).add($),()=>{this.handlers.get(L)?.delete($)}}subscribeListener(L,$,V){let Y=new WeakRef(L),G=(X)=>{let z=Y.deref();if(z===void 0){U();return}return V.call(z,X)},U=this.on($,G),K=()=>{U();let X=Y.deref();if(X===void 0)return;let z=this.listenerSubscriptions.get(X);if(!z)return;if(z.delete(K),z.size===0)this.listenerSubscriptions.delete(X),this.reclaim.unregister(X)},W=this.listenerSubscriptions.get(L);if(!W)W=new Set,this.listenerSubscriptions.set(L,W);return W.add(K),this.reclaim.register(L,U,L),K}unsubscribeListener(L){let $=this.listenerSubscriptions.get(L);if(!$)return 0;let V=Array.from($);for(let Y of V)Y();return this.listenerSubscriptions.delete(L),this.reclaim.unregister(L),V.length}listenerCount(L){if(L==="*")return this.wildcardHandlers.size;return this.handlers.get(L)?.size??0}onAny(L){return this.wildcardHandlers.add(L),()=>{this.wildcardHandlers.delete(L)}}once(L,$){let V=async(Y)=>{this.handlers.get(L)?.delete(V),await $(Y)};return this.on(L,V)}async emit(L,$){let V={...$,timestamp:Date.now()};this.logEvent(L,V);let Y=this.handlers.get(L);if(Y)for(let G of Y)try{await G(V)}catch(U){m.error(`Error in queue event handler for ${L}:`,U)}for(let G of this.wildcardHandlers)try{await G(L,V)}catch(U){m.error("Error in wildcard queue event handler:",U)}}logEvent(L,$){let V=$.jobId?`[${$.jobId}]`:"",Y=$.queueName?`on ${$.queueName}`:"";switch(L){case"job:added":m.debug(`Job added ${V} ${Y}`);break;case"job:processing":m.debug(`Job processing ${V} ${Y}`);break;case"job:completed":m.info(`Job completed ${V} ${Y} in ${$.duration}ms`);break;case"job:failed":m.error(`Job failed ${V} ${Y}:`,$.error);break;case"job:retrying":m.warn(`Job retrying ${V} ${Y} (attempt ${$.attemptsMade})`);break;case"job:stalled":m.warn(`Job stalled ${V} ${Y}`);break;case"queue:error":m.error(`Queue error ${Y}:`,$.error);break}}off(L){this.handlers.delete(L)}removeAllListeners(){this.handlers.clear(),this.wildcardHandlers.clear(),this.listenerSubscriptions=new WeakMap}}function $1(){if(!o1)o1=new k1;return o1}function c4(L,$){let V=$1();if(L==="*")return V.onAny($);return V.on(L,$)}function d(L,$){return $1().emit(L,$)}function d4(L,$){return async(...V)=>{let Y=V[0]?.id||"unknown",G=Date.now();await d("job:processing",{jobId:Y,queueName:L,data:V[0]?.data});try{let U=await $(...V);return await d("job:completed",{jobId:Y,queueName:L,result:U,duration:Date.now()-G}),U}catch(U){throw await d("job:failed",{jobId:Y,queueName:L,error:U,duration:Date.now()-G}),U}}}function n4(L){return function($,V){if(typeof V!=="object"||V===null)throw TypeError(`@OnQueueEvent('${L}') requires standard (TC39) decorators. Legacy decorators ('experimentalDecorators: true') give a method decorator no construction-time hook, so the handler cannot be bound to an instance.`);if(V.kind!=="method")throw TypeError(`@OnQueueEvent('${L}') can only decorate a class method, but it was applied to a ${V.kind}${V.name===void 0?"":` ('${String(V.name)}')`}. Move the handler into a method, or call onQueueEvent('${L}', handler) directly.`);if(V.static)throw TypeError(`@OnQueueEvent('${L}') cannot decorate the static method '${String(V.name)}': a static method has no instance, so it would subscribe at class-definition time and stay subscribed for the life of the process, even if the class is never used. Use an instance method, or subscribe explicitly with onQueueEvent('${L}', MyClass.${String(V.name)}).`);return V.addInitializer(function(){$1().subscribeListener(this,L,$)}),$}}class I1{jobCounts={added:0,completed:0,failed:0,processing:0};completions=[];errors=[];unsubscribe=[];constructor(){this.setupListeners()}setupListeners(){let L=$1();this.unsubscribe.push(L.on("job:added",()=>{this.jobCounts.added++}),L.on("job:processing",()=>{this.jobCounts.processing++}),L.on("job:completed",($)=>{this.jobCounts.completed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1);let V=$.duration||0;if(this.completions.push({timestamp:Date.now(),duration:V}),this.completions.length>1000)this.completions.shift()}),L.on("job:failed",($)=>{if(this.jobCounts.failed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1),$.error){if(this.errors.push({error:$.error,timestamp:Date.now()}),this.errors.length>100)this.errors.shift()}}))}getThroughputPerMinute(){let L=Date.now()-60000;return this.completions.filter((V)=>V.timestamp>=L).length}getAverageProcessingTime(){let L=Date.now()-60000,$=this.completions.filter((V)=>V.timestamp>=L);if($.length===0)return 0;return $.reduce((V,Y)=>V+Y.duration,0)/$.length}getMetrics(){return{counts:{...this.jobCounts},averageDuration:this.getAverageProcessingTime(),recentErrors:[...this.errors],throughputPerMinute:this.getThroughputPerMinute()}}reset(){this.jobCounts={added:0,completed:0,failed:0,processing:0},this.completions=[],this.errors=[]}stop(){this.unsubscribe.forEach((L)=>L()),this.unsubscribe=[]}}function v1(){if(!a1)a1=new I1;return a1}class s4{workers=new Map;register(L,$){this.workers.set(L,{id:L,status:"idle",queue:$,processedCount:0,failedCount:0,lastActivityAt:new Date().toISOString(),startedAt:new Date().toISOString()})}markActive(L){let $=this.workers.get(L);if($)$.status="active",$.lastActivityAt=new Date().toISOString()}markIdle(L){let $=this.workers.get(L);if($)$.status="idle",$.lastActivityAt=new Date().toISOString()}recordCompletion(L){let $=this.workers.get(L);if($)$.processedCount++,$.lastActivityAt=new Date().toISOString()}recordFailure(L){let $=this.workers.get(L);if($)$.failedCount++,$.lastActivityAt=new Date().toISOString()}unregister(L){let $=this.workers.get(L);if($)$.status="stopped"}getAll(){return Array.from(this.workers.values())}clear(){this.workers.clear()}}function A1(){return P0}var o1=null,a1=null,P0;var k=v(()=>{P0=new s4});var l={};p(l,{recordBatchJobFailure:()=>U4,recordBatchJobCompletion:()=>G4,isBatchCancelled:()=>UL,getBatchCallbacks:()=>D1,batchRecordToHash:()=>$L,batchRecordFromHash:()=>Y4,batchCounterIncrements:()=>r4,PendingBatch:()=>w1,DispatchedBatch:()=>u,Batch:()=>e1});var{RedisClient:M0}=globalThis.Bun;import{log as T}from"@stacksjs/logging";import{env as B0}from"@stacksjs/env";function h(){return B0.QUEUE_DRIVER||"sync"}class w1{jobs;options={thenCallbacks:[],catchCallbacks:[],finallyCallbacks:[],progressCallbacks:[]};constructor(L){this.jobs=L.map(($)=>("job"in $)?$:{job:$})}name(L){return this.options.name=L,this}onQueue(L){return this.options.queue=L,this}allowFailures(){return this.options.allowFailures=!0,this}then(L){return this.options.thenCallbacks.push(L),this}catch(L){return this.options.catchCallbacks.push(L),this}finally(L){return this.options.finallyCallbacks.push(L),this}progress(L){return this.options.progressCallbacks.push(L),this}thenHandler(L){return this.options.thenHandler=L,this}catchHandler(L){return this.options.catchHandler=L,this}finallyHandler(L){return this.options.finallyHandler=L,this}async dispatch(){let L=crypto.randomUUID(),$=this.jobs.length;if($===0)throw Error("Cannot dispatch an empty batch");let V=h();await E0({id:L,name:this.options.name||"",total_jobs:$,pending_jobs:$,failed_jobs:0,failed_job_ids:"[]",options:JSON.stringify({queue:this.options.queue,allowFailures:this.options.allowFailures||!1}),cancelled_at:null,created_at:new Date().toISOString().slice(0,19).replace("T"," "),finished_at:null,then_handler:this.options.thenHandler?JSON.stringify(this.options.thenHandler):null,catch_handler:this.options.catchHandler?JSON.stringify(this.options.catchHandler):null,finally_handler:this.options.finallyHandler?JSON.stringify(this.options.finallyHandler):null}),T0(L,this.options);try{let{emitQueueEvent:Y}=await Promise.resolve().then(() => (k(),b));await Y("batch:added",{jobId:L,data:{name:this.options.name,totalJobs:$}})}catch{}for(let Y=0;Y<this.jobs.length;Y++){let G=this.jobs[Y];if(!G)continue;let{job:U,payload:K}=G,W={...K,_batchId:L,_batchIndex:Y};if(this.options.queue&&!U.queue)U.queue=this.options.queue;if(V==="sync")try{await U.dispatchNow(W),await G4(L)}catch(X){await U4(L,`${L}:${Y}`,X)}else await U.dispatch(W)}return T.info(`[Batch] Dispatched batch "${this.options.name||L}" with ${$} jobs`),new u(L)}getJobs(){return[...this.jobs]}getOptions(){return this.options}}class u{id;constructor(L){this.id=L}async fresh(){return n(this.id)}async getName(){return(await this.fresh())?.name||""}async totalJobs(){return(await this.fresh())?.total_jobs||0}async pendingJobs(){return(await this.fresh())?.pending_jobs||0}async failedJobs(){return(await this.fresh())?.failed_jobs||0}async completedJobs(){let L=await this.fresh();if(!L)return 0;return L.total_jobs-L.pending_jobs}async progress(){let L=await this.fresh();if(!L||L.total_jobs===0)return 0;let $=L.total_jobs-L.pending_jobs;return Math.round($/L.total_jobs*100)}async finished(){return(await this.fresh())?.finished_at!==null}async cancelled(){return(await this.fresh())?.cancelled_at!==null}async hasFailures(){return((await this.fresh())?.failed_jobs||0)>0}async failedJobIds(){let L=await this.fresh();if(!L)return[];try{return JSON.parse(L.failed_job_ids||"[]")}catch{return[]}}async cancel(){if(h()==="redis")await w0(this.id);else await k0(this.id);T.info(`[Batch] Cancelled batch ${this.id}`);let $=D1(this.id);if($)for(let V of $.finallyCallbacks)try{await V(this)}catch(Y){T.error(`[Batch] Error in finally callback for batch ${this.id}:`,Y)}}async add(L){let $=await this.fresh();if(!$)throw Error(`Batch ${this.id} not found`);if($.cancelled_at)throw Error(`Batch ${this.id} has been cancelled`);if($.finished_at)throw Error(`Batch ${this.id} has already finished`);let V=L.map((U)=>("job"in U)?U:{job:U});if(!await S0(this.id,V.length))throw Error(`Batch ${this.id} was cancelled, finished or deleted before the jobs could be added`);let G=JSON.parse($.options||"{}");for(let U=0;U<V.length;U++){let K=V[U];if(!K)continue;let{job:W,payload:X}=K,z={...X,_batchId:this.id,_batchIndex:$.total_jobs+U};if(G.queue&&!W.queue)W.queue=G.queue;await W.dispatch(z)}T.info(`[Batch] Added ${V.length} jobs to batch ${this.id}`)}async delete(){await R0(this.id),y1(this.id)}}class e1{static create(L){return new w1(L)}static async find(L){if(!await n(L))return null;return new u(L)}static async all(){return(await N0()).map(($)=>new u($.id))}static async prune(L=24){return C0(L)}}function T0(L,$){L4.set(L,$)}function D1(L){return L4.get(L)}function y1(L){L4.delete(L)}async function E0(L){if(h()==="redis")await v0(L);else await t4(L)}async function n(L){if(h()==="redis")return y0(L);return o4(L)}async function N0(){if(h()==="redis")return VL();return a4()}async function J0(L,$){if(h()==="redis")await YL(L,$);else await $4(L,$)}async function S0(L,$){if($===0)return!0;if(h()==="redis")try{let U=await U1(),K=`${G1}${L}`;return await U.hincrby(K,"total_jobs",$),await U.hincrby(K,"pending_jobs",$),U.close(),!0}catch{}let{db:V,sql:Y}=await import("@stacksjs/database"),G=await V.updateTable("job_batches").set(r4(Y,$)).where("id","=",L).whereNull("cancelled_at").whereNull("finished_at").executeTakeFirst();return c(G)>0}function r4(L,$){return{total_jobs:L`total_jobs + ${$}`,pending_jobs:L`pending_jobs + ${$}`}}async function R0(L){if(h()==="redis")await GL(L);else await e4(L)}async function C0(L){if(h()==="redis")return f0(L);return LL(L)}function q0(L){return!!(L.then_handler||L.catch_handler||L.finally_handler)}async function t4(L){let{db:$}=await import("@stacksjs/database"),V={id:L.id,name:L.name,total_jobs:L.total_jobs,pending_jobs:L.pending_jobs,failed_jobs:L.failed_jobs,failed_job_ids:L.failed_job_ids,options:L.options,cancelled_at:L.cancelled_at,created_at:L.created_at,finished_at:L.finished_at};if(!q0(L)){await $.insertInto("job_batches").values(V).execute();return}try{await $.insertInto("job_batches").values({...V,then_handler:L.then_handler??null,catch_handler:L.catch_handler??null,finally_handler:L.finally_handler??null}).execute()}catch(Y){T.warn(`[Batch] Could not persist terminal handlers for batch ${L.id}: ${Y?.message}. The job_batches table predates the then_handler/catch_handler/finally_handler columns; recreate it to enable handlers that survive a worker restart.`),await $.insertInto("job_batches").values(V).execute()}}async function o4(L){let{db:$}=await import("@stacksjs/database");return await $.selectFrom("job_batches").where("id","=",L).selectAll().executeTakeFirst()||null}async function a4(){let{db:L}=await import("@stacksjs/database");return await L.selectFrom("job_batches").selectAll().orderBy("created_at","desc").execute()}async function $4(L,$){let{db:V}=await import("@stacksjs/database");await V.updateTable("job_batches").set($).where("id","=",L).execute()}async function e4(L){let{db:$}=await import("@stacksjs/database");await $.deleteFrom("job_batches").where("id","=",L).execute()}async function k0(L){await $4(L,{cancelled_at:new Date().toISOString().slice(0,19).replace("T"," "),finished_at:new Date().toISOString().slice(0,19).replace("T"," ")})}async function LL(L){let{db:$}=await import("@stacksjs/database"),V=new Date(Date.now()-L*60*60*1000).toISOString().slice(0,19).replace("T"," "),Y=await $.deleteFrom("job_batches").whereNotNull("finished_at").where("finished_at","<",V).executeTakeFirst();return Number(Y?.numDeletedRows??0)}async function I0(){let{queue:L}=await import("@stacksjs/config"),$=L?.connections?.redis?.redis;if($?.url)return $.url;let V=$?.password?`:${encodeURIComponent($.password)}@`:"",Y=$?.db?`/${$.db}`:"";return`redis://${V}${$?.host||"localhost"}:${$?.port||6379}${Y}`}async function U1(){let L=new M0(await I0());return await L.connect(),L}function $L(L){return{id:L.id,name:L.name,total_jobs:String(L.total_jobs),pending_jobs:String(L.pending_jobs),failed_jobs:String(L.failed_jobs),failed_job_ids:L.failed_job_ids,options:L.options,cancelled_at:L.cancelled_at||"",created_at:L.created_at,finished_at:L.finished_at||"",then_handler:L.then_handler||"",catch_handler:L.catch_handler||"",finally_handler:L.finally_handler||""}}async function v0(L){try{let $=await U1();await $.hset(`${G1}${L.id}`,$L(L)),await $.sadd(V4,L.id),$.close()}catch{await t4(L)}}function Y4(L){if(!L?.id)return null;return{id:L.id,name:L.name??"",total_jobs:Number(L.total_jobs),pending_jobs:Number(L.pending_jobs),failed_jobs:Number(L.failed_jobs),failed_job_ids:L.failed_job_ids??"",options:L.options??"",cancelled_at:L.cancelled_at||null,created_at:L.created_at??"",finished_at:L.finished_at||null,then_handler:L.then_handler||null,catch_handler:L.catch_handler||null,finally_handler:L.finally_handler||null}}async function y0(L){try{let $=await U1(),V=`${G1}${L}`,Y=await $.hgetall(V);return $.close(),Y4(Y)}catch{return o4(L)}}async function VL(){try{let L=await U1(),$=await L.smembers(V4),V=[];for(let Y of $){let G=Y4(await L.hgetall(`${G1}${Y}`));if(G)V.push(G)}return L.close(),V}catch{return a4()}}async function YL(L,$){try{let V=await U1(),Y=`${G1}${L}`,G={};for(let[U,K]of Object.entries($))G[U]=K===null?"":String(K);await V.hset(Y,G),V.close()}catch{await $4(L,$)}}async function GL(L){try{let $=await U1();await $.del(`${G1}${L}`),await $.srem(V4,L),$.close()}catch{await e4(L)}}async function w0(L){let $=new Date().toISOString().slice(0,19).replace("T"," ");await YL(L,{cancelled_at:$,finished_at:$})}async function f0(L){try{let $=await VL(),V=Date.now()-L*60*60*1000,Y=0;for(let G of $)if(G.finished_at){if(new Date(G.finished_at).getTime()<V)await GL(G.id),Y++}return Y}catch{return LL(L)}}function V1(L){if(!L)return null;try{let $=JSON.parse(L);if($&&($.kind==="job"||$.kind==="module"))return $;return T.warn(`[Batch] handler JSON has unknown kind '${$?.kind}' \u2014 skipping`),null}catch($){return T.warn(`[Batch] failed to parse persistent handler: ${$.message}`),null}}async function Y1(L,$){try{if(L.kind==="job"){let{Jobs:G}=await Promise.resolve().then(() => (x1(),f1));await G.dispatch(L.name,{...L.payload??{},_batchId:$});return}let V=await import(L.module).catch((G)=>{return T.warn(`[Batch] persistent handler module not found: ${L.module} (${G.message})`),null});if(!V)return;let Y=V[L.export];if(typeof Y!=="function"){T.warn(`[Batch] persistent handler export '${L.export}' is not a function on ${L.module}`);return}await Y(L.payload,$)}catch(V){T.error(`[Batch] persistent handler threw for batch ${$}:`,V)}}async function G4(L){let{db:$,sql:V}=await import("@stacksjs/database");await $.updateTable("job_batches").set({pending_jobs:V`GREATEST(pending_jobs - 1, 0)`}).where("id","=",L).where("pending_jobs",">",0).execute();let Y=new Date().toISOString().slice(0,19).replace("T"," "),G=await $.updateTable("job_batches").set({finished_at:Y}).where("id","=",L).where("pending_jobs","=",0).whereNull("finished_at").executeTakeFirst(),U=c(G)>0,K=D1(L),W=new u(L);if(K)for(let X of K.progressCallbacks)try{await X(W)}catch(z){T.error(`[Batch] Error in progress callback for batch ${L}:`,z)}if(U){try{let{emitQueueEvent:O}=await Promise.resolve().then(() => (k(),b));await O("batch:completed",{jobId:L})}catch{}let X=await n(L);if(!X){y1(L),T.info(`[Batch] Batch ${L} finished (record vanished)`);return}let z=JSON.parse(X.options||"{}"),A=!((X.failed_jobs||0)>0)||z.allowFailures;if(K){if(A)for(let O of K.thenCallbacks)try{await O(W)}catch(Z){T.error(`[Batch] Error in then callback for batch ${L}:`,Z)}for(let O of K.finallyCallbacks)try{await O(W)}catch(Z){T.error(`[Batch] Error in finally callback for batch ${L}:`,Z)}y1(L)}if(A){let O=V1(X.then_handler);if(O)await Y1(O,L)}else{let O=V1(X.catch_handler);if(O)await Y1(O,L)}let x=V1(X.finally_handler);if(x)await Y1(x,L);T.info(`[Batch] Batch ${L} finished`)}}async function U4(L,$,V){let{db:Y,sql:G}=await import("@stacksjs/database"),U=await n(L);if(!U)return;let K=JSON.parse(U.options||"{}");await Y.updateTable("job_batches").set({pending_jobs:G`GREATEST(pending_jobs - 1, 0)`,failed_jobs:G`failed_jobs + 1`}).where("id","=",L).where("pending_jobs",">",0).execute();try{let Z=await n(L);if(Z){let F=[];try{F=JSON.parse(Z.failed_job_ids||"[]")}catch{F=[]}F.push($),await J0(L,{failed_job_ids:JSON.stringify(F)})}}catch{}let W=new u(L),X=D1(L);if(X)for(let Z of X.catchCallbacks)try{await Z(W,V)}catch(F){T.error(`[Batch] Error in catch callback for batch ${L}:`,F)}try{let{emitQueueEvent:Z}=await Promise.resolve().then(() => (k(),b));await Z("batch:failed",{jobId:L,error:V})}catch{}let z=new Date().toISOString().slice(0,19).replace("T"," "),_=Y.updateTable("job_batches").set(K.allowFailures?{finished_at:z}:{finished_at:z,cancelled_at:z}).where("id","=",L).whereNull("finished_at");if(K.allowFailures)_=_.where("pending_jobs","=",0);let A=await _.executeTakeFirst();if(c(A)===0)return;let x=!!K.allowFailures;if(X){if(x)for(let Z of X.thenCallbacks)try{await Z(W)}catch(F){T.error(`[Batch] Error in then callback for batch ${L}:`,F)}for(let Z of X.finallyCallbacks)try{await Z(W)}catch(F){T.error(`[Batch] Error in finally callback for batch ${L}:`,F)}y1(L)}let O=await n(L);if(O){if(x){let F=V1(O.then_handler);if(F)await Y1(F,L)}else{let F=V1(O.catch_handler);if(F)await Y1(F,L)}let Z=V1(O.finally_handler);if(Z)await Y1(Z,L)}T.info(`[Batch] Batch ${L} finished with failure(s)`)}async function UL(L){return(await n(L))?.cancelled_at!==null}var L4,G1="stacks:batch:",V4="stacks:batches";var g=v(()=>{q1();L4=new Map});var f1={};p(f1,{runJob:()=>j1,resolveJobFile:()=>ZL,jobBatch:()=>_L,job:()=>zL,Jobs:()=>WL});import{appPath as j0,frameworkPath as m0}from"@stacksjs/path";import{env as h0}from"@stacksjs/env";import{enqueueAfterCommit as g0,isInTransaction as p0}from"@stacksjs/database";function b0(){return h0.QUEUE_DRIVER||"sync"}function u0(){if(KL)return;KL=!0,console.warn("[queue] .afterCommit() was called outside of `db.transaction(...)`. Dispatching immediately. Wrap the call in a transaction or drop .afterCommit() to silence this message.")}class w{name;payload;options={};txMode="auto";constructor(L,$){this.name=L;this.payload=$}onQueue(L){return this.options.queue=L,this}delay(L){return this.options.delay=L,this}tries(L){return this.options.tries=L,this}timeout(L){return this.options.timeout=L,this}backoff(L){return this.options.backoff=L,this}withContext(L){return this.options.context=L,this}withIdempotencyKey(L){return this.options.idempotencyKey=L,this}afterCommit(){return this.txMode="after",this}withoutCommit(){return this.txMode="immediate",this}async dispatch(){let{isFaked:L,getFakeQueue:$}=await Promise.resolve().then(() => i1);if(L()){$()?.dispatch(this.name,this.payload,this.options);return}if(this.txMode!=="immediate"){if(p0()){let Y=this.runDispatchPipeline.bind(this);if(g0(async()=>{await Y()}))return}else if(this.txMode==="after")u0()}await this.runDispatchPipeline()}async runDispatchPipeline(){let L=!1;if(this.options.idempotencyKey){let $=await n1(this.options.idempotencyKey,this.name,this.options.queue);if($==="duplicate")return;L=$==="claimed"}try{if(await R1(this.name,this.payload)){let V=y(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});if(await J1({queue:this.options.queue||"default",payload:j(V),exception:`quarantined: ${this.name}`},"poison-detected"))return}let $=b0();if($==="database")await this.dispatchToDatabase();else if($==="redis")await this.dispatchToRedis();else if($==="sync")await j1(this.name,{payload:this.payload,context:this.options.context,traceId:await XL()});else if($==="sqs"||$==="memory"||$==="beanstalkd")throw Error(`[queue] Driver "${$}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);else throw Error(`[queue] Unknown QUEUE_DRIVER "${$}". Allowed values: redis, database, sync.`)}catch($){if(L&&this.options.idempotencyKey)await s1(this.options.idempotencyKey);throw $}}async dispatchIf(L){if(L)await this.dispatch()}async dispatchUnless(L){if(!L)await this.dispatch()}async dispatchToDatabase(){let L=Math.floor(Date.now()/1000),$=this.options.delay?L+this.options.delay:L,V=y(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff},await XL()),Y=j(V),{db:G}=await import("@stacksjs/database");await G.insertInto("jobs").values({queue:this.options.queue||"default",payload:Y,attempts:0,reserved_at:null,available_at:$,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(){let L=y(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});_1(L);let{RedisQueue:$}=await Promise.resolve().then(() => (F1(),O1)),{queue:V}=await import("@stacksjs/config"),Y=V?.connections?.redis;if(!Y)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new $(this.options.queue||"default",Y).add(L,{delay:this.options.delay,maxTries:this.options.tries,timeout:this.options.timeout,backoff:this.options.backoff})}async dispatchNow(){await j1(this.name,{payload:this.payload,context:this.options.context})}}function zL(L,$){return new w(L,$)}function _L(L){let{PendingBatch:$}=(g(),rL(l));return new $(L)}async function ZL(L){let $=[j0(`Jobs/${L}.ts`),m0(`defaults/app/Jobs/${L}.ts`)];try{let V=import.meta.resolve("@stacksjs/defaults/package.json"),Y=new URL(".",V).pathname;$.push(`${Y}app/Jobs/${L}.ts`)}catch{}for(let V of $)if(await Bun.file(V).exists())return V;return null}async function j1(L,$={}){let{withTraceId:V}=await import("@stacksjs/router"),Y=$.traceId??`job:${L}:${Math.random().toString(36).slice(2,10)}`;await V(Y,async()=>{let G=await ZL(L);if(!G)throw Error(`Job ${L} not found. Looked in app/Jobs/${L}.ts and the framework defaults (storage/framework/defaults/app/Jobs, @stacksjs/defaults).`);let K=(await import(G)).default;if(!K)throw Error(`Job ${L} does not export a default`);if(typeof K.handle==="function")await K.handle($.payload);else if(typeof K.action==="string"){let{runAction:W}=await import("@stacksjs/actions");await W(K.action)}else if(typeof K.action==="function")await K.action();else if(typeof K==="function")await K($.payload,$.context);else throw Error(`Job ${L} does not have a valid handler`)})}async function XL(){try{let{getTraceId:L}=await import("@stacksjs/router");return L()}catch{return}}var KL=!1,WL;var x1=v(()=>{S1();a();r1();C1();WL={make(L,$){return new w(L,$)},async dispatch(L,$){await new w(L,$).dispatch()},async dispatchIf(L,$,V){if(L)await new w($,V).dispatch()},async dispatchUnless(L,$,V){if(!L)await new w($,V).dispatch()},async dispatchNow(L,$){await new w(L,$).dispatchNow()},dispatchAfter(L,$,V){return new w($,V).delay(L)},async dispatchOnce(L,$,V){await new w($,V).withIdempotencyKey(L).dispatch()},async dispatchAfterCommit(L,$){await new w(L,$).afterCommit().dispatch()}}});var m1={};p(m1,{resumeQueue:()=>DL,recordCircuitSuccess:()=>QL,recordCircuitFailure:()=>HL,pauseQueue:()=>AL,listCircuitState:()=>xL,isCircuitOpen:()=>FL});import{db as S}from"@stacksjs/database";function s(){if(OL)return;OL=!0,console.warn("[queue/circuit-breaker] queue_circuit_state table missing \u2014 circuit breaker disabled. "+"Run migrations to enable.")}async function X4(L,$){try{let V=await S.selectFrom("queue_circuit_state").where("queue_name","=",L).selectAll().executeTakeFirst();if(V)return V;return await S.insertInto("queue_circuit_state").values({queue_name:L,success_count:0,failure_count:0,window_start:$,paused_at:null,resume_at:null}).execute(),{queue_name:L,success_count:0,failure_count:0,window_start:$,paused_at:null,resume_at:null}}catch(V){if(B(V))return s(),null;let Y=V?.message??"";if(Y.includes("UNIQUE constraint")||Y.includes("Duplicate entry"))return await S.selectFrom("queue_circuit_state").where("queue_name","=",L).selectAll().executeTakeFirst();throw V}}async function FL(L){try{let $=await S.selectFrom("queue_circuit_state").where("queue_name","=",L).select(["paused_at","resume_at"]).executeTakeFirst();if(!$||!$.paused_at)return!1;if($.resume_at){let V=Date.parse($.resume_at.replace(" ","T")+"Z");if(Number.isFinite(V)&&Date.now()>=V)return await S.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",L).execute(),!1}return!0}catch($){if(B($))return s(),!1;throw $}}async function QL(L,$={}){let V=$.windowSeconds??300,Y=new Date().toISOString().slice(0,19).replace("T"," "),G=await X4(L,Y);if(!G)return;try{if(K4(G.window_start,V)){await S.updateTable("queue_circuit_state").set({success_count:1,failure_count:0,window_start:Y}).where("queue_name","=",L).execute();return}await S.updateTable("queue_circuit_state").set({success_count:G.success_count+1}).where("queue_name","=",L).execute()}catch(U){if(B(U)){s();return}throw U}}async function HL(L,$={}){let V=$.failureRateThreshold??0.5,Y=$.windowSeconds??300,G=$.pauseSeconds??300,U=$.minObservations??10,K=new Date,W=K.toISOString().slice(0,19).replace("T"," "),X=await X4(L,W);if(!X)return!1;if(X.paused_at)return!1;try{let{success_count:z,failure_count:_}=X;if(K4(X.window_start,Y))z=0,_=0;_+=1;let A=z+_,x=A===0?0:_/A;if(A>=U&&x>=V){let Z=new Date(K.getTime()+G*1000).toISOString().slice(0,19).replace("T"," ");return await S.updateTable("queue_circuit_state").set({success_count:z,failure_count:_,window_start:W,paused_at:W,resume_at:Z}).where("queue_name","=",L).execute(),!0}return await S.updateTable("queue_circuit_state").set({success_count:z,failure_count:_,window_start:K4(X.window_start,Y)?W:X.window_start}).where("queue_name","=",L).execute(),!1}catch(z){if(B(z))return s(),!1;throw z}}function K4(L,$){if(!L)return!0;let V=Date.parse(L.replace(" ","T")+"Z");if(!Number.isFinite(V))return!0;return Date.now()-V>$*1000}async function AL(L,$=300){let V=new Date,Y=V.toISOString().slice(0,19).replace("T"," "),G=new Date(V.getTime()+$*1000).toISOString().slice(0,19).replace("T"," ");await X4(L,Y);try{await S.updateTable("queue_circuit_state").set({paused_at:Y,resume_at:G}).where("queue_name","=",L).execute()}catch(U){if(B(U)){s();return}throw U}}async function DL(L){try{await S.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",L).execute()}catch($){if(B($)){s();return}throw $}}async function xL(){try{return await S.selectFrom("queue_circuit_state").selectAll().execute()??[]}catch(L){if(B(L))return s(),[];throw L}}var OL=!1;var P1=()=>{};a();import{env as F0}from"@stacksjs/env";function I4(){return F0.QUEUE_DRIVER||"sync"}class v4{name;description;action;handle;queue;rate;tries;timeout;backoff;backoffConfig;enabled;constructor(L){this.name=L.name,this.description=L.description,this.handle=L.handle,this.queue=L.queue,this.rate=L.rate,this.action=L.action,this.tries=L.tries,this.timeout=L.timeout,this.backoff=L.backoff,this.backoffConfig=L.backoffConfig,this.enabled=L.enabled}async dispatch(L){let{isFaked:$,getFakeQueue:V}=await Promise.resolve().then(() => i1);if($()){V()?.dispatch(this.name||"UnknownJob",L,{queue:this.queue,tries:this.tries,timeout:this.timeout});return}let Y=I4();if(Y==="sync")return this.dispatchNow(L);if(Y==="redis")return this.dispatchToRedis(L);if(Y==="database")return this.dispatchToDatabase(L);if(Y==="sqs"||Y==="memory"||Y==="beanstalkd")throw Error(`[queue] Driver "${Y}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);throw Error(`[queue] Unknown QUEUE_DRIVER "${Y}". Allowed values: redis, database, sync.`)}async dispatchIf(L,$){if(L)return this.dispatch($)}async dispatchUnless(L,$){if(!L)return this.dispatch($)}async dispatchAfter(L,$){let V=I4();if(V==="redis")return this.dispatchToRedis($,{delay:L});if(V==="database")return this.dispatchToDatabase($,{delay:L});return await new Promise((Y)=>setTimeout(Y,L*1000)),await this.dispatchNow($)}async dispatchNow(L){if(typeof this.handle==="function")await this.handle(L);else if(typeof this.action==="string"){let{runAction:$}=await import("@stacksjs/actions");await $(this.action)}else if(typeof this.action==="function")await this.action();else throw Error(`Job ${this.name} does not have a valid handler`)}async dispatchToDatabase(L,$){let V=Math.floor(Date.now()/1000),Y=$?.delay?V+$.delay:V,G=y(this.name??this.constructor.name,L,{queue:this.queue??"default",tries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0}),U=j(G),{db:K}=await import("@stacksjs/database");await K.insertInto("jobs").values({queue:this.queue||"default",payload:U,attempts:0,reserved_at:null,available_at:Y,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(L,$){let V=y(this.name??this.constructor.name,L,{queue:this.queue??"default",tries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0});_1(V);let{RedisQueue:Y}=await Promise.resolve().then(() => (F1(),O1)),{queue:G}=await import("@stacksjs/config"),U=G?.connections?.redis;if(!U)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new Y(this.queue||"default",U).add(V,{delay:$?.delay,maxTries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0})}}x1();r1();S1();C1();P1();a();var z4=(L)=>`__job_progress__:${L}`,W4=(L)=>`__job_cancel__:${L}`;function l0(L){return Number.isFinite(L)?Math.max(0,Math.min(100,L)):0}async function i0(L,$,V){let{cache:Y}=await import("@stacksjs/cache");await Y.set(z4(L),{percent:l0($),message:V,updatedAt:Date.now()},3600)}async function c0(L){let{cache:$}=await import("@stacksjs/cache");return await $.get(z4(L))??null}async function d0(L){let{cache:$}=await import("@stacksjs/cache");await $.set(W4(L),1,3600)}async function n0(L){let{cache:$}=await import("@stacksjs/cache");return Boolean(await $.get(W4(L)))}async function s0(L){let{cache:$}=await import("@stacksjs/cache");await Promise.all([$.del(z4(L)),$.del(W4(L))])}import{log as K1}from"@stacksjs/logging";import*as PL from"@stacksjs/path";class ML{jobs=new Map;initialized=!1;register(L){this.jobs.set(L.name,L),K1.debug(`Registered job: ${L.name} (${L.type})`)}get(L){return this.jobs.get(L)}all(){return Array.from(this.jobs.values())}byQueue(L){return this.all().filter(($)=>$.config.queue===L)}scheduled(){return this.all().filter((L)=>L.config.rate||L.config.schedule)}has(L){return this.jobs.has(L)}clear(){this.jobs.clear(),this.initialized=!1}setInitialized(L){this.initialized=L}isInitialized(){return this.initialized}}var r=new ML;async function _4(L){let $=L||PL.userJobsPath(),V=[];try{let Y=new Bun.Glob("**/*.{ts,js}"),G={cwd:$,onlyFiles:!0,absolute:!0};for await(let U of Y.scan(G)){if(U.includes(".test.")||U.includes(".spec.")||U.endsWith("index.ts")||U.endsWith("index.js"))continue;try{let K=await r0(U);if(K)V.push(K),r.register(K)}catch(K){K1.warn(`Failed to load job from ${U}: ${K.message}`)}}return r.setInitialized(!0),K1.info(`Discovered ${V.length} jobs from ${$}`),V}catch(Y){return K1.error(`Failed to discover jobs: ${Y.message}`),[]}}async function r0(L){try{let $=await import(L),V=L.split("/").pop()?.replace(/\.(ts|js)$/,"")||"UnknownJob";if($.default&&typeof $.default==="function"){let Y=$.default;if(typeof Y.handle==="function"||typeof Y.prototype?.handle==="function"){let G=Y.config||{};return{name:G.name||V,path:L,config:{name:G.name||V,description:G.description,queue:G.queue||"default",tries:G.retries||3,timeout:G.timeout,withoutOverlapping:G.withoutOverlapping,schedule:G.schedule,retryAfter:G.retryAfter},type:"class",module:Y}}}if($.default&&typeof $.default==="object"){let Y=$.default;if(typeof Y.handle==="function"||typeof Y.action==="string")return{name:Y.name||V,path:L,config:{name:Y.name||V,description:Y.description,queue:Y.queue||"default",tries:Y.tries||3,backoff:Y.backoff,rate:Y.rate,timeout:Y.timeout||Y.timeOut,backoffConfig:Y.backoffConfig},type:"function",module:Y}}return null}catch($){return K1.debug(`Could not load job from ${L}: ${$.message}`),null}}function t0(L){return r.get(L)}function o0(){return r.all()}function Z4(){return r.scheduled()}async function a0(L,$){let V=r.get(L);if(!V)throw Error(`Job "${L}" not found. Did you run discoverJobs()?`);try{if(V.type==="class"){if(typeof V.module.handle==="function")return await V.module.handle($);return await new V.module().handle($)}else{if(typeof V.module.handle==="function")return await V.module.handle($);throw Error(`Job "${L}" does not have a handle method`)}}catch(Y){throw K1.error(`Failed to execute job "${L}": ${Y.message}`),Y}}function e0(L){let $=L.config;return{name:$.name,queue:$.queue,tries:$.tries,backoff:$.backoff,timeout:$.timeout,backoffConfig:$.backoffConfig,rate:$.rate}}import{log as E}from"@stacksjs/logging";k();import{log as TL}from"@stacksjs/logging";var BL=!1;async function EL(){if(BL)return!0;try{let{db:L}=await import("@stacksjs/database");return await L.unsafe("CREATE TABLE IF NOT EXISTS scheduled_job_runs (job_name VARCHAR(255) PRIMARY KEY, last_run_at VARCHAR(64) NOT NULL)").execute(),BL=!0,!0}catch(L){return TL.debug(`[scheduler] run-marker persistence unavailable, using in-memory lastRun: ${L instanceof Error?L.message:String(L)}`),!1}}async function NL(L){if(!await EL())return null;try{let{db:$}=await import("@stacksjs/database"),V=await $.selectFrom("scheduled_job_runs").where("job_name","=",L).select(["last_run_at"]).executeTakeFirst();if(!V?.last_run_at)return null;let Y=new Date(V.last_run_at);return Number.isNaN(Y.getTime())?null:Y}catch{return null}}async function JL(L,$){if(!await EL())return;try{let{db:V}=await import("@stacksjs/database"),Y=$.toISOString();await V.deleteFrom("scheduled_job_runs").where("job_name","=",L).execute(),await V.insertInto("scheduled_job_runs").values({job_name:L,last_run_at:Y}).execute()}catch{}}function L9(L){return`%"jobName":"${L.replace(/[\\%_]/g,(V)=>`\\${V}`)}"%`}async function SL(L){try{let{db:$}=await import("@stacksjs/database"),V=await $.unsafe("SELECT 1 AS present FROM jobs WHERE payload LIKE ? ESCAPE '\\' LIMIT 1",[L9(L)]).execute(),Y=Array.isArray(V)?V:V?.rows??[];return Array.isArray(Y)&&Y.length>0}catch($){return TL.debug(`[scheduler] overlap check unavailable, dispatching anyway: ${$ instanceof Error?$.message:String($)}`),!1}}q1();var IL={checkInterval:60000,preventOverlapping:!0},P={isRunning:!1,isShuttingDown:!1,checkInterval:null,jobs:new Map,config:{...IL}},RL=new Set;function $9(L,$){if(RL.has(L))return;RL.add(L),E.warn(`[scheduler] Cron expression "${L}" specifies seconds="${$}" but the scheduler `+"ticks at minute granularity \u2014 the seconds field is being ignored. Use a 5-field expression "+"to avoid this warning, or wait for sub-minute scheduling support.")}var CL=!1,qL=new Map;function V9(L){let $=qL.get(L);if(!$)$=new Intl.DateTimeFormat("en-US",{timeZone:L,hour12:!1,month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",weekday:"short"}),qL.set(L,$);return $}function O4(L,$){if($&&$!=="local"&&$!=="system")try{let V=V9($).formatToParts(L),Y=(K)=>V.find((W)=>W.type===K)?.value??"",G={Sun:0,Mon:1,Tue:2,Wed:3,Thu:4,Fri:5,Sat:6},U=Number(Y("hour"));if(U===24)U=0;return{minute:Number(Y("minute")),hour:U,day:Number(Y("day")),month:Number(Y("month")),dayOfWeek:G[Y("weekday")]??L.getDay()}}catch{if(!CL)CL=!0,E.warn(`[scheduler] Invalid timezone "${$}"; falling back to system local time.`)}return{minute:L.getMinutes(),hour:L.getHours(),day:L.getDate(),month:L.getMonth()+1,dayOfWeek:L.getDay()}}function Y9(L,$,V){let Y=O4(new Date,V),G=Y.minute,U=Y.hour,K=Y.day,W=Y.month,X=Y.dayOfWeek;if($){let F=O4($,V);if(F.minute===G&&F.hour===U&&F.day===K)return!1}let z=L.trim().split(/\s+/);if(z.length===6){let F=z[0];if(F&&F!=="0"&&F!=="*")$9(L,F);z=z.slice(1)}if(z.length<5)return E.warn(`Invalid cron expression: ${L}`),!1;let[_,A,x,O,Z]=z;return f(_,G,0,59)&&f(A,U,0,23)&&f(x,K,1,31)&&f(O,W,1,12)&&f(Z,X,0,6)}function f(L,$,V,Y){if(L==="*")return!0;if(L.includes(","))return L.split(",").map((K)=>Number.parseInt(K.trim(),10)).includes($);if(L.includes("-")){let[U,K]=L.split("-").map((W)=>Number.parseInt(W.trim(),10));return $>=U&&$<=K}if(L.includes("/")){let[U,K]=L.split("/"),W=Number.parseInt(K,10);if(U==="*")return $%W===0;if(U.includes("-")){let[X,z]=U.split("-").map((_)=>Number.parseInt(_.trim(),10));return $>=X&&$<=z&&($-X)%W===0}}let G=Number.parseInt(L,10);return!Number.isNaN(G)&&$===G}function vL(L){let V={"@yearly":"0 0 1 1 *","@annually":"0 0 1 1 *","@monthly":"0 0 1 * *","@weekly":"0 0 * * 0","@daily":"0 0 * * *","@midnight":"0 0 * * *","@hourly":"0 * * * *"}[L.toLowerCase()];if(V)return V;let Y=L.match(/^Every\.(\w+)$/i);if(Y&&Y[1]!==void 0){let U=Y[1].toLowerCase();return{second:"* * * * *",fiveseconds:"* * * * *",tenseconds:"* * * * *",thirtyseconds:"* * * * *",minute:"* * * * *",fiveminutes:"*/5 * * * *",tenminutes:"*/10 * * * *",fifteenminutes:"*/15 * * * *",thirtyminutes:"*/30 * * * *",hour:"0 * * * *",twohours:"0 */2 * * *",sixhours:"0 */6 * * *",twelvehours:"0 */12 * * *",day:"0 0 * * *",week:"0 0 * * 0",month:"0 0 1 * *"}[U]||null}let G=L.split(/\s+/).length;if(G>=5&&G<=6)return L;return null}function yL(L,$){let V=L.trim().split(/\s+/),Y=V.length===6?V.slice(1):V;if(Y.length<5)return null;let[G,U,K,W,X]=Y,z=new Date;z.setSeconds(0,0);let _=527040;for(let A=1;A<=_;A++){let x=new Date(z.getTime()+A*60000),O=O4(x,$);if(f(G,O.minute,0,59)&&f(U,O.hour,0,23)&&f(K,O.day,1,31)&&f(W,O.month,1,12)&&f(X,O.dayOfWeek,0,6))return x}return null}async function G9(L={}){if(P.isRunning){E.warn("Scheduler is already running");return}P.config={...IL,...L},P.isRunning=!0,P.isShuttingDown=!1,await _4();let $=Z4();for(let G of $){let U=G.config.rate||G.config.schedule;if(U){let K=vL(U);if(K){let W=await NL(G.name);P.jobs.set(G.name,{job:G,lastRun:W,nextRun:yL(K,P.config.timezone),isRunning:!1}),E.info(`Registered scheduled job: ${G.name} (${K})`)}else E.warn(`Invalid schedule for job ${G.name}: ${U}`)}}if(P.jobs.size===0){E.info("No scheduled jobs found");return}E.info(`Scheduler started with ${P.jobs.size} job(s)`),process.on("SIGINT",()=>F4()),process.on("SIGTERM",()=>F4());let V=!1,Y=()=>{if(P.isShuttingDown)return;let G=P.config.checkInterval,U=G-Date.now()%G;P.checkInterval=setTimeout(()=>{if(!P.isShuttingDown&&!V)V=!0,kL().catch((K)=>E.error("Scheduler check failed:",K)).finally(()=>{V=!1});Y()},U),P.checkInterval?.unref?.()};Y(),await kL()}async function kL(){for(let[L,$]of P.jobs){let V=$.job.config.rate||$.job.config.schedule;if(!V)continue;let Y=vL(V);if(!Y)continue;if(Y9(Y,$.lastRun,P.config.timezone)){if((P.config.preventOverlapping||$.job.config.withoutOverlapping)&&await SL(L)){E.debug(`Skipping ${L}: previous execution still running`);continue}if($.isRunning){E.debug(`Skipping ${L}: a dispatch for it is already in flight`);continue}try{$.isRunning=!0,$.lastRun=new Date,$.nextRun=yL(Y,P.config.timezone),await JL(L,$.lastRun),E.info(`Dispatching scheduled job: ${L}`),await d("job:added",{jobId:`scheduled-${L}-${Date.now()}`,queueName:$.job.config.queue||"default",jobName:L}),await t1(L,{queue:$.job.config.queue||"default",payload:{},maxTries:$.job.config.tries||3,timeout:$.job.config.timeout||60}),$.isRunning=!1,E.info(`Scheduled job ${L} dispatched to queue`)}catch(U){$.isRunning=!1,E.error(`Failed to dispatch scheduled job ${L}:`,U)}}}}async function F4(){if(!P.isRunning)return;if(E.info("Stopping scheduler..."),P.isShuttingDown=!0,P.checkInterval)clearTimeout(P.checkInterval),P.checkInterval=null;P.isRunning=!1,P.jobs.clear(),E.info("Scheduler stopped")}function U9(){return{isRunning:P.isRunning,jobCount:P.jobs.size,jobs:Array.from(P.jobs.entries()).map(([L,$])=>({name:L,schedule:$.job.config.rate||$.job.config.schedule,lastRun:$.lastRun,nextRun:$.nextRun,isRunning:$.isRunning}))}}function K9(){return P.isRunning}function X9(){return new Map(P.jobs)}async function z9(L){let $=P.jobs.get(L);if(!$)throw Error(`Scheduled job "${L}" not found`);E.info(`Manually triggering scheduled job: ${L}`),await t1(L,{queue:$.job.config.queue||"default",payload:{},maxTries:$.job.config.tries||3,timeout:$.job.config.timeout||60})}k();k();import{log as W9}from"@stacksjs/logging";var _9={maxPendingWarning:1000,maxPendingCritical:5000,maxFailedWarning:10,maxFailedCritical:100,maxJobAgeWarning:3600,maxJobAgeCritical:86400,maxErrorRateWarning:0.1,maxErrorRateCritical:0.5};async function Q4(L={}){let $={..._9,...L},V=[],Y=new Date,G=Math.floor(Y.getTime()/1000);try{let{db:U}=await import("@stacksjs/database"),K=await U.selectFrom("jobs").selectAll().execute(),W=await U.selectFrom("failed_jobs").selectAll().execute(),X=new Map;for(let H of K){let M=H.queue||"default";if(!X.has(M))X.set(M,{pending:0,processing:0,delayed:0});let J=X.get(M);if(H.reserved_at)J.processing++;else if(H.available_at&&H.available_at>G)J.delayed++;else if(J.pending++,H.created_at){let q=typeof H.created_at==="number"?H.created_at:Math.floor(new Date(H.created_at).getTime()/1000),M4=G-q;if(!J.oldestAge||M4>J.oldestAge)J.oldestAge=M4}}let z=new Map;for(let H of W){let M=H.queue||"default";z.set(M,(z.get(M)||0)+1)}let _=[],A=new Set([...X.keys(),...z.keys()]),x=$.queues?[...A].filter((H)=>$.queues.includes(H)):[...A],O=0,Z=0,F=0,I=0;for(let H of x){let M=X.get(H)||{pending:0,processing:0,delayed:0},J=z.get(H)||0;O+=M.pending,Z+=M.processing,F+=M.delayed,I+=J;let q="healthy";if(M.pending>=$.maxPendingCritical)q="unhealthy",V.push({level:"critical",message:`Queue "${H}" has ${M.pending} pending jobs (threshold: ${$.maxPendingCritical})`,queue:H,timestamp:Y.toISOString()});else if(M.pending>=$.maxPendingWarning)q="degraded",V.push({level:"warning",message:`Queue "${H}" has ${M.pending} pending jobs (threshold: ${$.maxPendingWarning})`,queue:H,timestamp:Y.toISOString()});if(J>=$.maxFailedCritical)q="unhealthy",V.push({level:"critical",message:`Queue "${H}" has ${J} failed jobs (threshold: ${$.maxFailedCritical})`,queue:H,timestamp:Y.toISOString()});else if(J>=$.maxFailedWarning){if(q==="healthy")q="degraded";V.push({level:"warning",message:`Queue "${H}" has ${J} failed jobs (threshold: ${$.maxFailedWarning})`,queue:H,timestamp:Y.toISOString()})}if(M.oldestAge){if(M.oldestAge>=$.maxJobAgeCritical)q="unhealthy",V.push({level:"critical",message:`Queue "${H}" has a job waiting for ${Math.floor(M.oldestAge/3600)} hours`,queue:H,timestamp:Y.toISOString()});else if(M.oldestAge>=$.maxJobAgeWarning){if(q==="healthy")q="degraded";V.push({level:"warning",message:`Queue "${H}" has a job waiting for ${Math.floor(M.oldestAge/60)} minutes`,queue:H,timestamp:Y.toISOString()})}}_.push({name:H,status:q,pending:M.pending,processing:M.processing,delayed:M.delayed,failed:J,oldestJobAge:M.oldestAge})}let x4=v1().getMetrics(),uL=x4.throughputPerMinute,lL=x4.averageDuration,P4=O+Z+F+I,W1=P4>0?I/P4:0,o="healthy";if(_.some((H)=>H.status==="unhealthy"))o="unhealthy";else if(_.some((H)=>H.status==="degraded"))o="degraded";if(W1>=$.maxErrorRateCritical)o="unhealthy",V.push({level:"critical",message:`Overall error rate is ${(W1*100).toFixed(1)}% (threshold: ${$.maxErrorRateCritical*100}%)`,timestamp:Y.toISOString()});else if(W1>=$.maxErrorRateWarning){if(o==="healthy")o="degraded";V.push({level:"warning",message:`Overall error rate is ${(W1*100).toFixed(1)}% (threshold: ${$.maxErrorRateWarning*100}%)`,timestamp:Y.toISOString()})}let iL=A1().getAll().map((H)=>({id:H.id,status:H.status,queue:H.queue,processedCount:H.processedCount,failedCount:H.failedCount,lastActivityAt:H.lastActivityAt}));return{status:o,timestamp:Y.toISOString(),queues:_,workers:iL,metrics:{totalPending:O,totalProcessing:Z,totalDelayed:F,totalFailed:I,throughputPerMinute:uL,averageProcessingTime:lL,errorRate:W1},alerts:V}}catch(U){W9.error("Failed to perform queue health check:",U);let W=A1().getAll().map((X)=>({id:X.id,status:X.status,queue:X.queue,processedCount:X.processedCount,failedCount:X.failedCount,lastActivityAt:X.lastActivityAt}));return{status:"unhealthy",timestamp:Y.toISOString(),queues:[],workers:W,metrics:{totalPending:0,totalProcessing:0,totalDelayed:0,totalFailed:0,throughputPerMinute:0,averageProcessingTime:0,errorRate:0},alerts:[{level:"critical",message:`Health check failed: ${U.message}`,timestamp:Y.toISOString()}]}}}function Z9(L={}){return async($)=>{let V=await Q4(L),Y=V.status==="healthy"?200:V.status==="degraded"?207:503;return new Response(JSON.stringify(V,null,2),{status:Y,headers:{"Content-Type":"application/json","Cache-Control":"no-store"}})}}async function O9(L={}){return(await Q4(L)).status==="healthy"}import{log as C}from"@stacksjs/logging";var F9={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function M1(L){return String(L).replace(/[&<>"']/g,($)=>F9[$]??$)}class H4{config;notificationCount=0;lastResetTime=Date.now();pendingBatch=[];batchTimeout=null;activeFlush=null;constructor(L){this.config=L}async notify(L){if(this.config.filter&&!this.config.filter(L))return;if(this.config.rateLimit){let $=Date.now();if($-this.lastResetTime>3600000)this.notificationCount=0,this.lastResetTime=$;if(this.notificationCount>=this.config.rateLimit){C.debug("Rate limit reached for failed job notifications");return}}if(this.config.batch){if(this.pendingBatch.push(L),!this.batchTimeout)this.batchTimeout=setTimeout(()=>{this.batchTimeout=null,this.activeFlush=this.flushBatch().catch(($)=>C.error("Failed to flush notification batch:",$)).finally(()=>{this.activeFlush=null})},this.config.batchInterval||60000),this.batchTimeout.unref?.();return}await this.sendNotifications([L])}async flushBatch(){if(this.pendingBatch.length===0)return;let L=[...this.pendingBatch];if(this.pendingBatch=[],this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;await this.sendNotifications(L)}async shutdown(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.activeFlush)await this.activeFlush.catch(()=>{});await this.flushBatch().catch((L)=>C.error("Failed to flush notification batch on shutdown:",L))}async sendNotifications(L){let $=[];for(let V of this.config.channels)switch(V){case"email":if(this.config.email)$.push(this.sendEmail(L));break;case"slack":if(this.config.slack)$.push(this.sendSlack(L));break;case"discord":if(this.config.discord)$.push(this.sendDiscord(L));break;case"webhook":if(this.config.webhook)$.push(this.sendWebhook(L));break;case"log":this.logFailures(L);break}try{await Promise.all($)}catch(V){C.error("Failed to send job failure notifications:",V)}finally{this.notificationCount+=L.length}}async sendEmail(L){let $=this.config.email,V=$.subject||`[Queue] ${L.length} Job(s) Failed`,Y=this.formatEmailBody(L);try{let{mail:G}=await import("@stacksjs/email");await G.send({to:Array.isArray($.to)?$.to:[$.to],from:$.from,subject:V,html:Y})}catch(G){C.error("Failed to send email notification:",G)}}formatEmailBody(L){let $=L.map((V)=>`
3
3
  <tr>
4
4
  <td style="padding: 8px; border: 1px solid #ddd;">${M1(String(V.id))}</td>
5
5
  <td style="padding: 8px; border: 1px solid #ddd;">${M1(V.name)}</td>
@@ -28,4 +28,4 @@ var{defineProperty:h1,getOwnPropertyNames:iL,getOwnPropertyDescriptor:cL}=Object
28
28
  </table>
29
29
  `}async sendSlack(L){let $=this.config.slack,V=[{type:"header",text:{type:"plain_text",text:`\uD83D\uDEA8 ${L.length} Job(s) Failed`,emoji:!0}},...L.slice(0,10).map((Y)=>({type:"section",text:{type:"mrkdwn",text:`*${Y.name}*
30
30
  Queue: ${Y.queue} | Attempts: ${Y.attempts}/${Y.maxAttempts}
31
- \`\`\`${Y.exception.slice(0,200)}\`\`\``}}))];if(L.length>10)V.push({type:"section",text:{type:"mrkdwn",text:`_...and ${L.length-10} more failed jobs_`}});try{let Y=await fetch($.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({channel:$.channel,username:$.username||"Queue Monitor",icon_emoji:$.iconEmoji||":warning:",blocks:V})});if(!Y.ok)C.error(`Slack notification failed with status ${Y.status}`)}catch(Y){C.error("Failed to send Slack notification:",Y)}}async sendDiscord(L){let $=this.config.discord,V=L.slice(0,10).map((Y)=>({title:`\u274C ${Y.name}`,color:15158332,fields:[{name:"Queue",value:Y.queue,inline:!0},{name:"Attempts",value:`${Y.attempts}/${Y.maxAttempts}`,inline:!0},{name:"Failed At",value:Y.failedAt.toISOString(),inline:!0},{name:"Exception",value:`\`\`\`${Y.exception.slice(0,500)}\`\`\``}],timestamp:Y.failedAt.toISOString()}));try{let Y=await fetch($.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:$.username||"Queue Monitor",avatar_url:$.avatarUrl,content:`\uD83D\uDEA8 **${L.length} Job(s) Failed**`,embeds:V})});if(!Y.ok)C.error(`Discord notification failed with status ${Y.status}`)}catch(Y){C.error("Failed to send Discord notification:",Y)}}async sendWebhook(L){let $=this.config.webhook,V={event:"jobs.failed",timestamp:new Date().toISOString(),count:L.length,jobs:L.map((G)=>({id:G.id,name:G.name,queue:G.queue,attempts:G.attempts,maxAttempts:G.maxAttempts,exception:G.exception,failedAt:G.failedAt.toISOString()}))},Y={"Content-Type":"application/json",...$.headers};if($.secret){let G=JSON.stringify(V),U=await this.generateSignature(G,$.secret);Y["X-Signature"]=U}try{let G=await fetch($.url,{method:"POST",headers:Y,body:JSON.stringify(V)});if(!G.ok)C.error(`Webhook notification failed with status ${G.status}`)}catch(G){C.error("Failed to send webhook notification:",G)}}async generateSignature(L,$){let V=new TextEncoder,Y=await crypto.subtle.importKey("raw",V.encode($),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),G=await crypto.subtle.sign("HMAC",Y,V.encode(L));return Array.from(new Uint8Array(G)).map((U)=>U.toString(16).padStart(2,"0")).join("")}logFailures(L){for(let $ of L)C.error(`[Queue] Job "${$.name}" failed on queue "${$.queue}" after ${$.attempts} attempts: ${$.exception}`)}async cleanup(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.pendingBatch.length>0)await this.flushBatch()}}var B1=null;function F9(L){return B1=new Q4(L),B1}function H9(){return B1}async function Q9(L){if(B1)await B1.notify(L)}g();a();q1();import{err as A9,ok as wL}from"@stacksjs/error-handling";import{log as D}from"@stacksjs/logging";import W1 from"process";import{env as D9}from"@stacksjs/env";W1.on("unhandledRejection",(L,$)=>{D.error(`Unhandled Rejection: ${L}`)});W1.on("uncaughtException",(L)=>{D.error(`Uncaught Exception: ${L.message}`)});var i=0,X1=!1,N="",t=new Set,fL=new Map;function hL(L){return t.add(L),L.finally(()=>t.delete(L)),L}function x9(){let L=W1.env.STACKS_QUEUE_RESERVATION_TTL_SEC,$=L===void 0?Number.NaN:Number.parseInt(L,10);return Number.isFinite($)&&$>0?$:3600}function P9(){let L=W1.env.STACKS_QUEUE_RETRY_JITTER;if(L===void 0)return 0.2;let $=Number(L);if(!Number.isFinite($)||$<0)return 0.2;return Math.min($,1)}function M9(L,$,V=Math.random){if(!(L>0)||$<=0)return L;return Math.round(L+L*$*V())}function B9(){let L=W1.env.STACKS_QUEUE_SWEEP_INTERVAL_SEC,$=L===void 0?Number.NaN:Number.parseInt(L,10);return(Number.isFinite($)&&$>0?$:60)*1000}async function jL(){let L=x9(),$=Math.floor(Date.now()/1000)-L,V=Math.floor(Date.now()/1000);try{let{db:Y}=await import("@stacksjs/database"),G=await Y.updateTable("jobs").set({reserved_at:null,available_at:V}).where("reserved_at","<=",$).executeTakeFirst(),U=c(G);if(U>0)D.warn(`[queue] Requeued ${U} job(s) whose reservation exceeded the ${L}s TTL \u2014 likely victims of a worker crash. Set `+"STACKS_QUEUE_RESERVATION_TTL_SEC to tune.");return U}catch(Y){return D.error("[queue] Reservation sweep failed",{reason:Y instanceof Error?Y.message:String(Y)}),0}}function T9(L){if(!L||typeof L!=="object")return!1;let $=L.status??L.statusCode;if(typeof $==="number"&&$>=400&&$<500)return!0;let V=L.name;if(typeof V==="string"&&(V==="ValidationError"||V==="ModelNotFoundError"))return!0;return!1}function E9(){return D9.QUEUE_DRIVER||"sync"}async function N9(L,$={}){try{D.info("Starting queue processor..."),X1=!0,N=`worker-${W1.pid}-${Date.now()}`;let V=$.concurrency||1,Y=E9(),{getWorkerTracker:G,getGlobalMetrics:U}=await Promise.resolve().then(() => (k(),b));if(U(),G().register(N,L||"default"),Y==="redis")return D.info("Using Redis queue driver (bun-queue)"),await v9(L||"default",V),wL(void 0);let K;if(L)K=[L];else if(K=await gL(),K.length===0)K=["default"];return D.info(`Processing queues: ${K.join(", ")}`),await J9(K,V),wL(void 0)}catch(V){return X1=!1,A9(V)}}async function gL(){try{let{db:L}=await import("@stacksjs/database"),V=(await L.selectFrom("jobs").select("queue").distinct().execute()).map((Y)=>Y.queue).filter((Y)=>Boolean(Y));return V.length>0?V:["default"]}catch{return["default"]}}async function J9(L,$){D.info("Listening for jobs...");let V=L,Y=Date.now(),G=1e4,U=Date.now(),K=B9();await jL();while(X1)try{let z=Date.now();if(z-Y>G){try{let X=await gL();if(X.length>0)V=X}catch{}Y=z}if(z-U>K)await jL(),U=z;for(let X of V){try{let{isCircuitOpen:_}=await Promise.resolve().then(() => (P1(),m1));if(await _(X))continue}catch{}let W=[];try{W=await S9(X,$)}catch(_){let A=Date.now(),x=fL.get(X)??0;if(A-x>60000)fL.set(X,A),D.error(`[queue] Could not reserve jobs on "${X}" \u2014 retrying each cycle:`,_);continue}await Promise.all(W.map(async(_)=>{try{D.info(`Processing job ${_.id} from queue "${X}"`),await hL(R9(_))}catch{D.error(`Unexpected error processing job ${_.id}`)}}))}await D4(1000)}catch{await D4(3000)}}async function S9(L,$){let V=Math.floor(Date.now()/1000),{db:Y}=await import("@stacksjs/database"),G=[];for(let U=0;U<$;U++){let K=await Y.selectFrom("jobs").where("queue","=",L).whereNull("reserved_at").where("available_at","<=",V).orderBy("id","asc").limit(1).selectAll().executeTakeFirst();if(!K)break;let z=await Y.updateTable("jobs").set({reserved_at:V,attempts:(K.attempts||0)+1}).where("id","=",K.id).whereNull("reserved_at").executeTakeFirst();if(c(z)>0)G.push(K)}return G}async function R9(L){let $=L.id,V=L.queue||"default";i++;let Y=Date.now(),{emitQueueEvent:G,getWorkerTracker:U}=await Promise.resolve().then(() => (k(),b)),K=U();K.markActive(N);let z;try{z=JSON.parse(L.payload||"{}").jobName}catch{}await G("job:processing",{jobId:String($),queueName:V,jobName:z});try{let _=JSON.parse(L.payload||"{}").payload?._batchId;if(_){let{isBatchCancelled:A}=await Promise.resolve().then(() => (g(),l));if(await A(_)){D.info(`[Queue] Skipping job ${$} - batch ${_} has been cancelled`),await A4($),i--,K.markIdle(N);return}}}catch{}let X=null;try{let W=JSON.parse(L.payload||"{}"),_=k9(W);if(_===void 0)await mL(W);else await I9(mL(W),_*1000,`Job ${$} exceeded ${_}s timeout`)}catch(W){X=W instanceof Error?W:Error(String(W))}if(!X)try{await A4($),D.info(`[Queue] Job ${$} completed`),K.recordCompletion(N);try{let{recordCircuitSuccess:W}=await Promise.resolve().then(() => (P1(),m1));await W(L.queue??"default")}catch{}await G("job:completed",{jobId:String($),queueName:V,duration:Date.now()-Y});try{let _=JSON.parse(L.payload||"{}").payload?._batchId;if(_){let{recordBatchJobCompletion:A}=await Promise.resolve().then(() => (g(),l));await A(_)}}catch{}}catch{D.info(`[Queue] Failed to delete completed job ${$}`)}else{let W=X.message;D.info(`[Queue] Job ${$} failed: ${W}`),K.recordFailure(N),await G("job:failed",{jobId:String($),queueName:V,error:X,duration:Date.now()-Y,attemptsMade:(L.attempts||0)+1});let _=1,A={};try{A=JSON.parse(L.payload||"{}"),_=A.options?.tries||1}catch{}let x=(L.attempts||0)+1;if(x>=_){D.info(`[Queue] Job ${$} exceeded max attempts (${x}/${_})`);let O=!1;if(A?._retriedFromFailed===!0)try{let{moveToDeadLetter:Z}=await Promise.resolve().then(() => (S1(),m4));if(O=await Z({queue:L.queue,payload:L.payload,exception:X.stack||X.message},"repeat-failure",2),O)D.info(`[Queue] Job ${$} re-failed after retry \u2014 moved to dead_letter_jobs`)}catch{O=!1}if(!O){D.info(`[Queue] Moving job ${$} to failed_jobs`);try{O=await q9(L,X,{attempts:x,maxAttempts:_,durationMs:Date.now()-Y})}catch{O=!1}}if(O)try{await A4($)}catch{D.info(`[Queue] Failed to delete failed job ${$}`)}else D.error(`[Queue] Job ${$} exhausted its retries but could NOT be persisted to failed_jobs \u2014 leaving it in the queue to avoid data loss (the reservation sweep will retry it). Check that the failed_jobs table exists and is writable.`);try{let{recordFailureForPoison:Z}=await Promise.resolve().then(() => (C1(),i4)),{recordCircuitFailure:F}=await Promise.resolve().then(() => (P1(),m1)),I=A?.jobName??"unknown";await Z(I,A?.payload),await F(L.queue??"default")}catch{}try{let Z=A.payload?._batchId;if(Z){let{recordBatchJobFailure:F}=await Promise.resolve().then(() => (g(),l));await F(Z,String($),X)}}catch{}}else{let O=A.options?.backoff,Z=30;if(Array.isArray(O)&&O.length>0){let F=Math.min(x-1,O.length-1);Z=O[F]}else if(typeof O==="number"&&O>0)Z=O;if(Z=Number(Z),!Number.isFinite(Z)||Z<0)Z=30;Z=M9(Z,P9()),D.info(`[Queue] Job ${$} will be retried in ${Z}s (attempt ${x}/${_})`);try{await C9($,Z),D.info(`[Queue] Job ${$} released for retry`)}catch{D.info(`[Queue] Failed to release job ${$} for retry`)}}}i--,K.markIdle(N)}async function A4(L){let{db:$}=await import("@stacksjs/database");await $.deleteFrom("jobs").where("id","=",L).execute()}async function C9(L,$=30){let V=Math.floor(Date.now()/1000)+$;D.debug(`Releasing job ${L} for retry at ${V}`);try{let{db:Y}=await import("@stacksjs/database");await Y.updateTable("jobs").set({reserved_at:null,available_at:V}).where("id","=",L).execute(),D.debug(`Job ${L} released successfully`)}catch{D.error(`Failed to release job ${L}`)}}async function q9(L,$,V){try{let Y=new Date().toISOString().slice(0,19).replace("T"," "),G=crypto.randomUUID(),U=$.stack||$.message,{db:K}=await import("@stacksjs/database");return await K.insertInto("failed_jobs").values({uuid:G,connection:"database",queue:L.queue,payload:L.payload,exception:U,attempts:V.attempts,max_attempts:V.maxAttempts,duration_ms:V.durationMs,failed_at:Y}).execute(),!0}catch(Y){return D.error("Failed to log failed job:",Y),!1}}function k9(L){if(!L||typeof L!=="object")return;let V=L.options?.timeout;if(typeof V!=="number"||!Number.isFinite(V)||V<=0)return;return V}async function I9(L,$,V){let Y,G=new Promise((U,K)=>{Y=setTimeout(()=>K(Error(V)),$)});try{return await Promise.race([L,G])}finally{if(Y!==void 0)clearTimeout(Y)}}async function mL(L){let $=T1(L);if(!$.ok)throw Error(`[queue] Cannot deserialize job envelope: ${$.reason}`+($.detail?` (${$.detail})`:""));let{runJob:V}=await Promise.resolve().then(() => (x1(),f1));await V($.envelope.jobName,{payload:$.envelope.payload,traceId:$.envelope.traceId})}async function v9(L,$){let{RedisQueue:V}=await Promise.resolve().then(() => (F1(),O1)),{queue:Y}=await import("@stacksjs/config"),G=Y?.connections?.redis;if(!G)throw Error("Redis queue connection is not configured. Check config/queue.ts");let U=new V(L,G),{emitQueueEvent:K,getWorkerTracker:z}=await Promise.resolve().then(() => (k(),b)),X=z(),W=async(_)=>{i++,X.markActive(N);let A=Date.now(),x=T1(_.data);if(!x.ok){i--,X.markIdle(N),D.error(`[Queue] Skipping Redis job ${_.id} - unparseable envelope: ${x.reason}${x.detail?` (${x.detail})`:""}`);return}let O=x.envelope,Z=O.payload?._batchId;if(Z)try{let{isBatchCancelled:F}=await Promise.resolve().then(() => (g(),l));if(await F(Z)){D.info(`[Queue] Skipping Redis job ${_.id} - batch ${Z} has been cancelled`),i--,X.markIdle(N);return}}catch{}await K("job:processing",{jobId:String(_.id),queueName:L});try{let{runJob:F}=await Promise.resolve().then(() => (x1(),f1));if(await F(O.jobName,{payload:O.payload,traceId:O.traceId}),X.recordCompletion(N),await K("job:completed",{jobId:String(_.id),queueName:L,duration:Date.now()-A}),Z)try{let{recordBatchJobCompletion:I}=await Promise.resolve().then(() => (g(),l));await I(Z)}catch{}D.info(`[Queue] Redis job ${_.id} completed`)}catch(F){if(X.recordFailure(N),await K("job:failed",{jobId:String(_.id),queueName:L,error:F instanceof Error?F:Error(String(F)),duration:Date.now()-A}),Z)try{let{recordBatchJobFailure:I}=await Promise.resolve().then(() => (g(),l));await I(Z,String(_.id),F instanceof Error?F:Error(String(F)))}catch{}if(D.error(`[Queue] Redis job ${_.id} failed: ${F}`),T9(F)){D.info(`[Queue] Redis job ${_.id} hit a non-retryable error \u2014 skipping retry`);return}throw F}finally{i--,X.markIdle(N)}};U.process($,(_)=>hL(W(_))),D.info(`Listening for Redis jobs on queue "${L}" with concurrency ${$}...`);while(X1)await D4(1000);await U.close()}async function y9(L={}){let $=L.graceMs??1e4;if(X1=!1,t.size>0&&$>0){D.info(`[queue] Draining ${t.size} in-flight job(s) (grace ${$}ms)`);let V=Promise.allSettled([...t]),Y=new Promise((U)=>setTimeout(()=>U("timeout"),$));if(await Promise.race([V.then(()=>"drained"),Y])==="timeout"&&t.size>0)D.warn(`[queue] Drain timed out with ${t.size} job(s) still active. Their reservations will be reclaimed by the next worker's sweep (Q-2).`)}if(N){let{getWorkerTracker:V}=await Promise.resolve().then(() => (k(),b));V().unregister(N)}D.info("Queue processor stopped")}async function w9(){let{db:L}=await import("@stacksjs/database"),$=await L.selectFrom("failed_jobs").selectAll().execute();for(let V of $)await pL(Number(V.id))}async function pL(L){let $=Math.floor(Date.now()/1000),V=new Date().toISOString().slice(0,19).replace("T"," "),{db:Y}=await import("@stacksjs/database"),U=(await Y.selectFrom("failed_jobs").where("id","=",L).selectAll().execute())[0];if(!U)throw Error(`Failed job ${L} not found`);let K=U.payload;try{let z=JSON.parse(U.payload||"{}");z._retriedFromFailed=!0,K=JSON.stringify(z)}catch{}await Y.insertInto("jobs").values({queue:U.queue,payload:K,attempts:0,reserved_at:null,available_at:$,created_at:V}).execute(),await Y.deleteFrom("failed_jobs").where("id","=",L).execute(),D.info(`Failed job ${L} has been re-queued`)}function f9(){return i}function j9(){return X1}function D4(L){return new Promise(($)=>setTimeout($,L))}async function g$(){let{RedisQueue:L}=await Promise.resolve().then(() => (F1(),O1));return L}export{d4 as withEvents,u4 as unquarantineJob,X9 as triggerJob,a0 as toJobOptions,F4 as stopScheduler,y9 as stopProcessor,Y9 as startScheduler,N9 as startProcessor,l0 as setJobProgress,j as serializeEnvelope,R4 as runTestJob,j1 as runJob,pL as retryFailedJob,f4 as retryDeadLetterJob,DL as resumeQueue,l1 as restore,s1 as releaseDispatchKey,p4 as recordFailureForPoison,H0 as recordDispatchedKey,HL as recordCircuitSuccess,QL as recordCircuitFailure,U4 as recordBatchJobFailure,G4 as recordBatchJobCompletion,b4 as quarantineJob,j4 as purgeDeadLetterJobs,AL as pauseQueue,T1 as parseEnvelope,c4 as onQueueEvent,Q9 as notifyJobFailed,J1 as moveToDeadLetter,l4 as listQuarantined,w4 as listDeadLetterJobs,xL as listCircuitState,r as jobRegistry,_L as jobBatch,WL as job,j9 as isWorkerRunning,U9 as isSchedulerRunning,Z9 as isQueueHealthy,R1 as isQuarantined,d0 as isJobCancelled,J4 as isFaked,FL as isCircuitOpen,UL as isBatchCancelled,Q1 as hashPayload,F0 as hasDispatchedKey,A1 as getWorkerTracker,G9 as getSchedulerStatus,Z4 as getScheduledJobs,K9 as getRegisteredJobs,g$ as getRedisQueue,$1 as getQueueEvents,i0 as getJobProgress,r0 as getJob,v1 as getGlobalMetrics,N4 as getFakeQueue,H9 as getFailedJobNotifier,D1 as getBatchCallbacks,t0 as getAllJobs,f9 as getActiveJobCount,u1 as fake,C4 as expectJobToFail,o0 as executeJob,w9 as executeFailedJobs,d as emitQueueEvent,_4 as discoverJobs,S4 as createQueueTester,_9 as createHealthCheckHandler,y as createEnvelope,F9 as configureFailedJobNotifications,n0 as clearJobState,$0 as clearEnvelopeWarnings,n1 as claimDispatchKey,H4 as checkQueueHealth,c0 as cancelJob,_1 as assertEnvelopeSerializable,E1 as QueueTester,I1 as QueueMetrics,k1 as QueueEvents,w1 as PendingBatch,n4 as OnQueueEvent,zL as Jobs,v4 as Job,oL as JOB_ENVELOPE_VERSION,Q4 as FailedJobNotifier,u as DispatchedBatch,e1 as Batch};
31
+ \`\`\`${Y.exception.slice(0,200)}\`\`\``}}))];if(L.length>10)V.push({type:"section",text:{type:"mrkdwn",text:`_...and ${L.length-10} more failed jobs_`}});try{let Y=await fetch($.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({channel:$.channel,username:$.username||"Queue Monitor",icon_emoji:$.iconEmoji||":warning:",blocks:V})});if(!Y.ok)C.error(`Slack notification failed with status ${Y.status}`)}catch(Y){C.error("Failed to send Slack notification:",Y)}}async sendDiscord(L){let $=this.config.discord,V=L.slice(0,10).map((Y)=>({title:`\u274C ${Y.name}`,color:15158332,fields:[{name:"Queue",value:Y.queue,inline:!0},{name:"Attempts",value:`${Y.attempts}/${Y.maxAttempts}`,inline:!0},{name:"Failed At",value:Y.failedAt.toISOString(),inline:!0},{name:"Exception",value:`\`\`\`${Y.exception.slice(0,500)}\`\`\``}],timestamp:Y.failedAt.toISOString()}));try{let Y=await fetch($.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:$.username||"Queue Monitor",avatar_url:$.avatarUrl,content:`\uD83D\uDEA8 **${L.length} Job(s) Failed**`,embeds:V})});if(!Y.ok)C.error(`Discord notification failed with status ${Y.status}`)}catch(Y){C.error("Failed to send Discord notification:",Y)}}async sendWebhook(L){let $=this.config.webhook,V={event:"jobs.failed",timestamp:new Date().toISOString(),count:L.length,jobs:L.map((G)=>({id:G.id,name:G.name,queue:G.queue,attempts:G.attempts,maxAttempts:G.maxAttempts,exception:G.exception,failedAt:G.failedAt.toISOString()}))},Y={"Content-Type":"application/json",...$.headers};if($.secret){let G=JSON.stringify(V),U=await this.generateSignature(G,$.secret);Y["X-Signature"]=U}try{let G=await fetch($.url,{method:"POST",headers:Y,body:JSON.stringify(V)});if(!G.ok)C.error(`Webhook notification failed with status ${G.status}`)}catch(G){C.error("Failed to send webhook notification:",G)}}async generateSignature(L,$){let V=new TextEncoder,Y=await crypto.subtle.importKey("raw",V.encode($),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),G=await crypto.subtle.sign("HMAC",Y,V.encode(L));return Array.from(new Uint8Array(G)).map((U)=>U.toString(16).padStart(2,"0")).join("")}logFailures(L){for(let $ of L)C.error(`[Queue] Job "${$.name}" failed on queue "${$.queue}" after ${$.attempts} attempts: ${$.exception}`)}async cleanup(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.pendingBatch.length>0)await this.flushBatch()}}var B1=null;function Q9(L){return B1=new H4(L),B1}function H9(){return B1}async function A9(L){if(B1)await B1.notify(L)}g();a();q1();import{err as D9,ok as wL}from"@stacksjs/error-handling";import{log as D}from"@stacksjs/logging";import X1 from"process";import{env as P9}from"@stacksjs/env";var fL=!1;function x9(){if(fL)return;fL=!0,X1.on("unhandledRejection",(L,$)=>{D.error(`Unhandled Rejection: ${L}`)}),X1.on("uncaughtException",(L)=>{D.error(`Uncaught Exception: ${L.message}`)})}var i=0,z1=!1,N="",t=new Set,jL=new Map;function gL(L){return t.add(L),L.finally(()=>t.delete(L)),L}function M9(){let L=X1.env.STACKS_QUEUE_RESERVATION_TTL_SEC,$=L===void 0?Number.NaN:Number.parseInt(L,10);return Number.isFinite($)&&$>0?$:3600}function B9(){let L=X1.env.STACKS_QUEUE_RETRY_JITTER;if(L===void 0)return 0.2;let $=Number(L);if(!Number.isFinite($)||$<0)return 0.2;return Math.min($,1)}function T9(L,$,V=Math.random){if(!(L>0)||$<=0)return L;return Math.round(L+L*$*V())}function E9(){let L=X1.env.STACKS_QUEUE_SWEEP_INTERVAL_SEC,$=L===void 0?Number.NaN:Number.parseInt(L,10);return(Number.isFinite($)&&$>0?$:60)*1000}async function mL(){let L=M9(),$=Math.floor(Date.now()/1000)-L,V=Math.floor(Date.now()/1000);try{let{db:Y}=await import("@stacksjs/database"),G=await Y.updateTable("jobs").set({reserved_at:null,available_at:V}).where("reserved_at","<=",$).executeTakeFirst(),U=c(G);if(U>0)D.warn(`[queue] Requeued ${U} job(s) whose reservation exceeded the ${L}s TTL \u2014 likely victims of a worker crash. Set `+"STACKS_QUEUE_RESERVATION_TTL_SEC to tune.");return U}catch(Y){return D.error("[queue] Reservation sweep failed",{reason:Y instanceof Error?Y.message:String(Y)}),0}}function N9(L){if(!L||typeof L!=="object")return!1;let $=L.status??L.statusCode;if(typeof $==="number"&&$>=400&&$<500)return!0;let V=L.name;if(typeof V==="string"&&(V==="ValidationError"||V==="ModelNotFoundError"))return!0;return!1}function J9(){return P9.QUEUE_DRIVER||"sync"}async function S9(L,$={}){try{D.info("Starting queue processor..."),x9(),z1=!0,N=`worker-${X1.pid}-${Date.now()}`;let V=$.concurrency||1,Y=J9(),{getWorkerTracker:G,getGlobalMetrics:U}=await Promise.resolve().then(() => (k(),b));if(U(),G().register(N,L||"default"),Y==="redis")return D.info("Using Redis queue driver (bun-queue)"),await w9(L||"default",V),wL(void 0);let K;if(L)K=[L];else if(K=await pL(),K.length===0)K=["default"];return D.info(`Processing queues: ${K.join(", ")}`),await R9(K,V),wL(void 0)}catch(V){return z1=!1,D9(V)}}async function pL(){try{let{db:L}=await import("@stacksjs/database"),V=(await L.selectFrom("jobs").select("queue").distinct().execute()).map((Y)=>Y.queue).filter((Y)=>Boolean(Y));return V.length>0?V:["default"]}catch{return["default"]}}async function R9(L,$){D.info("Listening for jobs...");let V=L,Y=Date.now(),G=1e4,U=Date.now(),K=E9();await mL();while(z1)try{let W=Date.now();if(W-Y>G){try{let X=await pL();if(X.length>0)V=X}catch{}Y=W}if(W-U>K)await mL(),U=W;for(let X of V){try{let{isCircuitOpen:_}=await Promise.resolve().then(() => (P1(),m1));if(await _(X))continue}catch{}let z=[];try{z=await C9(X,$)}catch(_){let A=Date.now(),x=jL.get(X)??0;if(A-x>60000)jL.set(X,A),D.error(`[queue] Could not reserve jobs on "${X}" \u2014 retrying each cycle:`,_);continue}await Promise.all(z.map(async(_)=>{try{D.info(`Processing job ${_.id} from queue "${X}"`),await gL(q9(_))}catch{D.error(`Unexpected error processing job ${_.id}`)}}))}await D4(1000)}catch{await D4(3000)}}async function C9(L,$){let V=Math.floor(Date.now()/1000),{db:Y}=await import("@stacksjs/database"),G=[];for(let U=0;U<$;U++){let K=await Y.selectFrom("jobs").where("queue","=",L).whereNull("reserved_at").where("available_at","<=",V).orderBy("id","asc").limit(1).selectAll().executeTakeFirst();if(!K)break;let W=await Y.updateTable("jobs").set({reserved_at:V,attempts:(K.attempts||0)+1}).where("id","=",K.id).whereNull("reserved_at").executeTakeFirst();if(c(W)>0)G.push(K)}return G}async function q9(L){let $=L.id,V=L.queue||"default";i++;let Y=Date.now(),{emitQueueEvent:G,getWorkerTracker:U}=await Promise.resolve().then(() => (k(),b)),K=U();K.markActive(N);let W;try{W=JSON.parse(L.payload||"{}").jobName}catch{}await G("job:processing",{jobId:String($),queueName:V,jobName:W});try{let _=JSON.parse(L.payload||"{}").payload?._batchId;if(_){let{isBatchCancelled:A}=await Promise.resolve().then(() => (g(),l));if(await A(_)){D.info(`[Queue] Skipping job ${$} - batch ${_} has been cancelled`),await A4($),i--,K.markIdle(N);return}}}catch{}let X=null;try{let z=JSON.parse(L.payload||"{}"),_=v9(z);if(_===void 0)await hL(z);else await y9(hL(z),_*1000,`Job ${$} exceeded ${_}s timeout`)}catch(z){X=z instanceof Error?z:Error(String(z))}if(!X)try{await A4($),D.info(`[Queue] Job ${$} completed`),K.recordCompletion(N);try{let{recordCircuitSuccess:z}=await Promise.resolve().then(() => (P1(),m1));await z(L.queue??"default")}catch{}await G("job:completed",{jobId:String($),queueName:V,duration:Date.now()-Y});try{let _=JSON.parse(L.payload||"{}").payload?._batchId;if(_){let{recordBatchJobCompletion:A}=await Promise.resolve().then(() => (g(),l));await A(_)}}catch{}}catch{D.info(`[Queue] Failed to delete completed job ${$}`)}else{let z=X.message;D.info(`[Queue] Job ${$} failed: ${z}`),K.recordFailure(N),await G("job:failed",{jobId:String($),queueName:V,error:X,duration:Date.now()-Y,attemptsMade:(L.attempts||0)+1});let _=1,A={};try{A=JSON.parse(L.payload||"{}"),_=A.options?.tries||1}catch{}let x=(L.attempts||0)+1;if(x>=_){D.info(`[Queue] Job ${$} exceeded max attempts (${x}/${_})`);let O=!1;if(A?._retriedFromFailed===!0)try{let{moveToDeadLetter:Z}=await Promise.resolve().then(() => (S1(),m4));if(O=await Z({queue:L.queue,payload:L.payload,exception:X.stack||X.message},"repeat-failure",2),O)D.info(`[Queue] Job ${$} re-failed after retry \u2014 moved to dead_letter_jobs`)}catch{O=!1}if(!O){D.info(`[Queue] Moving job ${$} to failed_jobs`);try{O=await I9(L,X,{attempts:x,maxAttempts:_,durationMs:Date.now()-Y})}catch{O=!1}}if(O)try{await A4($)}catch{D.info(`[Queue] Failed to delete failed job ${$}`)}else D.error(`[Queue] Job ${$} exhausted its retries but could NOT be persisted to failed_jobs \u2014 leaving it in the queue to avoid data loss (the reservation sweep will retry it). Check that the failed_jobs table exists and is writable.`);try{let{recordFailureForPoison:Z}=await Promise.resolve().then(() => (C1(),i4)),{recordCircuitFailure:F}=await Promise.resolve().then(() => (P1(),m1)),I=A?.jobName??"unknown";await Z(I,A?.payload),await F(L.queue??"default")}catch{}try{let Z=A.payload?._batchId;if(Z){let{recordBatchJobFailure:F}=await Promise.resolve().then(() => (g(),l));await F(Z,String($),X)}}catch{}}else{let O=A.options?.backoff,Z=30;if(Array.isArray(O)&&O.length>0){let F=Math.min(x-1,O.length-1);Z=O[F]}else if(typeof O==="number"&&O>0)Z=O;if(Z=Number(Z),!Number.isFinite(Z)||Z<0)Z=30;Z=T9(Z,B9()),D.info(`[Queue] Job ${$} will be retried in ${Z}s (attempt ${x}/${_})`);try{await k9($,Z),D.info(`[Queue] Job ${$} released for retry`)}catch{D.info(`[Queue] Failed to release job ${$} for retry`)}}}i--,K.markIdle(N)}async function A4(L){let{db:$}=await import("@stacksjs/database");await $.deleteFrom("jobs").where("id","=",L).execute()}async function k9(L,$=30){let V=Math.floor(Date.now()/1000)+$;D.debug(`Releasing job ${L} for retry at ${V}`);try{let{db:Y}=await import("@stacksjs/database");await Y.updateTable("jobs").set({reserved_at:null,available_at:V}).where("id","=",L).execute(),D.debug(`Job ${L} released successfully`)}catch{D.error(`Failed to release job ${L}`)}}async function I9(L,$,V){try{let Y=new Date().toISOString().slice(0,19).replace("T"," "),G=crypto.randomUUID(),U=$.stack||$.message,{db:K}=await import("@stacksjs/database");return await K.insertInto("failed_jobs").values({uuid:G,connection:"database",queue:L.queue,payload:L.payload,exception:U,attempts:V.attempts,max_attempts:V.maxAttempts,duration_ms:V.durationMs,failed_at:Y}).execute(),!0}catch(Y){return D.error("Failed to log failed job:",Y),!1}}function v9(L){if(!L||typeof L!=="object")return;let V=L.options?.timeout;if(typeof V!=="number"||!Number.isFinite(V)||V<=0)return;return V}async function y9(L,$,V){let Y,G=new Promise((U,K)=>{Y=setTimeout(()=>K(Error(V)),$)});try{return await Promise.race([L,G])}finally{if(Y!==void 0)clearTimeout(Y)}}async function hL(L){let $=T1(L);if(!$.ok)throw Error(`[queue] Cannot deserialize job envelope: ${$.reason}`+($.detail?` (${$.detail})`:""));let{runJob:V}=await Promise.resolve().then(() => (x1(),f1));await V($.envelope.jobName,{payload:$.envelope.payload,traceId:$.envelope.traceId})}async function w9(L,$){let{RedisQueue:V}=await Promise.resolve().then(() => (F1(),O1)),{queue:Y}=await import("@stacksjs/config"),G=Y?.connections?.redis;if(!G)throw Error("Redis queue connection is not configured. Check config/queue.ts");let U=new V(L,G),{emitQueueEvent:K,getWorkerTracker:W}=await Promise.resolve().then(() => (k(),b)),X=W(),z=async(_)=>{i++,X.markActive(N);let A=Date.now(),x=T1(_.data);if(!x.ok){i--,X.markIdle(N),D.error(`[Queue] Skipping Redis job ${_.id} - unparseable envelope: ${x.reason}${x.detail?` (${x.detail})`:""}`);return}let O=x.envelope,Z=O.payload?._batchId;if(Z)try{let{isBatchCancelled:F}=await Promise.resolve().then(() => (g(),l));if(await F(Z)){D.info(`[Queue] Skipping Redis job ${_.id} - batch ${Z} has been cancelled`),i--,X.markIdle(N);return}}catch{}await K("job:processing",{jobId:String(_.id),queueName:L});try{let{runJob:F}=await Promise.resolve().then(() => (x1(),f1));if(await F(O.jobName,{payload:O.payload,traceId:O.traceId}),X.recordCompletion(N),await K("job:completed",{jobId:String(_.id),queueName:L,duration:Date.now()-A}),Z)try{let{recordBatchJobCompletion:I}=await Promise.resolve().then(() => (g(),l));await I(Z)}catch{}D.info(`[Queue] Redis job ${_.id} completed`)}catch(F){if(X.recordFailure(N),await K("job:failed",{jobId:String(_.id),queueName:L,error:F instanceof Error?F:Error(String(F)),duration:Date.now()-A}),Z)try{let{recordBatchJobFailure:I}=await Promise.resolve().then(() => (g(),l));await I(Z,String(_.id),F instanceof Error?F:Error(String(F)))}catch{}if(D.error(`[Queue] Redis job ${_.id} failed: ${F}`),N9(F)){D.info(`[Queue] Redis job ${_.id} hit a non-retryable error \u2014 skipping retry`);return}throw F}finally{i--,X.markIdle(N)}};U.process($,(_)=>gL(z(_))),D.info(`Listening for Redis jobs on queue "${L}" with concurrency ${$}...`);while(z1)await D4(1000);await U.close()}async function f9(L={}){let $=L.graceMs??1e4;if(z1=!1,t.size>0&&$>0){D.info(`[queue] Draining ${t.size} in-flight job(s) (grace ${$}ms)`);let V=Promise.allSettled([...t]),Y=new Promise((U)=>setTimeout(()=>U("timeout"),$));if(await Promise.race([V.then(()=>"drained"),Y])==="timeout"&&t.size>0)D.warn(`[queue] Drain timed out with ${t.size} job(s) still active. Their reservations will be reclaimed by the next worker's sweep (Q-2).`)}if(N){let{getWorkerTracker:V}=await Promise.resolve().then(() => (k(),b));V().unregister(N)}D.info("Queue processor stopped")}async function j9(){let{db:L}=await import("@stacksjs/database"),$=await L.selectFrom("failed_jobs").selectAll().execute();for(let V of $)await bL(Number(V.id))}async function bL(L){let $=Math.floor(Date.now()/1000),V=new Date().toISOString().slice(0,19).replace("T"," "),{db:Y}=await import("@stacksjs/database"),U=(await Y.selectFrom("failed_jobs").where("id","=",L).selectAll().execute())[0];if(!U)throw Error(`Failed job ${L} not found`);let K=U.payload;try{let W=JSON.parse(U.payload||"{}");W._retriedFromFailed=!0,K=JSON.stringify(W)}catch{}await Y.insertInto("jobs").values({queue:U.queue,payload:K,attempts:0,reserved_at:null,available_at:$,created_at:V}).execute(),await Y.deleteFrom("failed_jobs").where("id","=",L).execute(),D.info(`Failed job ${L} has been re-queued`)}function m9(){return i}function h9(){return z1}function D4(L){return new Promise(($)=>setTimeout($,L))}async function b$(){let{RedisQueue:L}=await Promise.resolve().then(() => (F1(),O1));return L}export{d4 as withEvents,u4 as unquarantineJob,z9 as triggerJob,e0 as toJobOptions,F4 as stopScheduler,f9 as stopProcessor,G9 as startScheduler,S9 as startProcessor,i0 as setJobProgress,j as serializeEnvelope,R4 as runTestJob,j1 as runJob,bL as retryFailedJob,f4 as retryDeadLetterJob,DL as resumeQueue,l1 as restore,s1 as releaseDispatchKey,p4 as recordFailureForPoison,H0 as recordDispatchedKey,QL as recordCircuitSuccess,HL as recordCircuitFailure,U4 as recordBatchJobFailure,G4 as recordBatchJobCompletion,b4 as quarantineJob,j4 as purgeDeadLetterJobs,AL as pauseQueue,T1 as parseEnvelope,c4 as onQueueEvent,A9 as notifyJobFailed,J1 as moveToDeadLetter,l4 as listQuarantined,w4 as listDeadLetterJobs,xL as listCircuitState,r as jobRegistry,_L as jobBatch,zL as job,h9 as isWorkerRunning,K9 as isSchedulerRunning,O9 as isQueueHealthy,R1 as isQuarantined,n0 as isJobCancelled,J4 as isFaked,FL as isCircuitOpen,UL as isBatchCancelled,H1 as hashPayload,Q0 as hasDispatchedKey,A1 as getWorkerTracker,U9 as getSchedulerStatus,Z4 as getScheduledJobs,X9 as getRegisteredJobs,b$ as getRedisQueue,$1 as getQueueEvents,c0 as getJobProgress,t0 as getJob,v1 as getGlobalMetrics,N4 as getFakeQueue,H9 as getFailedJobNotifier,D1 as getBatchCallbacks,o0 as getAllJobs,m9 as getActiveJobCount,u1 as fake,C4 as expectJobToFail,a0 as executeJob,j9 as executeFailedJobs,d as emitQueueEvent,_4 as discoverJobs,S4 as createQueueTester,Z9 as createHealthCheckHandler,y as createEnvelope,Q9 as configureFailedJobNotifications,s0 as clearJobState,V0 as clearEnvelopeWarnings,n1 as claimDispatchKey,Q4 as checkQueueHealth,d0 as cancelJob,_1 as assertEnvelopeSerializable,E1 as QueueTester,I1 as QueueMetrics,k1 as QueueEvents,w1 as PendingBatch,n4 as OnQueueEvent,WL as Jobs,v4 as Job,aL as JOB_ENVELOPE_VERSION,H4 as FailedJobNotifier,u as DispatchedBatch,e1 as Batch};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/queue",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.371",
5
+ "version": "0.70.375",
6
6
  "description": "The Stacks Queue system powered by bun-queue.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [