@stacksjs/queue 0.70.87 → 0.70.88

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/job.d.ts DELETED
@@ -1,150 +0,0 @@
1
- /**
2
- * Create a job builder for dispatching file-based jobs
3
- *
4
- * @example
5
- * ```typescript
6
- * // Basic dispatch
7
- * await job('SendWelcomeEmail', { userId: 1 }).dispatch()
8
- *
9
- * // With options
10
- * await job('ProcessOrder', { orderId: 123 })
11
- * .onQueue('orders')
12
- * .tries(3)
13
- * .backoff([10, 30, 60])
14
- * .dispatch()
15
- *
16
- * // Immediate execution
17
- * await job('SendNotification', { message: 'Hello' }).dispatchNow()
18
- * ```
19
- */
20
- export declare function job(name: string, payload?: any): JobBuilder;
21
- /**
22
- * Create a job batch for dispatching multiple jobs together
23
- *
24
- * @example
25
- * ```typescript
26
- * import SendWelcomeEmail from '~/app/Jobs/SendWelcomeEmail'
27
- * import ProcessOrder from '~/app/Jobs/ProcessOrder'
28
- *
29
- * const batch = await jobBatch([
30
- * { job: SendWelcomeEmail, payload: { email: 'user@example.com' } },
31
- * { job: ProcessOrder, payload: { orderId: 123 } },
32
- * ])
33
- * .name('Onboard User')
34
- * .allowFailures()
35
- * .then(async (_b) => console.log('All done!'))
36
- * .dispatch()
37
- * ```
38
- */
39
- export declare function jobBatch(jobs: Array<import('./action').Job | import('./batch').BatchableJob>): import('./batch').PendingBatch;
40
- /**
41
- * Run a job immediately by name
42
- *
43
- * This loads the job from app/Jobs/{name}.ts and executes it
44
- *
45
- * Wraps execution in `withTraceId(...)` so log lines, db queries, and
46
- * downstream HTTP calls emitted from inside the job carry the same
47
- * trace id as the request (or a fresh one for cron-triggered runs).
48
- * `options.traceId` lets dispatchers attach a parent id explicitly;
49
- * absent that, we mint one of the form `job:<name>:<random>` so
50
- * background work is at least correlatable to itself.
51
- */
52
- export declare function runJob(name: string, options?: { payload?: any; context?: any; traceId?: string }): Promise<void>;
53
- /*.ts`; this `Jobs` facade is the *call-site* form for
54
- * dispatching by name from anywhere in the app. It lets one-liner
55
- * dispatches skip the builder while still allowing the chainable form
56
- * via `Jobs.make(...)` for option-heavy cases.
57
- *
58
- * @example
59
- * ```typescript
60
- * // One-shot dispatch (no options)
61
- * await Jobs.dispatch('SendWelcomeEmail', { userId: 1 })
62
- *
63
- * // Conditional dispatch
64
- * await Jobs.dispatchIf(user.isNew, 'SendWelcomeEmail', { userId: user.id })
65
- *
66
- * // With options — fall back to the builder
67
- * await Jobs.make('ProcessOrder', { orderId: 123 })
68
- * .onQueue('orders')
69
- * .tries(3)
70
- * .dispatch()
71
- *
72
- * // Immediate (synchronous) execution, ignoring the queue
73
- * await Jobs.dispatchNow('SendNotification', { message: 'Hello' })
74
- * ```
75
- * @defaultValue
76
- * ```ts
77
- * {
78
- * dispatch: () => unknown,
79
- * dispatchIf: () => unknown,
80
- * dispatchUnless: () => unknown,
81
- * dispatchNow: () => unknown,
82
- * dispatchOnce: () => unknown
83
- * }
84
- * ```
85
- */
86
- export declare const Jobs: {
87
- /** Construct a builder without dispatching — for chained option calls. */
88
- make: (name: string, payload?: any) => JobBuilder;
89
- /** Dispatch a job in one call. Equivalent to `job(name, payload).dispatch()`. */
90
- dispatch: (name: string, payload?: any) => Promise<void>;
91
- /** Dispatch only if `condition` is truthy. */
92
- dispatchIf: (condition: boolean, name: string, payload?: any) => Promise<void>;
93
- /** Dispatch unless `condition` is truthy. */
94
- dispatchUnless: (condition: boolean, name: string, payload?: any) => Promise<void>;
95
- /** Run the job synchronously, bypassing the queue. */
96
- dispatchNow: (name: string, payload?: any) => Promise<void>;
97
- /** Schedule the job to run after `seconds` of delay. */
98
- dispatchAfter: (seconds: number, name: string, payload?: any) => JobBuilder;
99
- /**
100
- * Dispatch with an idempotency key in one call
101
- * (stacksjs/stacks#1872 Q-8). Equivalent to
102
- * `Jobs.make(name, payload).withIdempotencyKey(key).dispatch()`.
103
- */
104
- dispatchOnce: (key: string, name: string, payload?: any) => Promise<void>
105
- };
106
- /**
107
- * Options for job dispatch
108
- */
109
- export declare interface JobDispatchOptions {
110
- queue?: string
111
- delay?: number
112
- tries?: number
113
- timeout?: number
114
- backoff?: number[]
115
- context?: any
116
- idempotencyKey?: string
117
- }
118
- /**
119
- * Per-builder override of the transaction-context default.
120
- * `'auto'` (the default) respects the current transaction scope:
121
- * buffers when inside `db.transaction(...)`, dispatches immediately
122
- * otherwise. `'after'` forces buffering even outside a transaction
123
- * (warn-and-dispatch since there's nothing to buffer against).
124
- * `'immediate'` forces immediate dispatch even inside a transaction
125
- * — used for fire-and-forget side-effects that legitimately want
126
- * to run before the surrounding transaction commits (analytics,
127
- * lock probes, etc.).
128
- *
129
- * See stacksjs/stacks#1882.
130
- */
131
- declare type TransactionMode = 'auto' | 'after' | 'immediate';
132
- /**
133
- * Fluent job builder for file-based jobs
134
- */
135
- declare class JobBuilder {
136
- constructor(name: string, payload?: any);
137
- onQueue(queue: string): this;
138
- delay(seconds: number): this;
139
- tries(count: number): this;
140
- timeout(seconds: number): this;
141
- backoff(delays: number[]): this;
142
- withContext(context: any): this;
143
- withIdempotencyKey(key: string): this;
144
- afterCommit(): this;
145
- withoutCommit(): this;
146
- dispatch(): Promise<void>;
147
- dispatchIf(condition: boolean): Promise<void>;
148
- dispatchUnless(condition: boolean): Promise<void>;
149
- dispatchNow(): Promise<void>;
150
- }
@@ -1,70 +0,0 @@
1
- import { log } from '@stacksjs/logging';
2
- /**
3
- * Configure failed job notifications
4
- */
5
- export declare function configureFailedJobNotifications(config: FailedJobNotificationConfig): FailedJobNotifier;
6
- /**
7
- * Get the global notifier
8
- */
9
- export declare function getFailedJobNotifier(): FailedJobNotifier | null;
10
- /**
11
- * Notify about a failed job using the global notifier
12
- */
13
- export declare function notifyJobFailed(job: FailedJobInfo): Promise<void>;
14
- /**
15
- * Failed job info
16
- */
17
- export declare interface FailedJobInfo {
18
- id: string | number
19
- name: string
20
- queue: string
21
- payload: any
22
- exception: string
23
- failedAt: Date
24
- attempts: number
25
- maxAttempts: number
26
- }
27
- /**
28
- * Notification configuration
29
- */
30
- export declare interface FailedJobNotificationConfig {
31
- channels: NotificationChannel[]
32
- email?: {
33
- to: string | string[]
34
- from?: string
35
- subject?: string
36
- }
37
- slack?: {
38
- webhookUrl: string
39
- channel?: string
40
- username?: string
41
- iconEmoji?: string
42
- }
43
- discord?: {
44
- webhookUrl: string
45
- username?: string
46
- avatarUrl?: string
47
- }
48
- webhook?: {
49
- url: string
50
- headers?: Record<string, string>
51
- secret?: string
52
- }
53
- rateLimit?: number
54
- batch?: boolean
55
- batchInterval?: number
56
- filter?: (job: FailedJobInfo) => boolean
57
- }
58
- /**
59
- * Notification channel types
60
- */
61
- export type NotificationChannel = 'email' | 'slack' | 'discord' | 'webhook' | 'log';
62
- /**
63
- * Failed job notification manager
64
- */
65
- export declare class FailedJobNotifier {
66
- constructor(config: FailedJobNotificationConfig);
67
- notify(job: FailedJobInfo): Promise<void>;
68
- shutdown(): Promise<void>;
69
- cleanup(): Promise<void>;
70
- }
package/dist/poison.d.ts DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * Compute the dedup key for a job+payload combination. Stable
3
- * sha256 truncated to 32 hex chars — short enough to fit in
4
- * indexed columns, wide enough to avoid collisions in practice.
5
- */
6
- export declare function hashPayload(payload: unknown): string;
7
- /**
8
- * Record a failure against `(jobName, payload-hash)`. If this push
9
- * makes the count exceed the threshold within the window, the row
10
- * is marked `quarantined_at` and the function returns `true`.
11
- *
12
- * Called from the worker's failure path AFTER the job lands in
13
- * `failed_jobs`. Failures outside the window reset the count.
14
- */
15
- export declare function recordFailureForPoison(jobName: string, payload: unknown, config?: PoisonConfig): Promise<boolean>;
16
- /**
17
- * Is `(jobName, payload)` currently quarantined? Called from the
18
- * dispatch path — true means "skip the queue, route directly to
19
- * the DLQ".
20
- */
21
- export declare function isQuarantined(jobName: string, payload: unknown): Promise<boolean>;
22
- /**
23
- * Manually quarantine a job class. Useful when an operator knows
24
- * a particular flow is broken (e.g. third-party API outage) and
25
- * wants to stop the queue from churning on it before the failure
26
- * threshold trips.
27
- *
28
- * If `payload` is omitted, every payload-hash for the job name is
29
- * matched (uses wildcard `payload_hash = '*'`). For class-wide
30
- * quarantine without per-payload granularity.
31
- */
32
- export declare function quarantineJob(jobName: string, payload?: unknown): Promise<void>;
33
- /**
34
- * Lift the quarantine for a job class. Deletes every
35
- * `job_quarantine` row matching `jobName` (across all payload
36
- * hashes) so future dispatches go to the queue normally.
37
- */
38
- export declare function unquarantineJob(jobName: string): Promise<void>;
39
- /**
40
- * Read the set of currently-quarantined job names + payload hashes
41
- * for the `queue:quarantine` CLI list view.
42
- */
43
- export declare function listQuarantined(): Promise<Array<{
44
- id: number
45
- job_name: string
46
- payload_hash: string
47
- failure_count: number
48
- quarantined_at: string | null
49
- }>>;
50
- export declare interface PoisonConfig {
51
- maxFailures?: number
52
- windowMinutes?: number
53
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Load a job's persisted `lastRun`, or null when there's none / persistence is
3
- * unavailable. Used at startup so a restart doesn't re-fire a job that already
4
- * ran this minute.
5
- */
6
- export declare function loadPersistedLastRun(jobName: string): Promise<Date | null>;
7
- /**
8
- * Persist a job's `lastRun` (bounded to one row per job). Delete-then-insert is
9
- * a portable upsert that avoids the per-dialect UPDATE-affected-rows ambiguity
10
- * (MySQL reports 0 rows on an unchanged value); a single scheduler process owns
11
- * this table, so there is no concurrent-writer race. Best-effort.
12
- */
13
- export declare function persistLastRun(jobName: string, when: Date): Promise<void>;
14
- /** Test hook: reset the one-time table-ensured flag. */
15
- export declare function __resetSchedulerPersistenceForTests(): void;
@@ -1,69 +0,0 @@
1
- import { type DiscoveredJob } from './discovery';
2
- /**
3
- * Extract the wall-clock fields a cron expression matches against, in the
4
- * configured timezone (stacksjs/stacks#1984). `SchedulerConfig.timezone` was
5
- * documented ("Timezone for cron expressions") but never applied — cron ran in
6
- * the server's local time regardless, so `0 9 * * *` fired at 9am server-local
7
- * rather than 9am in the configured zone. With no timezone set we keep
8
- * local-time semantics; an invalid zone warns once and falls back to local.
9
- */
10
- export declare function getCronParts(date: Date, timeZone?: string): CronParts;
11
- /**
12
- * Calculate next run time for a cron expression
13
- */
14
- export declare function calculateNextRun(cronExpression: string, timeZone?: string): Date | null;
15
- /**
16
- * Start the scheduler
17
- */
18
- export declare function startScheduler(config?: Partial<SchedulerConfig>): Promise<void>;
19
- /**
20
- * Stop the scheduler
21
- */
22
- export declare function stopScheduler(): Promise<void>;
23
- /**
24
- * Get scheduler status
25
- */
26
- export declare function getSchedulerStatus(): {
27
- isRunning: boolean
28
- jobCount: number
29
- jobs: Array<{
30
- name: string
31
- schedule: string | undefined
32
- lastRun: Date | null
33
- nextRun: Date | null
34
- isRunning: boolean
35
- }>
36
- };
37
- /**
38
- * Check if scheduler is running
39
- */
40
- export declare function isSchedulerRunning(): boolean;
41
- /**
42
- * Get registered scheduled jobs
43
- */
44
- export declare function getRegisteredJobs(): Map<string, ScheduledJobState>;
45
- /**
46
- * Manually trigger a scheduled job
47
- */
48
- export declare function triggerJob(name: string): Promise<void>;
49
- /**
50
- * Scheduler configuration
51
- */
52
- declare interface SchedulerConfig {
53
- checkInterval: number
54
- timezone?: string
55
- preventOverlapping: boolean
56
- }
57
- /**
58
- * Scheduled job state
59
- */
60
- declare interface ScheduledJobState {
61
- job: DiscoveredJob
62
- lastRun: Date | null
63
- nextRun: Date | null
64
- isRunning: boolean
65
- }
66
- /**
67
- * Parse a cron expression and check if it should run now
68
- */
69
- declare interface CronParts { minute: number, hour: number, day: number, month: number, dayOfWeek: number }
package/dist/testing.d.ts DELETED
@@ -1,74 +0,0 @@
1
- import type { JobOptions } from '@stacksjs/types';
2
- /**
3
- * Enable fake queue for testing
4
- */
5
- export declare function fake(): FakeQueue;
6
- /**
7
- * Get the fake queue instance
8
- */
9
- export declare function getFakeQueue(): FakeQueue | null;
10
- /**
11
- * Check if queue is faked
12
- */
13
- export declare function isFaked(): boolean;
14
- /**
15
- * Restore real queue behavior
16
- */
17
- export declare function restore(): void;
18
- /**
19
- * Create a queue tester instance
20
- */
21
- export declare function createQueueTester(): QueueTester;
22
- /**
23
- * Test helper to run a job synchronously
24
- */
25
- export declare function runJob<T, R>(jobModule: { handle: (data: T) => Promise<R> | R }, data: T): Promise<R>;
26
- /**
27
- * Test helper to expect a job to fail
28
- */
29
- export declare function expectJobToFail<T>(jobModule: { handle: (data: T) => Promise<any> }, data: T, expectedError?: string | RegExp): Promise<Error>;
30
- /**
31
- * Dispatched job record for assertions
32
- */
33
- export declare interface DispatchedJob<T = any> {
34
- name: string
35
- data: T
36
- options: Partial<JobOptions>
37
- dispatchedAt: Date
38
- queue: string
39
- }
40
- /**
41
- * Fake queue for testing
42
- */
43
- declare class FakeQueue {
44
- dispatch<T>(name: string, data: T, options?: Partial<JobOptions>): void;
45
- push<T>(name: string, data: T, options?: Partial<JobOptions>): void;
46
- dispatched(name?: string): DispatchedJob[];
47
- pushed(name?: string): DispatchedJob[];
48
- assertDispatched(name: string, callback?: (job: DispatchedJob) => boolean): void;
49
- assertNotDispatched(name: string): void;
50
- assertDispatchedTimes(name: string, times: number): void;
51
- assertNothingDispatched(): void;
52
- assertPushed(name: string, callback?: (job: DispatchedJob) => boolean): void;
53
- assertPushedWithDelay(name: string, delay: number): void;
54
- assertPushedOn(queue: string, name: string): void;
55
- processJob<T>(name: string, handler: (data: T) => Promise<any>): Promise<void>;
56
- processed(name?: string): DispatchedJob[];
57
- failed(name?: string): Array<{ job: DispatchedJob, error: Error }>;
58
- reset(): void;
59
- }
60
- /**
61
- * Queue testing helper class
62
- */
63
- export declare class QueueTester {
64
- constructor();
65
- dispatch<T>(name: string, data: T, options?: Partial<JobOptions>): this;
66
- push<T>(name: string, data: T, options?: Partial<JobOptions>): this;
67
- assertDispatched(name: string, callback?: (job: DispatchedJob) => boolean): this;
68
- assertNotDispatched(name: string): this;
69
- assertDispatchedTimes(name: string, times: number): this;
70
- assertNothingDispatched(): this;
71
- dispatched(name?: string): DispatchedJob[];
72
- reset(): this;
73
- cleanup(): void;
74
- }
package/dist/utils.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import type { QueueOption } from '@stacksjs/types';
2
- /**
3
- * Affected-row count from an UPDATE result, across every shape the query
4
- * builders in the wild return: Kysely's `numUpdatedRows` bigint,
5
- * bun-query-builder's `{ changes, lastInsertRowid }` object, or a plain
6
- * number. `Number({...})` is NaN, and `NaN > 0` is false — which made the
7
- * queue worker's CAS claim treat every SUCCESSFUL reservation as lost:
8
- * jobs were reserved then discarded, one per poll, forever, with nothing
9
- * processed and nothing logged (found on stacksjs/status production,
10
- * 2026-07-04, 1,600+ jobs stuck reserved).
11
- */
12
- export declare function updatedRowCount(result: unknown): number;
13
- export declare function storeJob(name: string, options: QueueOption): Promise<void>;
package/dist/worker.d.ts DELETED
@@ -1,40 +0,0 @@
1
- import type { Result } from '@stacksjs/error-handling';
2
- /**
3
- * Start the queue processor (worker)
4
- * This is the main function called by `buddy queue:work`
5
- */
6
- export declare function startProcessor(queueName?: string, options?: { concurrency?: number }): Promise<Result<undefined, Error>>;
7
- /**
8
- * Stop the queue processor, draining in-flight jobs within a grace
9
- * window before returning. The `signalled to stop` flag stops the
10
- * polling loop from claiming new jobs; any handler that's currently
11
- * mid-execution gets up to `graceMs` to finish.
12
- *
13
- * `graceMs` defaults to 10s (matches the buddy CLI's existing
14
- * SIGTERM → SIGKILL window). Pass 0 to skip the wait entirely
15
- * (test runs, CI).
16
- *
17
- * When the grace window expires with jobs still active, the function
18
- * resolves anyway — the reservation-sweep (Q-2) will requeue them on
19
- * the next worker startup. The alternative (hanging the process
20
- * forever) is worse than the requeue cost.
21
- *
22
- * See stacksjs/stacks#1872 Q-10.
23
- */
24
- export declare function stopProcessor(options?: { graceMs?: number }): Promise<void>;
25
- /**
26
- * Retry all failed jobs
27
- */
28
- export declare function executeFailedJobs(): Promise<void>;
29
- /**
30
- * Retry a specific failed job
31
- */
32
- export declare function retryFailedJob(id: number): Promise<void>;
33
- /**
34
- * Get the count of currently active (processing) jobs
35
- */
36
- export declare function getActiveJobCount(): number;
37
- /**
38
- * Check if the worker is currently running
39
- */
40
- export declare function isWorkerRunning(): boolean;