@stacksjs/queue 0.75.262 → 0.75.263
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/notifications.js +1 -1
- package/package.json +12 -12
package/dist/notifications.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{log}from"@stacksjs/logging";const HTML_ESCAPE_MAP={"&":"&","<":"<",">":">",'"':""","'":"'"};function escapeHtml(value){return String(value).replace(/[&<>"']/g,(c)=>HTML_ESCAPE_MAP[c]??c)}export class FailedJobNotifier{config;notificationCount=0;lastResetTime=Date.now();pendingBatch=[];batchTimeout=null;activeFlush=null;constructor(config){this.config=config}async notify(job){if(this.config.filter&&!this.config.filter(job))return;if(this.config.rateLimit){const now=Date.now();if(now-this.lastResetTime>3600000){this.notificationCount=0;this.lastResetTime=now}if(this.notificationCount>=this.config.rateLimit){log.debug("Rate limit reached for failed job notifications");return}}if(this.config.batch){this.pendingBatch.push(job);if(!this.batchTimeout){this.batchTimeout=setTimeout(()=>{this.batchTimeout=null;this.activeFlush=this.flushBatch().catch((error)=>log.error("Failed to flush notification batch:",error)).finally(()=>{this.activeFlush=null})},this.config.batchInterval||60000);this.batchTimeout.unref?.()}return}await this.sendNotifications([job])}async flushBatch(){if(this.pendingBatch.length===0)return;const batch=[...this.pendingBatch];this.pendingBatch=[];if(this.batchTimeout){clearTimeout(this.batchTimeout);this.batchTimeout=null}await this.sendNotifications(batch)}async shutdown(){if(this.batchTimeout){clearTimeout(this.batchTimeout);this.batchTimeout=null}if(this.activeFlush)await this.activeFlush.catch(()=>{});await this.flushBatch().catch((error)=>log.error("Failed to flush notification batch on shutdown:",error))}async sendNotifications(jobs){const promises=[];for(const channel of this.config.channels)switch(channel){case"email":if(this.config.email)promises.push(this.sendEmail(jobs));break;case"slack":if(this.config.slack)promises.push(this.sendSlack(jobs));break;case"discord":if(this.config.discord)promises.push(this.sendDiscord(jobs));break;case"webhook":if(this.config.webhook)promises.push(this.sendWebhook(jobs));break;case"log":this.logFailures(jobs);break}try{await Promise.all(promises)}catch(error){log.error("Failed to send job failure notifications:",error)}finally{this.notificationCount+=jobs.length}}async sendEmail(jobs){const config=this.config.email,subject=config.subject||`[Queue] ${jobs.length} Job(s) Failed`,body=this.formatEmailBody(jobs);try{const{mail}=await import("@stacksjs/email");await mail.
|
|
1
|
+
import{log}from"@stacksjs/logging";const HTML_ESCAPE_MAP={"&":"&","<":"<",">":">",'"':""","'":"'"};function escapeHtml(value){return String(value).replace(/[&<>"']/g,(c)=>HTML_ESCAPE_MAP[c]??c)}export class FailedJobNotifier{config;notificationCount=0;lastResetTime=Date.now();pendingBatch=[];batchTimeout=null;activeFlush=null;constructor(config){this.config=config}async notify(job){if(this.config.filter&&!this.config.filter(job))return;if(this.config.rateLimit){const now=Date.now();if(now-this.lastResetTime>3600000){this.notificationCount=0;this.lastResetTime=now}if(this.notificationCount>=this.config.rateLimit){log.debug("Rate limit reached for failed job notifications");return}}if(this.config.batch){this.pendingBatch.push(job);if(!this.batchTimeout){this.batchTimeout=setTimeout(()=>{this.batchTimeout=null;this.activeFlush=this.flushBatch().catch((error)=>log.error("Failed to flush notification batch:",error)).finally(()=>{this.activeFlush=null})},this.config.batchInterval||60000);this.batchTimeout.unref?.()}return}await this.sendNotifications([job])}async flushBatch(){if(this.pendingBatch.length===0)return;const batch=[...this.pendingBatch];this.pendingBatch=[];if(this.batchTimeout){clearTimeout(this.batchTimeout);this.batchTimeout=null}await this.sendNotifications(batch)}async shutdown(){if(this.batchTimeout){clearTimeout(this.batchTimeout);this.batchTimeout=null}if(this.activeFlush)await this.activeFlush.catch(()=>{});await this.flushBatch().catch((error)=>log.error("Failed to flush notification batch on shutdown:",error))}async sendNotifications(jobs){const promises=[];for(const channel of this.config.channels)switch(channel){case"email":if(this.config.email)promises.push(this.sendEmail(jobs));break;case"slack":if(this.config.slack)promises.push(this.sendSlack(jobs));break;case"discord":if(this.config.discord)promises.push(this.sendDiscord(jobs));break;case"webhook":if(this.config.webhook)promises.push(this.sendWebhook(jobs));break;case"log":this.logFailures(jobs);break}try{await Promise.all(promises)}catch(error){log.error("Failed to send job failure notifications:",error)}finally{this.notificationCount+=jobs.length}}async sendEmail(jobs){const config=this.config.email,subject=config.subject||`[Queue] ${jobs.length} Job(s) Failed`,body=this.formatEmailBody(jobs);try{const{mail}=await import("@stacksjs/email");await mail.sendOrFail({to:Array.isArray(config.to)?config.to:[config.to],from:config.from?{address:config.from}:void 0,subject,html:body})}catch(error){log.error("Failed to send email notification:",error)}}formatEmailBody(jobs){const rows=jobs.map((job)=>`
|
|
2
2
|
<tr>
|
|
3
3
|
<td style="padding: 8px; border: 1px solid #ddd;">${escapeHtml(String(job.id))}</td>
|
|
4
4
|
<td style="padding: 8px; border: 1px solid #ddd;">${escapeHtml(job.name)}</td>
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/queue",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.263",
|
|
6
6
|
"description": "The Stacks Queue system powered by bun-queue.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -63,23 +63,23 @@
|
|
|
63
63
|
"prepublishOnly": "bun run build"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@stacksjs/action-runner": "0.75.
|
|
66
|
+
"@stacksjs/action-runner": "0.75.263",
|
|
67
67
|
"@stacksjs/bun-queue": "^0.1.8",
|
|
68
|
-
"@stacksjs/cache": "0.75.
|
|
69
|
-
"@stacksjs/config": "0.75.
|
|
70
|
-
"@stacksjs/cron": "0.75.
|
|
71
|
-
"@stacksjs/database": "0.75.
|
|
72
|
-
"@stacksjs/env": "0.75.
|
|
73
|
-
"@stacksjs/error-handling": "0.75.
|
|
74
|
-
"@stacksjs/logging": "0.75.
|
|
75
|
-
"@stacksjs/path": "0.75.
|
|
68
|
+
"@stacksjs/cache": "0.75.263",
|
|
69
|
+
"@stacksjs/config": "0.75.263",
|
|
70
|
+
"@stacksjs/cron": "0.75.263",
|
|
71
|
+
"@stacksjs/database": "0.75.263",
|
|
72
|
+
"@stacksjs/env": "0.75.263",
|
|
73
|
+
"@stacksjs/error-handling": "0.75.263",
|
|
74
|
+
"@stacksjs/logging": "0.75.263",
|
|
75
|
+
"@stacksjs/path": "0.75.263"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"better-dx": "^0.2.24"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@stacksjs/email": "0.75.
|
|
82
|
-
"@stacksjs/router": "0.75.
|
|
81
|
+
"@stacksjs/email": "0.75.263",
|
|
82
|
+
"@stacksjs/router": "0.75.263"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|
|
85
85
|
"@stacksjs/email": {
|