@stacksjs/queue 0.70.88 → 0.70.90
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/action.d.ts +46 -0
- package/dist/batch.d.ts +150 -0
- package/dist/circuit-breaker.d.ts +52 -0
- package/dist/dead-letter.d.ts +61 -0
- package/dist/discovery.d.ts +79 -0
- package/dist/envelope.d.ts +89 -0
- package/dist/events.d.ts +138 -0
- package/dist/health.d.ts +85 -0
- package/dist/idempotency.d.ts +39 -0
- package/dist/index.d.ts +184 -0
- package/dist/index.js +31 -0
- package/dist/job-progress.d.ts +53 -0
- package/dist/job.d.ts +150 -0
- package/dist/notifications.d.ts +70 -0
- package/dist/poison.d.ts +53 -0
- package/dist/scheduler-persistence.d.ts +15 -0
- package/dist/scheduler.d.ts +69 -0
- package/dist/testing.d.ts +74 -0
- package/dist/utils.d.ts +13 -0
- package/dist/worker.d.ts +40 -0
- package/package.json +1 -1
package/dist/job.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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;
|
|
@@ -0,0 +1,69 @@
|
|
|
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 }
|
|
@@ -0,0 +1,74 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
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;
|