@stacksjs/queue 0.74.31 → 0.74.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +7 -7
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var q=(e,t,n)=>()=>{if(e)try{t=e(e=0)}catch(r){n=[r]}if(n)throw n[0];return t};function A(e,t,n,r){return{jobName:e,payload:t,options:n,envelopeVersion:1,dispatchedAt:new Date().toISOString(),...r?{traceId:r}:{}}}function I(e){try{return JSON.stringify(e)}catch(t){throw yn(e,t)}}function le(e){I(e)}function yn(e,t){if(vn(t)){let i=null;try{i=Ne(e,"",new Set)}catch{}let o=i?`\`${i.path}\` is a BigInt (${i.value}n)`:"it holds a BigInt";return Error(`[queue] Job "${e.jobName}" was not dispatched: ${o}, and job envelopes travel as JSON, which cannot represent one. Convert it where you dispatch - \`String(value)\` keeps every digit, \`Number(value)\` is exact below 2^53 - and convert it back inside the job handler. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.`,{cause:t})}if(_n(t))return Error(`[queue] Job "${e.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 - 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:t});let r=t instanceof Error?t.message:String(t);return Error(`[queue] Job "${e.jobName}" was not dispatched: serializing its payload threw (${r}). JSON rejects only two things on its own and this was neither, so the throw came from the payload - a \`toJSON()\` or a property getter. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.`,{cause:t})}function vn(e){return e instanceof TypeError&&/bigint/i.test(e.message)}function _n(e){return e instanceof TypeError&&/circular|cyclic/i.test(e.message)}function Ne(e,t,n){if(typeof e==="bigint")return{path:t||"envelope",value:e};if(!e||typeof e!=="object")return null;if(n.has(e))return null;if(n.add(e),Array.isArray(e)){for(let r=0;r<e.length;r++){let i=Ne(e[r],`${t}[${r}]`,n);if(i)return i}return null}for(let[r,i]of Object.entries(e)){let o=Ne(i,t?`${t}.${r}`:r,n);if(o)return o}return null}function vt(e,t){if(Ae.has(e))return;Ae.add(e),console.warn(t)}function kn(){Ae.clear()}function je(e){let t;if(typeof e==="string")try{t=JSON.parse(e)}catch{return{ok:!1,reason:"malformed",detail:"not valid JSON"}}else if(e&&typeof e==="object")t=e;else return{ok:!1,reason:"malformed",detail:`expected string or object, got ${typeof e}`};if(t.envelopeVersion===1){if(typeof t.jobName!=="string")return{ok:!1,reason:"missing-job-name"};return{ok:!0,envelope:{jobName:t.jobName,payload:t.payload,options:t.options??void 0,envelopeVersion:1,dispatchedAt:typeof t.dispatchedAt==="string"?t.dispatchedAt:new Date(0).toISOString(),...typeof t.traceId==="string"&&t.traceId?{traceId:t.traceId}:{}},source:"v1"}}if(typeof t.envelopeVersion==="number"&&t.envelopeVersion>1)return{ok:!1,reason:"unknown-version",detail:`envelopeVersion=${t.envelopeVersion}, this worker speaks v1`};if(typeof t.jobName==="string")return vt("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:t.jobName,payload:t.payload,options:t.options??void 0,envelopeVersion:1,dispatchedAt:typeof t.dispatchedAt==="string"?t.dispatchedAt:new Date(0).toISOString(),...typeof t.traceId==="string"&&t.traceId?{traceId:t.traceId}:{}},source:"v0-implicit"};if(typeof t.job==="string")return vt("laravel-legacy","[queue/envelope] Processing Laravel-legacy job envelope (`{ job, data }` shape). Will continue to process but the queue table contains migration-era rows - consider flushing once they drain."),{ok:!0,envelope:{jobName:t.job.replace(/^App\\+Jobs\\+/,"").replace(/^.*\\/,""),payload:t.data,options:void 0,envelopeVersion:1,dispatchedAt:new Date(0).toISOString()},source:"laravel-legacy"};return{ok:!1,reason:"missing-job-name"}}var wn=1,Ae;var ee=q(()=>{Ae=new Set});import{getActionRunner as xn,runNamedAction as Pe,setActionRunner as Jn}from"@stacksjs/action-runner";var Se=()=>{};class _t{dispatchedJobs=[];pushedJobs=[];processedJobs=[];failedJobs=[];dispatch(e,t,n={}){this.dispatchedJobs.push({name:e,data:t,options:n,dispatchedAt:new Date,queue:n.queue||"default"})}push(e,t,n={}){this.pushedJobs.push({name:e,data:t,options:n,dispatchedAt:new Date,queue:n.queue||"default"})}dispatched(e){if(e)return this.dispatchedJobs.filter((t)=>t.name===e);return[...this.dispatchedJobs]}pushed(e){if(e)return this.pushedJobs.filter((t)=>t.name===e);return[...this.pushedJobs]}assertDispatched(e,t){let n=this.dispatched(e);if(n.length===0)throw Error(`Expected job "${e}" to be dispatched, but it was not.`);if(t){if(n.filter(t).length===0)throw Error(`Expected job "${e}" to be dispatched matching the callback, but no matching jobs were found.`)}}assertNotDispatched(e){let t=this.dispatched(e);if(t.length>0)throw Error(`Expected job "${e}" to not be dispatched, but it was dispatched ${t.length} time(s).`)}assertDispatchedTimes(e,t){let n=this.dispatched(e);if(n.length!==t)throw Error(`Expected job "${e}" to be dispatched ${t} time(s), but it was dispatched ${n.length} time(s).`)}assertNothingDispatched(){if(this.dispatchedJobs.length>0){let e=[...new Set(this.dispatchedJobs.map((t)=>t.name))].join(", ");throw Error(`Expected no jobs to be dispatched, but found: ${e}`)}}assertPushed(e,t){let n=this.pushed(e);if(n.length===0)throw Error(`Expected job "${e}" to be pushed, but it was not.`);if(t){if(n.filter(t).length===0)throw Error(`Expected job "${e}" to be pushed matching the callback, but no matching jobs were found.`)}}assertPushedWithDelay(e,t){if(this.pushed(e).filter((i)=>i.options?.delay===t).length===0)throw Error(`Expected job "${e}" to be pushed with delay ${t}ms, but no matching jobs were found.`)}assertPushedOn(e,t){if(this.pushed(t).filter((r)=>r.queue===e).length===0)throw Error(`Expected job "${t}" to be pushed on queue "${e}", but it was not.`)}async processJob(e,t){let n=this.dispatchedJobs.find((r)=>r.name===e);if(!n)throw Error(`No dispatched job found with name "${e}"`);try{await t(n.data),this.processedJobs.push(n)}catch(r){throw this.failedJobs.push({job:n,error:r}),r}}processed(e){if(e)return this.processedJobs.filter((t)=>t.name===e);return[...this.processedJobs]}failed(e){if(e)return this.failedJobs.filter((t)=>t.job.name===e);return[...this.failedJobs]}reset(){this.dispatchedJobs=[],this.pushedJobs=[],this.processedJobs=[],this.failedJobs=[]}}function kt(){return de=new _t,de}function he(){return de}function pe(){return de!==null}function xt(){de=null}class Fe{queue;constructor(){this.queue=kt()}dispatch(e,t,n={}){return this.queue.dispatch(e,t,n),this}push(e,t,n={}){return this.queue.push(e,t,n),this}assertDispatched(e,t){return this.queue.assertDispatched(e,t),this}assertNotDispatched(e){return this.queue.assertNotDispatched(e),this}assertDispatchedTimes(e,t){return this.queue.assertDispatchedTimes(e,t),this}assertNothingDispatched(){return this.queue.assertNothingDispatched(),this}dispatched(e){return this.queue.dispatched(e)}reset(){return this.queue.reset(),this}cleanup(){xt()}}function jn(){return new Fe}async function Pn(e,t){return await e.handle(t)}async function Sn(e,t,n){try{throw await e.handle(t),Error("Expected job to fail, but it succeeded")}catch(r){if(r.message==="Expected job to fail, but it succeeded")throw r;if(n){let i=r.message;if(typeof n==="string"){if(!i.includes(n))throw Error(`Expected error to contain "${n}", got "${i}"`)}else if(!n.test(i))throw Error(`Expected error to match ${n}, got "${i}"`)}return r}}var de=null;import{Queue as En,batch as Pi,chain as Si,dispatch as Ei,dispatchAfter as Ti,dispatchSync as Ri,getQueueManager as Ci,QueueManager as qi,setQueueManager as Di}from"@stacksjs/bun-queue";import{log as Ie}from"@stacksjs/logging";class Q{queue;config;isProcessing=!1;constructor(e,t){this.config=t,this.queue=new En(e,{driver:"redis",prefix:t.prefix,redis:t.redis?{url:t.redis.url||this.buildRedisUrl(t.redis)}:void 0,defaultJobOptions:t.defaultJobOptions?{delay:t.defaultJobOptions.delay,attempts:t.defaultJobOptions.attempts,backoff:t.defaultJobOptions.backoff,removeOnComplete:t.defaultJobOptions.removeOnComplete,removeOnFail:t.defaultJobOptions.removeOnFail,priority:t.defaultJobOptions.priority,lifo:t.defaultJobOptions.lifo,timeout:t.defaultJobOptions.timeout,jobId:t.defaultJobOptions.jobId,dependsOn:t.defaultJobOptions.dependsOn,keepJobs:t.defaultJobOptions.keepJobs,deadLetter:t.defaultJobOptions.deadLetter}:void 0,limiter:t.limiter,metrics:t.metrics,stalledJobCheckInterval:t.stalledJobCheckInterval,maxStalledJobRetries:t.maxStalledJobRetries,distributedLock:t.distributedLock,defaultDeadLetterOptions:t.defaultDeadLetterOptions,horizontalScaling:t.horizontalScaling,logLevel:t.logLevel}),Ie.debug(`Redis queue "${e}" initialized`)}buildRedisUrl(e){let t=e.host||"localhost",n=e.port||6379,r=e.password?`:${encodeURIComponent(e.password)}@`:"",i=Number.isFinite(e.db)&&e.db>=0?e.db:0;return`redis://${r}${t}:${n}/${i}`}async add(e,t){let n={delay:t?.delay?t.delay*1000:void 0,attempts:t?.maxTries,priority:t?.priority,timeout:t?.timeout?t.timeout*1000:void 0,backoff:Array.isArray(t?.backoff)?t.backoff.map((r)=>(Number(r)||1)*1000):typeof t?.backoff==="number"?{type:"fixed",delay:(t.backoff||1)*1000}:void 0};return this.queue.add(e,n)}process(e,t){if(this.isProcessing){Ie.warn("Queue is already processing");return}this.isProcessing=!0,this.queue.process(e,t),Ie.info(`Started processing queue with concurrency ${e}`)}async getJob(e){return this.queue.getJob(e)}async getJobs(e){return this.queue.getJobs(e)}async getJobCounts(){return this.queue.getJobCounts()}async removeJob(e){return this.queue.removeJob(e)}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(e){return this.queue.scheduleCron({cronExpression:e.cron,data:e.data,timezone:e.tz,jobId:e.name})}async unscheduleCron(e){return this.queue.unscheduleCron(e)}async getDeadLetterJobs(){return this.queue.getDeadLetterJobs()}async republishDeadLetterJob(e){return this.queue.republishDeadLetterJob(e)}async clearDeadLetterQueue(){return this.queue.clearDeadLetterQueue()}async bulkRemove(e){return this.queue.bulkRemove(e)}async getClusterInfo(){return this.queue.getClusterInfo()}isLeader(){return this.queue.isLeader()}getQueue(){return this.queue}on(e,t){this.queue.events.on(e,t)}}var fe=()=>{};function v(e){let t=e,n=t?.errno;if(typeof n==="string"&&n.toUpperCase()==="42P01")return!0;if(typeof n==="number"&&n===1146)return!0;if((typeof t?.code==="string"?t.code.toUpperCase():"")==="42P01")return!0;let i=t?.message??"";return i.includes("no such table")||i.includes("doesn't exist")||i.includes("does not exist")}import{db as te}from"@stacksjs/database";function Ee(){if(Pt)return;Pt=!0,console.warn("[queue/dlq] dead_letter_jobs table missing - DLQ disabled. Run migrations to enable poison-message isolation.")}async function me(e,t,n=1){let r=new Date().toISOString().slice(0,19).replace("T"," ");try{return await te.insertInto("dead_letter_jobs").values({uuid:e.uuid??crypto.randomUUID(),connection:e.connection??"database",queue:e.queue??"default",payload:e.payload??"{}",exception:e.exception??"unknown",reason:t,total_failures:n,first_failed_at:e.failed_at??r,last_failed_at:r,dead_lettered_at:r}).execute(),!0}catch(i){if(v(i))return Ee(),!1;throw i}}async function Rn(e={}){try{let t=te.selectFrom("dead_letter_jobs").selectAll();if(e.queue)t=t.where("queue","=",e.queue);if(e.reason)t=t.where("reason","=",e.reason);if(e.sinceCutoffMs){let r=new Date(e.sinceCutoffMs).toISOString().slice(0,19).replace("T"," ");t=t.where("dead_lettered_at",">=",r)}if(e.limit&&e.limit>0)t=t.limit(e.limit);return await t.execute()??[]}catch(t){if(v(t))return Ee(),[];throw t}}async function Cn(e){try{let t=await te.selectFrom("dead_letter_jobs").where("id","=",e).selectAll().executeTakeFirst();if(!t)return!1;let n=Math.floor(Date.now()/1000);return await te.insertInto("jobs").values({queue:t.queue,payload:t.payload,attempts:0,reserved_at:null,available_at:n,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),await te.deleteFrom("dead_letter_jobs").where("id","=",e).execute(),!0}catch(t){if(v(t))return Ee(),!1;throw t}}async function qn(e=30){try{let t=new Date(Date.now()-e*24*60*60*1000).toISOString().slice(0,19).replace("T"," "),n=await te.deleteFrom("dead_letter_jobs").where("dead_lettered_at","<",t).execute();return Number(n?.numDeletedRows??n?.[0]?.numDeletedRows??n?.affectedRows??0)}catch(t){if(v(t))return Ee(),0;throw t}}var Pt=!1;var Te=()=>{};function Qe(){if(St)return;St=!0,console.warn("[queue/idempotency] job_idempotency table missing - idempotency keys are accepted but NOT enforced. Run migrations to enable dedup.")}async function Dn(e){try{let{db:t}=await import("@stacksjs/database"),n=await t.selectFrom("job_idempotency").where("idempotency_key","=",e).select(["idempotency_key"]).executeTakeFirst();return Boolean(n)}catch(t){if(v(t))return Qe(),!1;throw t}}async function $n(e,t,n){try{let{db:r}=await import("@stacksjs/database");await r.insertInto("job_idempotency").values({idempotency_key:e,job_name:t,queue:n??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}catch(r){if(v(r)){Qe();return}let i=r?.message??"";if(i.includes("UNIQUE constraint")||i.includes("Duplicate entry"))return;throw r}}async function Be(e,t,n){try{let{db:r}=await import("@stacksjs/database");return await r.insertInto("job_idempotency").values({idempotency_key:e,job_name:t,queue:n??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),"claimed"}catch(r){if(v(r))return Qe(),"unenforced";let i=r?.message??"";if(i.includes("UNIQUE constraint")||i.includes("Duplicate entry"))return"duplicate";throw r}}async function Me(e){try{let{db:t}=await import("@stacksjs/database");await t.deleteFrom("job_idempotency").where("idempotency_key","=",e).execute()}catch{}}var St=!1;var He=()=>{};import{createHash as Nn}from"crypto";import{db as D}from"@stacksjs/database";function be(){if(Et)return;Et=!0,console.warn("[queue/poison] job_quarantine table missing - poison detection disabled. Run migrations to enable.")}function Re(e){let t;try{t=typeof e==="string"?e:JSON.stringify(e??null)}catch{t=String(e)}return Nn("sha256").update(t).digest("hex").slice(0,32)}async function Le(e,t,n={}){let r=n.maxFailures??5,i=n.windowMinutes??60,o=Re(t),a=new Date,l=a.toISOString().slice(0,19).replace("T"," ");try{let s=await D.selectFrom("job_quarantine").where("job_name","=",e).where("payload_hash","=",o).selectAll().executeTakeFirst();if(!s)return await D.insertInto("job_quarantine").values({job_name:e,payload_hash:o,failure_count:1,window_start:l,quarantined_at:null}).execute(),!1;if(s.quarantined_at)return!0;let u=Date.parse(s.window_start.replace(" ","T")+"Z"),c=a.getTime()-u,d=i*60*1000;if(Number.isFinite(u)&&c>d)return await D.updateTable("job_quarantine").set({failure_count:1,window_start:l}).where("id","=",s.id).execute(),!1;let m=s.failure_count+1;if(m>=r)return await D.updateTable("job_quarantine").set({failure_count:m,quarantined_at:l}).where("id","=",s.id).execute(),!0;return await D.updateTable("job_quarantine").set({failure_count:m}).where("id","=",s.id).execute(),!1}catch(s){if(v(s))return be(),!1;throw s}}async function Ue(e,t){let n=Re(t);try{let r=await D.selectFrom("job_quarantine").where("job_name","=",e).where("payload_hash","in",[n,"*"]).whereNotNull("quarantined_at").select(["id"]).executeTakeFirst();return Boolean(r)}catch(r){if(v(r))return be(),!1;throw r}}async function An(e,t){let n=t===void 0?"*":Re(t),r=new Date().toISOString().slice(0,19).replace("T"," ");try{let i=await D.selectFrom("job_quarantine").where("job_name","=",e).where("payload_hash","=",n).select(["id"]).executeTakeFirst();if(i)await D.updateTable("job_quarantine").set({quarantined_at:r}).where("id","=",i.id).execute();else await D.insertInto("job_quarantine").values({job_name:e,payload_hash:n,failure_count:0,window_start:r,quarantined_at:r}).execute()}catch(i){if(v(i)){be();return}throw i}}async function On(e){try{await D.deleteFrom("job_quarantine").where("job_name","=",e).execute()}catch(t){if(v(t)){be();return}throw t}}async function Fn(){try{return await D.selectFrom("job_quarantine").whereNotNull("quarantined_at").selectAll().execute()??[]}catch(e){if(v(e))return be(),[];throw e}}var Et=!1;var Ce=()=>{};function U(e){let t=e?.numUpdatedRows;if(t===null||t===void 0)return 0;if(typeof t==="object")return Number(t.changes??0);return Number(t)}function In(e,t){let n=A(e,t.payload||{},{queue:t.queue,tries:t.maxTries,timeout:t.timeout,backoff:Array.isArray(t.backoff)?t.backoff:void 0});return{queue:t.queue||"default",payload:I(n),attempts:0,available_at:Qn(t.delay||0),created_at:new Date().toISOString().slice(0,19).replace("T"," ")}}async function We(e,t){let n=In(e,t),{db:r}=await import("@stacksjs/database");await r.insertInto("jobs").values(n).execute()}function Qn(e){let t=Date.now();return Math.floor(t/1000+e)}var qe=q(()=>{ee()});import{log as B}from"@stacksjs/logging";class Ke{handlers=new Map;wildcardHandlers=new Set;listenerSubscriptions=new WeakMap;reclaim=new FinalizationRegistry((e)=>{e()});on(e,t){if(!this.handlers.has(e))this.handlers.set(e,new Set);return this.handlers.get(e).add(t),()=>{this.handlers.get(e)?.delete(t)}}subscribeListener(e,t,n){let r=new WeakRef(e),i=(s)=>{let u=r.deref();if(u===void 0){o();return}return n.call(u,s)},o=this.on(t,i),a=()=>{o();let s=r.deref();if(s===void 0)return;let u=this.listenerSubscriptions.get(s);if(!u)return;if(u.delete(a),u.size===0)this.listenerSubscriptions.delete(s),this.reclaim.unregister(s)},l=this.listenerSubscriptions.get(e);if(!l)l=new Set,this.listenerSubscriptions.set(e,l);return l.add(a),this.reclaim.register(e,o,e),a}unsubscribeListener(e){let t=this.listenerSubscriptions.get(e);if(!t)return 0;let n=Array.from(t);for(let r of n)r();return this.listenerSubscriptions.delete(e),this.reclaim.unregister(e),n.length}listenerCount(e){if(e==="*")return this.wildcardHandlers.size;return this.handlers.get(e)?.size??0}onAny(e){return this.wildcardHandlers.add(e),()=>{this.wildcardHandlers.delete(e)}}once(e,t){let n=async(r)=>{this.handlers.get(e)?.delete(n),await t(r)};return this.on(e,n)}async emit(e,t){let n={...t,timestamp:Date.now()};this.logEvent(e,n);let r=this.handlers.get(e);if(r)for(let i of r)try{await i(n)}catch(o){B.error(`Error in queue event handler for ${e}:`,o)}for(let i of this.wildcardHandlers)try{await i(e,n)}catch(o){B.error("Error in wildcard queue event handler:",o)}}logEvent(e,t){let n=t.jobId?`[${t.jobId}]`:"",r=t.queueName?`on ${t.queueName}`:"";switch(e){case"job:added":B.debug(`Job added ${n} ${r}`);break;case"job:processing":B.debug(`Job processing ${n} ${r}`);break;case"job:completed":B.info(`Job completed ${n} ${r} in ${t.duration}ms`);break;case"job:failed":B.error(`Job failed ${n} ${r}:`,t.error);break;case"job:retrying":B.warn(`Job retrying ${n} ${r} (attempt ${t.attemptsMade})`);break;case"job:stalled":B.warn(`Job stalled ${n} ${r}`);break;case"queue:error":B.error(`Queue error ${r}:`,t.error);break}}off(e){this.handlers.delete(e)}removeAllListeners(){this.handlers.clear(),this.wildcardHandlers.clear(),this.listenerSubscriptions=new WeakMap}}function ge(){if(!ze)ze=new Ke;return ze}function Bn(e,t){let n=ge();if(e==="*")return n.onAny(t);return n.on(e,t)}function _(e,t){return ge().emit(e,t)}function Mn(e,t){return async(...n)=>{let r=n[0]?.id||"unknown",i=Date.now();await _("job:processing",{jobId:r,queueName:e,data:n[0]?.data});try{let o=await t(...n);return await _("job:completed",{jobId:r,queueName:e,result:o,duration:Date.now()-i}),o}catch(o){throw await _("job:failed",{jobId:r,queueName:e,error:o,duration:Date.now()-i}),o}}}function Hn(e){return function(t,n){if(typeof n!=="object"||n===null)throw TypeError(`@OnQueueEvent('${e}') 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(n.kind!=="method")throw TypeError(`@OnQueueEvent('${e}') can only decorate a class method, but it was applied to a ${n.kind}${n.name===void 0?"":` ('${String(n.name)}')`}. Move the handler into a method, or call onQueueEvent('${e}', handler) directly.`);if(n.static)throw TypeError(`@OnQueueEvent('${e}') cannot decorate the static method '${String(n.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('${e}', MyClass.${String(n.name)}).`);return n.addInitializer(function(){ge().subscribeListener(this,e,t)}),t}}class Ge{jobCounts={added:0,completed:0,failed:0,processing:0};completions=[];errors=[];unsubscribe=[];constructor(){this.setupListeners()}setupListeners(){let e=ge();this.unsubscribe.push(e.on("job:added",()=>{this.jobCounts.added++}),e.on("job:processing",()=>{this.jobCounts.processing++}),e.on("job:completed",(t)=>{this.jobCounts.completed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1);let n=t.duration||0;if(this.completions.push({timestamp:Date.now(),duration:n}),this.completions.length>1000)this.completions.shift()}),e.on("job:failed",(t)=>{if(this.jobCounts.failed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1),t.error){if(this.errors.push({error:t.error,timestamp:Date.now()}),this.errors.length>100)this.errors.shift()}}))}getThroughputPerMinute(){let e=Date.now()-60000;return this.completions.filter((n)=>n.timestamp>=e).length}getAverageProcessingTime(){let e=Date.now()-60000,t=this.completions.filter((n)=>n.timestamp>=e);if(t.length===0)return 0;return t.reduce((n,r)=>n+r.duration,0)/t.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((e)=>e()),this.unsubscribe=[]}}function we(){if(!Ve)Ve=new Ge;return Ve}class Tt{workers=new Map;register(e,t){this.workers.set(e,{id:e,status:"idle",queue:t,processedCount:0,failedCount:0,lastActivityAt:new Date().toISOString(),startedAt:new Date().toISOString()})}markActive(e){let t=this.workers.get(e);if(t)t.status="active",t.lastActivityAt=new Date().toISOString()}markIdle(e){let t=this.workers.get(e);if(t)t.status="idle",t.lastActivityAt=new Date().toISOString()}recordCompletion(e){let t=this.workers.get(e);if(t)t.processedCount++,t.lastActivityAt=new Date().toISOString()}recordFailure(e){let t=this.workers.get(e);if(t)t.failedCount++,t.lastActivityAt=new Date().toISOString()}unregister(e){let t=this.workers.get(e);if(t)t.status="stopped"}getAll(){return Array.from(this.workers.values())}clear(){this.workers.clear()}}function E(){return Ln}var ze=null,Ve=null,Ln;var T=q(()=>{Ln=new Tt});var{RedisClient:Un}=globalThis.Bun;import{log as k}from"@stacksjs/logging";import{env as Wn}from"@stacksjs/env";function N(){return Wn.QUEUE_DRIVER||"sync"}class ye{jobs;options={thenCallbacks:[],catchCallbacks:[],finallyCallbacks:[],progressCallbacks:[]};constructor(e){this.jobs=e.map((t)=>("job"in t)?t:{job:t})}name(e){return this.options.name=e,this}onQueue(e){return this.options.queue=e,this}allowFailures(){return this.options.allowFailures=!0,this}then(e){return this.options.thenCallbacks.push(e),this}catch(e){return this.options.catchCallbacks.push(e),this}finally(e){return this.options.finallyCallbacks.push(e),this}progress(e){return this.options.progressCallbacks.push(e),this}thenHandler(e){return this.options.thenHandler=e,this}catchHandler(e){return this.options.catchHandler=e,this}finallyHandler(e){return this.options.finallyHandler=e,this}async dispatch(){let e=crypto.randomUUID(),t=this.jobs.length;if(t===0)throw Error("Cannot dispatch an empty batch");let n=N();await Vn({id:e,name:this.options.name||"",total_jobs:t,pending_jobs:t,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}),zn(e,this.options);try{await Promise.resolve().then(() => T());await _("batch:added",{jobId:e,data:{name:this.options.name,totalJobs:t}})}catch{}for(let r=0;r<this.jobs.length;r++){let i=this.jobs[r];if(!i)continue;let{job:o,payload:a}=i,l={...a,_batchId:e,_batchIndex:r};if(this.options.queue&&!o.queue)o.queue=this.options.queue;if(n==="sync")try{await o.dispatchNow(l),await ie(e)}catch(s){await oe(e,`${e}:${r}`,s)}else await o.dispatch(l)}return k.info(`[Batch] Dispatched batch "${this.options.name||e}" with ${t} jobs`),new W(e)}getJobs(){return[...this.jobs]}getOptions(){return this.options}}class W{id;constructor(e){this.id=e}async fresh(){return H(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 e=await this.fresh();if(!e)return 0;return e.total_jobs-e.pending_jobs}async progress(){let e=await this.fresh();if(!e||e.total_jobs===0)return 0;let t=e.total_jobs-e.pending_jobs;return Math.round(t/e.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 e=await this.fresh();if(!e)return[];try{return JSON.parse(e.failed_job_ids||"[]")}catch{return[]}}async cancel(){if(N()==="redis")await sr(this.id);else await nr(this.id);k.info(`[Batch] Cancelled batch ${this.id}`);let t=$e(this.id);if(t)for(let n of t.finallyCallbacks)try{await n(this)}catch(r){k.error(`[Batch] Error in finally callback for batch ${this.id}:`,r)}}async add(e){let t=await this.fresh();if(!t)throw Error(`Batch ${this.id} not found`);if(t.cancelled_at)throw Error(`Batch ${this.id} has been cancelled`);if(t.finished_at)throw Error(`Batch ${this.id} has already finished`);let n=e.map((o)=>("job"in o)?o:{job:o});if(!await Yn(this.id,n.length))throw Error(`Batch ${this.id} was cancelled, finished or deleted before the jobs could be added`);let i=JSON.parse(t.options||"{}");for(let o=0;o<n.length;o++){let a=n[o];if(!a)continue;let{job:l,payload:s}=a,u={...s,_batchId:this.id,_batchIndex:t.total_jobs+o};if(i.queue&&!l.queue)l.queue=i.queue;await l.dispatch(u)}k.info(`[Batch] Added ${n.length} jobs to batch ${this.id}`)}async delete(){await Zn(this.id),De(this.id)}}class Rt{static create(e){return new ye(e)}static async find(e){if(!await H(e))return null;return new W(e)}static async all(){return(await Kn()).map((t)=>new W(t.id))}static async prune(e=24){return er(e)}}function zn(e,t){Ye.set(e,t)}function $e(e){return Ye.get(e)}function De(e){Ye.delete(e)}async function Vn(e){if(N()==="redis")await or(e);else await Ct(e)}async function H(e){if(N()==="redis")return ar(e);return qt(e)}async function Kn(){if(N()==="redis")return Ot();return Dt()}async function Gn(e,t){if(N()==="redis")await et(e,t);else await Xe(e,t)}async function Yn(e,t){if(t===0)return!0;if(N()==="redis")try{let o=await V(),a=`${z}${e}`;return await o.hincrby(a,"total_jobs",t),await o.hincrby(a,"pending_jobs",t),o.close(),!0}catch{}let{db:n,sql:r}=await import("@stacksjs/database"),i=await n.updateTable("job_batches").set(Xn(r,t)).where("id","=",e).whereNull("cancelled_at").whereNull("finished_at").executeTakeFirst();return U(i)>0}function Xn(e,t){return{total_jobs:e`total_jobs + ${t}`,pending_jobs:e`pending_jobs + ${t}`}}async function Zn(e){if(N()==="redis")await It(e);else await $t(e)}async function er(e){if(N()==="redis")return cr(e);return Nt(e)}function tr(e){return!!(e.then_handler||e.catch_handler||e.finally_handler)}async function Ct(e){let{db:t}=await import("@stacksjs/database"),n={id:e.id,name:e.name,total_jobs:e.total_jobs,pending_jobs:e.pending_jobs,failed_jobs:e.failed_jobs,failed_job_ids:e.failed_job_ids,options:e.options,cancelled_at:e.cancelled_at,created_at:e.created_at,finished_at:e.finished_at};if(!tr(e)){await t.insertInto("job_batches").values(n).execute();return}try{await t.insertInto("job_batches").values({...n,then_handler:e.then_handler??null,catch_handler:e.catch_handler??null,finally_handler:e.finally_handler??null}).execute()}catch(r){k.warn(`[Batch] Could not persist terminal handlers for batch ${e.id}: ${r?.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 t.insertInto("job_batches").values(n).execute()}}async function qt(e){let{db:t}=await import("@stacksjs/database");return await t.selectFrom("job_batches").where("id","=",e).selectAll().executeTakeFirst()||null}async function Dt(){let{db:e}=await import("@stacksjs/database");return await e.selectFrom("job_batches").selectAll().orderBy("created_at","desc").execute()}async function Xe(e,t){let{db:n}=await import("@stacksjs/database");await n.updateTable("job_batches").set(t).where("id","=",e).execute()}async function $t(e){let{db:t}=await import("@stacksjs/database");await t.deleteFrom("job_batches").where("id","=",e).execute()}async function nr(e){await Xe(e,{cancelled_at:new Date().toISOString().slice(0,19).replace("T"," "),finished_at:new Date().toISOString().slice(0,19).replace("T"," ")})}async function Nt(e){let{db:t}=await import("@stacksjs/database"),n=new Date(Date.now()-e*60*60*1000).toISOString().slice(0,19).replace("T"," "),r=await t.deleteFrom("job_batches").whereNotNull("finished_at").where("finished_at","<",n).executeTakeFirst();return Number(r?.numDeletedRows??0)}async function rr(){let{queue:e}=await import("@stacksjs/config"),t=e?.connections?.redis?.redis;if(t?.url)return t.url;let n=t?.password?`:${encodeURIComponent(t.password)}@`:"",r=t?.db?`/${t.db}`:"";return`redis://${n}${t?.host||"localhost"}:${t?.port||6379}${r}`}async function V(){let e=new Un(await rr());return await e.connect(),e}function ir(e){return{id:e.id,name:e.name,total_jobs:String(e.total_jobs),pending_jobs:String(e.pending_jobs),failed_jobs:String(e.failed_jobs),failed_job_ids:e.failed_job_ids,options:e.options,cancelled_at:e.cancelled_at||"",created_at:e.created_at,finished_at:e.finished_at||"",then_handler:e.then_handler||"",catch_handler:e.catch_handler||"",finally_handler:e.finally_handler||""}}async function or(e){try{let t=await V();await t.hset(`${z}${e.id}`,ir(e)),await t.sadd(Ze,e.id),t.close()}catch{await Ct(e)}}function At(e){if(!e?.id)return null;return{id:e.id,name:e.name??"",total_jobs:Number(e.total_jobs),pending_jobs:Number(e.pending_jobs),failed_jobs:Number(e.failed_jobs),failed_job_ids:e.failed_job_ids??"",options:e.options??"",cancelled_at:e.cancelled_at||null,created_at:e.created_at??"",finished_at:e.finished_at||null,then_handler:e.then_handler||null,catch_handler:e.catch_handler||null,finally_handler:e.finally_handler||null}}async function ar(e){try{let t=await V(),n=`${z}${e}`,r=await t.hgetall(n);return t.close(),At(r)}catch{return qt(e)}}async function Ot(){try{let e=await V(),t=await e.smembers(Ze),n=[];for(let r of t){let i=At(await e.hgetall(`${z}${r}`));if(i)n.push(i)}return e.close(),n}catch{return Dt()}}async function et(e,t){try{let n=await V(),r=`${z}${e}`,i={};for(let[o,a]of Object.entries(t))i[o]=a===null?"":String(a);await n.hset(r,i),n.close()}catch{await Xe(e,t)}}async function Ft(e,t,n){let r=await V();try{let i=`${z}${e}`,o=await r.hincrby(i,"pending_jobs",-1),a=Math.max(o,0);if(o<0)await r.hset(i,{pending_jobs:"0"});if(t)await r.hincrby(i,"failed_jobs",1);let s=!(await r.hgetall(i)).finished_at&&(!t||n?a===0:!0),u=new Date().toISOString().slice(0,19).replace("T"," "),c=s&&await r.hsetnx(i,"terminal_claimed","1");if(c)await r.hset(i,{finished_at:u});if(c&&t&&!n)await r.hset(i,{cancelled_at:u});return c}finally{r.close()}}async function It(e){try{let t=await V();await t.del(`${z}${e}`),await t.srem(Ze,e),t.close()}catch{await $t(e)}}async function sr(e){let t=new Date().toISOString().slice(0,19).replace("T"," ");await et(e,{cancelled_at:t,finished_at:t})}async function cr(e){try{let t=await Ot(),n=Date.now()-e*60*60*1000,r=0;for(let i of t)if(i.finished_at){if(new Date(i.finished_at).getTime()<n)await It(i.id),r++}return r}catch{return Nt(e)}}function ne(e){if(!e)return null;try{let t=JSON.parse(e);if(t&&(t.kind==="job"||t.kind==="module"))return t;return k.warn(`[Batch] handler JSON has unknown kind '${t?.kind}' - skipping`),null}catch(t){return k.warn(`[Batch] failed to parse persistent handler: ${t.message}`),null}}async function re(e,t){try{if(e.kind==="job"){await Promise.resolve().then(() => _e());await tt.dispatch(e.name,{...e.payload??{},_batchId:t});return}let n=await import(e.module).catch((i)=>(k.warn(`[Batch] persistent handler module not found: ${e.module} (${i.message})`),null));if(!n)return;let r=n[e.export];if(typeof r!=="function"){k.warn(`[Batch] persistent handler export '${e.export}' is not a function on ${e.module}`);return}await r(e.payload,t)}catch(n){k.error(`[Batch] persistent handler threw for batch ${t}:`,n)}}async function ie(e){let t=N(),n=!1;if(t==="redis")n=await Ft(e,!1,!1);else{let{db:o,sql:a}=await import("@stacksjs/database");await o.updateTable("job_batches").set({pending_jobs:a`GREATEST(pending_jobs - 1, 0)`}).where("id","=",e).where("pending_jobs",">",0).execute();let l=new Date().toISOString().slice(0,19).replace("T"," "),s=await o.updateTable("job_batches").set({finished_at:l}).where("id","=",e).where("pending_jobs","=",0).whereNull("finished_at").executeTakeFirst();n=U(s)>0}let r=$e(e),i=new W(e);if(r)for(let o of r.progressCallbacks)try{await o(i)}catch(a){k.error(`[Batch] Error in progress callback for batch ${e}:`,a)}if(n){try{await Promise.resolve().then(() => T());await _("batch:completed",{jobId:e})}catch{}let o=await H(e);if(!o){De(e),k.info(`[Batch] Batch ${e} finished (record vanished)`);return}let a=JSON.parse(o.options||"{}"),s=!((o.failed_jobs||0)>0)||a.allowFailures;if(r){if(s)for(let c of r.thenCallbacks)try{await c(i)}catch(d){k.error(`[Batch] Error in then callback for batch ${e}:`,d)}for(let c of r.finallyCallbacks)try{await c(i)}catch(d){k.error(`[Batch] Error in finally callback for batch ${e}:`,d)}De(e)}if(s){let c=ne(o.then_handler);if(c)await re(c,e)}else{let c=ne(o.catch_handler);if(c)await re(c,e)}let u=ne(o.finally_handler);if(u)await re(u,e);k.info(`[Batch] Batch ${e} finished`)}}async function oe(e,t,n){let r=await H(e);if(!r)return;let i=JSON.parse(r.options||"{}"),o=new W(e),a=$e(e),l=!1;if(N()==="redis"){l=await Ft(e,!0,!!i.allowFailures);let c=await H(e);if(c){let d=[];try{d=JSON.parse(c.failed_job_ids||"[]")}catch{d=[]}d.push(t),await et(e,{failed_job_ids:JSON.stringify(d)})}if(a)for(let d of a.catchCallbacks)try{await d(o,n)}catch(m){k.error(`[Batch] Error in catch callback for batch ${e}:`,m)}try{await Promise.resolve().then(() => T());await _("batch:failed",{jobId:e,error:n})}catch{}}else{let{db:c,sql:d}=await import("@stacksjs/database");await c.updateTable("job_batches").set({pending_jobs:d`GREATEST(pending_jobs - 1, 0)`,failed_jobs:d`failed_jobs + 1`}).where("id","=",e).where("pending_jobs",">",0).execute();try{let w=await H(e);if(w){let j=[];try{j=JSON.parse(w.failed_job_ids||"[]")}catch{j=[]}j.push(t),await Gn(e,{failed_job_ids:JSON.stringify(j)})}}catch{}if(a)for(let w of a.catchCallbacks)try{await w(o,n)}catch(j){k.error(`[Batch] Error in catch callback for batch ${e}:`,j)}try{await Promise.resolve().then(() => T());await _("batch:failed",{jobId:e,error:n})}catch{}let m=new Date().toISOString().slice(0,19).replace("T"," "),h=c.updateTable("job_batches").set(i.allowFailures?{finished_at:m}:{finished_at:m,cancelled_at:m}).where("id","=",e).whereNull("finished_at");if(i.allowFailures)h=h.where("pending_jobs","=",0);let b=await h.executeTakeFirst();l=U(b)>0}if(!l)return;let s=!!i.allowFailures;if(a){if(s)for(let c of a.thenCallbacks)try{await c(o)}catch(d){k.error(`[Batch] Error in then callback for batch ${e}:`,d)}for(let c of a.finallyCallbacks)try{await c(o)}catch(d){k.error(`[Batch] Error in finally callback for batch ${e}:`,d)}De(e)}let u=await H(e);if(u){if(s){let d=ne(u.then_handler);if(d)await re(d,e)}else{let d=ne(u.catch_handler);if(d)await re(d,e)}let c=ne(u.finally_handler);if(c)await re(c,e)}k.info(`[Batch] Batch ${e} finished with failure(s)`)}async function ve(e){return(await H(e))?.cancelled_at!==null}var Ye,z="stacks:batch:",Ze="stacks:batches";var M=q(()=>{qe();Ye=new Map});import{appPath as ur,frameworkPath as lr}from"@stacksjs/path";import{env as dr}from"@stacksjs/env";import{enqueueAfterCommit as hr,isInTransaction as pr}from"@stacksjs/database";function fr(){return dr.QUEUE_DRIVER||"sync"}function mr(){if(Qt)return;Qt=!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 O{name;payload;options={};txMode="auto";constructor(e,t){this.name=e;this.payload=t}onQueue(e){return this.options.queue=e,this}delay(e){return this.options.delay=e,this}tries(e){return this.options.tries=e,this}timeout(e){return this.options.timeout=e,this}backoff(e){return this.options.backoff=e,this}withContext(e){return this.options.context=e,this}withIdempotencyKey(e){return this.options.idempotencyKey=e,this}afterCommit(){return this.txMode="after",this}withoutCommit(){return this.txMode="immediate",this}async dispatch(){await Promise.resolve();if(pe()){he()?.dispatch(this.name,this.payload,this.options);return}if(this.txMode!=="immediate"){if(pr()){let r=this.runDispatchPipeline.bind(this);if(hr(async()=>{await r()}))return}else if(this.txMode==="after")mr()}await this.runDispatchPipeline()}async runDispatchPipeline(){let e=!1;if(this.options.idempotencyKey){let t=await Be(this.options.idempotencyKey,this.name,this.options.queue);if(t==="duplicate")return;e=t==="claimed"}try{if(await Ue(this.name,this.payload)){let n=A(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});if(await me({queue:this.options.queue||"default",payload:I(n),exception:`quarantined: ${this.name}`},"poison-detected"))return}let t=fr();if(t==="database")await this.dispatchToDatabase();else if(t==="redis")await this.dispatchToRedis();else if(t==="sync")await K(this.name,{payload:this.payload,context:this.options.context,traceId:await Bt()});else if(t==="sqs"||t==="memory"||t==="beanstalkd")throw Error(`[queue] Driver "${t}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);else throw Error(`[queue] Unknown QUEUE_DRIVER "${t}". Allowed values: redis, database, sync.`)}catch(t){if(e&&this.options.idempotencyKey)await Me(this.options.idempotencyKey);throw t}}async dispatchIf(e){if(e)await this.dispatch()}async dispatchUnless(e){if(!e)await this.dispatch()}async dispatchToDatabase(){let e=Math.floor(Date.now()/1000),t=this.options.delay?e+this.options.delay:e,n=A(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff},await Bt()),r=I(n),{db:i}=await import("@stacksjs/database");await i.insertInto("jobs").values({queue:this.options.queue||"default",payload:r,attempts:0,reserved_at:null,available_at:t,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(){let e=A(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});le(e);await Promise.resolve().then(() => fe());let{queue:n}=await import("@stacksjs/config"),r=n?.connections?.redis;if(!r)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new Q(this.options.queue||"default",r).add(e,{delay:this.options.delay,maxTries:this.options.tries,timeout:this.options.timeout,backoff:this.options.backoff})}async dispatchNow(){await K(this.name,{payload:this.payload,context:this.options.context})}}function br(e,t){return new O(e,t)}function gr(e){M();return new ye(e)}async function wr(e){let t=[ur(`Jobs/${e}.ts`),lr(`defaults/app/Jobs/${e}.ts`)];try{let n=import.meta.resolve("@stacksjs/defaults/package.json"),r=new URL(".",n).pathname;t.push(`${r}app/Jobs/${e}.ts`)}catch{}for(let n of t)if(await Bun.file(n).exists())return n;return null}async function K(e,t={}){let{withTraceId:n}=await import("@stacksjs/router"),r=t.traceId??`job:${e}:${Math.random().toString(36).slice(2,10)}`;await n(r,async()=>{let i=await wr(e);if(!i)throw Error(`Job ${e} not found. Looked in app/Jobs/${e}.ts and the framework defaults (storage/framework/defaults/app/Jobs, @stacksjs/defaults).`);let a=(await import(i)).default;if(!a)throw Error(`Job ${e} does not export a default`);if(typeof a.handle==="function")await a.handle(t.payload);else if(typeof a.action==="string")await Pe(a.action);else if(typeof a.action==="function")await a.action();else if(typeof a==="function")await a(t.payload,t.context);else throw Error(`Job ${e} does not have a valid handler`)})}async function Bt(){try{let{getTraceId:e}=await import("@stacksjs/router");return e()}catch{return}}var Qt=!1,tt;var _e=q(()=>{Te();ee();He();Ce();Se();tt={make(e,t){return new O(e,t)},async dispatch(e,t){await new O(e,t).dispatch()},async dispatchIf(e,t,n){if(e)await new O(t,n).dispatch()},async dispatchUnless(e,t,n){if(!e)await new O(t,n).dispatch()},async dispatchNow(e,t){await new O(e,t).dispatchNow()},dispatchAfter(e,t,n){return new O(t,n).delay(e)},async dispatchOnce(e,t,n){await new O(t,n).withIdempotencyKey(e).dispatch()},async dispatchAfterCommit(e,t){await new O(e,t).afterCommit().dispatch()}}});import{db as R}from"@stacksjs/database";function G(){if(Mt)return;Mt=!0,console.warn("[queue/circuit-breaker] queue_circuit_state table missing - circuit breaker disabled. Run migrations to enable.")}async function rt(e,t){try{let n=await R.selectFrom("queue_circuit_state").where("queue_name","=",e).selectAll().executeTakeFirst();if(n)return n;return await R.insertInto("queue_circuit_state").values({queue_name:e,success_count:0,failure_count:0,window_start:t,paused_at:null,resume_at:null}).execute(),{queue_name:e,success_count:0,failure_count:0,window_start:t,paused_at:null,resume_at:null}}catch(n){if(v(n))return G(),null;let r=n?.message??"";if(r.includes("UNIQUE constraint")||r.includes("Duplicate entry"))return await R.selectFrom("queue_circuit_state").where("queue_name","=",e).selectAll().executeTakeFirst();throw n}}async function it(e){try{let t=await R.selectFrom("queue_circuit_state").where("queue_name","=",e).select(["paused_at","resume_at"]).executeTakeFirst();if(!t||!t.paused_at)return!1;if(t.resume_at){let n=Date.parse(t.resume_at.replace(" ","T")+"Z");if(Number.isFinite(n)&&Date.now()>=n)return await R.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",e).execute(),!1}return!0}catch(t){if(v(t))return G(),!1;throw t}}async function ot(e,t={}){let n=t.windowSeconds??300,r=new Date().toISOString().slice(0,19).replace("T"," "),i=await rt(e,r);if(!i)return;try{if(nt(i.window_start,n)){await R.updateTable("queue_circuit_state").set({success_count:1,failure_count:0,window_start:r}).where("queue_name","=",e).execute();return}await R.updateTable("queue_circuit_state").set({success_count:i.success_count+1}).where("queue_name","=",e).execute()}catch(o){if(v(o)){G();return}throw o}}async function at(e,t={}){let n=t.failureRateThreshold??0.5,r=t.windowSeconds??300,i=t.pauseSeconds??300,o=t.minObservations??10,a=new Date,l=a.toISOString().slice(0,19).replace("T"," "),s=await rt(e,l);if(!s)return!1;if(s.paused_at)return!1;try{let{success_count:u,failure_count:c}=s;if(nt(s.window_start,r))u=0,c=0;c+=1;let d=u+c,m=d===0?0:c/d;if(d>=o&&m>=n){let b=new Date(a.getTime()+i*1000).toISOString().slice(0,19).replace("T"," ");return await R.updateTable("queue_circuit_state").set({success_count:u,failure_count:c,window_start:l,paused_at:l,resume_at:b}).where("queue_name","=",e).execute(),!0}return await R.updateTable("queue_circuit_state").set({success_count:u,failure_count:c,window_start:nt(s.window_start,r)?l:s.window_start}).where("queue_name","=",e).execute(),!1}catch(u){if(v(u))return G(),!1;throw u}}function nt(e,t){if(!e)return!0;let n=Date.parse(e.replace(" ","T")+"Z");if(!Number.isFinite(n))return!0;return Date.now()-n>t*1000}async function yr(e,t=300){let n=new Date,r=n.toISOString().slice(0,19).replace("T"," "),i=new Date(n.getTime()+t*1000).toISOString().slice(0,19).replace("T"," ");await rt(e,r);try{await R.updateTable("queue_circuit_state").set({paused_at:r,resume_at:i}).where("queue_name","=",e).execute()}catch(o){if(v(o)){G();return}throw o}}async function vr(e){try{await R.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",e).execute()}catch(t){if(v(t)){G();return}throw t}}async function _r(){try{return await R.selectFrom("queue_circuit_state").selectAll().execute()??[]}catch(e){if(v(e))return G(),[];throw e}}var Mt=!1;var ke=()=>{};ee();Se();import{env as Tn}from"@stacksjs/env";function Jt(){return Tn.QUEUE_DRIVER||"sync"}class jt{name;description;action;handle;queue;rate;tries;timeout;backoff;backoffConfig;enabled;constructor(e){this.name=e.name,this.description=e.description,this.handle=e.handle,this.queue=e.queue,this.rate=e.rate,this.action=e.action,this.tries=e.tries,this.timeout=e.timeout,this.backoff=e.backoff,this.backoffConfig=e.backoffConfig,this.enabled=e.enabled}async dispatch(...[e]){await Promise.resolve();if(pe()){he()?.dispatch(this.name||"UnknownJob",e,{queue:this.queue,tries:this.tries,timeout:this.timeout});return}let r=Jt();if(r==="sync")return this.dispatchNow(...[e]);if(r==="redis")return this.dispatchToRedis(e);if(r==="database")return this.dispatchToDatabase(e);if(r==="sqs"||r==="memory"||r==="beanstalkd")throw Error(`[queue] Driver "${r}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);throw Error(`[queue] Unknown QUEUE_DRIVER "${r}". Allowed values: redis, database, sync.`)}async dispatchIf(e,...[t]){if(e)return this.dispatch(...[t])}async dispatchUnless(e,...[t]){if(!e)return this.dispatch(...[t])}async dispatchAfter(e,...[t]){let n=Jt();if(n==="redis")return this.dispatchToRedis(t,{delay:e});if(n==="database")return this.dispatchToDatabase(t,{delay:e});return await new Promise((r)=>setTimeout(r,e*1000)),await this.dispatchNow(...[t])}async dispatchNow(...[e]){if(typeof this.handle==="function")await this.handle(e);else if(typeof this.action==="string")await Pe(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(e,t){let n=Math.floor(Date.now()/1000),r=t?.delay?n+t.delay:n,i=A(this.name??this.constructor.name,e,{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}),o=I(i),{db:a}=await import("@stacksjs/database");await a.insertInto("jobs").values({queue:this.queue||"default",payload:o,attempts:0,reserved_at:null,available_at:r,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(e,t){let n=A(this.name??this.constructor.name,e,{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});le(n);await Promise.resolve().then(() => fe());let{queue:i}=await import("@stacksjs/config"),o=i?.connections?.redis;if(!o)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new Q(this.queue||"default",o).add(n,{delay:t?.delay,maxTries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0})}}Se();_e();He();Te();Ce();ke();ee();var st=(e)=>`__job_progress__:${e}`,ct=(e)=>`__job_cancel__:${e}`;function kr(e){return Number.isFinite(e)?Math.max(0,Math.min(100,e)):0}async function xr(e,t,n){let{cache:r}=await import("@stacksjs/cache");await r.set(st(e),{percent:kr(t),message:n,updatedAt:Date.now()},3600)}async function Jr(e){let{cache:t}=await import("@stacksjs/cache");return await t.get(st(e))??null}async function jr(e){let{cache:t}=await import("@stacksjs/cache");await t.set(ct(e),1,3600)}async function Pr(e){let{cache:t}=await import("@stacksjs/cache");return Boolean(await t.get(ct(e)))}async function Sr(e){let{cache:t}=await import("@stacksjs/cache");await Promise.all([t.del(st(e)),t.del(ct(e))])}import{log as ae}from"@stacksjs/logging";import*as Ht from"@stacksjs/path";class Lt{jobs=new Map;initialized=!1;register(e){this.jobs.set(e.name,e),ae.debug(`Registered job: ${e.name} (${e.type})`)}get(e){return this.jobs.get(e)}all(){return Array.from(this.jobs.values())}byQueue(e){return this.all().filter((t)=>t.config.queue===e)}scheduled(){return this.all().filter((e)=>e.config.rate||e.config.schedule)}has(e){return this.jobs.has(e)}clear(){this.jobs.clear(),this.initialized=!1}setInitialized(e){this.initialized=e}isInitialized(){return this.initialized}}var Y=new Lt;async function ut(e){let t=e||Ht.userJobsPath(),n=[];try{let r=new Bun.Glob("**/*.{ts,js}"),i={cwd:t,onlyFiles:!0,absolute:!0};for await(let o of r.scan(i)){if(o.includes(".test.")||o.includes(".spec.")||o.endsWith("index.ts")||o.endsWith("index.js"))continue;try{let a=await Er(o);if(a)n.push(a),Y.register(a)}catch(a){ae.warn(`Failed to load job from ${o}: ${a.message}`)}}return Y.setInitialized(!0),ae.info(`Discovered ${n.length} jobs from ${t}`),n}catch(r){return ae.error(`Failed to discover jobs: ${r.message}`),[]}}async function Er(e){try{let t=await import(e),n=e.split("/").pop()?.replace(/\.(ts|js)$/,"")||"UnknownJob";if(t.default&&typeof t.default==="function"){let r=t.default;if(typeof r.handle==="function"||typeof r.prototype?.handle==="function"){let i=r.config||{};return{name:i.name||n,path:e,config:{name:i.name||n,description:i.description,queue:i.queue||"default",tries:i.retries||3,timeout:i.timeout,withoutOverlapping:i.withoutOverlapping,schedule:i.schedule,retryAfter:i.retryAfter},type:"class",module:r}}}if(t.default&&typeof t.default==="object"){let r=t.default;if(typeof r.handle==="function"||typeof r.action==="string")return{name:r.name||n,path:e,config:{name:r.name||n,description:r.description,queue:r.queue||"default",tries:r.tries||3,backoff:r.backoff,rate:r.rate,timeout:r.timeout||r.timeOut,backoffConfig:r.backoffConfig},type:"function",module:r}}return null}catch(t){return ae.debug(`Could not load job from ${e}: ${t.message}`),null}}function Tr(e){return Y.get(e)}function Rr(){return Y.all()}function lt(){return Y.scheduled()}async function Cr(e,t){let n=Y.get(e);if(!n)throw Error(`Job "${e}" not found. Did you run discoverJobs()?`);try{if(n.type==="class"){if(typeof n.module.handle==="function")return await n.module.handle(t);return await new n.module().handle(t)}else{if(typeof n.module.handle==="function")return await n.module.handle(t);throw Error(`Job "${e}" does not have a handle method`)}}catch(r){throw ae.error(`Failed to execute job "${e}": ${r.message}`),r}}function qr(e){let t=e.config;return{name:t.name,queue:t.queue,tries:t.tries,backoff:t.backoff,timeout:t.timeout,backoffConfig:t.backoffConfig,rate:t.rate}}import{log as x}from"@stacksjs/logging";T();import{log as Wt}from"@stacksjs/logging";var Ut=!1;async function zt(){if(Ut)return!0;try{let{db:e}=await import("@stacksjs/database");return await e.unsafe("CREATE TABLE IF NOT EXISTS scheduled_job_runs (job_name VARCHAR(255) PRIMARY KEY, last_run_at VARCHAR(64) NOT NULL)").execute(),Ut=!0,!0}catch(e){return Wt.debug(`[scheduler] run-marker persistence unavailable, using in-memory lastRun: ${e instanceof Error?e.message:String(e)}`),!1}}async function Vt(e){if(!await zt())return null;try{let{db:t}=await import("@stacksjs/database"),n=await t.selectFrom("scheduled_job_runs").where("job_name","=",e).select(["last_run_at"]).executeTakeFirst();if(!n?.last_run_at)return null;let r=new Date(n.last_run_at);return Number.isNaN(r.getTime())?null:r}catch{return null}}async function Kt(e,t){if(!await zt())return;try{let{db:n}=await import("@stacksjs/database"),r=t.toISOString();await n.deleteFrom("scheduled_job_runs").where("job_name","=",e).execute(),await n.insertInto("scheduled_job_runs").values({job_name:e,last_run_at:r}).execute()}catch{}}function Dr(e){return`%"jobName":"${e.replace(/[\\%_]/g,(n)=>`\\${n}`)}"%`}async function Gt(e){try{let{db:t}=await import("@stacksjs/database"),r=await t.unsafe("SELECT 1 AS present FROM jobs WHERE payload LIKE ? ESCAPE '\\' LIMIT 1",[Dr(e)]).execute(),i=Array.isArray(r)?r:r?.rows??[];return Array.isArray(i)&&i.length>0}catch(t){return Wt.debug(`[scheduler] overlap check unavailable, dispatching anyway: ${t instanceof Error?t.message:String(t)}`),!1}}qe();var tn={checkInterval:60000,preventOverlapping:!0},g={isRunning:!1,isShuttingDown:!1,checkInterval:null,jobs:new Map,config:{...tn}},Yt=new Set;function $r(e,t){if(Yt.has(e))return;Yt.add(e),x.warn(`[scheduler] Cron expression "${e}" specifies seconds="${t}" but the scheduler ticks at minute granularity - the seconds field is being ignored. Use a 5-field expression to avoid this warning, or wait for sub-minute scheduling support.`)}var Xt=!1,Zt=new Map;function Nr(e){let t=Zt.get(e);if(!t)t=new Intl.DateTimeFormat("en-US",{timeZone:e,hour12:!1,month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",weekday:"short"}),Zt.set(e,t);return t}function dt(e,t){if(t&&t!=="local"&&t!=="system")try{let n=Nr(t).formatToParts(e),r=(a)=>n.find((l)=>l.type===a)?.value??"",i={Sun:0,Mon:1,Tue:2,Wed:3,Thu:4,Fri:5,Sat:6},o=Number(r("hour"));if(o===24)o=0;return{minute:Number(r("minute")),hour:o,day:Number(r("day")),month:Number(r("month")),dayOfWeek:i[r("weekday")]??e.getDay()}}catch{if(!Xt)Xt=!0,x.warn(`[scheduler] Invalid timezone "${t}"; falling back to system local time.`)}return{minute:e.getMinutes(),hour:e.getHours(),day:e.getDate(),month:e.getMonth()+1,dayOfWeek:e.getDay()}}function Ar(e,t,n){let r=dt(new Date,n),{minute:i,hour:o,day:a,month:l,dayOfWeek:s}=r;if(t){let w=dt(t,n);if(w.minute===i&&w.hour===o&&w.day===a)return!1}let u=e.trim().split(/\s+/);if(u.length===6){let w=u[0];if(w&&w!=="0"&&w!=="*")$r(e,w);u=u.slice(1)}if(u.length<5)return x.warn(`Invalid cron expression: ${e}`),!1;let[c,d,m,h,b]=u;return F(c,i,0,59)&&F(d,o,0,23)&&F(m,a,1,31)&&F(h,l,1,12)&&F(b,s,0,6)}function F(e,t,n,r){if(e==="*")return!0;if(e.includes(","))return e.split(",").map((a)=>Number.parseInt(a.trim(),10)).includes(t);if(e.includes("-")){let[o,a]=e.split("-").map((l)=>Number.parseInt(l.trim(),10));return t>=o&&t<=a}if(e.includes("/")){let[o,a]=e.split("/"),l=Number.parseInt(a,10);if(o==="*")return t%l===0;if(o.includes("-")){let[s,u]=o.split("-").map((c)=>Number.parseInt(c.trim(),10));return t>=s&&t<=u&&(t-s)%l===0}}let i=Number.parseInt(e,10);return!Number.isNaN(i)&&t===i}function nn(e){let n={"@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 * * * *"}[e.toLowerCase()];if(n)return n;let r=e.match(/^Every\.(\w+)$/i);if(r&&r[1]!==void 0){let o=r[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 * *"}[o]||null}let i=e.split(/\s+/).length;if(i>=5&&i<=6)return e;return null}function rn(e,t){let n=e.trim().split(/\s+/),r=n.length===6?n.slice(1):n;if(r.length<5)return null;let[i,o,a,l,s]=r,u=new Date;u.setSeconds(0,0);let c=527040;for(let d=1;d<=c;d++){let m=new Date(u.getTime()+d*60000),h=dt(m,t);if(F(i,h.minute,0,59)&&F(o,h.hour,0,23)&&F(a,h.day,1,31)&&F(l,h.month,1,12)&&F(s,h.dayOfWeek,0,6))return m}return null}async function Or(e={}){if(g.isRunning){x.warn("Scheduler is already running");return}g.config={...tn,...e},g.isRunning=!0,g.isShuttingDown=!1,await ut();let t=lt();for(let i of t){let o=i.config.rate||i.config.schedule;if(o){let a=nn(o);if(a){let l=await Vt(i.name);g.jobs.set(i.name,{job:i,lastRun:l,nextRun:rn(a,g.config.timezone),isRunning:!1}),x.info(`Registered scheduled job: ${i.name} (${a})`)}else x.warn(`Invalid schedule for job ${i.name}: ${o}`)}}if(g.jobs.size===0){x.info("No scheduled jobs found");return}x.info(`Scheduler started with ${g.jobs.size} job(s)`),process.on("SIGINT",()=>ht()),process.on("SIGTERM",()=>ht());let n=!1,r=()=>{if(g.isShuttingDown)return;let i=g.config.checkInterval,o=i-Date.now()%i;g.checkInterval=setTimeout(()=>{if(!g.isShuttingDown&&!n)n=!0,en().catch((a)=>x.error("Scheduler check failed:",a)).finally(()=>{n=!1});r()},o),g.checkInterval?.unref?.()};r(),await en()}async function en(){for(let[e,t]of g.jobs){let n=t.job.config.rate||t.job.config.schedule;if(!n)continue;let r=nn(n);if(!r)continue;if(Ar(r,t.lastRun,g.config.timezone)){if((g.config.preventOverlapping||t.job.config.withoutOverlapping)&&await Gt(e)){x.debug(`Skipping ${e}: previous execution still running`);continue}if(t.isRunning){x.debug(`Skipping ${e}: a dispatch for it is already in flight`);continue}try{t.isRunning=!0,t.lastRun=new Date,t.nextRun=rn(r,g.config.timezone),await Kt(e,t.lastRun),x.info(`Dispatching scheduled job: ${e}`),await _("job:added",{jobId:`scheduled-${e}-${Date.now()}`,queueName:t.job.config.queue||"default",jobName:e}),await We(e,{queue:t.job.config.queue||"default",payload:{},maxTries:t.job.config.tries||3,timeout:t.job.config.timeout||60}),t.isRunning=!1,x.info(`Scheduled job ${e} dispatched to queue`)}catch(o){t.isRunning=!1,x.error(`Failed to dispatch scheduled job ${e}:`,o)}}}}async function ht(){if(!g.isRunning)return;if(x.info("Stopping scheduler..."),g.isShuttingDown=!0,g.checkInterval)clearTimeout(g.checkInterval),g.checkInterval=null;g.isRunning=!1,g.jobs.clear(),x.info("Scheduler stopped")}function Fr(){return{isRunning:g.isRunning,jobCount:g.jobs.size,jobs:Array.from(g.jobs.entries()).map(([e,t])=>({name:e,schedule:t.job.config.rate||t.job.config.schedule,lastRun:t.lastRun,nextRun:t.nextRun,isRunning:t.isRunning}))}}function Ir(){return g.isRunning}function Qr(){return new Map(g.jobs)}async function Br(e){let t=g.jobs.get(e);if(!t)throw Error(`Scheduled job "${e}" not found`);x.info(`Manually triggering scheduled job: ${e}`),await We(e,{queue:t.job.config.queue||"default",payload:{},maxTries:t.job.config.tries||3,timeout:t.job.config.timeout||60})}T();T();import{log as Mr}from"@stacksjs/logging";function on(e,t=Math.floor(Date.now()/1000)){if(e.reserved_at)return"processing";let n=typeof e.available_at==="number"?e.available_at:typeof e.available_at==="string"&&e.available_at.trim()?Number(e.available_at):Number.NaN;if(Number.isFinite(n)&&n>t)return"delayed";return"pending"}var Hr={maxPendingWarning:1000,maxPendingCritical:5000,maxFailedWarning:10,maxFailedCritical:100,maxJobAgeWarning:3600,maxJobAgeCritical:86400,maxErrorRateWarning:0.1,maxErrorRateCritical:0.5};async function pt(e={}){let t={...Hr,...e},n=[],r=new Date,i=Math.floor(r.getTime()/1000);try{let{db:o}=await import("@stacksjs/database"),a=await o.selectFrom("jobs").selectAll().execute(),l=await o.selectFrom("failed_jobs").selectAll().execute(),s=new Map;for(let p of a){let y=p.queue||"default";if(!s.has(y))s.set(y,{pending:0,processing:0,delayed:0});let P=s.get(y),S=on(p,i);if(S==="processing")P.processing++;else if(S==="delayed")P.delayed++;else if(P.pending++,p.created_at){let gn=typeof p.created_at==="number"?p.created_at:Math.floor(new Date(p.created_at).getTime()/1000),yt=i-gn;if(!P.oldestAge||yt>P.oldestAge)P.oldestAge=yt}}let u=new Map;for(let p of l){let y=p.queue||"default";u.set(y,(u.get(y)||0)+1)}let c=[],d=new Set([...s.keys(),...u.keys()]),m=t.queues?[...d].filter((p)=>t.queues.includes(p)):[...d],h=0,b=0,w=0,j=0;for(let p of m){let y=s.get(p)||{pending:0,processing:0,delayed:0},P=u.get(p)||0;h+=y.pending,b+=y.processing,w+=y.delayed,j+=P;let S="healthy";if(y.pending>=t.maxPendingCritical)S="unhealthy",n.push({level:"critical",message:`Queue "${p}" has ${y.pending} pending jobs (threshold: ${t.maxPendingCritical})`,queue:p,timestamp:r.toISOString()});else if(y.pending>=t.maxPendingWarning)S="degraded",n.push({level:"warning",message:`Queue "${p}" has ${y.pending} pending jobs (threshold: ${t.maxPendingWarning})`,queue:p,timestamp:r.toISOString()});if(P>=t.maxFailedCritical)S="unhealthy",n.push({level:"critical",message:`Queue "${p}" has ${P} failed jobs (threshold: ${t.maxFailedCritical})`,queue:p,timestamp:r.toISOString()});else if(P>=t.maxFailedWarning){if(S==="healthy")S="degraded";n.push({level:"warning",message:`Queue "${p}" has ${P} failed jobs (threshold: ${t.maxFailedWarning})`,queue:p,timestamp:r.toISOString()})}if(y.oldestAge){if(y.oldestAge>=t.maxJobAgeCritical)S="unhealthy",n.push({level:"critical",message:`Queue "${p}" has a job waiting for ${Math.floor(y.oldestAge/3600)} hours`,queue:p,timestamp:r.toISOString()});else if(y.oldestAge>=t.maxJobAgeWarning){if(S==="healthy")S="degraded";n.push({level:"warning",message:`Queue "${p}" has a job waiting for ${Math.floor(y.oldestAge/60)} minutes`,queue:p,timestamp:r.toISOString()})}}c.push({name:p,status:S,pending:y.pending,processing:y.processing,delayed:y.delayed,failed:P,oldestJobAge:y.oldestAge})}let gt=we().getMetrics(),{throughputPerMinute:fn,averageDuration:mn}=gt,wt=h+b+w+j,ue=wt>0?j/wt:0,Z="healthy";if(c.some((p)=>p.status==="unhealthy"))Z="unhealthy";else if(c.some((p)=>p.status==="degraded"))Z="degraded";if(ue>=t.maxErrorRateCritical)Z="unhealthy",n.push({level:"critical",message:`Overall error rate is ${(ue*100).toFixed(1)}% (threshold: ${t.maxErrorRateCritical*100}%)`,timestamp:r.toISOString()});else if(ue>=t.maxErrorRateWarning){if(Z==="healthy")Z="degraded";n.push({level:"warning",message:`Overall error rate is ${(ue*100).toFixed(1)}% (threshold: ${t.maxErrorRateWarning*100}%)`,timestamp:r.toISOString()})}let bn=E().getAll().map((p)=>({id:p.id,status:p.status,queue:p.queue,processedCount:p.processedCount,failedCount:p.failedCount,lastActivityAt:p.lastActivityAt}));return{status:Z,timestamp:r.toISOString(),queues:c,workers:bn,metrics:{totalPending:h,totalProcessing:b,totalDelayed:w,totalFailed:j,throughputPerMinute:fn,averageProcessingTime:mn,errorRate:ue},alerts:n}}catch(o){Mr.error("Failed to perform queue health check:",o);let l=E().getAll().map((s)=>({id:s.id,status:s.status,queue:s.queue,processedCount:s.processedCount,failedCount:s.failedCount,lastActivityAt:s.lastActivityAt}));return{status:"unhealthy",timestamp:r.toISOString(),queues:[],workers:l,metrics:{totalPending:0,totalProcessing:0,totalDelayed:0,totalFailed:0,throughputPerMinute:0,averageProcessingTime:0,errorRate:0},alerts:[{level:"critical",message:`Health check failed: ${o.message}`,timestamp:r.toISOString()}]}}}function Lr(e={}){return async(t)=>{let n=await pt(e),r=n.status==="healthy"?200:n.status==="degraded"?207:503;return new Response(JSON.stringify(n,null,2),{status:r,headers:{"Content-Type":"application/json","Cache-Control":"no-store"}})}}async function Ur(e={}){return(await pt(e)).status==="healthy"}import{log as C}from"@stacksjs/logging";var Wr={"&":"&","<":"<",">":">",'"':""","'":"'"};function xe(e){return String(e).replace(/[&<>"']/g,(t)=>Wr[t]??t)}class ft{config;notificationCount=0;lastResetTime=Date.now();pendingBatch=[];batchTimeout=null;activeFlush=null;constructor(e){this.config=e}async notify(e){if(this.config.filter&&!this.config.filter(e))return;if(this.config.rateLimit){let t=Date.now();if(t-this.lastResetTime>3600000)this.notificationCount=0,this.lastResetTime=t;if(this.notificationCount>=this.config.rateLimit){C.debug("Rate limit reached for failed job notifications");return}}if(this.config.batch){if(this.pendingBatch.push(e),!this.batchTimeout)this.batchTimeout=setTimeout(()=>{this.batchTimeout=null,this.activeFlush=this.flushBatch().catch((t)=>C.error("Failed to flush notification batch:",t)).finally(()=>{this.activeFlush=null})},this.config.batchInterval||60000),this.batchTimeout.unref?.();return}await this.sendNotifications([e])}async flushBatch(){if(this.pendingBatch.length===0)return;let e=[...this.pendingBatch];if(this.pendingBatch=[],this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;await this.sendNotifications(e)}async shutdown(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.activeFlush)await this.activeFlush.catch(()=>{});await this.flushBatch().catch((e)=>C.error("Failed to flush notification batch on shutdown:",e))}async sendNotifications(e){let t=[];for(let n of this.config.channels)switch(n){case"email":if(this.config.email)t.push(this.sendEmail(e));break;case"slack":if(this.config.slack)t.push(this.sendSlack(e));break;case"discord":if(this.config.discord)t.push(this.sendDiscord(e));break;case"webhook":if(this.config.webhook)t.push(this.sendWebhook(e));break;case"log":this.logFailures(e);break}try{await Promise.all(t)}catch(n){C.error("Failed to send job failure notifications:",n)}finally{this.notificationCount+=e.length}}async sendEmail(e){let t=this.config.email,n=t.subject||`[Queue] ${e.length} Job(s) Failed`,r=this.formatEmailBody(e);try{let{mail:i}=await import("@stacksjs/email");await i.send({to:Array.isArray(t.to)?t.to:[t.to],from:t.from?{address:t.from}:void 0,subject:n,html:r})}catch(i){C.error("Failed to send email notification:",i)}}formatEmailBody(e){let t=e.map((n)=>`
|
|
2
|
+
var Cn=Object.defineProperty;var qn=(e)=>e;function Dn(e,t){this[e]=qn.bind(null,t)}var $n=(e,t)=>{for(var n in t)Cn(e,n,{get:t[n],enumerable:!0,configurable:!0,set:Dn.bind(t,n)})};var q=(e,t,n)=>()=>{if(e)try{t=e(e=0)}catch(r){n=[r]}if(n)throw n[0];return t};function A(e,t,n,r){return{jobName:e,payload:t,options:n,envelopeVersion:1,dispatchedAt:new Date().toISOString(),...r?{traceId:r}:{}}}function I(e){try{return JSON.stringify(e)}catch(t){throw An(e,t)}}function le(e){I(e)}function An(e,t){if(On(t)){let i=null;try{i=Ae(e,"",new Set)}catch{}let o=i?`\`${i.path}\` is a BigInt (${i.value}n)`:"it holds a BigInt";return Error(`[queue] Job "${e.jobName}" was not dispatched: ${o}, and job envelopes travel as JSON, which cannot represent one. Convert it where you dispatch - \`String(value)\` keeps every digit, \`Number(value)\` is exact below 2^53 - and convert it back inside the job handler. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.`,{cause:t})}if(Fn(t))return Error(`[queue] Job "${e.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 - 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:t});let r=t instanceof Error?t.message:String(t);return Error(`[queue] Job "${e.jobName}" was not dispatched: serializing its payload threw (${r}). JSON rejects only two things on its own and this was neither, so the throw came from the payload - a \`toJSON()\` or a property getter. See the JSON round-trip contract in @stacksjs/queue's src/envelope.ts.`,{cause:t})}function On(e){return e instanceof TypeError&&/bigint/i.test(e.message)}function Fn(e){return e instanceof TypeError&&/circular|cyclic/i.test(e.message)}function Ae(e,t,n){if(typeof e==="bigint")return{path:t||"envelope",value:e};if(!e||typeof e!=="object")return null;if(n.has(e))return null;if(n.add(e),Array.isArray(e)){for(let r=0;r<e.length;r++){let i=Ae(e[r],`${t}[${r}]`,n);if(i)return i}return null}for(let[r,i]of Object.entries(e)){let o=Ae(i,t?`${t}.${r}`:r,n);if(o)return o}return null}function xt(e,t){if(Oe.has(e))return;Oe.add(e),console.warn(t)}function In(){Oe.clear()}function Je(e){let t;if(typeof e==="string")try{t=JSON.parse(e)}catch{return{ok:!1,reason:"malformed",detail:"not valid JSON"}}else if(e&&typeof e==="object")t=e;else return{ok:!1,reason:"malformed",detail:`expected string or object, got ${typeof e}`};if(t.envelopeVersion===1){if(typeof t.jobName!=="string")return{ok:!1,reason:"missing-job-name"};return{ok:!0,envelope:{jobName:t.jobName,payload:t.payload,options:t.options??void 0,envelopeVersion:1,dispatchedAt:typeof t.dispatchedAt==="string"?t.dispatchedAt:new Date(0).toISOString(),...typeof t.traceId==="string"&&t.traceId?{traceId:t.traceId}:{}},source:"v1"}}if(typeof t.envelopeVersion==="number"&&t.envelopeVersion>1)return{ok:!1,reason:"unknown-version",detail:`envelopeVersion=${t.envelopeVersion}, this worker speaks v1`};if(typeof t.jobName==="string")return xt("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:t.jobName,payload:t.payload,options:t.options??void 0,envelopeVersion:1,dispatchedAt:typeof t.dispatchedAt==="string"?t.dispatchedAt:new Date(0).toISOString(),...typeof t.traceId==="string"&&t.traceId?{traceId:t.traceId}:{}},source:"v0-implicit"};if(typeof t.job==="string")return xt("laravel-legacy","[queue/envelope] Processing Laravel-legacy job envelope (`{ job, data }` shape). Will continue to process but the queue table contains migration-era rows - consider flushing once they drain."),{ok:!0,envelope:{jobName:t.job.replace(/^App\\+Jobs\\+/,"").replace(/^.*\\/,""),payload:t.data,options:void 0,envelopeVersion:1,dispatchedAt:new Date(0).toISOString()},source:"laravel-legacy"};return{ok:!1,reason:"missing-job-name"}}var Nn=1,Oe;var ee=q(()=>{Oe=new Set});import{getActionRunner as Qn,runNamedAction as je,setActionRunner as Bn}from"@stacksjs/action-runner";var Pe=()=>{};var Be={};$n(Be,{QueueTester:()=>Se,createQueueTester:()=>St,expectJobToFail:()=>Tt,fake:()=>Ie,getFakeQueue:()=>jt,isFaked:()=>Pt,restore:()=>Qe,runJob:()=>Et});class Jt{dispatchedJobs=[];pushedJobs=[];processedJobs=[];failedJobs=[];dispatch(e,t,n={}){this.dispatchedJobs.push({name:e,data:t,options:n,dispatchedAt:new Date,queue:n.queue||"default"})}push(e,t,n={}){this.pushedJobs.push({name:e,data:t,options:n,dispatchedAt:new Date,queue:n.queue||"default"})}dispatched(e){if(e)return this.dispatchedJobs.filter((t)=>t.name===e);return[...this.dispatchedJobs]}pushed(e){if(e)return this.pushedJobs.filter((t)=>t.name===e);return[...this.pushedJobs]}assertDispatched(e,t){let n=this.dispatched(e);if(n.length===0)throw Error(`Expected job "${e}" to be dispatched, but it was not.`);if(t){if(n.filter(t).length===0)throw Error(`Expected job "${e}" to be dispatched matching the callback, but no matching jobs were found.`)}}assertNotDispatched(e){let t=this.dispatched(e);if(t.length>0)throw Error(`Expected job "${e}" to not be dispatched, but it was dispatched ${t.length} time(s).`)}assertDispatchedTimes(e,t){let n=this.dispatched(e);if(n.length!==t)throw Error(`Expected job "${e}" to be dispatched ${t} time(s), but it was dispatched ${n.length} time(s).`)}assertNothingDispatched(){if(this.dispatchedJobs.length>0){let e=[...new Set(this.dispatchedJobs.map((t)=>t.name))].join(", ");throw Error(`Expected no jobs to be dispatched, but found: ${e}`)}}assertPushed(e,t){let n=this.pushed(e);if(n.length===0)throw Error(`Expected job "${e}" to be pushed, but it was not.`);if(t){if(n.filter(t).length===0)throw Error(`Expected job "${e}" to be pushed matching the callback, but no matching jobs were found.`)}}assertPushedWithDelay(e,t){if(this.pushed(e).filter((i)=>i.options?.delay===t).length===0)throw Error(`Expected job "${e}" to be pushed with delay ${t}ms, but no matching jobs were found.`)}assertPushedOn(e,t){if(this.pushed(t).filter((r)=>r.queue===e).length===0)throw Error(`Expected job "${t}" to be pushed on queue "${e}", but it was not.`)}async processJob(e,t){let n=this.dispatchedJobs.find((r)=>r.name===e);if(!n)throw Error(`No dispatched job found with name "${e}"`);try{await t(n.data),this.processedJobs.push(n)}catch(r){throw this.failedJobs.push({job:n,error:r}),r}}processed(e){if(e)return this.processedJobs.filter((t)=>t.name===e);return[...this.processedJobs]}failed(e){if(e)return this.failedJobs.filter((t)=>t.job.name===e);return[...this.failedJobs]}reset(){this.dispatchedJobs=[],this.pushedJobs=[],this.processedJobs=[],this.failedJobs=[]}}function Ie(){return de=new Jt,de}function jt(){return de}function Pt(){return de!==null}function Qe(){de=null}class Se{queue;constructor(){this.queue=Ie()}dispatch(e,t,n={}){return this.queue.dispatch(e,t,n),this}push(e,t,n={}){return this.queue.push(e,t,n),this}assertDispatched(e,t){return this.queue.assertDispatched(e,t),this}assertNotDispatched(e){return this.queue.assertNotDispatched(e),this}assertDispatchedTimes(e,t){return this.queue.assertDispatchedTimes(e,t),this}assertNothingDispatched(){return this.queue.assertNothingDispatched(),this}dispatched(e){return this.queue.dispatched(e)}reset(){return this.queue.reset(),this}cleanup(){Qe()}}function St(){return new Se}async function Et(e,t){return await e.handle(t)}async function Tt(e,t,n){try{throw await e.handle(t),Error("Expected job to fail, but it succeeded")}catch(r){if(r.message==="Expected job to fail, but it succeeded")throw r;if(n){let i=r.message;if(typeof n==="string"){if(!i.includes(n))throw Error(`Expected error to contain "${n}", got "${i}"`)}else if(!n.test(i))throw Error(`Expected error to match ${n}, got "${i}"`)}return r}}var de=null;import{Queue as Mn,batch as Ii,chain as Qi,dispatch as Bi,dispatchAfter as Mi,dispatchSync as Hi,getQueueManager as Li,QueueManager as Ui,setQueueManager as Wi}from"@stacksjs/bun-queue";import{log as Me}from"@stacksjs/logging";class Q{queue;config;isProcessing=!1;constructor(e,t){this.config=t,this.queue=new Mn(e,{driver:"redis",prefix:t.prefix,redis:t.redis?{url:t.redis.url||this.buildRedisUrl(t.redis)}:void 0,defaultJobOptions:t.defaultJobOptions?{delay:t.defaultJobOptions.delay,attempts:t.defaultJobOptions.attempts,backoff:t.defaultJobOptions.backoff,removeOnComplete:t.defaultJobOptions.removeOnComplete,removeOnFail:t.defaultJobOptions.removeOnFail,priority:t.defaultJobOptions.priority,lifo:t.defaultJobOptions.lifo,timeout:t.defaultJobOptions.timeout,jobId:t.defaultJobOptions.jobId,dependsOn:t.defaultJobOptions.dependsOn,keepJobs:t.defaultJobOptions.keepJobs,deadLetter:t.defaultJobOptions.deadLetter}:void 0,limiter:t.limiter,metrics:t.metrics,stalledJobCheckInterval:t.stalledJobCheckInterval,maxStalledJobRetries:t.maxStalledJobRetries,distributedLock:t.distributedLock,defaultDeadLetterOptions:t.defaultDeadLetterOptions,horizontalScaling:t.horizontalScaling,logLevel:t.logLevel}),Me.debug(`Redis queue "${e}" initialized`)}buildRedisUrl(e){let t=e.host||"localhost",n=e.port||6379,r=e.password?`:${encodeURIComponent(e.password)}@`:"",i=Number.isFinite(e.db)&&e.db>=0?e.db:0;return`redis://${r}${t}:${n}/${i}`}async add(e,t){let n={delay:t?.delay?t.delay*1000:void 0,attempts:t?.maxTries,priority:t?.priority,timeout:t?.timeout?t.timeout*1000:void 0,backoff:Array.isArray(t?.backoff)?t.backoff.map((r)=>(Number(r)||1)*1000):typeof t?.backoff==="number"?{type:"fixed",delay:(t.backoff||1)*1000}:void 0};return this.queue.add(e,n)}process(e,t){if(this.isProcessing){Me.warn("Queue is already processing");return}this.isProcessing=!0,this.queue.process(e,t),Me.info(`Started processing queue with concurrency ${e}`)}async getJob(e){return this.queue.getJob(e)}async getJobs(e){return this.queue.getJobs(e)}async getJobCounts(){return this.queue.getJobCounts()}async removeJob(e){return this.queue.removeJob(e)}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(e){return this.queue.scheduleCron({cronExpression:e.cron,data:e.data,timezone:e.tz,jobId:e.name})}async unscheduleCron(e){return this.queue.unscheduleCron(e)}async getDeadLetterJobs(){return this.queue.getDeadLetterJobs()}async republishDeadLetterJob(e){return this.queue.republishDeadLetterJob(e)}async clearDeadLetterQueue(){return this.queue.clearDeadLetterQueue()}async bulkRemove(e){return this.queue.bulkRemove(e)}async getClusterInfo(){return this.queue.getClusterInfo()}isLeader(){return this.queue.isLeader()}getQueue(){return this.queue}on(e,t){this.queue.events.on(e,t)}}var he=()=>{};function v(e){let t=e,n=t?.errno;if(typeof n==="string"&&n.toUpperCase()==="42P01")return!0;if(typeof n==="number"&&n===1146)return!0;if((typeof t?.code==="string"?t.code.toUpperCase():"")==="42P01")return!0;let i=t?.message??"";return i.includes("no such table")||i.includes("doesn't exist")||i.includes("does not exist")}import{db as te}from"@stacksjs/database";function Ee(){if($t)return;$t=!0,console.warn("[queue/dlq] dead_letter_jobs table missing - DLQ disabled. Run migrations to enable poison-message isolation.")}async function pe(e,t,n=1){let r=new Date().toISOString().slice(0,19).replace("T"," ");try{return await te.insertInto("dead_letter_jobs").values({uuid:e.uuid??crypto.randomUUID(),connection:e.connection??"database",queue:e.queue??"default",payload:e.payload??"{}",exception:e.exception??"unknown",reason:t,total_failures:n,first_failed_at:e.failed_at??r,last_failed_at:r,dead_lettered_at:r}).execute(),!0}catch(i){if(v(i))return Ee(),!1;throw i}}async function Ln(e={}){try{let t=te.selectFrom("dead_letter_jobs").selectAll();if(e.queue)t=t.where("queue","=",e.queue);if(e.reason)t=t.where("reason","=",e.reason);if(e.sinceCutoffMs){let r=new Date(e.sinceCutoffMs).toISOString().slice(0,19).replace("T"," ");t=t.where("dead_lettered_at",">=",r)}if(e.limit&&e.limit>0)t=t.limit(e.limit);return await t.execute()??[]}catch(t){if(v(t))return Ee(),[];throw t}}async function Un(e){try{let t=await te.selectFrom("dead_letter_jobs").where("id","=",e).selectAll().executeTakeFirst();if(!t)return!1;let n=Math.floor(Date.now()/1000);return await te.insertInto("jobs").values({queue:t.queue,payload:t.payload,attempts:0,reserved_at:null,available_at:n,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),await te.deleteFrom("dead_letter_jobs").where("id","=",e).execute(),!0}catch(t){if(v(t))return Ee(),!1;throw t}}async function Wn(e=30){try{let t=new Date(Date.now()-e*24*60*60*1000).toISOString().slice(0,19).replace("T"," "),n=await te.deleteFrom("dead_letter_jobs").where("dead_lettered_at","<",t).execute();return Number(n?.numDeletedRows??n?.[0]?.numDeletedRows??n?.affectedRows??0)}catch(t){if(v(t))return Ee(),0;throw t}}var $t=!1;var Te=()=>{};function Re(){return Nt??=import("@stacksjs/database").catch((e)=>{throw Nt=void 0,e})}function He(){if(At)return;At=!0,console.warn("[queue/idempotency] job_idempotency table missing - idempotency keys are accepted but NOT enforced. Run migrations to enable dedup.")}async function zn(e){try{let{db:t}=await Re(),n=await t.selectFrom("job_idempotency").where("idempotency_key","=",e).select(["idempotency_key"]).executeTakeFirst();return Boolean(n)}catch(t){if(v(t))return He(),!1;throw t}}async function Vn(e,t,n){try{let{db:r}=await Re();await r.insertInto("job_idempotency").values({idempotency_key:e,job_name:t,queue:n??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}catch(r){if(v(r)){He();return}let i=r?.message??"";if(i.includes("UNIQUE constraint")||i.includes("Duplicate entry"))return;throw r}}async function Le(e,t,n){try{let{db:r}=await Re();return await r.insertInto("job_idempotency").values({idempotency_key:e,job_name:t,queue:n??"default",dispatched_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute(),"claimed"}catch(r){if(v(r))return He(),"unenforced";let i=r?.message??"";if(i.includes("UNIQUE constraint")||i.includes("Duplicate entry"))return"duplicate";throw r}}async function Ue(e){try{let{db:t}=await Re();await t.deleteFrom("job_idempotency").where("idempotency_key","=",e).execute()}catch{}}var Nt,At=!1;var We=()=>{};import{hash as Kn}from"crypto";import{db as D}from"@stacksjs/database";function fe(){if(Ot)return;Ot=!0,console.warn("[queue/poison] job_quarantine table missing - poison detection disabled. Run migrations to enable.")}function Ce(e){let t;try{t=typeof e==="string"?e:JSON.stringify(e??null)}catch{t=String(e)}return Kn("sha256",t,"hex").slice(0,32)}async function ze(e,t,n={}){let r=n.maxFailures??5,i=n.windowMinutes??60,o=Ce(t),a=new Date,l=a.toISOString().slice(0,19).replace("T"," ");try{let s=await D.selectFrom("job_quarantine").where("job_name","=",e).where("payload_hash","=",o).selectAll().executeTakeFirst();if(!s)return await D.insertInto("job_quarantine").values({job_name:e,payload_hash:o,failure_count:1,window_start:l,quarantined_at:null}).execute(),!1;if(s.quarantined_at)return!0;let u=Date.parse(s.window_start.replace(" ","T")+"Z"),c=a.getTime()-u,d=i*60*1000;if(Number.isFinite(u)&&c>d)return await D.updateTable("job_quarantine").set({failure_count:1,window_start:l}).where("id","=",s.id).execute(),!1;let m=s.failure_count+1;if(m>=r)return await D.updateTable("job_quarantine").set({failure_count:m,quarantined_at:l}).where("id","=",s.id).execute(),!0;return await D.updateTable("job_quarantine").set({failure_count:m}).where("id","=",s.id).execute(),!1}catch(s){if(v(s))return fe(),!1;throw s}}async function Ve(e,t){let n=Ce(t);try{let r=await D.selectFrom("job_quarantine").where("job_name","=",e).where("payload_hash","in",[n,"*"]).whereNotNull("quarantined_at").select(["id"]).executeTakeFirst();return Boolean(r)}catch(r){if(v(r))return fe(),!1;throw r}}async function Gn(e,t){let n=t===void 0?"*":Ce(t),r=new Date().toISOString().slice(0,19).replace("T"," ");try{let i=await D.selectFrom("job_quarantine").where("job_name","=",e).where("payload_hash","=",n).select(["id"]).executeTakeFirst();if(i)await D.updateTable("job_quarantine").set({quarantined_at:r}).where("id","=",i.id).execute();else await D.insertInto("job_quarantine").values({job_name:e,payload_hash:n,failure_count:0,window_start:r,quarantined_at:r}).execute()}catch(i){if(v(i)){fe();return}throw i}}async function Yn(e){try{await D.deleteFrom("job_quarantine").where("job_name","=",e).execute()}catch(t){if(v(t)){fe();return}throw t}}async function Xn(){try{return await D.selectFrom("job_quarantine").whereNotNull("quarantined_at").selectAll().execute()??[]}catch(e){if(v(e))return fe(),[];throw e}}var Ot=!1;var qe=()=>{};function U(e){let t=e?.numUpdatedRows;if(t===null||t===void 0)return 0;if(typeof t==="object")return Number(t.changes??0);return Number(t)}function Zn(e,t){let n=A(e,t.payload||{},{queue:t.queue,tries:t.maxTries,timeout:t.timeout,backoff:Array.isArray(t.backoff)?t.backoff:void 0});return{queue:t.queue||"default",payload:I(n),attempts:0,available_at:er(t.delay||0),created_at:new Date().toISOString().slice(0,19).replace("T"," ")}}async function Ke(e,t){let n=Zn(e,t),{db:r}=await import("@stacksjs/database");await r.insertInto("jobs").values(n).execute()}function er(e){let t=Date.now();return Math.floor(t/1000+e)}var De=q(()=>{ee()});import{log as B}from"@stacksjs/logging";class Xe{handlers=new Map;wildcardHandlers=new Set;listenerSubscriptions=new WeakMap;reclaim=new FinalizationRegistry((e)=>{e()});on(e,t){if(!this.handlers.has(e))this.handlers.set(e,new Set);return this.handlers.get(e).add(t),()=>{this.handlers.get(e)?.delete(t)}}subscribeListener(e,t,n){let r=new WeakRef(e),i=(s)=>{let u=r.deref();if(u===void 0){o();return}return n.call(u,s)},o=this.on(t,i),a=()=>{o();let s=r.deref();if(s===void 0)return;let u=this.listenerSubscriptions.get(s);if(!u)return;if(u.delete(a),u.size===0)this.listenerSubscriptions.delete(s),this.reclaim.unregister(s)},l=this.listenerSubscriptions.get(e);if(!l)l=new Set,this.listenerSubscriptions.set(e,l);return l.add(a),this.reclaim.register(e,o,e),a}unsubscribeListener(e){let t=this.listenerSubscriptions.get(e);if(!t)return 0;let n=Array.from(t);for(let r of n)r();return this.listenerSubscriptions.delete(e),this.reclaim.unregister(e),n.length}listenerCount(e){if(e==="*")return this.wildcardHandlers.size;return this.handlers.get(e)?.size??0}onAny(e){return this.wildcardHandlers.add(e),()=>{this.wildcardHandlers.delete(e)}}once(e,t){let n=async(r)=>{this.handlers.get(e)?.delete(n),await t(r)};return this.on(e,n)}async emit(e,t){let n={...t,timestamp:Date.now()};this.logEvent(e,n);let r=this.handlers.get(e);if(r)for(let i of r)try{await i(n)}catch(o){B.error(`Error in queue event handler for ${e}:`,o)}for(let i of this.wildcardHandlers)try{await i(e,n)}catch(o){B.error("Error in wildcard queue event handler:",o)}}logEvent(e,t){let n=t.jobId?`[${t.jobId}]`:"",r=t.queueName?`on ${t.queueName}`:"";switch(e){case"job:added":B.debug(`Job added ${n} ${r}`);break;case"job:processing":B.debug(`Job processing ${n} ${r}`);break;case"job:completed":B.info(`Job completed ${n} ${r} in ${t.duration}ms`);break;case"job:failed":B.error(`Job failed ${n} ${r}:`,t.error);break;case"job:retrying":B.warn(`Job retrying ${n} ${r} (attempt ${t.attemptsMade})`);break;case"job:stalled":B.warn(`Job stalled ${n} ${r}`);break;case"queue:error":B.error(`Queue error ${r}:`,t.error);break}}off(e){this.handlers.delete(e)}removeAllListeners(){this.handlers.clear(),this.wildcardHandlers.clear(),this.listenerSubscriptions=new WeakMap}}function me(){if(!Ge)Ge=new Xe;return Ge}function tr(e,t){let n=me();if(e==="*")return n.onAny(t);return n.on(e,t)}function _(e,t){return me().emit(e,t)}function nr(e,t){return async(...n)=>{let r=n[0]?.id||"unknown",i=Date.now();await _("job:processing",{jobId:r,queueName:e,data:n[0]?.data});try{let o=await t(...n);return await _("job:completed",{jobId:r,queueName:e,result:o,duration:Date.now()-i}),o}catch(o){throw await _("job:failed",{jobId:r,queueName:e,error:o,duration:Date.now()-i}),o}}}function rr(e){return function(t,n){if(typeof n!=="object"||n===null)throw TypeError(`@OnQueueEvent('${e}') 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(n.kind!=="method")throw TypeError(`@OnQueueEvent('${e}') can only decorate a class method, but it was applied to a ${n.kind}${n.name===void 0?"":` ('${String(n.name)}')`}. Move the handler into a method, or call onQueueEvent('${e}', handler) directly.`);if(n.static)throw TypeError(`@OnQueueEvent('${e}') cannot decorate the static method '${String(n.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('${e}', MyClass.${String(n.name)}).`);return n.addInitializer(function(){me().subscribeListener(this,e,t)}),t}}class Ze{jobCounts={added:0,completed:0,failed:0,processing:0};completions=[];errors=[];unsubscribe=[];constructor(){this.setupListeners()}setupListeners(){let e=me();this.unsubscribe.push(e.on("job:added",()=>{this.jobCounts.added++}),e.on("job:processing",()=>{this.jobCounts.processing++}),e.on("job:completed",(t)=>{this.jobCounts.completed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1);let n=t.duration||0;if(this.completions.push({timestamp:Date.now(),duration:n}),this.completions.length>1000)this.completions.shift()}),e.on("job:failed",(t)=>{if(this.jobCounts.failed++,this.jobCounts.processing=Math.max(0,this.jobCounts.processing-1),t.error){if(this.errors.push({error:t.error,timestamp:Date.now()}),this.errors.length>100)this.errors.shift()}}))}getThroughputPerMinute(){let e=Date.now()-60000;return this.completions.filter((n)=>n.timestamp>=e).length}getAverageProcessingTime(){let e=Date.now()-60000,t=this.completions.filter((n)=>n.timestamp>=e);if(t.length===0)return 0;return t.reduce((n,r)=>n+r.duration,0)/t.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((e)=>e()),this.unsubscribe=[]}}function be(){if(!Ye)Ye=new Ze;return Ye}class Ft{workers=new Map;register(e,t){this.workers.set(e,{id:e,status:"idle",queue:t,processedCount:0,failedCount:0,lastActivityAt:new Date().toISOString(),startedAt:new Date().toISOString()})}markActive(e){let t=this.workers.get(e);if(t)t.status="active",t.lastActivityAt=new Date().toISOString()}markIdle(e){let t=this.workers.get(e);if(t)t.status="idle",t.lastActivityAt=new Date().toISOString()}recordCompletion(e){let t=this.workers.get(e);if(t)t.processedCount++,t.lastActivityAt=new Date().toISOString()}recordFailure(e){let t=this.workers.get(e);if(t)t.failedCount++,t.lastActivityAt=new Date().toISOString()}unregister(e){let t=this.workers.get(e);if(t)t.status="stopped"}getAll(){return Array.from(this.workers.values())}clear(){this.workers.clear()}}function E(){return ir}var Ge=null,Ye=null,ir;var T=q(()=>{ir=new Ft});var{RedisClient:or}=globalThis.Bun;import{log as k}from"@stacksjs/logging";import{env as ar}from"@stacksjs/env";function N(){return ar.QUEUE_DRIVER||"sync"}class ge{jobs;options={thenCallbacks:[],catchCallbacks:[],finallyCallbacks:[],progressCallbacks:[]};constructor(e){this.jobs=e.map((t)=>("job"in t)?t:{job:t})}name(e){return this.options.name=e,this}onQueue(e){return this.options.queue=e,this}allowFailures(){return this.options.allowFailures=!0,this}then(e){return this.options.thenCallbacks.push(e),this}catch(e){return this.options.catchCallbacks.push(e),this}finally(e){return this.options.finallyCallbacks.push(e),this}progress(e){return this.options.progressCallbacks.push(e),this}thenHandler(e){return this.options.thenHandler=e,this}catchHandler(e){return this.options.catchHandler=e,this}finallyHandler(e){return this.options.finallyHandler=e,this}async dispatch(){let e=crypto.randomUUID(),t=this.jobs.length;if(t===0)throw Error("Cannot dispatch an empty batch");let n=N();await cr({id:e,name:this.options.name||"",total_jobs:t,pending_jobs:t,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}),sr(e,this.options);try{await Promise.resolve().then(() => T());await _("batch:added",{jobId:e,data:{name:this.options.name,totalJobs:t}})}catch{}for(let r=0;r<this.jobs.length;r++){let i=this.jobs[r];if(!i)continue;let{job:o,payload:a}=i,l={...a,_batchId:e,_batchIndex:r};if(this.options.queue&&!o.queue)o.queue=this.options.queue;if(n==="sync")try{await o.dispatchNow(l),await ie(e)}catch(s){await oe(e,`${e}:${r}`,s)}else await o.dispatch(l)}return k.info(`[Batch] Dispatched batch "${this.options.name||e}" with ${t} jobs`),new W(e)}getJobs(){return[...this.jobs]}getOptions(){return this.options}}class W{id;constructor(e){this.id=e}async fresh(){return H(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 e=await this.fresh();if(!e)return 0;return e.total_jobs-e.pending_jobs}async progress(){let e=await this.fresh();if(!e||e.total_jobs===0)return 0;let t=e.total_jobs-e.pending_jobs;return Math.round(t/e.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 e=await this.fresh();if(!e)return[];try{return JSON.parse(e.failed_job_ids||"[]")}catch{return[]}}async cancel(){if(N()==="redis")await _r(this.id);else await br(this.id);k.info(`[Batch] Cancelled batch ${this.id}`);let t=Ne(this.id);if(t)for(let n of t.finallyCallbacks)try{await n(this)}catch(r){k.error(`[Batch] Error in finally callback for batch ${this.id}:`,r)}}async add(e){let t=await this.fresh();if(!t)throw Error(`Batch ${this.id} not found`);if(t.cancelled_at)throw Error(`Batch ${this.id} has been cancelled`);if(t.finished_at)throw Error(`Batch ${this.id} has already finished`);let n=e.map((o)=>("job"in o)?o:{job:o});if(!await dr(this.id,n.length))throw Error(`Batch ${this.id} was cancelled, finished or deleted before the jobs could be added`);let i=JSON.parse(t.options||"{}");for(let o=0;o<n.length;o++){let a=n[o];if(!a)continue;let{job:l,payload:s}=a,u={...s,_batchId:this.id,_batchIndex:t.total_jobs+o};if(i.queue&&!l.queue)l.queue=i.queue;await l.dispatch(u)}k.info(`[Batch] Added ${n.length} jobs to batch ${this.id}`)}async delete(){await pr(this.id),$e(this.id)}}class It{static create(e){return new ge(e)}static async find(e){if(!await H(e))return null;return new W(e)}static async all(){return(await ur()).map((t)=>new W(t.id))}static async prune(e=24){return fr(e)}}function sr(e,t){et.set(e,t)}function Ne(e){return et.get(e)}function $e(e){et.delete(e)}async function cr(e){if(N()==="redis")await yr(e);else await Qt(e)}async function H(e){if(N()==="redis")return vr(e);return Bt(e)}async function ur(){if(N()==="redis")return Wt();return Mt()}async function lr(e,t){if(N()==="redis")await rt(e,t);else await tt(e,t)}async function dr(e,t){if(t===0)return!0;if(N()==="redis")try{let o=await V(),a=`${z}${e}`;return await o.hincrby(a,"total_jobs",t),await o.hincrby(a,"pending_jobs",t),o.close(),!0}catch{}let{db:n,sql:r}=await import("@stacksjs/database"),i=await n.updateTable("job_batches").set(hr(r,t)).where("id","=",e).whereNull("cancelled_at").whereNull("finished_at").executeTakeFirst();return U(i)>0}function hr(e,t){return{total_jobs:e`total_jobs + ${t}`,pending_jobs:e`pending_jobs + ${t}`}}async function pr(e){if(N()==="redis")await Vt(e);else await Ht(e)}async function fr(e){if(N()==="redis")return kr(e);return Lt(e)}function mr(e){return!!(e.then_handler||e.catch_handler||e.finally_handler)}async function Qt(e){let{db:t}=await import("@stacksjs/database"),n={id:e.id,name:e.name,total_jobs:e.total_jobs,pending_jobs:e.pending_jobs,failed_jobs:e.failed_jobs,failed_job_ids:e.failed_job_ids,options:e.options,cancelled_at:e.cancelled_at,created_at:e.created_at,finished_at:e.finished_at};if(!mr(e)){await t.insertInto("job_batches").values(n).execute();return}try{await t.insertInto("job_batches").values({...n,then_handler:e.then_handler??null,catch_handler:e.catch_handler??null,finally_handler:e.finally_handler??null}).execute()}catch(r){k.warn(`[Batch] Could not persist terminal handlers for batch ${e.id}: ${r?.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 t.insertInto("job_batches").values(n).execute()}}async function Bt(e){let{db:t}=await import("@stacksjs/database");return await t.selectFrom("job_batches").where("id","=",e).selectAll().executeTakeFirst()||null}async function Mt(){let{db:e}=await import("@stacksjs/database");return await e.selectFrom("job_batches").selectAll().orderBy("created_at","desc").execute()}async function tt(e,t){let{db:n}=await import("@stacksjs/database");await n.updateTable("job_batches").set(t).where("id","=",e).execute()}async function Ht(e){let{db:t}=await import("@stacksjs/database");await t.deleteFrom("job_batches").where("id","=",e).execute()}async function br(e){await tt(e,{cancelled_at:new Date().toISOString().slice(0,19).replace("T"," "),finished_at:new Date().toISOString().slice(0,19).replace("T"," ")})}async function Lt(e){let{db:t}=await import("@stacksjs/database"),n=new Date(Date.now()-e*60*60*1000).toISOString().slice(0,19).replace("T"," "),r=await t.deleteFrom("job_batches").whereNotNull("finished_at").where("finished_at","<",n).executeTakeFirst();return Number(r?.numDeletedRows??0)}async function gr(){let{queue:e}=await import("@stacksjs/config"),t=e?.connections?.redis?.redis;if(t?.url)return t.url;let n=t?.password?`:${encodeURIComponent(t.password)}@`:"",r=t?.db?`/${t.db}`:"";return`redis://${n}${t?.host||"localhost"}:${t?.port||6379}${r}`}async function V(){let e=new or(await gr());return await e.connect(),e}function wr(e){return{id:e.id,name:e.name,total_jobs:String(e.total_jobs),pending_jobs:String(e.pending_jobs),failed_jobs:String(e.failed_jobs),failed_job_ids:e.failed_job_ids,options:e.options,cancelled_at:e.cancelled_at||"",created_at:e.created_at,finished_at:e.finished_at||"",then_handler:e.then_handler||"",catch_handler:e.catch_handler||"",finally_handler:e.finally_handler||""}}async function yr(e){try{let t=await V();await t.hset(`${z}${e.id}`,wr(e)),await t.sadd(nt,e.id),t.close()}catch{await Qt(e)}}function Ut(e){if(!e?.id)return null;return{id:e.id,name:e.name??"",total_jobs:Number(e.total_jobs),pending_jobs:Number(e.pending_jobs),failed_jobs:Number(e.failed_jobs),failed_job_ids:e.failed_job_ids??"",options:e.options??"",cancelled_at:e.cancelled_at||null,created_at:e.created_at??"",finished_at:e.finished_at||null,then_handler:e.then_handler||null,catch_handler:e.catch_handler||null,finally_handler:e.finally_handler||null}}async function vr(e){try{let t=await V(),n=`${z}${e}`,r=await t.hgetall(n);return t.close(),Ut(r)}catch{return Bt(e)}}async function Wt(){try{let e=await V(),t=await e.smembers(nt),n=[];for(let r of t){let i=Ut(await e.hgetall(`${z}${r}`));if(i)n.push(i)}return e.close(),n}catch{return Mt()}}async function rt(e,t){try{let n=await V(),r=`${z}${e}`,i={};for(let[o,a]of Object.entries(t))i[o]=a===null?"":String(a);await n.hset(r,i),n.close()}catch{await tt(e,t)}}async function zt(e,t,n){let r=await V();try{let i=`${z}${e}`,o=await r.hincrby(i,"pending_jobs",-1),a=Math.max(o,0);if(o<0)await r.hset(i,{pending_jobs:"0"});if(t)await r.hincrby(i,"failed_jobs",1);let s=!(await r.hgetall(i)).finished_at&&(!t||n?a===0:!0),u=new Date().toISOString().slice(0,19).replace("T"," "),c=s&&await r.hsetnx(i,"terminal_claimed","1");if(c)await r.hset(i,{finished_at:u});if(c&&t&&!n)await r.hset(i,{cancelled_at:u});return c}finally{r.close()}}async function Vt(e){try{let t=await V();await t.del(`${z}${e}`),await t.srem(nt,e),t.close()}catch{await Ht(e)}}async function _r(e){let t=new Date().toISOString().slice(0,19).replace("T"," ");await rt(e,{cancelled_at:t,finished_at:t})}async function kr(e){try{let t=await Wt(),n=Date.now()-e*60*60*1000,r=0;for(let i of t)if(i.finished_at){if(new Date(i.finished_at).getTime()<n)await Vt(i.id),r++}return r}catch{return Lt(e)}}function ne(e){if(!e)return null;try{let t=JSON.parse(e);if(t&&(t.kind==="job"||t.kind==="module"))return t;return k.warn(`[Batch] handler JSON has unknown kind '${t?.kind}' - skipping`),null}catch(t){return k.warn(`[Batch] failed to parse persistent handler: ${t.message}`),null}}async function re(e,t){try{if(e.kind==="job"){await Promise.resolve().then(() => ye());await it.dispatch(e.name,{...e.payload??{},_batchId:t});return}let n=await import(e.module).catch((i)=>(k.warn(`[Batch] persistent handler module not found: ${e.module} (${i.message})`),null));if(!n)return;let r=n[e.export];if(typeof r!=="function"){k.warn(`[Batch] persistent handler export '${e.export}' is not a function on ${e.module}`);return}await r(e.payload,t)}catch(n){k.error(`[Batch] persistent handler threw for batch ${t}:`,n)}}async function ie(e){let t=N(),n=!1;if(t==="redis")n=await zt(e,!1,!1);else{let{db:o,sql:a}=await import("@stacksjs/database");await o.updateTable("job_batches").set({pending_jobs:a`GREATEST(pending_jobs - 1, 0)`}).where("id","=",e).where("pending_jobs",">",0).execute();let l=new Date().toISOString().slice(0,19).replace("T"," "),s=await o.updateTable("job_batches").set({finished_at:l}).where("id","=",e).where("pending_jobs","=",0).whereNull("finished_at").executeTakeFirst();n=U(s)>0}let r=Ne(e),i=new W(e);if(r)for(let o of r.progressCallbacks)try{await o(i)}catch(a){k.error(`[Batch] Error in progress callback for batch ${e}:`,a)}if(n){try{await Promise.resolve().then(() => T());await _("batch:completed",{jobId:e})}catch{}let o=await H(e);if(!o){$e(e),k.info(`[Batch] Batch ${e} finished (record vanished)`);return}let a=JSON.parse(o.options||"{}"),s=!((o.failed_jobs||0)>0)||a.allowFailures;if(r){if(s)for(let c of r.thenCallbacks)try{await c(i)}catch(d){k.error(`[Batch] Error in then callback for batch ${e}:`,d)}for(let c of r.finallyCallbacks)try{await c(i)}catch(d){k.error(`[Batch] Error in finally callback for batch ${e}:`,d)}$e(e)}if(s){let c=ne(o.then_handler);if(c)await re(c,e)}else{let c=ne(o.catch_handler);if(c)await re(c,e)}let u=ne(o.finally_handler);if(u)await re(u,e);k.info(`[Batch] Batch ${e} finished`)}}async function oe(e,t,n){let r=await H(e);if(!r)return;let i=JSON.parse(r.options||"{}"),o=new W(e),a=Ne(e),l=!1;if(N()==="redis"){l=await zt(e,!0,!!i.allowFailures);let c=await H(e);if(c){let d=[];try{d=JSON.parse(c.failed_job_ids||"[]")}catch{d=[]}d.push(t),await rt(e,{failed_job_ids:JSON.stringify(d)})}if(a)for(let d of a.catchCallbacks)try{await d(o,n)}catch(m){k.error(`[Batch] Error in catch callback for batch ${e}:`,m)}try{await Promise.resolve().then(() => T());await _("batch:failed",{jobId:e,error:n})}catch{}}else{let{db:c,sql:d}=await import("@stacksjs/database");await c.updateTable("job_batches").set({pending_jobs:d`GREATEST(pending_jobs - 1, 0)`,failed_jobs:d`failed_jobs + 1`}).where("id","=",e).where("pending_jobs",">",0).execute();try{let w=await H(e);if(w){let j=[];try{j=JSON.parse(w.failed_job_ids||"[]")}catch{j=[]}j.push(t),await lr(e,{failed_job_ids:JSON.stringify(j)})}}catch{}if(a)for(let w of a.catchCallbacks)try{await w(o,n)}catch(j){k.error(`[Batch] Error in catch callback for batch ${e}:`,j)}try{await Promise.resolve().then(() => T());await _("batch:failed",{jobId:e,error:n})}catch{}let m=new Date().toISOString().slice(0,19).replace("T"," "),h=c.updateTable("job_batches").set(i.allowFailures?{finished_at:m}:{finished_at:m,cancelled_at:m}).where("id","=",e).whereNull("finished_at");if(i.allowFailures)h=h.where("pending_jobs","=",0);let b=await h.executeTakeFirst();l=U(b)>0}if(!l)return;let s=!!i.allowFailures;if(a){if(s)for(let c of a.thenCallbacks)try{await c(o)}catch(d){k.error(`[Batch] Error in then callback for batch ${e}:`,d)}for(let c of a.finallyCallbacks)try{await c(o)}catch(d){k.error(`[Batch] Error in finally callback for batch ${e}:`,d)}$e(e)}let u=await H(e);if(u){if(s){let d=ne(u.then_handler);if(d)await re(d,e)}else{let d=ne(u.catch_handler);if(d)await re(d,e)}let c=ne(u.finally_handler);if(c)await re(c,e)}k.info(`[Batch] Batch ${e} finished with failure(s)`)}async function we(e){return(await H(e))?.cancelled_at!==null}var et,z="stacks:batch:",nt="stacks:batches";var M=q(()=>{De();et=new Map});import{appPath as xr,frameworkPath as Jr}from"@stacksjs/path";import{env as jr}from"@stacksjs/env";import{enqueueAfterCommit as Pr,isInTransaction as Sr}from"@stacksjs/database";function en(){return Yt??=import("@stacksjs/router").catch((e)=>{throw Yt=void 0,e})}function Er(){return jr.QUEUE_DRIVER||"sync"}function Tr(){if(Xt)return;Xt=!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 O{name;payload;options={};txMode="auto";constructor(e,t){this.name=e;this.payload=t}onQueue(e){return this.options.queue=e,this}delay(e){return this.options.delay=e,this}tries(e){return this.options.tries=e,this}timeout(e){return this.options.timeout=e,this}backoff(e){return this.options.backoff=e,this}withContext(e){return this.options.context=e,this}withIdempotencyKey(e){return this.options.idempotencyKey=e,this}afterCommit(){return this.txMode="after",this}withoutCommit(){return this.txMode="immediate",this}async dispatch(){let{isFaked:e,getFakeQueue:t}=await(Kt??=Promise.resolve().then(() => Be).catch((n)=>{throw Kt=void 0,n}));if(e()){t()?.dispatch(this.name,this.payload,this.options);return}if(this.txMode!=="immediate"){if(Sr()){let r=this.runDispatchPipeline.bind(this);if(Pr(async()=>{await r()}))return}else if(this.txMode==="after")Tr()}await this.runDispatchPipeline()}async runDispatchPipeline(){let e=!1;if(this.options.idempotencyKey){let t=await Le(this.options.idempotencyKey,this.name,this.options.queue);if(t==="duplicate")return;e=t==="claimed"}try{if(await Ve(this.name,this.payload)){let n=A(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});if(await pe({queue:this.options.queue||"default",payload:I(n),exception:`quarantined: ${this.name}`},"poison-detected"))return}let t=Er();if(t==="database")await this.dispatchToDatabase();else if(t==="redis")await this.dispatchToRedis();else if(t==="sync")await K(this.name,{payload:this.payload,context:this.options.context,traceId:await Zt()});else if(t==="sqs"||t==="memory"||t==="beanstalkd")throw Error(`[queue] Driver "${t}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);else throw Error(`[queue] Unknown QUEUE_DRIVER "${t}". Allowed values: redis, database, sync.`)}catch(t){if(e&&this.options.idempotencyKey)await Ue(this.options.idempotencyKey);throw t}}async dispatchIf(e){if(e)await this.dispatch()}async dispatchUnless(e){if(!e)await this.dispatch()}async dispatchToDatabase(){let e=Math.floor(Date.now()/1000),t=this.options.delay?e+this.options.delay:e,n=A(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff},await Zt()),r=I(n),{db:i}=await(Gt??=import("@stacksjs/database").catch((o)=>{throw Gt=void 0,o}));await i.insertInto("jobs").values({queue:this.options.queue||"default",payload:r,attempts:0,reserved_at:null,available_at:t,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(){let e=A(this.name,this.payload,{queue:this.options.queue,timeout:this.options.timeout,tries:this.options.tries,backoff:this.options.backoff});le(e);await Promise.resolve().then(() => he());let{queue:n}=await import("@stacksjs/config"),r=n?.connections?.redis;if(!r)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new Q(this.options.queue||"default",r).add(e,{delay:this.options.delay,maxTries:this.options.tries,timeout:this.options.timeout,backoff:this.options.backoff})}async dispatchNow(){await K(this.name,{payload:this.payload,context:this.options.context})}}function Rr(e,t){return new O(e,t)}function Cr(e){M();return new ge(e)}async function qr(e){let t=[xr(`Jobs/${e}.ts`),Jr(`defaults/app/Jobs/${e}.ts`)];for(let r of t)if(await Bun.file(r).exists())return r;let n;try{let r=import.meta.resolve("@stacksjs/defaults/package.json");n=`${new URL(".",r).pathname}app/Jobs/${e}.ts`}catch{}if(n&&await Bun.file(n).exists())return n;return null}async function K(e,t={}){let{withTraceId:n}=await en(),r=t.traceId??`job:${e}:${Math.random().toString(36).slice(2,10)}`;await n(r,async()=>{let i=await qr(e);if(!i)throw Error(`Job ${e} not found. Looked in app/Jobs/${e}.ts and the framework defaults (storage/framework/defaults/app/Jobs, @stacksjs/defaults).`);let a=(await import(i)).default;if(!a)throw Error(`Job ${e} does not export a default`);if(typeof a.handle==="function")await a.handle(t.payload);else if(typeof a.action==="string")await je(a.action);else if(typeof a.action==="function")await a.action();else if(typeof a==="function")await a(t.payload,t.context);else throw Error(`Job ${e} does not have a valid handler`)})}async function Zt(){try{let{getTraceId:e}=await en();return e()}catch{return}}var Kt,Gt,Yt,Xt=!1,it;var ye=q(()=>{Te();ee();We();qe();Pe();it={make(e,t){return new O(e,t)},async dispatch(e,t){await new O(e,t).dispatch()},async dispatchIf(e,t,n){if(e)await new O(t,n).dispatch()},async dispatchUnless(e,t,n){if(!e)await new O(t,n).dispatch()},async dispatchNow(e,t){await new O(e,t).dispatchNow()},dispatchAfter(e,t,n){return new O(t,n).delay(e)},async dispatchOnce(e,t,n){await new O(t,n).withIdempotencyKey(e).dispatch()},async dispatchAfterCommit(e,t){await new O(e,t).afterCommit().dispatch()}}});import{db as R}from"@stacksjs/database";function G(){if(tn)return;tn=!0,console.warn("[queue/circuit-breaker] queue_circuit_state table missing - circuit breaker disabled. Run migrations to enable.")}async function at(e,t){try{let n=await R.selectFrom("queue_circuit_state").where("queue_name","=",e).selectAll().executeTakeFirst();if(n)return n;return await R.insertInto("queue_circuit_state").values({queue_name:e,success_count:0,failure_count:0,window_start:t,paused_at:null,resume_at:null}).execute(),{queue_name:e,success_count:0,failure_count:0,window_start:t,paused_at:null,resume_at:null}}catch(n){if(v(n))return G(),null;let r=n?.message??"";if(r.includes("UNIQUE constraint")||r.includes("Duplicate entry"))return await R.selectFrom("queue_circuit_state").where("queue_name","=",e).selectAll().executeTakeFirst();throw n}}async function st(e){try{let t=await R.selectFrom("queue_circuit_state").where("queue_name","=",e).select(["paused_at","resume_at"]).executeTakeFirst();if(!t||!t.paused_at)return!1;if(t.resume_at){let n=Date.parse(t.resume_at.replace(" ","T")+"Z");if(Number.isFinite(n)&&Date.now()>=n)return await R.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",e).execute(),!1}return!0}catch(t){if(v(t))return G(),!1;throw t}}async function ct(e,t={}){let n=t.windowSeconds??300,r=new Date().toISOString().slice(0,19).replace("T"," "),i=await at(e,r);if(!i)return;try{if(ot(i.window_start,n)){await R.updateTable("queue_circuit_state").set({success_count:1,failure_count:0,window_start:r}).where("queue_name","=",e).execute();return}await R.updateTable("queue_circuit_state").set({success_count:i.success_count+1}).where("queue_name","=",e).execute()}catch(o){if(v(o)){G();return}throw o}}async function ut(e,t={}){let n=t.failureRateThreshold??0.5,r=t.windowSeconds??300,i=t.pauseSeconds??300,o=t.minObservations??10,a=new Date,l=a.toISOString().slice(0,19).replace("T"," "),s=await at(e,l);if(!s)return!1;if(s.paused_at)return!1;try{let{success_count:u,failure_count:c}=s;if(ot(s.window_start,r))u=0,c=0;c+=1;let d=u+c,m=d===0?0:c/d;if(d>=o&&m>=n){let b=new Date(a.getTime()+i*1000).toISOString().slice(0,19).replace("T"," ");return await R.updateTable("queue_circuit_state").set({success_count:u,failure_count:c,window_start:l,paused_at:l,resume_at:b}).where("queue_name","=",e).execute(),!0}return await R.updateTable("queue_circuit_state").set({success_count:u,failure_count:c,window_start:ot(s.window_start,r)?l:s.window_start}).where("queue_name","=",e).execute(),!1}catch(u){if(v(u))return G(),!1;throw u}}function ot(e,t){if(!e)return!0;let n=Date.parse(e.replace(" ","T")+"Z");if(!Number.isFinite(n))return!0;return Date.now()-n>t*1000}async function Dr(e,t=300){let n=new Date,r=n.toISOString().slice(0,19).replace("T"," "),i=new Date(n.getTime()+t*1000).toISOString().slice(0,19).replace("T"," ");await at(e,r);try{await R.updateTable("queue_circuit_state").set({paused_at:r,resume_at:i}).where("queue_name","=",e).execute()}catch(o){if(v(o)){G();return}throw o}}async function $r(e){try{await R.updateTable("queue_circuit_state").set({paused_at:null,resume_at:null,success_count:0,failure_count:0}).where("queue_name","=",e).execute()}catch(t){if(v(t)){G();return}throw t}}async function Nr(){try{return await R.selectFrom("queue_circuit_state").selectAll().execute()??[]}catch(e){if(v(e))return G(),[];throw e}}var tn=!1;var ve=()=>{};ee();Pe();import{env as Hn}from"@stacksjs/env";var Rt,Ct;function qt(){return Hn.QUEUE_DRIVER||"sync"}class Dt{name;description;action;handle;queue;rate;tries;timeout;backoff;backoffConfig;enabled;constructor(e){this.name=e.name,this.description=e.description,this.handle=e.handle,this.queue=e.queue,this.rate=e.rate,this.action=e.action,this.tries=e.tries,this.timeout=e.timeout,this.backoff=e.backoff,this.backoffConfig=e.backoffConfig,this.enabled=e.enabled}async dispatch(...[e]){let{isFaked:t,getFakeQueue:n}=await(Rt??=Promise.resolve().then(() => Be).catch((i)=>{throw Rt=void 0,i}));if(t()){n()?.dispatch(this.name||"UnknownJob",e,{queue:this.queue,tries:this.tries,timeout:this.timeout});return}let r=qt();if(r==="sync")return this.dispatchNow(...[e]);if(r==="redis")return this.dispatchToRedis(e);if(r==="database")return this.dispatchToDatabase(e);if(r==="sqs"||r==="memory"||r==="beanstalkd")throw Error(`[queue] Driver "${r}" is not implemented yet. Set QUEUE_DRIVER to one of: redis, database, sync.`);throw Error(`[queue] Unknown QUEUE_DRIVER "${r}". Allowed values: redis, database, sync.`)}async dispatchIf(e,...[t]){if(e)return this.dispatch(...[t])}async dispatchUnless(e,...[t]){if(!e)return this.dispatch(...[t])}async dispatchAfter(e,...[t]){let n=qt();if(n==="redis")return this.dispatchToRedis(t,{delay:e});if(n==="database")return this.dispatchToDatabase(t,{delay:e});return await new Promise((r)=>setTimeout(r,e*1000)),await this.dispatchNow(...[t])}async dispatchNow(...[e]){if(typeof this.handle==="function")await this.handle(e);else if(typeof this.action==="string")await je(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(e,t){let n=Math.floor(Date.now()/1000),r=t?.delay?n+t.delay:n,i=A(this.name??this.constructor.name,e,{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}),o=I(i),{db:a}=await(Ct??=import("@stacksjs/database").catch((l)=>{throw Ct=void 0,l}));await a.insertInto("jobs").values({queue:this.queue||"default",payload:o,attempts:0,reserved_at:null,available_at:r,created_at:new Date().toISOString().slice(0,19).replace("T"," ")}).execute()}async dispatchToRedis(e,t){let n=A(this.name??this.constructor.name,e,{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});le(n);await Promise.resolve().then(() => he());let{queue:i}=await import("@stacksjs/config"),o=i?.connections?.redis;if(!o)throw Error("Redis queue connection is not configured. Check config/queue.ts");await new Q(this.queue||"default",o).add(n,{delay:t?.delay,maxTries:typeof this.tries==="number"?this.tries:void 0,timeout:this.timeout,backoff:Array.isArray(this.backoff)?this.backoff:void 0})}}Pe();ye();We();Te();qe();ve();ee();var lt=(e)=>`__job_progress__:${e}`,dt=(e)=>`__job_cancel__:${e}`;var _e;function Ar(e){return Number.isFinite(e)?Math.max(0,Math.min(100,e)):0}async function Or(e,t,n){let{cache:r}=_e??=await import("@stacksjs/cache");await r.set(lt(e),{percent:Ar(t),message:n,updatedAt:Date.now()},3600)}async function Fr(e){let{cache:t}=_e??=await import("@stacksjs/cache");return await t.get(lt(e))??null}async function Ir(e){let{cache:t}=_e??=await import("@stacksjs/cache");await t.set(dt(e),1,3600)}async function Qr(e){let{cache:t}=_e??=await import("@stacksjs/cache");return Boolean(await t.get(dt(e)))}async function Br(e){let{cache:t}=_e??=await import("@stacksjs/cache");await Promise.all([t.del(lt(e)),t.del(dt(e))])}import{log as ae}from"@stacksjs/logging";import*as nn from"@stacksjs/path";class rn{jobs=new Map;initialized=!1;register(e){this.jobs.set(e.name,e),ae.debug(`Registered job: ${e.name} (${e.type})`)}get(e){return this.jobs.get(e)}all(){return Array.from(this.jobs.values())}byQueue(e){return this.all().filter((t)=>t.config.queue===e)}scheduled(){return this.all().filter((e)=>e.config.rate||e.config.schedule)}has(e){return this.jobs.has(e)}clear(){this.jobs.clear(),this.initialized=!1}setInitialized(e){this.initialized=e}isInitialized(){return this.initialized}}var Y=new rn;async function ht(e){let t=e||nn.userJobsPath(),n=[];try{let r=new Bun.Glob("**/*.{ts,js}"),i={cwd:t,onlyFiles:!0,absolute:!0};for await(let o of r.scan(i)){if(o.includes(".test.")||o.includes(".spec.")||o.endsWith("index.ts")||o.endsWith("index.js"))continue;try{let a=await Mr(o);if(a)n.push(a),Y.register(a)}catch(a){ae.warn(`Failed to load job from ${o}: ${a.message}`)}}return Y.setInitialized(!0),ae.info(`Discovered ${n.length} jobs from ${t}`),n}catch(r){return ae.error(`Failed to discover jobs: ${r.message}`),[]}}async function Mr(e){try{let t=await import(e),n=e.split("/").pop()?.replace(/\.(ts|js)$/,"")||"UnknownJob";if(t.default&&typeof t.default==="function"){let r=t.default;if(typeof r.handle==="function"||typeof r.prototype?.handle==="function"){let i=r.config||{};return{name:i.name||n,path:e,config:{name:i.name||n,description:i.description,queue:i.queue||"default",tries:i.retries||3,timeout:i.timeout,withoutOverlapping:i.withoutOverlapping,schedule:i.schedule,retryAfter:i.retryAfter},type:"class",module:r}}}if(t.default&&typeof t.default==="object"){let r=t.default;if(typeof r.handle==="function"||typeof r.action==="string")return{name:r.name||n,path:e,config:{name:r.name||n,description:r.description,queue:r.queue||"default",tries:r.tries||3,backoff:r.backoff,rate:r.rate,timeout:r.timeout||r.timeOut,backoffConfig:r.backoffConfig},type:"function",module:r}}return null}catch(t){return ae.debug(`Could not load job from ${e}: ${t.message}`),null}}function Hr(e){return Y.get(e)}function Lr(){return Y.all()}function pt(){return Y.scheduled()}async function Ur(e,t){let n=Y.get(e);if(!n)throw Error(`Job "${e}" not found. Did you run discoverJobs()?`);try{if(n.type==="class"){if(typeof n.module.handle==="function")return await n.module.handle(t);return await new n.module().handle(t)}else{if(typeof n.module.handle==="function")return await n.module.handle(t);throw Error(`Job "${e}" does not have a handle method`)}}catch(r){throw ae.error(`Failed to execute job "${e}": ${r.message}`),r}}function Wr(e){let t=e.config;return{name:t.name,queue:t.queue,tries:t.tries,backoff:t.backoff,timeout:t.timeout,backoffConfig:t.backoffConfig,rate:t.rate}}import{log as x}from"@stacksjs/logging";T();import{log as an}from"@stacksjs/logging";var on=!1;async function sn(){if(on)return!0;try{let{db:e}=await import("@stacksjs/database");return await e.unsafe("CREATE TABLE IF NOT EXISTS scheduled_job_runs (job_name VARCHAR(255) PRIMARY KEY, last_run_at VARCHAR(64) NOT NULL)").execute(),on=!0,!0}catch(e){return an.debug(`[scheduler] run-marker persistence unavailable, using in-memory lastRun: ${e instanceof Error?e.message:String(e)}`),!1}}async function cn(e){if(!await sn())return null;try{let{db:t}=await import("@stacksjs/database"),n=await t.selectFrom("scheduled_job_runs").where("job_name","=",e).select(["last_run_at"]).executeTakeFirst();if(!n?.last_run_at)return null;let r=new Date(n.last_run_at);return Number.isNaN(r.getTime())?null:r}catch{return null}}async function un(e,t){if(!await sn())return;try{let{db:n}=await import("@stacksjs/database"),r=t.toISOString();await n.deleteFrom("scheduled_job_runs").where("job_name","=",e).execute(),await n.insertInto("scheduled_job_runs").values({job_name:e,last_run_at:r}).execute()}catch{}}function zr(e){return`%"jobName":"${e.replace(/[\\%_]/g,(n)=>`\\${n}`)}"%`}async function ln(e){try{let{db:t}=await import("@stacksjs/database"),r=await t.unsafe("SELECT 1 AS present FROM jobs WHERE payload LIKE ? ESCAPE '\\' LIMIT 1",[zr(e)]).execute(),i=Array.isArray(r)?r:r?.rows??[];return Array.isArray(i)&&i.length>0}catch(t){return an.debug(`[scheduler] overlap check unavailable, dispatching anyway: ${t instanceof Error?t.message:String(t)}`),!1}}De();var mn={checkInterval:60000,preventOverlapping:!0},g={isRunning:!1,isShuttingDown:!1,checkInterval:null,jobs:new Map,config:{...mn}},dn=new Set;function Vr(e,t){if(dn.has(e))return;dn.add(e),x.warn(`[scheduler] Cron expression "${e}" specifies seconds="${t}" but the scheduler ticks at minute granularity - the seconds field is being ignored. Use a 5-field expression to avoid this warning, or wait for sub-minute scheduling support.`)}var hn=!1,pn=new Map;function Kr(e){let t=pn.get(e);if(!t)t=new Intl.DateTimeFormat("en-US",{timeZone:e,hour12:!1,month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",weekday:"short"}),pn.set(e,t);return t}function ft(e,t){if(t&&t!=="local"&&t!=="system")try{let n=Kr(t).formatToParts(e),r=(a)=>n.find((l)=>l.type===a)?.value??"",i={Sun:0,Mon:1,Tue:2,Wed:3,Thu:4,Fri:5,Sat:6},o=Number(r("hour"));if(o===24)o=0;return{minute:Number(r("minute")),hour:o,day:Number(r("day")),month:Number(r("month")),dayOfWeek:i[r("weekday")]??e.getDay()}}catch{if(!hn)hn=!0,x.warn(`[scheduler] Invalid timezone "${t}"; falling back to system local time.`)}return{minute:e.getMinutes(),hour:e.getHours(),day:e.getDate(),month:e.getMonth()+1,dayOfWeek:e.getDay()}}function Gr(e,t,n){let r=ft(new Date,n),{minute:i,hour:o,day:a,month:l,dayOfWeek:s}=r;if(t){let w=ft(t,n);if(w.minute===i&&w.hour===o&&w.day===a)return!1}let u=e.trim().split(/\s+/);if(u.length===6){let w=u[0];if(w&&w!=="0"&&w!=="*")Vr(e,w);u=u.slice(1)}if(u.length<5)return x.warn(`Invalid cron expression: ${e}`),!1;let[c,d,m,h,b]=u;return F(c,i,0,59)&&F(d,o,0,23)&&F(m,a,1,31)&&F(h,l,1,12)&&F(b,s,0,6)}function F(e,t,n,r){if(e==="*")return!0;if(e.includes(","))return e.split(",").map((a)=>Number.parseInt(a.trim(),10)).includes(t);if(e.includes("-")){let[o,a]=e.split("-").map((l)=>Number.parseInt(l.trim(),10));return t>=o&&t<=a}if(e.includes("/")){let[o,a]=e.split("/"),l=Number.parseInt(a,10);if(o==="*")return t%l===0;if(o.includes("-")){let[s,u]=o.split("-").map((c)=>Number.parseInt(c.trim(),10));return t>=s&&t<=u&&(t-s)%l===0}}let i=Number.parseInt(e,10);return!Number.isNaN(i)&&t===i}function bn(e){let n={"@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 * * * *"}[e.toLowerCase()];if(n)return n;let r=e.match(/^Every\.(\w+)$/i);if(r&&r[1]!==void 0){let o=r[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 * *"}[o]||null}let i=e.split(/\s+/).length;if(i>=5&&i<=6)return e;return null}function gn(e,t){let n=e.trim().split(/\s+/),r=n.length===6?n.slice(1):n;if(r.length<5)return null;let[i,o,a,l,s]=r,u=new Date;u.setSeconds(0,0);let c=527040;for(let d=1;d<=c;d++){let m=new Date(u.getTime()+d*60000),h=ft(m,t);if(F(i,h.minute,0,59)&&F(o,h.hour,0,23)&&F(a,h.day,1,31)&&F(l,h.month,1,12)&&F(s,h.dayOfWeek,0,6))return m}return null}async function Yr(e={}){if(g.isRunning){x.warn("Scheduler is already running");return}g.config={...mn,...e},g.isRunning=!0,g.isShuttingDown=!1,await ht();let t=pt();for(let i of t){let o=i.config.rate||i.config.schedule;if(o){let a=bn(o);if(a){let l=await cn(i.name);g.jobs.set(i.name,{job:i,lastRun:l,nextRun:gn(a,g.config.timezone),isRunning:!1}),x.info(`Registered scheduled job: ${i.name} (${a})`)}else x.warn(`Invalid schedule for job ${i.name}: ${o}`)}}if(g.jobs.size===0){x.info("No scheduled jobs found");return}x.info(`Scheduler started with ${g.jobs.size} job(s)`),process.on("SIGINT",()=>mt()),process.on("SIGTERM",()=>mt());let n=!1,r=()=>{if(g.isShuttingDown)return;let i=g.config.checkInterval,o=i-Date.now()%i;g.checkInterval=setTimeout(()=>{if(!g.isShuttingDown&&!n)n=!0,fn().catch((a)=>x.error("Scheduler check failed:",a)).finally(()=>{n=!1});r()},o),g.checkInterval?.unref?.()};r(),await fn()}async function fn(){for(let[e,t]of g.jobs){let n=t.job.config.rate||t.job.config.schedule;if(!n)continue;let r=bn(n);if(!r)continue;if(Gr(r,t.lastRun,g.config.timezone)){if((g.config.preventOverlapping||t.job.config.withoutOverlapping)&&await ln(e)){x.debug(`Skipping ${e}: previous execution still running`);continue}if(t.isRunning){x.debug(`Skipping ${e}: a dispatch for it is already in flight`);continue}try{t.isRunning=!0,t.lastRun=new Date,t.nextRun=gn(r,g.config.timezone),await un(e,t.lastRun),x.info(`Dispatching scheduled job: ${e}`),await _("job:added",{jobId:`scheduled-${e}-${Date.now()}`,queueName:t.job.config.queue||"default",jobName:e}),await Ke(e,{queue:t.job.config.queue||"default",payload:{},maxTries:t.job.config.tries||3,timeout:t.job.config.timeout||60}),t.isRunning=!1,x.info(`Scheduled job ${e} dispatched to queue`)}catch(o){t.isRunning=!1,x.error(`Failed to dispatch scheduled job ${e}:`,o)}}}}async function mt(){if(!g.isRunning)return;if(x.info("Stopping scheduler..."),g.isShuttingDown=!0,g.checkInterval)clearTimeout(g.checkInterval),g.checkInterval=null;g.isRunning=!1,g.jobs.clear(),x.info("Scheduler stopped")}function Xr(){return{isRunning:g.isRunning,jobCount:g.jobs.size,jobs:Array.from(g.jobs.entries()).map(([e,t])=>({name:e,schedule:t.job.config.rate||t.job.config.schedule,lastRun:t.lastRun,nextRun:t.nextRun,isRunning:t.isRunning}))}}function Zr(){return g.isRunning}function ei(){return new Map(g.jobs)}async function ti(e){let t=g.jobs.get(e);if(!t)throw Error(`Scheduled job "${e}" not found`);x.info(`Manually triggering scheduled job: ${e}`),await Ke(e,{queue:t.job.config.queue||"default",payload:{},maxTries:t.job.config.tries||3,timeout:t.job.config.timeout||60})}T();T();import{log as ni}from"@stacksjs/logging";function wn(e,t=Math.floor(Date.now()/1000)){if(e.reserved_at)return"processing";let n=typeof e.available_at==="number"?e.available_at:typeof e.available_at==="string"&&e.available_at.trim()?Number(e.available_at):Number.NaN;if(Number.isFinite(n)&&n>t)return"delayed";return"pending"}var ri={maxPendingWarning:1000,maxPendingCritical:5000,maxFailedWarning:10,maxFailedCritical:100,maxJobAgeWarning:3600,maxJobAgeCritical:86400,maxErrorRateWarning:0.1,maxErrorRateCritical:0.5};async function bt(e={}){let t={...ri,...e},n=[],r=new Date,i=Math.floor(r.getTime()/1000);try{let{db:o}=await import("@stacksjs/database"),a=await o.selectFrom("jobs").selectAll().execute(),l=await o.selectFrom("failed_jobs").selectAll().execute(),s=new Map;for(let p of a){let y=p.queue||"default";if(!s.has(y))s.set(y,{pending:0,processing:0,delayed:0});let P=s.get(y),S=wn(p,i);if(S==="processing")P.processing++;else if(S==="delayed")P.delayed++;else if(P.pending++,p.created_at){let Rn=typeof p.created_at==="number"?p.created_at:Math.floor(new Date(p.created_at).getTime()/1000),kt=i-Rn;if(!P.oldestAge||kt>P.oldestAge)P.oldestAge=kt}}let u=new Map;for(let p of l){let y=p.queue||"default";u.set(y,(u.get(y)||0)+1)}let c=[],d=new Set([...s.keys(),...u.keys()]),m=t.queues?[...d].filter((p)=>t.queues.includes(p)):[...d],h=0,b=0,w=0,j=0;for(let p of m){let y=s.get(p)||{pending:0,processing:0,delayed:0},P=u.get(p)||0;h+=y.pending,b+=y.processing,w+=y.delayed,j+=P;let S="healthy";if(y.pending>=t.maxPendingCritical)S="unhealthy",n.push({level:"critical",message:`Queue "${p}" has ${y.pending} pending jobs (threshold: ${t.maxPendingCritical})`,queue:p,timestamp:r.toISOString()});else if(y.pending>=t.maxPendingWarning)S="degraded",n.push({level:"warning",message:`Queue "${p}" has ${y.pending} pending jobs (threshold: ${t.maxPendingWarning})`,queue:p,timestamp:r.toISOString()});if(P>=t.maxFailedCritical)S="unhealthy",n.push({level:"critical",message:`Queue "${p}" has ${P} failed jobs (threshold: ${t.maxFailedCritical})`,queue:p,timestamp:r.toISOString()});else if(P>=t.maxFailedWarning){if(S==="healthy")S="degraded";n.push({level:"warning",message:`Queue "${p}" has ${P} failed jobs (threshold: ${t.maxFailedWarning})`,queue:p,timestamp:r.toISOString()})}if(y.oldestAge){if(y.oldestAge>=t.maxJobAgeCritical)S="unhealthy",n.push({level:"critical",message:`Queue "${p}" has a job waiting for ${Math.floor(y.oldestAge/3600)} hours`,queue:p,timestamp:r.toISOString()});else if(y.oldestAge>=t.maxJobAgeWarning){if(S==="healthy")S="degraded";n.push({level:"warning",message:`Queue "${p}" has a job waiting for ${Math.floor(y.oldestAge/60)} minutes`,queue:p,timestamp:r.toISOString()})}}c.push({name:p,status:S,pending:y.pending,processing:y.processing,delayed:y.delayed,failed:P,oldestJobAge:y.oldestAge})}let vt=be().getMetrics(),{throughputPerMinute:Sn,averageDuration:En}=vt,_t=h+b+w+j,ue=_t>0?j/_t:0,Z="healthy";if(c.some((p)=>p.status==="unhealthy"))Z="unhealthy";else if(c.some((p)=>p.status==="degraded"))Z="degraded";if(ue>=t.maxErrorRateCritical)Z="unhealthy",n.push({level:"critical",message:`Overall error rate is ${(ue*100).toFixed(1)}% (threshold: ${t.maxErrorRateCritical*100}%)`,timestamp:r.toISOString()});else if(ue>=t.maxErrorRateWarning){if(Z==="healthy")Z="degraded";n.push({level:"warning",message:`Overall error rate is ${(ue*100).toFixed(1)}% (threshold: ${t.maxErrorRateWarning*100}%)`,timestamp:r.toISOString()})}let Tn=E().getAll().map((p)=>({id:p.id,status:p.status,queue:p.queue,processedCount:p.processedCount,failedCount:p.failedCount,lastActivityAt:p.lastActivityAt}));return{status:Z,timestamp:r.toISOString(),queues:c,workers:Tn,metrics:{totalPending:h,totalProcessing:b,totalDelayed:w,totalFailed:j,throughputPerMinute:Sn,averageProcessingTime:En,errorRate:ue},alerts:n}}catch(o){ni.error("Failed to perform queue health check:",o);let l=E().getAll().map((s)=>({id:s.id,status:s.status,queue:s.queue,processedCount:s.processedCount,failedCount:s.failedCount,lastActivityAt:s.lastActivityAt}));return{status:"unhealthy",timestamp:r.toISOString(),queues:[],workers:l,metrics:{totalPending:0,totalProcessing:0,totalDelayed:0,totalFailed:0,throughputPerMinute:0,averageProcessingTime:0,errorRate:0},alerts:[{level:"critical",message:`Health check failed: ${o.message}`,timestamp:r.toISOString()}]}}}function ii(e={}){return async(t)=>{let n=await bt(e),r=n.status==="healthy"?200:n.status==="degraded"?207:503;return new Response(JSON.stringify(n,null,2),{status:r,headers:{"Content-Type":"application/json","Cache-Control":"no-store"}})}}async function oi(e={}){return(await bt(e)).status==="healthy"}import{log as C}from"@stacksjs/logging";var ai={"&":"&","<":"<",">":">",'"':""","'":"'"};function ke(e){return String(e).replace(/[&<>"']/g,(t)=>ai[t]??t)}class gt{config;notificationCount=0;lastResetTime=Date.now();pendingBatch=[];batchTimeout=null;activeFlush=null;constructor(e){this.config=e}async notify(e){if(this.config.filter&&!this.config.filter(e))return;if(this.config.rateLimit){let t=Date.now();if(t-this.lastResetTime>3600000)this.notificationCount=0,this.lastResetTime=t;if(this.notificationCount>=this.config.rateLimit){C.debug("Rate limit reached for failed job notifications");return}}if(this.config.batch){if(this.pendingBatch.push(e),!this.batchTimeout)this.batchTimeout=setTimeout(()=>{this.batchTimeout=null,this.activeFlush=this.flushBatch().catch((t)=>C.error("Failed to flush notification batch:",t)).finally(()=>{this.activeFlush=null})},this.config.batchInterval||60000),this.batchTimeout.unref?.();return}await this.sendNotifications([e])}async flushBatch(){if(this.pendingBatch.length===0)return;let e=[...this.pendingBatch];if(this.pendingBatch=[],this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;await this.sendNotifications(e)}async shutdown(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.activeFlush)await this.activeFlush.catch(()=>{});await this.flushBatch().catch((e)=>C.error("Failed to flush notification batch on shutdown:",e))}async sendNotifications(e){let t=[];for(let n of this.config.channels)switch(n){case"email":if(this.config.email)t.push(this.sendEmail(e));break;case"slack":if(this.config.slack)t.push(this.sendSlack(e));break;case"discord":if(this.config.discord)t.push(this.sendDiscord(e));break;case"webhook":if(this.config.webhook)t.push(this.sendWebhook(e));break;case"log":this.logFailures(e);break}try{await Promise.all(t)}catch(n){C.error("Failed to send job failure notifications:",n)}finally{this.notificationCount+=e.length}}async sendEmail(e){let t=this.config.email,n=t.subject||`[Queue] ${e.length} Job(s) Failed`,r=this.formatEmailBody(e);try{let{mail:i}=await import("@stacksjs/email");await i.send({to:Array.isArray(t.to)?t.to:[t.to],from:t.from?{address:t.from}:void 0,subject:n,html:r})}catch(i){C.error("Failed to send email notification:",i)}}formatEmailBody(e){let t=e.map((n)=>`
|
|
3
3
|
<tr>
|
|
4
|
-
<td style="padding: 8px; border: 1px solid #ddd;">${
|
|
5
|
-
<td style="padding: 8px; border: 1px solid #ddd;">${
|
|
6
|
-
<td style="padding: 8px; border: 1px solid #ddd;">${
|
|
4
|
+
<td style="padding: 8px; border: 1px solid #ddd;">${ke(String(n.id))}</td>
|
|
5
|
+
<td style="padding: 8px; border: 1px solid #ddd;">${ke(n.name)}</td>
|
|
6
|
+
<td style="padding: 8px; border: 1px solid #ddd;">${ke(n.queue)}</td>
|
|
7
7
|
<td style="padding: 8px; border: 1px solid #ddd;">${n.attempts}/${n.maxAttempts}</td>
|
|
8
|
-
<td style="padding: 8px; border: 1px solid #ddd;">${
|
|
9
|
-
<td style="padding: 8px; border: 1px solid #ddd;"><pre style="max-width: 300px; overflow: auto;">${
|
|
8
|
+
<td style="padding: 8px; border: 1px solid #ddd;">${ke(n.failedAt.toISOString())}</td>
|
|
9
|
+
<td style="padding: 8px; border: 1px solid #ddd;"><pre style="max-width: 300px; overflow: auto;">${ke(n.exception)}</pre></td>
|
|
10
10
|
</tr>
|
|
11
11
|
`).join("");return`
|
|
12
12
|
<h2>Failed Jobs Report</h2>
|
|
@@ -28,4 +28,4 @@ var q=(e,t,n)=>()=>{if(e)try{t=e(e=0)}catch(r){n=[r]}if(n)throw n[0];return t};f
|
|
|
28
28
|
</table>
|
|
29
29
|
`}async sendSlack(e){let t=this.config.slack,n=[{type:"header",text:{type:"plain_text",text:`\uD83D\uDEA8 ${e.length} Job(s) Failed`,emoji:!0}},...e.slice(0,10).map((r)=>({type:"section",text:{type:"mrkdwn",text:`*${r.name}*
|
|
30
30
|
Queue: ${r.queue} | Attempts: ${r.attempts}/${r.maxAttempts}
|
|
31
|
-
\`\`\`${r.exception.slice(0,200)}\`\`\``}}))];if(e.length>10)n.push({type:"section",text:{type:"mrkdwn",text:`_...and ${e.length-10} more failed jobs_`}});try{let r=await fetch(t.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({channel:t.channel,username:t.username||"Queue Monitor",icon_emoji:t.iconEmoji||":warning:",blocks:n})});if(!r.ok)C.error(`Slack notification failed with status ${r.status}`)}catch(r){C.error("Failed to send Slack notification:",r)}}async sendDiscord(e){let t=this.config.discord,n=e.slice(0,10).map((r)=>({title:`\u274C ${r.name}`,color:15158332,fields:[{name:"Queue",value:r.queue,inline:!0},{name:"Attempts",value:`${r.attempts}/${r.maxAttempts}`,inline:!0},{name:"Failed At",value:r.failedAt.toISOString(),inline:!0},{name:"Exception",value:`\`\`\`${r.exception.slice(0,500)}\`\`\``}],timestamp:r.failedAt.toISOString()}));try{let r=await fetch(t.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:t.username||"Queue Monitor",avatar_url:t.avatarUrl,content:`\uD83D\uDEA8 **${e.length} Job(s) Failed**`,embeds:n})});if(!r.ok)C.error(`Discord notification failed with status ${r.status}`)}catch(r){C.error("Failed to send Discord notification:",r)}}async sendWebhook(e){let t=this.config.webhook,n={event:"jobs.failed",timestamp:new Date().toISOString(),count:e.length,jobs:e.map((i)=>({id:i.id,name:i.name,queue:i.queue,attempts:i.attempts,maxAttempts:i.maxAttempts,exception:i.exception,failedAt:i.failedAt.toISOString()}))},r={"Content-Type":"application/json",...t.headers};if(t.secret){let i=JSON.stringify(n),o=await this.generateSignature(i,t.secret);r["X-Signature"]=o}try{let i=await fetch(t.url,{method:"POST",headers:r,body:JSON.stringify(n)});if(!i.ok)C.error(`Webhook notification failed with status ${i.status}`)}catch(i){C.error("Failed to send webhook notification:",i)}}async generateSignature(e,t){let n=new TextEncoder,r=await crypto.subtle.importKey("raw",n.encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",r,n.encode(e));return Array.from(new Uint8Array(i)).map((o)=>o.toString(16).padStart(2,"0")).join("")}logFailures(e){for(let t of e)C.error(`[Queue] Job "${t.name}" failed on queue "${t.queue}" after ${t.attempts} attempts: ${t.exception}`)}async cleanup(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.pendingBatch.length>0)await this.flushBatch()}}var Je=null;function zr(e){return Je=new ft(e),Je}function Vr(){return Je}async function Kr(e){if(Je)await Je.notify(e)}M();ee();qe();import{err as Gr,ok as an}from"@stacksjs/error-handling";import{log as f}from"@stacksjs/logging";import se from"process";import{env as Xr}from"@stacksjs/env";var sn=!1;function Yr(){if(sn)return;sn=!0,se.on("unhandledRejection",(e,t)=>{f.error(`Unhandled Rejection: ${e}`)}),se.on("uncaughtException",(e)=>{f.error(`Uncaught Exception: ${e.message}`)})}var L=0,ce=!1,J="",X=new Set,cn=new Map;function dn(e){return X.add(e),e.finally(()=>X.delete(e)),e}function Zr(){let e=se.env.STACKS_QUEUE_RESERVATION_TTL_SEC,t=e===void 0?Number.NaN:Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:3600}function ei(){let e=se.env.STACKS_QUEUE_RETRY_JITTER;if(e===void 0)return 0.2;let t=Number(e);if(!Number.isFinite(t)||t<0)return 0.2;return Math.min(t,1)}function ti(e,t,n=Math.random){if(!(e>0)||t<=0)return e;return Math.round(e+e*t*n())}function ni(){let e=se.env.STACKS_QUEUE_SWEEP_INTERVAL_SEC,t=e===void 0?Number.NaN:Number.parseInt(e,10);return(Number.isFinite(t)&&t>0?t:60)*1000}async function un(){let e=Zr(),t=Math.floor(Date.now()/1000)-e,n=Math.floor(Date.now()/1000);try{let{db:r}=await import("@stacksjs/database"),i=await r.updateTable("jobs").set({reserved_at:null,available_at:n}).where("reserved_at","<=",t).executeTakeFirst(),o=U(i);if(o>0)f.warn(`[queue] Requeued ${o} job(s) whose reservation exceeded the ${e}s TTL - likely victims of a worker crash. Set STACKS_QUEUE_RESERVATION_TTL_SEC to tune.`);return o}catch(r){return f.error("[queue] Reservation sweep failed",{reason:r instanceof Error?r.message:String(r)}),0}}function ri(e){if(!e||typeof e!=="object")return!1;let t=e.status??e.statusCode;if(typeof t==="number"&&t>=400&&t<500)return!0;let n=e.name;if(typeof n==="string"&&(n==="ValidationError"||n==="ModelNotFoundError"))return!0;return!1}function ii(){return Xr.QUEUE_DRIVER||"sync"}async function oi(e,t={}){try{f.info("Starting queue processor..."),Yr(),ce=!0,J=`worker-${se.pid}-${Date.now()}`;let n=t.concurrency||1,r=ii();await Promise.resolve().then(() => T());if(we(),E().register(J,e||"default"),r==="redis")return f.info("Using Redis queue driver (bun-queue)"),await pi(e||"default",n),an(void 0);let a;if(e)a=[e];else if(a=await hn(),a.length===0)a=["default"];return f.info(`Processing queues: ${a.join(", ")}`),await ai(a,n),an(void 0)}catch(n){return ce=!1,Gr(n)}}async function hn(){try{let{db:e}=await import("@stacksjs/database"),n=(await e.selectFrom("jobs").select("queue").distinct().execute()).map((r)=>r.queue).filter((r)=>Boolean(r));return n.length>0?n:["default"]}catch{return["default"]}}async function ai(e,t){f.info("Listening for jobs...");let n=e,r=Date.now(),i=1e4,o=Date.now(),a=ni();await un();while(ce)try{let l=Date.now();if(l-r>i){try{let s=await hn();if(s.length>0)n=s}catch{}r=l}if(l-o>a)await un(),o=l;for(let s of n){try{await Promise.resolve().then(() => ke());if(await it(s))continue}catch{}let u=[];try{u=await si(s,t)}catch(c){let d=Date.now(),m=cn.get(s)??0;if(d-m>60000)cn.set(s,d),f.error(`[queue] Could not reserve jobs on "${s}" - retrying each cycle:`,c);continue}await Promise.all(u.map(async(c)=>{try{f.info(`Processing job ${c.id} from queue "${s}"`),await dn(ci(c))}catch{f.error(`Unexpected error processing job ${c.id}`)}}))}await bt(1000)}catch{await bt(3000)}}async function si(e,t){let n=Math.floor(Date.now()/1000),{db:r}=await import("@stacksjs/database"),i=[];for(let o=0;o<t;o++){let a=await r.selectFrom("jobs").where("queue","=",e).whereNull("reserved_at").where("available_at","<=",n).orderBy("id","asc").limit(1).selectAll().executeTakeFirst();if(!a)break;let l=await r.updateTable("jobs").set({reserved_at:n,attempts:Number(a.attempts||0)+1}).where("id","=",a.id).whereNull("reserved_at").executeTakeFirst();if(U(l)>0)i.push(a)}return i}async function ci(e){let t=e.id,n=e.queue||"default";L++;let r=Date.now();await Promise.resolve().then(() => T());let a=E();a.markActive(J);let l;try{l=JSON.parse(e.payload||"{}").jobName}catch{}await _("job:processing",{jobId:String(t),queueName:n,jobName:l});try{let c=JSON.parse(e.payload||"{}").payload?._batchId;if(c){await Promise.resolve().then(() => M());if(await ve(c)){f.info(`[Queue] Skipping job ${t} - batch ${c} has been cancelled`),await mt(t),L--,a.markIdle(J);return}}}catch{}let s=null;try{let u=JSON.parse(e.payload||"{}"),c=di(u);if(c===void 0)await ln(u);else await hi(ln(u),c*1000,`Job ${t} exceeded ${c}s timeout`)}catch(u){s=u instanceof Error?u:Error(String(u))}if(!s)try{await mt(t),f.info(`[Queue] Job ${t} completed`),a.recordCompletion(J);try{await Promise.resolve().then(() => ke());await ot(e.queue??"default")}catch{}await _("job:completed",{jobId:String(t),queueName:n,duration:Date.now()-r});try{let c=JSON.parse(e.payload||"{}").payload?._batchId;if(c){await Promise.resolve().then(() => M());await ie(c)}}catch{}}catch{f.info(`[Queue] Failed to delete completed job ${t}`)}else{let u=s.message;f.info(`[Queue] Job ${t} failed: ${u}`),a.recordFailure(J),await _("job:failed",{jobId:String(t),queueName:n,error:s,duration:Date.now()-r,attemptsMade:(e.attempts||0)+1});let c=1,d={};try{d=JSON.parse(e.payload||"{}"),c=d.options?.tries||1}catch{}let m=(e.attempts||0)+1;if(m>=c){f.info(`[Queue] Job ${t} exceeded max attempts (${m}/${c})`);let h=!1;if(d?._retriedFromFailed===!0)try{await Promise.resolve().then(() => Te());if(h=await me({queue:e.queue,payload:e.payload,exception:s.stack||s.message},"repeat-failure",2),h)f.info(`[Queue] Job ${t} re-failed after retry - moved to dead_letter_jobs`)}catch{h=!1}if(!h){f.info(`[Queue] Moving job ${t} to failed_jobs`);try{h=await li(e,s,{attempts:m,maxAttempts:c,durationMs:Date.now()-r})}catch{h=!1}}if(h)try{await mt(t)}catch{f.info(`[Queue] Failed to delete failed job ${t}`)}else f.error(`[Queue] Job ${t} exhausted its retries but could NOT be persisted to failed_jobs - 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{await Promise.resolve().then(() => Ce());await Promise.resolve().then(() => ke());let j=d?.jobName??"unknown";await Le(j,d?.payload),await at(e.queue??"default")}catch{}try{let b=d.payload?._batchId;if(b){await Promise.resolve().then(() => M());await oe(b,String(t),s)}}catch{}}else{let h=d.options?.backoff,b=30;if(Array.isArray(h)&&h.length>0){let w=Math.min(m-1,h.length-1);b=h[w]}else if(typeof h==="number"&&h>0)b=h;if(b=Number(b),!Number.isFinite(b)||b<0)b=30;b=ti(b,ei()),f.info(`[Queue] Job ${t} will be retried in ${b}s (attempt ${m}/${c})`);try{await ui(t,b),f.info(`[Queue] Job ${t} released for retry`)}catch{f.info(`[Queue] Failed to release job ${t} for retry`)}}}L--,a.markIdle(J)}async function mt(e){let{db:t}=await import("@stacksjs/database");await t.deleteFrom("jobs").where("id","=",e).execute()}async function ui(e,t=30){let n=Math.floor(Date.now()/1000)+t;f.debug(`Releasing job ${e} for retry at ${n}`);try{let{db:r}=await import("@stacksjs/database");await r.updateTable("jobs").set({reserved_at:null,available_at:n}).where("id","=",e).execute(),f.debug(`Job ${e} released successfully`)}catch{f.error(`Failed to release job ${e}`)}}async function li(e,t,n){try{let r=new Date().toISOString().slice(0,19).replace("T"," "),i=crypto.randomUUID(),o=t.stack||t.message,{db:a}=await import("@stacksjs/database");return await a.insertInto("failed_jobs").values({uuid:i,connection:"database",queue:e.queue,payload:e.payload,exception:o,attempts:n.attempts,max_attempts:n.maxAttempts,duration_ms:n.durationMs,failed_at:r}).execute(),!0}catch(r){return f.error("Failed to log failed job:",r),!1}}function di(e){if(!e||typeof e!=="object")return;let n=e.options?.timeout;if(typeof n!=="number"||!Number.isFinite(n)||n<=0)return;return n}async function hi(e,t,n){let r,i=new Promise((o,a)=>{r=setTimeout(()=>a(Error(n)),t)});try{return await Promise.race([e,i])}finally{if(r!==void 0)clearTimeout(r)}}async function ln(e){let t=je(e);if(!t.ok)throw Error(`[queue] Cannot deserialize job envelope: ${t.reason}`+(t.detail?` (${t.detail})`:""));await Promise.resolve().then(() => _e());await K(t.envelope.jobName,{payload:t.envelope.payload,traceId:t.envelope.traceId})}async function pi(e,t){await Promise.resolve().then(() => fe());let{queue:r}=await import("@stacksjs/config"),i=r?.connections?.redis;if(!i)throw Error("Redis queue connection is not configured. Check config/queue.ts");let o=new Q(e,i);await Promise.resolve().then(() => T());let s=E(),u=async(c)=>{L++,s.markActive(J);let d=Date.now(),m=je(c.data);if(!m.ok){L--,s.markIdle(J),f.error(`[Queue] Skipping Redis job ${c.id} - unparseable envelope: ${m.reason}${m.detail?` (${m.detail})`:""}`);return}let h=m.envelope,b=h.payload?._batchId;if(b)try{await Promise.resolve().then(() => M());if(await ve(b)){f.info(`[Queue] Skipping Redis job ${c.id} - batch ${b} has been cancelled`),L--,s.markIdle(J);return}}catch{}await _("job:processing",{jobId:String(c.id),queueName:e});try{await Promise.resolve().then(() => _e());if(await K(h.jobName,{payload:h.payload,traceId:h.traceId}),s.recordCompletion(J),await _("job:completed",{jobId:String(c.id),queueName:e,duration:Date.now()-d}),b)try{await Promise.resolve().then(() => M());await ie(b)}catch{}f.info(`[Queue] Redis job ${c.id} completed`)}catch(w){if(s.recordFailure(J),await _("job:failed",{jobId:String(c.id),queueName:e,error:w instanceof Error?w:Error(String(w)),duration:Date.now()-d}),b)try{await Promise.resolve().then(() => M());await oe(b,String(c.id),w instanceof Error?w:Error(String(w)))}catch{}if(f.error(`[Queue] Redis job ${c.id} failed: ${w}`),ri(w)){f.info(`[Queue] Redis job ${c.id} hit a non-retryable error - skipping retry`);return}throw w}finally{L--,s.markIdle(J)}};o.process(t,(c)=>dn(u(c))),f.info(`Listening for Redis jobs on queue "${e}" with concurrency ${t}...`);while(ce)await bt(1000);await o.close()}async function fi(e={}){let t=e.graceMs??1e4;if(ce=!1,X.size>0&&t>0){f.info(`[queue] Draining ${X.size} in-flight job(s) (grace ${t}ms)`);let n=Promise.allSettled([...X]),r=new Promise((o)=>setTimeout(()=>o("timeout"),t));if(await Promise.race([n.then(()=>"drained"),r])==="timeout"&&X.size>0)f.warn(`[queue] Drain timed out with ${X.size} job(s) still active. Their reservations will be reclaimed by the next worker's sweep (Q-2).`)}if(J){await Promise.resolve().then(() => T());E().unregister(J)}f.info("Queue processor stopped")}async function mi(){let{db:e}=await import("@stacksjs/database"),t=await e.selectFrom("failed_jobs").selectAll().execute();for(let n of t)await pn(Number(n.id))}async function pn(e){let t=Math.floor(Date.now()/1000),n=new Date().toISOString().slice(0,19).replace("T"," "),{db:r}=await import("@stacksjs/database"),o=(await r.selectFrom("failed_jobs").where("id","=",e).selectAll().execute())[0];if(!o)throw Error(`Failed job ${e} not found`);let a=typeof o.payload==="string"?o.payload:"",l=a;try{let s=JSON.parse(a||"{}");s._retriedFromFailed=!0,l=JSON.stringify(s)}catch{}await r.insertInto("jobs").values({queue:o.queue,payload:l,attempts:0,reserved_at:null,available_at:t,created_at:n}).execute(),await r.deleteFrom("failed_jobs").where("id","=",e).execute(),f.info(`Failed job ${e} has been re-queued`)}function bi(){return L}function gi(){return ce}function bt(e){return new Promise((t)=>setTimeout(t,e))}async function Bo(){await Promise.resolve().then(() => fe());return Q}export{Rt as Batch,W as DispatchedBatch,ft as FailedJobNotifier,wn as JOB_ENVELOPE_VERSION,jt as Job,tt as Jobs,Hn as OnQueueEvent,ye as PendingBatch,Ke as QueueEvents,Ge as QueueMetrics,Fe as QueueTester,le as assertEnvelopeSerializable,jr as cancelJob,pt as checkQueueHealth,Be as claimDispatchKey,kn as clearEnvelopeWarnings,Sr as clearJobState,zr as configureFailedJobNotifications,A as createEnvelope,Lr as createHealthCheckHandler,jn as createQueueTester,ut as discoverJobs,_ as emitQueueEvent,mi as executeFailedJobs,Cr as executeJob,Sn as expectJobToFail,kt as fake,xn as getActionRunner,bi as getActiveJobCount,Rr as getAllJobs,$e as getBatchCallbacks,Vr as getFailedJobNotifier,he as getFakeQueue,we as getGlobalMetrics,Tr as getJob,Jr as getJobProgress,ge as getQueueEvents,Bo as getRedisQueue,Qr as getRegisteredJobs,lt as getScheduledJobs,Fr as getSchedulerStatus,E as getWorkerTracker,Dn as hasDispatchedKey,Re as hashPayload,ve as isBatchCancelled,it as isCircuitOpen,pe as isFaked,Pr as isJobCancelled,Ue as isQuarantined,Ur as isQueueHealthy,Ir as isSchedulerRunning,gi as isWorkerRunning,br as job,gr as jobBatch,Y as jobRegistry,_r as listCircuitState,Rn as listDeadLetterJobs,Fn as listQuarantined,me as moveToDeadLetter,Kr as notifyJobFailed,Bn as onQueueEvent,je as parseEnvelope,yr as pauseQueue,qn as purgeDeadLetterJobs,An as quarantineJob,on as queuedJobState,ie as recordBatchJobCompletion,oe as recordBatchJobFailure,at as recordCircuitFailure,ot as recordCircuitSuccess,$n as recordDispatchedKey,Le as recordFailureForPoison,Me as releaseDispatchKey,xt as restore,vr as resumeQueue,Cn as retryDeadLetterJob,pn as retryFailedJob,K as runJob,Pn as runTestJob,I as serializeEnvelope,Jn as setActionRunner,xr as setJobProgress,oi as startProcessor,Or as startScheduler,fi as stopProcessor,ht as stopScheduler,qr as toJobOptions,Br as triggerJob,On as unquarantineJob,Mn as withEvents};
|
|
31
|
+
\`\`\`${r.exception.slice(0,200)}\`\`\``}}))];if(e.length>10)n.push({type:"section",text:{type:"mrkdwn",text:`_...and ${e.length-10} more failed jobs_`}});try{let r=await fetch(t.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({channel:t.channel,username:t.username||"Queue Monitor",icon_emoji:t.iconEmoji||":warning:",blocks:n})});if(!r.ok)C.error(`Slack notification failed with status ${r.status}`)}catch(r){C.error("Failed to send Slack notification:",r)}}async sendDiscord(e){let t=this.config.discord,n=e.slice(0,10).map((r)=>({title:`\u274C ${r.name}`,color:15158332,fields:[{name:"Queue",value:r.queue,inline:!0},{name:"Attempts",value:`${r.attempts}/${r.maxAttempts}`,inline:!0},{name:"Failed At",value:r.failedAt.toISOString(),inline:!0},{name:"Exception",value:`\`\`\`${r.exception.slice(0,500)}\`\`\``}],timestamp:r.failedAt.toISOString()}));try{let r=await fetch(t.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:t.username||"Queue Monitor",avatar_url:t.avatarUrl,content:`\uD83D\uDEA8 **${e.length} Job(s) Failed**`,embeds:n})});if(!r.ok)C.error(`Discord notification failed with status ${r.status}`)}catch(r){C.error("Failed to send Discord notification:",r)}}async sendWebhook(e){let t=this.config.webhook,n={event:"jobs.failed",timestamp:new Date().toISOString(),count:e.length,jobs:e.map((i)=>({id:i.id,name:i.name,queue:i.queue,attempts:i.attempts,maxAttempts:i.maxAttempts,exception:i.exception,failedAt:i.failedAt.toISOString()}))},r={"Content-Type":"application/json",...t.headers};if(t.secret){let i=JSON.stringify(n),o=await this.generateSignature(i,t.secret);r["X-Signature"]=o}try{let i=await fetch(t.url,{method:"POST",headers:r,body:JSON.stringify(n)});if(!i.ok)C.error(`Webhook notification failed with status ${i.status}`)}catch(i){C.error("Failed to send webhook notification:",i)}}async generateSignature(e,t){let n=new TextEncoder,r=await crypto.subtle.importKey("raw",n.encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",r,n.encode(e));return Array.from(new Uint8Array(i)).map((o)=>o.toString(16).padStart(2,"0")).join("")}logFailures(e){for(let t of e)C.error(`[Queue] Job "${t.name}" failed on queue "${t.queue}" after ${t.attempts} attempts: ${t.exception}`)}async cleanup(){if(this.batchTimeout)clearTimeout(this.batchTimeout),this.batchTimeout=null;if(this.pendingBatch.length>0)await this.flushBatch()}}var xe=null;function si(e){return xe=new gt(e),xe}function ci(){return xe}async function ui(e){if(xe)await xe.notify(e)}M();ee();De();import{err as li,ok as yn}from"@stacksjs/error-handling";import{log as f}from"@stacksjs/logging";import se from"process";import{env as hi}from"@stacksjs/env";var vn=!1;function di(){if(vn)return;vn=!0,se.on("unhandledRejection",(e,t)=>{f.error(`Unhandled Rejection: ${e}`)}),se.on("uncaughtException",(e)=>{f.error(`Uncaught Exception: ${e.message}`)})}var L=0,ce=!1,J="",X=new Set,_n=new Map;function Jn(e){return X.add(e),e.finally(()=>X.delete(e)),e}function pi(){let e=se.env.STACKS_QUEUE_RESERVATION_TTL_SEC,t=e===void 0?Number.NaN:Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:3600}function fi(){let e=se.env.STACKS_QUEUE_RETRY_JITTER;if(e===void 0)return 0.2;let t=Number(e);if(!Number.isFinite(t)||t<0)return 0.2;return Math.min(t,1)}function mi(e,t,n=Math.random){if(!(e>0)||t<=0)return e;return Math.round(e+e*t*n())}function bi(){let e=se.env.STACKS_QUEUE_SWEEP_INTERVAL_SEC,t=e===void 0?Number.NaN:Number.parseInt(e,10);return(Number.isFinite(t)&&t>0?t:60)*1000}async function kn(){let e=pi(),t=Math.floor(Date.now()/1000)-e,n=Math.floor(Date.now()/1000);try{let{db:r}=await import("@stacksjs/database"),i=await r.updateTable("jobs").set({reserved_at:null,available_at:n}).where("reserved_at","<=",t).executeTakeFirst(),o=U(i);if(o>0)f.warn(`[queue] Requeued ${o} job(s) whose reservation exceeded the ${e}s TTL - likely victims of a worker crash. Set STACKS_QUEUE_RESERVATION_TTL_SEC to tune.`);return o}catch(r){return f.error("[queue] Reservation sweep failed",{reason:r instanceof Error?r.message:String(r)}),0}}function gi(e){if(!e||typeof e!=="object")return!1;let t=e.status??e.statusCode;if(typeof t==="number"&&t>=400&&t<500)return!0;let n=e.name;if(typeof n==="string"&&(n==="ValidationError"||n==="ModelNotFoundError"))return!0;return!1}function wi(){return hi.QUEUE_DRIVER||"sync"}async function yi(e,t={}){try{f.info("Starting queue processor..."),di(),ce=!0,J=`worker-${se.pid}-${Date.now()}`;let n=t.concurrency||1,r=wi();await Promise.resolve().then(() => T());if(be(),E().register(J,e||"default"),r==="redis")return f.info("Using Redis queue driver (bun-queue)"),await Si(e||"default",n),yn(void 0);let a;if(e)a=[e];else if(a=await jn(),a.length===0)a=["default"];return f.info(`Processing queues: ${a.join(", ")}`),await vi(a,n),yn(void 0)}catch(n){return ce=!1,li(n)}}async function jn(){try{let{db:e}=await import("@stacksjs/database"),n=(await e.selectFrom("jobs").select("queue").distinct().execute()).map((r)=>r.queue).filter((r)=>Boolean(r));return n.length>0?n:["default"]}catch{return["default"]}}async function vi(e,t){f.info("Listening for jobs...");let n=e,r=Date.now(),i=1e4,o=Date.now(),a=bi();await kn();while(ce)try{let l=Date.now();if(l-r>i){try{let s=await jn();if(s.length>0)n=s}catch{}r=l}if(l-o>a)await kn(),o=l;for(let s of n){try{await Promise.resolve().then(() => ve());if(await st(s))continue}catch{}let u=[];try{u=await _i(s,t)}catch(c){let d=Date.now(),m=_n.get(s)??0;if(d-m>60000)_n.set(s,d),f.error(`[queue] Could not reserve jobs on "${s}" - retrying each cycle:`,c);continue}await Promise.all(u.map(async(c)=>{try{f.info(`Processing job ${c.id} from queue "${s}"`),await Jn(ki(c))}catch{f.error(`Unexpected error processing job ${c.id}`)}}))}await yt(1000)}catch{await yt(3000)}}async function _i(e,t){let n=Math.floor(Date.now()/1000),{db:r}=await import("@stacksjs/database"),i=[];for(let o=0;o<t;o++){let a=await r.selectFrom("jobs").where("queue","=",e).whereNull("reserved_at").where("available_at","<=",n).orderBy("id","asc").limit(1).selectAll().executeTakeFirst();if(!a)break;let l=await r.updateTable("jobs").set({reserved_at:n,attempts:Number(a.attempts||0)+1}).where("id","=",a.id).whereNull("reserved_at").executeTakeFirst();if(U(l)>0)i.push(a)}return i}async function ki(e){let t=e.id,n=e.queue||"default";L++;let r=Date.now();await Promise.resolve().then(() => T());let a=E();a.markActive(J);let l;try{l=JSON.parse(e.payload||"{}").jobName}catch{}await _("job:processing",{jobId:String(t),queueName:n,jobName:l});try{let c=JSON.parse(e.payload||"{}").payload?._batchId;if(c){await Promise.resolve().then(() => M());if(await we(c)){f.info(`[Queue] Skipping job ${t} - batch ${c} has been cancelled`),await wt(t),L--,a.markIdle(J);return}}}catch{}let s=null;try{let u=JSON.parse(e.payload||"{}"),c=ji(u);if(c===void 0)await xn(u);else await Pi(xn(u),c*1000,`Job ${t} exceeded ${c}s timeout`)}catch(u){s=u instanceof Error?u:Error(String(u))}if(!s)try{await wt(t),f.info(`[Queue] Job ${t} completed`),a.recordCompletion(J);try{await Promise.resolve().then(() => ve());await ct(e.queue??"default")}catch{}await _("job:completed",{jobId:String(t),queueName:n,duration:Date.now()-r});try{let c=JSON.parse(e.payload||"{}").payload?._batchId;if(c){await Promise.resolve().then(() => M());await ie(c)}}catch{}}catch{f.info(`[Queue] Failed to delete completed job ${t}`)}else{let u=s.message;f.info(`[Queue] Job ${t} failed: ${u}`),a.recordFailure(J),await _("job:failed",{jobId:String(t),queueName:n,error:s,duration:Date.now()-r,attemptsMade:(e.attempts||0)+1});let c=1,d={};try{d=JSON.parse(e.payload||"{}"),c=d.options?.tries||1}catch{}let m=(e.attempts||0)+1;if(m>=c){f.info(`[Queue] Job ${t} exceeded max attempts (${m}/${c})`);let h=!1;if(d?._retriedFromFailed===!0)try{await Promise.resolve().then(() => Te());if(h=await pe({queue:e.queue,payload:e.payload,exception:s.stack||s.message},"repeat-failure",2),h)f.info(`[Queue] Job ${t} re-failed after retry - moved to dead_letter_jobs`)}catch{h=!1}if(!h){f.info(`[Queue] Moving job ${t} to failed_jobs`);try{h=await Ji(e,s,{attempts:m,maxAttempts:c,durationMs:Date.now()-r})}catch{h=!1}}if(h)try{await wt(t)}catch{f.info(`[Queue] Failed to delete failed job ${t}`)}else f.error(`[Queue] Job ${t} exhausted its retries but could NOT be persisted to failed_jobs - 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{await Promise.resolve().then(() => qe());await Promise.resolve().then(() => ve());let j=d?.jobName??"unknown";await ze(j,d?.payload),await ut(e.queue??"default")}catch{}try{let b=d.payload?._batchId;if(b){await Promise.resolve().then(() => M());await oe(b,String(t),s)}}catch{}}else{let h=d.options?.backoff,b=30;if(Array.isArray(h)&&h.length>0){let w=Math.min(m-1,h.length-1);b=h[w]}else if(typeof h==="number"&&h>0)b=h;if(b=Number(b),!Number.isFinite(b)||b<0)b=30;b=mi(b,fi()),f.info(`[Queue] Job ${t} will be retried in ${b}s (attempt ${m}/${c})`);try{await xi(t,b),f.info(`[Queue] Job ${t} released for retry`)}catch{f.info(`[Queue] Failed to release job ${t} for retry`)}}}L--,a.markIdle(J)}async function wt(e){let{db:t}=await import("@stacksjs/database");await t.deleteFrom("jobs").where("id","=",e).execute()}async function xi(e,t=30){let n=Math.floor(Date.now()/1000)+t;f.debug(`Releasing job ${e} for retry at ${n}`);try{let{db:r}=await import("@stacksjs/database");await r.updateTable("jobs").set({reserved_at:null,available_at:n}).where("id","=",e).execute(),f.debug(`Job ${e} released successfully`)}catch{f.error(`Failed to release job ${e}`)}}async function Ji(e,t,n){try{let r=new Date().toISOString().slice(0,19).replace("T"," "),i=crypto.randomUUID(),o=t.stack||t.message,{db:a}=await import("@stacksjs/database");return await a.insertInto("failed_jobs").values({uuid:i,connection:"database",queue:e.queue,payload:e.payload,exception:o,attempts:n.attempts,max_attempts:n.maxAttempts,duration_ms:n.durationMs,failed_at:r}).execute(),!0}catch(r){return f.error("Failed to log failed job:",r),!1}}function ji(e){if(!e||typeof e!=="object")return;let n=e.options?.timeout;if(typeof n!=="number"||!Number.isFinite(n)||n<=0)return;return n}async function Pi(e,t,n){let r,i=new Promise((o,a)=>{r=setTimeout(()=>a(Error(n)),t)});try{return await Promise.race([e,i])}finally{if(r!==void 0)clearTimeout(r)}}async function xn(e){let t=Je(e);if(!t.ok)throw Error(`[queue] Cannot deserialize job envelope: ${t.reason}`+(t.detail?` (${t.detail})`:""));await Promise.resolve().then(() => ye());await K(t.envelope.jobName,{payload:t.envelope.payload,traceId:t.envelope.traceId})}async function Si(e,t){await Promise.resolve().then(() => he());let{queue:r}=await import("@stacksjs/config"),i=r?.connections?.redis;if(!i)throw Error("Redis queue connection is not configured. Check config/queue.ts");let o=new Q(e,i);await Promise.resolve().then(() => T());let s=E(),u=async(c)=>{L++,s.markActive(J);let d=Date.now(),m=Je(c.data);if(!m.ok){L--,s.markIdle(J),f.error(`[Queue] Skipping Redis job ${c.id} - unparseable envelope: ${m.reason}${m.detail?` (${m.detail})`:""}`);return}let h=m.envelope,b=h.payload?._batchId;if(b)try{await Promise.resolve().then(() => M());if(await we(b)){f.info(`[Queue] Skipping Redis job ${c.id} - batch ${b} has been cancelled`),L--,s.markIdle(J);return}}catch{}await _("job:processing",{jobId:String(c.id),queueName:e});try{await Promise.resolve().then(() => ye());if(await K(h.jobName,{payload:h.payload,traceId:h.traceId}),s.recordCompletion(J),await _("job:completed",{jobId:String(c.id),queueName:e,duration:Date.now()-d}),b)try{await Promise.resolve().then(() => M());await ie(b)}catch{}f.info(`[Queue] Redis job ${c.id} completed`)}catch(w){if(s.recordFailure(J),await _("job:failed",{jobId:String(c.id),queueName:e,error:w instanceof Error?w:Error(String(w)),duration:Date.now()-d}),b)try{await Promise.resolve().then(() => M());await oe(b,String(c.id),w instanceof Error?w:Error(String(w)))}catch{}if(f.error(`[Queue] Redis job ${c.id} failed: ${w}`),gi(w)){f.info(`[Queue] Redis job ${c.id} hit a non-retryable error - skipping retry`);return}throw w}finally{L--,s.markIdle(J)}};o.process(t,(c)=>Jn(u(c))),f.info(`Listening for Redis jobs on queue "${e}" with concurrency ${t}...`);while(ce)await yt(1000);await o.close()}async function Ei(e={}){let t=e.graceMs??1e4;if(ce=!1,X.size>0&&t>0){f.info(`[queue] Draining ${X.size} in-flight job(s) (grace ${t}ms)`);let n=Promise.allSettled([...X]),r=new Promise((o)=>setTimeout(()=>o("timeout"),t));if(await Promise.race([n.then(()=>"drained"),r])==="timeout"&&X.size>0)f.warn(`[queue] Drain timed out with ${X.size} job(s) still active. Their reservations will be reclaimed by the next worker's sweep (Q-2).`)}if(J){await Promise.resolve().then(() => T());E().unregister(J)}f.info("Queue processor stopped")}async function Ti(){let{db:e}=await import("@stacksjs/database"),t=await e.selectFrom("failed_jobs").selectAll().execute();for(let n of t)await Pn(Number(n.id))}async function Pn(e){let t=Math.floor(Date.now()/1000),n=new Date().toISOString().slice(0,19).replace("T"," "),{db:r}=await import("@stacksjs/database"),o=(await r.selectFrom("failed_jobs").where("id","=",e).selectAll().execute())[0];if(!o)throw Error(`Failed job ${e} not found`);let a=typeof o.payload==="string"?o.payload:"",l=a;try{let s=JSON.parse(a||"{}");s._retriedFromFailed=!0,l=JSON.stringify(s)}catch{}await r.insertInto("jobs").values({queue:o.queue,payload:l,attempts:0,reserved_at:null,available_at:t,created_at:n}).execute(),await r.deleteFrom("failed_jobs").where("id","=",e).execute(),f.info(`Failed job ${e} has been re-queued`)}function Ri(){return L}function Ci(){return ce}function yt(e){return new Promise((t)=>setTimeout(t,e))}async function ea(){await Promise.resolve().then(() => he());return Q}export{It as Batch,W as DispatchedBatch,gt as FailedJobNotifier,Nn as JOB_ENVELOPE_VERSION,Dt as Job,it as Jobs,rr as OnQueueEvent,ge as PendingBatch,Xe as QueueEvents,Ze as QueueMetrics,Se as QueueTester,le as assertEnvelopeSerializable,Ir as cancelJob,bt as checkQueueHealth,Le as claimDispatchKey,In as clearEnvelopeWarnings,Br as clearJobState,si as configureFailedJobNotifications,A as createEnvelope,ii as createHealthCheckHandler,St as createQueueTester,ht as discoverJobs,_ as emitQueueEvent,Ti as executeFailedJobs,Ur as executeJob,Tt as expectJobToFail,Ie as fake,Qn as getActionRunner,Ri as getActiveJobCount,Lr as getAllJobs,Ne as getBatchCallbacks,ci as getFailedJobNotifier,jt as getFakeQueue,be as getGlobalMetrics,Hr as getJob,Fr as getJobProgress,me as getQueueEvents,ea as getRedisQueue,ei as getRegisteredJobs,pt as getScheduledJobs,Xr as getSchedulerStatus,E as getWorkerTracker,zn as hasDispatchedKey,Ce as hashPayload,we as isBatchCancelled,st as isCircuitOpen,Pt as isFaked,Qr as isJobCancelled,Ve as isQuarantined,oi as isQueueHealthy,Zr as isSchedulerRunning,Ci as isWorkerRunning,Rr as job,Cr as jobBatch,Y as jobRegistry,Nr as listCircuitState,Ln as listDeadLetterJobs,Xn as listQuarantined,pe as moveToDeadLetter,ui as notifyJobFailed,tr as onQueueEvent,Je as parseEnvelope,Dr as pauseQueue,Wn as purgeDeadLetterJobs,Gn as quarantineJob,wn as queuedJobState,ie as recordBatchJobCompletion,oe as recordBatchJobFailure,ut as recordCircuitFailure,ct as recordCircuitSuccess,Vn as recordDispatchedKey,ze as recordFailureForPoison,Ue as releaseDispatchKey,Qe as restore,$r as resumeQueue,Un as retryDeadLetterJob,Pn as retryFailedJob,K as runJob,Et as runTestJob,I as serializeEnvelope,Bn as setActionRunner,Or as setJobProgress,yi as startProcessor,Yr as startScheduler,Ei as stopProcessor,mt as stopScheduler,Wr as toJobOptions,ti as triggerJob,Yn as unquarantineJob,nr as withEvents};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/queue",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.74.
|
|
5
|
+
"version": "0.74.33",
|
|
6
6
|
"description": "The Stacks Queue system powered by bun-queue.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -63,22 +63,22 @@
|
|
|
63
63
|
"prepublishOnly": "bun run build"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@stacksjs/action-runner": "0.74.
|
|
66
|
+
"@stacksjs/action-runner": "0.74.33",
|
|
67
67
|
"@stacksjs/bun-queue": "^0.1.8",
|
|
68
|
-
"@stacksjs/cache": "0.74.
|
|
69
|
-
"@stacksjs/config": "0.74.
|
|
70
|
-
"@stacksjs/database": "0.74.
|
|
71
|
-
"@stacksjs/env": "0.74.
|
|
72
|
-
"@stacksjs/error-handling": "0.74.
|
|
73
|
-
"@stacksjs/logging": "0.74.
|
|
74
|
-
"@stacksjs/path": "0.74.
|
|
68
|
+
"@stacksjs/cache": "0.74.33",
|
|
69
|
+
"@stacksjs/config": "0.74.33",
|
|
70
|
+
"@stacksjs/database": "0.74.33",
|
|
71
|
+
"@stacksjs/env": "0.74.33",
|
|
72
|
+
"@stacksjs/error-handling": "0.74.33",
|
|
73
|
+
"@stacksjs/logging": "0.74.33",
|
|
74
|
+
"@stacksjs/path": "0.74.33"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"better-dx": "^0.2.24"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@stacksjs/email": "0.74.
|
|
81
|
-
"@stacksjs/router": "0.74.
|
|
80
|
+
"@stacksjs/email": "0.74.33",
|
|
81
|
+
"@stacksjs/router": "0.74.33"
|
|
82
82
|
},
|
|
83
83
|
"peerDependenciesMeta": {
|
|
84
84
|
"@stacksjs/email": {
|