@odunlamizo/node-river 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Odunlami Zacchaeus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # node-river
2
+
3
+ A Node.js job queue and workflow engine with PostgreSQL support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install node-river
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### 1. Setup
14
+
15
+ ```ts
16
+ import { RiverClient } from 'node-river';
17
+ import { PgDriver } from 'node-river/drivers/pg';
18
+
19
+ const driver = new PgDriver({ connectionString: process.env.DATABASE_URL! });
20
+ const client = new RiverClient(driver, {
21
+ defaultQueue: 'default',
22
+ maxAttempts: 1,
23
+ });
24
+ ```
25
+
26
+ ### 2. Verify Connection
27
+
28
+ ```ts
29
+ await client.verifyConnection();
30
+ ```
31
+
32
+ ### 3. Insert a Job
33
+
34
+ ```ts
35
+ const result = await client.insert({ kind: 'sort_args', strings: ['banana', 'apple', 'cherry'] });
36
+ console.log(result.job); // Job details
37
+ ```
38
+
39
+ ### 4. Insert Unique Job
40
+
41
+ ```ts
42
+ const result = await client.insert(
43
+ { kind: 'sort_args', strings: ['banana', 'apple', 'cherry'] },
44
+ { uniqueOpts: { byArgs: ['strings'] } }
45
+ );
46
+ console.log(result.skipped); // true if duplicate
47
+ ```
48
+
49
+ ### 5. Insert Many Jobs Transactionally
50
+
51
+ ```ts
52
+ const jobs = [
53
+ { args: { kind: 'sort_args', strings: ['a', 'b'] }, opts: {} },
54
+ { args: { kind: 'sort_args', strings: ['c', 'd'] }, opts: {} },
55
+ ];
56
+ const results = await client.insertMany(jobs);
57
+ console.log(results.length); // 2
58
+ ```
59
+
60
+ ### 6. Use Transactions
61
+
62
+ ```ts
63
+ const tx = await driver.getTx();
64
+ try {
65
+ await tx.query('BEGIN');
66
+ const result = await client.insertTx(tx, { kind: 'sort_args', strings: ['x', 'y', 'z'] });
67
+ await tx.query('COMMIT');
68
+ } catch (e) {
69
+ await tx.query('ROLLBACK');
70
+ throw e;
71
+ } finally {
72
+ tx.release();
73
+ }
74
+ ```
75
+
76
+ ## License
77
+
78
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ RiverClient: () => RiverClient
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/client.ts
28
+ var RiverClient = class {
29
+ /**
30
+ * Creates a new RiverClient instance.
31
+ * @param driver - The queue driver implementation.
32
+ * @param configuration - Client configuration options.
33
+ */
34
+ constructor(driver, configuration) {
35
+ this.driver = driver;
36
+ this.configuration = configuration;
37
+ }
38
+ /**
39
+ * Checks if the driver can connect to the database. Throws on failure.
40
+ */
41
+ verifyConnection() {
42
+ return this.driver.verifyConnection();
43
+ }
44
+ /**
45
+ * Closes all database connections and cleans up resources.
46
+ */
47
+ close() {
48
+ return this.driver.close();
49
+ }
50
+ /**
51
+ * Inserts a job into the queue with the specified arguments and options.
52
+ * @param args - The job arguments to insert.
53
+ * @param opts - Optional insertion options.
54
+ * @returns A promise that resolves to the result of the insertion operation,
55
+ * including the job and whether the insert was skipped due to uniqueness.
56
+ */
57
+ insert(args, opts = {}) {
58
+ const defaultOpts = {
59
+ queue: this.configuration.defaultQueue,
60
+ maxAttempts: this.configuration.maxAttempts,
61
+ ...opts
62
+ };
63
+ return this.driver.insert(args, defaultOpts);
64
+ }
65
+ /**
66
+ * Inserts a job into the queue within an existing transaction or session.
67
+ * The transaction type (Tx) is determined by the driver implementation.
68
+ *
69
+ * @param tx - The transaction or session object to use for the insert.
70
+ * @param args - The job arguments to insert.
71
+ * @param opts - Optional insertion options.
72
+ * @returns A promise that resolves to the result of the insertion operation,
73
+ * including the job and whether the insert was skipped due to uniqueness.
74
+ */
75
+ insertTx(tx, args, opts = {}) {
76
+ const defaultOpts = {
77
+ queue: this.configuration.defaultQueue,
78
+ maxAttempts: this.configuration.maxAttempts,
79
+ ...opts
80
+ };
81
+ return this.driver.insertTx(tx, args, defaultOpts);
82
+ }
83
+ /**
84
+ * Inserts multiple jobs in sequence within a single transaction.
85
+ * If any insert fails, all previous inserts in the batch are rolled back.
86
+ *
87
+ * @param jobs - Array of job argument and option pairs to insert.
88
+ * @returns A promise that resolves to an array of InsertResult objects for each job.
89
+ */
90
+ async insertMany(jobs) {
91
+ const jobsWithDefaults = jobs.map((job) => ({
92
+ args: job.args,
93
+ opts: {
94
+ queue: this.configuration.defaultQueue,
95
+ maxAttempts: this.configuration.maxAttempts,
96
+ ...job.opts
97
+ }
98
+ }));
99
+ return this.driver.insertMany(jobsWithDefaults);
100
+ }
101
+ };
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ RiverClient
105
+ });
@@ -0,0 +1,355 @@
1
+ import { Buffer } from 'buffer';
2
+
3
+ /**
4
+ * Configuration options for the RiverQueue client.
5
+ */
6
+ interface ClientConfiguration {
7
+ /**
8
+ * Default queue name for job insertion
9
+ */
10
+ defaultQueue?: string;
11
+ /**
12
+ * Maximum number of attempts for jobs
13
+ */
14
+ maxAttempts?: number;
15
+ }
16
+
17
+ /**
18
+ * Represents the lifecycle state of a job, such as available, running, completed, or scheduled.
19
+ */
20
+ declare enum JobState {
21
+ /**
22
+ * Immediately eligible to be worked
23
+ */
24
+ Available = "available",
25
+ /**
26
+ * Manually cancelled by user; cleaned after a period
27
+ */
28
+ Cancelled = "cancelled",
29
+ /**
30
+ * Successfully run to completion; cleaned after a period
31
+ */
32
+ Completed = "completed",
33
+ /**
34
+ * Errored too many times; needs manual intervention
35
+ */
36
+ Discarded = "discarded",
37
+ /**
38
+ * Waiting for external action; not worked or deleted until moved
39
+ */
40
+ Pending = "pending",
41
+ /**
42
+ * Errored but will be retried; becomes Available when ready
43
+ */
44
+ Retryable = "retryable",
45
+ /**
46
+ * Actively running; may need rescue if stuck
47
+ */
48
+ Running = "running",
49
+ /**
50
+ * Scheduled for future execution; becomes Available when due
51
+ */
52
+ Scheduled = "scheduled"
53
+ }
54
+
55
+ /**
56
+ * Represents a failed job attempt, including error details and stack trace.
57
+ */
58
+ interface AttemptError {
59
+ /**
60
+ * Time the error occurred
61
+ */
62
+ at: string;
63
+ /**
64
+ * Attempt number when the error occurred
65
+ */
66
+ attempt: number;
67
+ /**
68
+ * Stringified error or panic value
69
+ */
70
+ error: string;
71
+ /**
72
+ * Stack trace from a job that panicked
73
+ */
74
+ trace: string;
75
+ }
76
+
77
+ /**
78
+ * Options for enforcing uniqueness constraints on jobs.
79
+ */
80
+ interface UniqueOpts {
81
+ /**
82
+ * Enforce uniqueness based on job arguments.
83
+ * True for all args, or specify keys to include.
84
+ */
85
+ byArgs?: true | string[];
86
+ /**
87
+ * Enforce uniqueness within a time period (seconds).
88
+ */
89
+ byPeriod?: number;
90
+ /**
91
+ * Enforce uniqueness within each queue.
92
+ */
93
+ byQueue?: true;
94
+ /**
95
+ * Enforce uniqueness across specified job states.
96
+ */
97
+ byState?: string[];
98
+ /**
99
+ * Exclude job kind from uniqueness computation.
100
+ */
101
+ excludeKind?: true;
102
+ }
103
+
104
+ /**
105
+ * Options for job insertion, such as queue, priority, scheduling, and metadata.
106
+ */
107
+ interface InsertOpts {
108
+ /**
109
+ * List of tags for grouping and categorizing jobs
110
+ */
111
+ tags?: string[];
112
+ /**
113
+ * Name of the job queue to insert into
114
+ */
115
+ queue?: string;
116
+ /**
117
+ * Job priority (1 = highest, 4 = lowest)
118
+ */
119
+ priority?: number;
120
+ /**
121
+ * Arbitrary metadata for the job
122
+ */
123
+ metadata?: Record<string, unknown>;
124
+ /**
125
+ * Maximum number of attempts before discarding the job
126
+ */
127
+ maxAttempts?: number;
128
+ /**
129
+ * Schedule the job for future execution
130
+ */
131
+ scheduledAt?: Date;
132
+ /**
133
+ * Options relating to job uniqueness.
134
+ * If not set, the job is never treated as unique.
135
+ */
136
+ uniqueOpts?: UniqueOpts;
137
+ }
138
+
139
+ /**
140
+ * Interface for job argument objects passed to job handlers.
141
+ *
142
+ * Every job must specify a `kind` to identify its type, and can include
143
+ * any number of additional properties relevant to the job's execution.
144
+ */
145
+ interface JobArgs {
146
+ /**
147
+ * Identifies the job type for routing and processing.
148
+ */
149
+ kind: string;
150
+ /**
151
+ * Arbitrary job parameters, allowing for flexible job payloads.
152
+ * All values should be JSON-compatible.
153
+ */
154
+ [key: string]: unknown;
155
+ }
156
+
157
+ /**
158
+ * Represents a job persisted in the database, including its state, metadata, and scheduling information.
159
+ */
160
+ interface Job<T extends JobArgs = JobArgs> {
161
+ /**
162
+ * Unique job ID, generated by the database
163
+ */
164
+ id: number;
165
+ /**
166
+ * Current state of the job (e.g., available, completed)
167
+ */
168
+ state: JobState;
169
+ /**
170
+ * Current attempt number, incremented each time the job is worked
171
+ */
172
+ attempt: number;
173
+ /**
174
+ * Maximum number of attempts before the job stops retrying
175
+ */
176
+ maxAttempts: number;
177
+ /**
178
+ * Last time the job was worked, null if never
179
+ */
180
+ attemptedAt: Date | null;
181
+ /**
182
+ * When the job record was created
183
+ */
184
+ createdAt: Date;
185
+ /**
186
+ * When the job was finalized (completed or errored for last time), null if not finalized
187
+ */
188
+ finalizedAt: Date | null;
189
+ /**
190
+ * When the job is scheduled to become available
191
+ */
192
+ scheduledAt: Date;
193
+ /**
194
+ * Job priority (1 = highest, 4 = lowest)
195
+ */
196
+ priority: number;
197
+ /**
198
+ * Job arguments, decoded from JSON
199
+ */
200
+ args: T;
201
+ /**
202
+ * Worker IDs that have worked this job, null if never
203
+ */
204
+ attemptedBy: string[] | null;
205
+ /**
206
+ * Errors for each attempt, ordered earliest to latest, null if none
207
+ */
208
+ errors: AttemptError[] | null;
209
+ /**
210
+ * Job type identifier, set at insertion
211
+ */
212
+ kind: string;
213
+ /**
214
+ * Arbitrary metadata associated with the job
215
+ */
216
+ metadata: Record<string, unknown>;
217
+ /**
218
+ * Name of the queue where the job will be worked
219
+ */
220
+ queue: string;
221
+ /**
222
+ * List of tags for grouping and categorizing jobs
223
+ */
224
+ tags: string[];
225
+ /**
226
+ * Unique key for job within its kind, used for unique insertions, null if not set
227
+ */
228
+ uniqueKey: Buffer | null;
229
+ /**
230
+ * States required for uniqueness, null if not set
231
+ */
232
+ uniqueStates: JobState[] | null;
233
+ }
234
+
235
+ /**
236
+ * Result of a job insertion operation.
237
+ *
238
+ * - If a unique job already exists, `job` is the existing job and `skipped` is true.
239
+ * - If the job was inserted, `job` is the new job and `skipped` is false.
240
+ */
241
+ interface InsertResult<T extends JobArgs = JobArgs> {
242
+ /**
243
+ * The inserted job, or the existing job if insertion was skipped due to uniqueness.
244
+ */
245
+ job: Job<T>;
246
+ /**
247
+ * True if insertion was skipped because a unique job already exists.
248
+ */
249
+ skipped: boolean;
250
+ }
251
+
252
+ /**
253
+ * Common interface for all RiverQueue drivers (e.g., Postgres, Prisma, Sequelize, etc.).
254
+ * The generic parameter Tx must be set to the driver's transaction/session type.
255
+ */
256
+ interface Driver<Tx> {
257
+ /**
258
+ * Checks if the driver can connect to the database. Throws on failure.
259
+ */
260
+ verifyConnection(): Promise<void>;
261
+ /**
262
+ * Closes all database connections and cleans up resources.
263
+ */
264
+ close(): Promise<void>;
265
+ /**
266
+ * Inserts a new job into the queue using the provided arguments and options.
267
+ * @param args - The job arguments to insert.
268
+ * @param opts - Options for job insertion.
269
+ * @returns A promise that resolves to the result of the insertion operation,
270
+ * including the job and whether the insert was skipped due to uniqueness.
271
+ */
272
+ insert<T extends JobArgs>(args: T, opts: InsertOpts): Promise<InsertResult<T>>;
273
+ /**
274
+ * Inserts a new job into the queue within an existing transaction or session.
275
+ * The type of `tx` is driver-specific and should match the transaction/session type for the driver.
276
+ *
277
+ * @param tx - The transaction or session object to use for the insert.
278
+ * @param args - The job arguments to insert.
279
+ * @param opts - Options for job insertion.
280
+ * @returns A promise that resolves to the result of the insertion operation.
281
+ */
282
+ insertTx<T extends JobArgs>(tx: Tx, args: T, opts: InsertOpts): Promise<InsertResult<T>>;
283
+ /**
284
+ * Inserts multiple jobs in sequence within a single transaction.
285
+ * If any insert fails, all previous inserts in the batch are rolled back.
286
+ *
287
+ * @param jobs - Array of job argument and option pairs to insert.
288
+ * @returns Array of InsertResult objects for each job.
289
+ */
290
+ insertMany<T extends JobArgs>(jobs: {
291
+ args: T;
292
+ opts: InsertOpts;
293
+ }[]): Promise<InsertResult<T>[]>;
294
+ /**
295
+ * Starts and returns a new transaction or session object for the driver.
296
+ * The returned object should be used for transactional operations such as insertTx.
297
+ *
298
+ * @returns A promise that resolves to the driver's transaction/session object.
299
+ */
300
+ getTx(): Promise<Tx>;
301
+ }
302
+
303
+ /**
304
+ * Provides methods to enqueue jobs and manage queue operations.
305
+ */
306
+ declare class RiverClient<D extends Driver<Tx>, Tx> {
307
+ private readonly driver;
308
+ private readonly configuration;
309
+ /**
310
+ * Creates a new RiverClient instance.
311
+ * @param driver - The queue driver implementation.
312
+ * @param configuration - Client configuration options.
313
+ */
314
+ constructor(driver: D, configuration: ClientConfiguration);
315
+ /**
316
+ * Checks if the driver can connect to the database. Throws on failure.
317
+ */
318
+ verifyConnection(): Promise<void>;
319
+ /**
320
+ * Closes all database connections and cleans up resources.
321
+ */
322
+ close(): Promise<void>;
323
+ /**
324
+ * Inserts a job into the queue with the specified arguments and options.
325
+ * @param args - The job arguments to insert.
326
+ * @param opts - Optional insertion options.
327
+ * @returns A promise that resolves to the result of the insertion operation,
328
+ * including the job and whether the insert was skipped due to uniqueness.
329
+ */
330
+ insert<T extends JobArgs>(args: T, opts?: InsertOpts): Promise<InsertResult<T>>;
331
+ /**
332
+ * Inserts a job into the queue within an existing transaction or session.
333
+ * The transaction type (Tx) is determined by the driver implementation.
334
+ *
335
+ * @param tx - The transaction or session object to use for the insert.
336
+ * @param args - The job arguments to insert.
337
+ * @param opts - Optional insertion options.
338
+ * @returns A promise that resolves to the result of the insertion operation,
339
+ * including the job and whether the insert was skipped due to uniqueness.
340
+ */
341
+ insertTx<T extends JobArgs>(tx: Tx, args: T, opts?: InsertOpts): Promise<InsertResult<T>>;
342
+ /**
343
+ * Inserts multiple jobs in sequence within a single transaction.
344
+ * If any insert fails, all previous inserts in the batch are rolled back.
345
+ *
346
+ * @param jobs - Array of job argument and option pairs to insert.
347
+ * @returns A promise that resolves to an array of InsertResult objects for each job.
348
+ */
349
+ insertMany<T extends JobArgs>(jobs: {
350
+ args: T;
351
+ opts: InsertOpts;
352
+ }[]): Promise<InsertResult<T>[]>;
353
+ }
354
+
355
+ export { RiverClient };
@@ -0,0 +1,355 @@
1
+ import { Buffer } from 'buffer';
2
+
3
+ /**
4
+ * Configuration options for the RiverQueue client.
5
+ */
6
+ interface ClientConfiguration {
7
+ /**
8
+ * Default queue name for job insertion
9
+ */
10
+ defaultQueue?: string;
11
+ /**
12
+ * Maximum number of attempts for jobs
13
+ */
14
+ maxAttempts?: number;
15
+ }
16
+
17
+ /**
18
+ * Represents the lifecycle state of a job, such as available, running, completed, or scheduled.
19
+ */
20
+ declare enum JobState {
21
+ /**
22
+ * Immediately eligible to be worked
23
+ */
24
+ Available = "available",
25
+ /**
26
+ * Manually cancelled by user; cleaned after a period
27
+ */
28
+ Cancelled = "cancelled",
29
+ /**
30
+ * Successfully run to completion; cleaned after a period
31
+ */
32
+ Completed = "completed",
33
+ /**
34
+ * Errored too many times; needs manual intervention
35
+ */
36
+ Discarded = "discarded",
37
+ /**
38
+ * Waiting for external action; not worked or deleted until moved
39
+ */
40
+ Pending = "pending",
41
+ /**
42
+ * Errored but will be retried; becomes Available when ready
43
+ */
44
+ Retryable = "retryable",
45
+ /**
46
+ * Actively running; may need rescue if stuck
47
+ */
48
+ Running = "running",
49
+ /**
50
+ * Scheduled for future execution; becomes Available when due
51
+ */
52
+ Scheduled = "scheduled"
53
+ }
54
+
55
+ /**
56
+ * Represents a failed job attempt, including error details and stack trace.
57
+ */
58
+ interface AttemptError {
59
+ /**
60
+ * Time the error occurred
61
+ */
62
+ at: string;
63
+ /**
64
+ * Attempt number when the error occurred
65
+ */
66
+ attempt: number;
67
+ /**
68
+ * Stringified error or panic value
69
+ */
70
+ error: string;
71
+ /**
72
+ * Stack trace from a job that panicked
73
+ */
74
+ trace: string;
75
+ }
76
+
77
+ /**
78
+ * Options for enforcing uniqueness constraints on jobs.
79
+ */
80
+ interface UniqueOpts {
81
+ /**
82
+ * Enforce uniqueness based on job arguments.
83
+ * True for all args, or specify keys to include.
84
+ */
85
+ byArgs?: true | string[];
86
+ /**
87
+ * Enforce uniqueness within a time period (seconds).
88
+ */
89
+ byPeriod?: number;
90
+ /**
91
+ * Enforce uniqueness within each queue.
92
+ */
93
+ byQueue?: true;
94
+ /**
95
+ * Enforce uniqueness across specified job states.
96
+ */
97
+ byState?: string[];
98
+ /**
99
+ * Exclude job kind from uniqueness computation.
100
+ */
101
+ excludeKind?: true;
102
+ }
103
+
104
+ /**
105
+ * Options for job insertion, such as queue, priority, scheduling, and metadata.
106
+ */
107
+ interface InsertOpts {
108
+ /**
109
+ * List of tags for grouping and categorizing jobs
110
+ */
111
+ tags?: string[];
112
+ /**
113
+ * Name of the job queue to insert into
114
+ */
115
+ queue?: string;
116
+ /**
117
+ * Job priority (1 = highest, 4 = lowest)
118
+ */
119
+ priority?: number;
120
+ /**
121
+ * Arbitrary metadata for the job
122
+ */
123
+ metadata?: Record<string, unknown>;
124
+ /**
125
+ * Maximum number of attempts before discarding the job
126
+ */
127
+ maxAttempts?: number;
128
+ /**
129
+ * Schedule the job for future execution
130
+ */
131
+ scheduledAt?: Date;
132
+ /**
133
+ * Options relating to job uniqueness.
134
+ * If not set, the job is never treated as unique.
135
+ */
136
+ uniqueOpts?: UniqueOpts;
137
+ }
138
+
139
+ /**
140
+ * Interface for job argument objects passed to job handlers.
141
+ *
142
+ * Every job must specify a `kind` to identify its type, and can include
143
+ * any number of additional properties relevant to the job's execution.
144
+ */
145
+ interface JobArgs {
146
+ /**
147
+ * Identifies the job type for routing and processing.
148
+ */
149
+ kind: string;
150
+ /**
151
+ * Arbitrary job parameters, allowing for flexible job payloads.
152
+ * All values should be JSON-compatible.
153
+ */
154
+ [key: string]: unknown;
155
+ }
156
+
157
+ /**
158
+ * Represents a job persisted in the database, including its state, metadata, and scheduling information.
159
+ */
160
+ interface Job<T extends JobArgs = JobArgs> {
161
+ /**
162
+ * Unique job ID, generated by the database
163
+ */
164
+ id: number;
165
+ /**
166
+ * Current state of the job (e.g., available, completed)
167
+ */
168
+ state: JobState;
169
+ /**
170
+ * Current attempt number, incremented each time the job is worked
171
+ */
172
+ attempt: number;
173
+ /**
174
+ * Maximum number of attempts before the job stops retrying
175
+ */
176
+ maxAttempts: number;
177
+ /**
178
+ * Last time the job was worked, null if never
179
+ */
180
+ attemptedAt: Date | null;
181
+ /**
182
+ * When the job record was created
183
+ */
184
+ createdAt: Date;
185
+ /**
186
+ * When the job was finalized (completed or errored for last time), null if not finalized
187
+ */
188
+ finalizedAt: Date | null;
189
+ /**
190
+ * When the job is scheduled to become available
191
+ */
192
+ scheduledAt: Date;
193
+ /**
194
+ * Job priority (1 = highest, 4 = lowest)
195
+ */
196
+ priority: number;
197
+ /**
198
+ * Job arguments, decoded from JSON
199
+ */
200
+ args: T;
201
+ /**
202
+ * Worker IDs that have worked this job, null if never
203
+ */
204
+ attemptedBy: string[] | null;
205
+ /**
206
+ * Errors for each attempt, ordered earliest to latest, null if none
207
+ */
208
+ errors: AttemptError[] | null;
209
+ /**
210
+ * Job type identifier, set at insertion
211
+ */
212
+ kind: string;
213
+ /**
214
+ * Arbitrary metadata associated with the job
215
+ */
216
+ metadata: Record<string, unknown>;
217
+ /**
218
+ * Name of the queue where the job will be worked
219
+ */
220
+ queue: string;
221
+ /**
222
+ * List of tags for grouping and categorizing jobs
223
+ */
224
+ tags: string[];
225
+ /**
226
+ * Unique key for job within its kind, used for unique insertions, null if not set
227
+ */
228
+ uniqueKey: Buffer | null;
229
+ /**
230
+ * States required for uniqueness, null if not set
231
+ */
232
+ uniqueStates: JobState[] | null;
233
+ }
234
+
235
+ /**
236
+ * Result of a job insertion operation.
237
+ *
238
+ * - If a unique job already exists, `job` is the existing job and `skipped` is true.
239
+ * - If the job was inserted, `job` is the new job and `skipped` is false.
240
+ */
241
+ interface InsertResult<T extends JobArgs = JobArgs> {
242
+ /**
243
+ * The inserted job, or the existing job if insertion was skipped due to uniqueness.
244
+ */
245
+ job: Job<T>;
246
+ /**
247
+ * True if insertion was skipped because a unique job already exists.
248
+ */
249
+ skipped: boolean;
250
+ }
251
+
252
+ /**
253
+ * Common interface for all RiverQueue drivers (e.g., Postgres, Prisma, Sequelize, etc.).
254
+ * The generic parameter Tx must be set to the driver's transaction/session type.
255
+ */
256
+ interface Driver<Tx> {
257
+ /**
258
+ * Checks if the driver can connect to the database. Throws on failure.
259
+ */
260
+ verifyConnection(): Promise<void>;
261
+ /**
262
+ * Closes all database connections and cleans up resources.
263
+ */
264
+ close(): Promise<void>;
265
+ /**
266
+ * Inserts a new job into the queue using the provided arguments and options.
267
+ * @param args - The job arguments to insert.
268
+ * @param opts - Options for job insertion.
269
+ * @returns A promise that resolves to the result of the insertion operation,
270
+ * including the job and whether the insert was skipped due to uniqueness.
271
+ */
272
+ insert<T extends JobArgs>(args: T, opts: InsertOpts): Promise<InsertResult<T>>;
273
+ /**
274
+ * Inserts a new job into the queue within an existing transaction or session.
275
+ * The type of `tx` is driver-specific and should match the transaction/session type for the driver.
276
+ *
277
+ * @param tx - The transaction or session object to use for the insert.
278
+ * @param args - The job arguments to insert.
279
+ * @param opts - Options for job insertion.
280
+ * @returns A promise that resolves to the result of the insertion operation.
281
+ */
282
+ insertTx<T extends JobArgs>(tx: Tx, args: T, opts: InsertOpts): Promise<InsertResult<T>>;
283
+ /**
284
+ * Inserts multiple jobs in sequence within a single transaction.
285
+ * If any insert fails, all previous inserts in the batch are rolled back.
286
+ *
287
+ * @param jobs - Array of job argument and option pairs to insert.
288
+ * @returns Array of InsertResult objects for each job.
289
+ */
290
+ insertMany<T extends JobArgs>(jobs: {
291
+ args: T;
292
+ opts: InsertOpts;
293
+ }[]): Promise<InsertResult<T>[]>;
294
+ /**
295
+ * Starts and returns a new transaction or session object for the driver.
296
+ * The returned object should be used for transactional operations such as insertTx.
297
+ *
298
+ * @returns A promise that resolves to the driver's transaction/session object.
299
+ */
300
+ getTx(): Promise<Tx>;
301
+ }
302
+
303
+ /**
304
+ * Provides methods to enqueue jobs and manage queue operations.
305
+ */
306
+ declare class RiverClient<D extends Driver<Tx>, Tx> {
307
+ private readonly driver;
308
+ private readonly configuration;
309
+ /**
310
+ * Creates a new RiverClient instance.
311
+ * @param driver - The queue driver implementation.
312
+ * @param configuration - Client configuration options.
313
+ */
314
+ constructor(driver: D, configuration: ClientConfiguration);
315
+ /**
316
+ * Checks if the driver can connect to the database. Throws on failure.
317
+ */
318
+ verifyConnection(): Promise<void>;
319
+ /**
320
+ * Closes all database connections and cleans up resources.
321
+ */
322
+ close(): Promise<void>;
323
+ /**
324
+ * Inserts a job into the queue with the specified arguments and options.
325
+ * @param args - The job arguments to insert.
326
+ * @param opts - Optional insertion options.
327
+ * @returns A promise that resolves to the result of the insertion operation,
328
+ * including the job and whether the insert was skipped due to uniqueness.
329
+ */
330
+ insert<T extends JobArgs>(args: T, opts?: InsertOpts): Promise<InsertResult<T>>;
331
+ /**
332
+ * Inserts a job into the queue within an existing transaction or session.
333
+ * The transaction type (Tx) is determined by the driver implementation.
334
+ *
335
+ * @param tx - The transaction or session object to use for the insert.
336
+ * @param args - The job arguments to insert.
337
+ * @param opts - Optional insertion options.
338
+ * @returns A promise that resolves to the result of the insertion operation,
339
+ * including the job and whether the insert was skipped due to uniqueness.
340
+ */
341
+ insertTx<T extends JobArgs>(tx: Tx, args: T, opts?: InsertOpts): Promise<InsertResult<T>>;
342
+ /**
343
+ * Inserts multiple jobs in sequence within a single transaction.
344
+ * If any insert fails, all previous inserts in the batch are rolled back.
345
+ *
346
+ * @param jobs - Array of job argument and option pairs to insert.
347
+ * @returns A promise that resolves to an array of InsertResult objects for each job.
348
+ */
349
+ insertMany<T extends JobArgs>(jobs: {
350
+ args: T;
351
+ opts: InsertOpts;
352
+ }[]): Promise<InsertResult<T>[]>;
353
+ }
354
+
355
+ export { RiverClient };
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ // src/client.ts
2
+ var RiverClient = class {
3
+ /**
4
+ * Creates a new RiverClient instance.
5
+ * @param driver - The queue driver implementation.
6
+ * @param configuration - Client configuration options.
7
+ */
8
+ constructor(driver, configuration) {
9
+ this.driver = driver;
10
+ this.configuration = configuration;
11
+ }
12
+ /**
13
+ * Checks if the driver can connect to the database. Throws on failure.
14
+ */
15
+ verifyConnection() {
16
+ return this.driver.verifyConnection();
17
+ }
18
+ /**
19
+ * Closes all database connections and cleans up resources.
20
+ */
21
+ close() {
22
+ return this.driver.close();
23
+ }
24
+ /**
25
+ * Inserts a job into the queue with the specified arguments and options.
26
+ * @param args - The job arguments to insert.
27
+ * @param opts - Optional insertion options.
28
+ * @returns A promise that resolves to the result of the insertion operation,
29
+ * including the job and whether the insert was skipped due to uniqueness.
30
+ */
31
+ insert(args, opts = {}) {
32
+ const defaultOpts = {
33
+ queue: this.configuration.defaultQueue,
34
+ maxAttempts: this.configuration.maxAttempts,
35
+ ...opts
36
+ };
37
+ return this.driver.insert(args, defaultOpts);
38
+ }
39
+ /**
40
+ * Inserts a job into the queue within an existing transaction or session.
41
+ * The transaction type (Tx) is determined by the driver implementation.
42
+ *
43
+ * @param tx - The transaction or session object to use for the insert.
44
+ * @param args - The job arguments to insert.
45
+ * @param opts - Optional insertion options.
46
+ * @returns A promise that resolves to the result of the insertion operation,
47
+ * including the job and whether the insert was skipped due to uniqueness.
48
+ */
49
+ insertTx(tx, args, opts = {}) {
50
+ const defaultOpts = {
51
+ queue: this.configuration.defaultQueue,
52
+ maxAttempts: this.configuration.maxAttempts,
53
+ ...opts
54
+ };
55
+ return this.driver.insertTx(tx, args, defaultOpts);
56
+ }
57
+ /**
58
+ * Inserts multiple jobs in sequence within a single transaction.
59
+ * If any insert fails, all previous inserts in the batch are rolled back.
60
+ *
61
+ * @param jobs - Array of job argument and option pairs to insert.
62
+ * @returns A promise that resolves to an array of InsertResult objects for each job.
63
+ */
64
+ async insertMany(jobs) {
65
+ const jobsWithDefaults = jobs.map((job) => ({
66
+ args: job.args,
67
+ opts: {
68
+ queue: this.configuration.defaultQueue,
69
+ maxAttempts: this.configuration.maxAttempts,
70
+ ...job.opts
71
+ }
72
+ }));
73
+ return this.driver.insertMany(jobsWithDefaults);
74
+ }
75
+ };
76
+ export {
77
+ RiverClient
78
+ };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@odunlamizo/node-river",
3
+ "version": "1.0.0",
4
+ "description": "Node.js library to support riverqueue integration.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup src/index.ts --dts --format cjs,esm",
14
+ "dev": "tsup src/index.ts --watch --dts --format cjs,esm",
15
+ "test": "jest --runInBand",
16
+ "lint": "eslint . --ext .ts,.js --ignore-pattern eslint.config.js",
17
+ "format": "prettier --write .",
18
+ "prepare": "husky install"
19
+ },
20
+ "lint-staged": {
21
+ "**/*.{js,ts}": [
22
+ "eslint --fix --ignore-pattern eslint.config.js",
23
+ "prettier --write"
24
+ ]
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/OdunlamiZO/node-river.git"
29
+ },
30
+ "keywords": [
31
+ "riverqueue",
32
+ "queue",
33
+ "node",
34
+ "library"
35
+ ],
36
+ "author": "Odunlami Zacchaeus",
37
+ "license": "ISC",
38
+ "bugs": {
39
+ "url": "https://github.com/OdunlamiZO/node-river/issues"
40
+ },
41
+ "homepage": "https://github.com/OdunlamiZO/node-river#readme",
42
+ "devDependencies": {
43
+ "@types/jest": "^30.0.0",
44
+ "@types/node": "^25.2.3",
45
+ "@types/pg": "^8.16.0",
46
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
47
+ "@typescript-eslint/parser": "^8.54.0",
48
+ "eslint": "^9.39.2",
49
+ "eslint-config-prettier": "^10.1.8",
50
+ "eslint-plugin-prettier": "^5.5.5",
51
+ "husky": "^8.0.0",
52
+ "jest": "^29.7.0",
53
+ "lint-staged": "^16.2.7",
54
+ "prettier": "^3.8.1",
55
+ "prettier-plugin-organize-imports": "^4.3.0",
56
+ "ts-jest": "^29.4.6",
57
+ "tsup": "^8.5.1",
58
+ "typescript": "^5.9.3"
59
+ },
60
+ "peerDependencies": {
61
+ "pg": ">=8.0.0"
62
+ }
63
+ }