@odunlamizo/node-river 1.0.8 → 1.1.1

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 CHANGED
@@ -1,77 +1,149 @@
1
+ import os from 'os';
2
+ import process from 'process';
3
+ import { clearTimeout, setTimeout } from 'timers';
4
+
5
+ // src/client.ts
6
+
7
+ // src/utils/constants.ts
8
+ var CLIENT_CONFIGURATION_DEFAULTS = {
9
+ pollInterval: 1e3
10
+ };
11
+
1
12
  // src/client.ts
2
13
  var RiverClient = class {
3
14
  /**
4
15
  * Creates a new RiverClient instance.
5
- * @param driver - The queue driver implementation.
6
- * @param configuration - Client configuration options.
16
+ * @param driver - The driver implementation (e.g. PgDriver).
17
+ * @param configuration - Client options: queues, max attempts, poll interval, and client ID.
7
18
  */
8
19
  constructor(driver, configuration) {
20
+ this.running = /* @__PURE__ */ new Map();
21
+ this.stopped = false;
22
+ this.pollTimer = null;
23
+ this.workers = /* @__PURE__ */ new Map();
9
24
  this.driver = driver;
10
25
  this.configuration = configuration;
26
+ this.clientId = configuration.clientId ?? `${os.hostname()}-${process.pid}`;
11
27
  }
12
28
  /**
13
- * Checks if the driver can connect to the database. Throws on failure.
29
+ * Verifies the driver can reach the database. Throws if the connection fails.
14
30
  */
15
- verifyConnection() {
31
+ async verifyConnection() {
16
32
  return this.driver.verifyConnection();
17
33
  }
18
34
  /**
19
- * Closes all database connections and cleans up resources.
35
+ * Stops the polling loop (if running) and closes all database connections.
36
+ * Always call this on shutdown to avoid resource leaks.
20
37
  */
21
- close() {
22
- return this.driver.close();
38
+ async close() {
39
+ this.stopped = true;
40
+ if (this.pollTimer) clearTimeout(this.pollTimer);
41
+ await this.driver.close();
23
42
  }
24
43
  /**
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.
44
+ * Inserts a single job into the specified queue.
45
+ * @param args - Job arguments including `kind` and any job-specific fields.
46
+ * @param opts - Insertion options. `queue` is required; `maxAttempts` defaults to the client configuration value.
47
+ * @returns The inserted job and a `skipped` flag indicating if it was deduplicated.
30
48
  */
31
- insert(args, opts = {}) {
49
+ async insert(args, opts) {
32
50
  const defaultOpts = {
33
- queue: this.configuration.defaultQueue,
34
51
  maxAttempts: this.configuration.maxAttempts,
35
52
  ...opts
36
53
  };
37
54
  return this.driver.insert(args, defaultOpts);
38
55
  }
39
56
  /**
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.
57
+ * Inserts a single job within an existing transaction. Use this when the job
58
+ * should only be enqueued if the surrounding transaction commits.
59
+ * @param tx - The active transaction object (type is driver-specific).
60
+ * @param args - Job arguments including `kind` and any job-specific fields.
61
+ * @param opts - Insertion options. `queue` is required; `maxAttempts` defaults to the client configuration value.
62
+ * @returns The inserted job and a `skipped` flag indicating if it was deduplicated.
48
63
  */
49
- insertTx(tx, args, opts = {}) {
64
+ async insertTx(tx, args, opts) {
50
65
  const defaultOpts = {
51
- queue: this.configuration.defaultQueue,
52
66
  maxAttempts: this.configuration.maxAttempts,
53
67
  ...opts
54
68
  };
55
69
  return this.driver.insertTx(tx, args, defaultOpts);
56
70
  }
57
71
  /**
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.
72
+ * Inserts multiple jobs in a single transaction. If any insert fails, all are rolled back.
73
+ * @param jobs - Array of `{ args, opts }` pairs. Each `opts` must include a `queue`.
74
+ * @returns An array of InsertResult objects in the same order as the input.
63
75
  */
64
76
  async insertMany(jobs) {
65
77
  const jobsWithDefaults = jobs.map((job) => ({
66
78
  args: job.args,
67
79
  opts: {
68
- queue: this.configuration.defaultQueue,
69
80
  maxAttempts: this.configuration.maxAttempts,
70
81
  ...job.opts
71
82
  }
72
83
  }));
73
84
  return this.driver.insertMany(jobsWithDefaults);
74
85
  }
86
+ /**
87
+ * Registers a worker for a specific job kind. When a job of this kind is claimed
88
+ * from the queue, the worker's `work()` method will be called with the job.
89
+ * Must be called before `work()` for the worker to receive jobs.
90
+ * @param kind - The job kind string that this worker handles (e.g. `"send_email"`).
91
+ * @param worker - The worker instance that implements the `Worker<T>` interface.
92
+ */
93
+ addWorker(kind, worker) {
94
+ this.workers.set(kind, worker);
95
+ }
96
+ /**
97
+ * Begins polling all configured queues and dispatching jobs to registered workers.
98
+ * Each queue is polled independently with its own concurrency limit.
99
+ * No-op if `queues` is not configured.
100
+ */
101
+ work() {
102
+ this.stopped = false;
103
+ this.poll();
104
+ }
105
+ poll() {
106
+ if (this.stopped) return;
107
+ this.fetchAndWork().finally(() => {
108
+ this.pollTimer = setTimeout(
109
+ () => this.poll(),
110
+ this.configuration.pollInterval ?? CLIENT_CONFIGURATION_DEFAULTS.pollInterval
111
+ );
112
+ });
113
+ }
114
+ async fetchAndWork() {
115
+ const queues = Object.entries(this.configuration.queues ?? {});
116
+ for (const [queue, queueConfig] of queues) {
117
+ const running = this.running.get(queue) ?? 0;
118
+ const slots = queueConfig.concurrency - running;
119
+ if (slots <= 0) continue;
120
+ const jobs = await this.driver.getAvailableJobs(queue, slots, this.clientId);
121
+ for (const job of jobs) {
122
+ this.running.set(job.queue, (this.running.get(job.queue) ?? 0) + 1);
123
+ this.processJob(job).finally(() => {
124
+ this.running.set(job.queue, Math.max(0, (this.running.get(job.queue) ?? 0) - 1));
125
+ });
126
+ }
127
+ }
128
+ }
129
+ async processJob(job) {
130
+ const worker = this.workers.get(job.kind);
131
+ if (!worker) {
132
+ await this.driver.failJob(
133
+ job.id,
134
+ new Error(`No worker registered for kind: ${job.kind}`),
135
+ job.attempt,
136
+ job.maxAttempts
137
+ );
138
+ return;
139
+ }
140
+ try {
141
+ await worker.work(job);
142
+ await this.driver.completeJob(job.id);
143
+ } catch (error) {
144
+ await this.driver.failJob(job.id, error, job.attempt, job.maxAttempts);
145
+ }
146
+ }
75
147
  };
76
148
 
77
149
  export { RiverClient };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":";AAMA,IAAqB,cAArB,MAA2D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASzD,WAAA,CAAY,QAAW,aAAA,EAAoC;AACzD,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,aAAA,GAAgB,aAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAA,GAAkC;AAChC,IAAA,OAAO,IAAA,CAAK,OAAO,gBAAA,EAAiB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,OAAO,KAAA,EAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAA,CAA0B,IAAA,EAAS,IAAA,GAAmB,EAAC,EAA6B;AAClF,IAAA,MAAM,WAAA,GAA0B;AAAA,MAC9B,KAAA,EAAO,KAAK,aAAA,CAAc,YAAA;AAAA,MAC1B,WAAA,EAAa,KAAK,aAAA,CAAc,WAAA;AAAA,MAChC,GAAG;AAAA,KACL;AAEA,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,WAAW,CAAA;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,QAAA,CAA4B,EAAA,EAAQ,IAAA,EAAS,IAAA,GAAmB,EAAC,EAA6B;AAC5F,IAAA,MAAM,WAAA,GAA0B;AAAA,MAC9B,KAAA,EAAO,KAAK,aAAA,CAAc,YAAA;AAAA,MAC1B,WAAA,EAAa,KAAK,aAAA,CAAc,WAAA;AAAA,MAChC,GAAG;AAAA,KACL;AAEA,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,EAAA,EAAI,MAAM,WAAW,CAAA;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WACJ,IAAA,EAC4B;AAC5B,IAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,MAC1C,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,IAAA,EAAM;AAAA,QACJ,KAAA,EAAO,KAAK,aAAA,CAAc,YAAA;AAAA,QAC1B,WAAA,EAAa,KAAK,aAAA,CAAc,WAAA;AAAA,QAChC,GAAG,GAAA,CAAI;AAAA;AACT,KACF,CAAE,CAAA;AAEF,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,UAAA,CAAW,gBAAgB,CAAA;AAAA,EAChD;AACF","file":"index.js","sourcesContent":["import { Driver } from './drivers';\nimport { ClientConfiguration, InsertOpts, InsertResult, JobArgs } from './types';\n\n/**\n * Provides methods to enqueue jobs and manage queue operations.\n */\nexport default class RiverClient<D extends Driver<Tx>, Tx> {\n private readonly driver: D;\n private readonly configuration: ClientConfiguration;\n\n /**\n * Creates a new RiverClient instance.\n * @param driver - The queue driver implementation.\n * @param configuration - Client configuration options.\n */\n constructor(driver: D, configuration: ClientConfiguration) {\n this.driver = driver;\n this.configuration = configuration;\n }\n\n /**\n * Checks if the driver can connect to the database. Throws on failure.\n */\n verifyConnection(): Promise<void> {\n return this.driver.verifyConnection();\n }\n\n /**\n * Closes all database connections and cleans up resources.\n */\n close(): Promise<void> {\n return this.driver.close();\n }\n\n /**\n * Inserts a job into the queue with the specified arguments and options.\n * @param args - The job arguments to insert.\n * @param opts - Optional insertion options.\n * @returns A promise that resolves to the result of the insertion operation,\n * including the job and whether the insert was skipped due to uniqueness.\n */\n insert<T extends JobArgs>(args: T, opts: InsertOpts = {}): Promise<InsertResult<T>> {\n const defaultOpts: InsertOpts = {\n queue: this.configuration.defaultQueue,\n maxAttempts: this.configuration.maxAttempts,\n ...opts,\n };\n\n return this.driver.insert(args, defaultOpts);\n }\n\n /**\n * Inserts a job into the queue within an existing transaction or session.\n * The transaction type (Tx) is determined by the driver implementation.\n *\n * @param tx - The transaction or session object to use for the insert.\n * @param args - The job arguments to insert.\n * @param opts - Optional insertion options.\n * @returns A promise that resolves to the result of the insertion operation,\n * including the job and whether the insert was skipped due to uniqueness.\n */\n insertTx<T extends JobArgs>(tx: Tx, args: T, opts: InsertOpts = {}): Promise<InsertResult<T>> {\n const defaultOpts: InsertOpts = {\n queue: this.configuration.defaultQueue,\n maxAttempts: this.configuration.maxAttempts,\n ...opts,\n };\n\n return this.driver.insertTx(tx, args, defaultOpts);\n }\n\n /**\n * Inserts multiple jobs in sequence within a single transaction.\n * If any insert fails, all previous inserts in the batch are rolled back.\n *\n * @param jobs - Array of job argument and option pairs to insert.\n * @returns A promise that resolves to an array of InsertResult objects for each job.\n */\n async insertMany<T extends JobArgs>(\n jobs: { args: T; opts: InsertOpts }[],\n ): Promise<InsertResult<T>[]> {\n const jobsWithDefaults = jobs.map((job) => ({\n args: job.args,\n opts: {\n queue: this.configuration.defaultQueue,\n maxAttempts: this.configuration.maxAttempts,\n ...job.opts,\n },\n }));\n\n return this.driver.insertMany(jobsWithDefaults);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/constants.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Default values applied when optional fields are not provided in ClientConfiguration.\n */\nexport const CLIENT_CONFIGURATION_DEFAULTS = {\n pollInterval: 1000,\n};\n","import os from 'os';\nimport process from 'process';\nimport { clearTimeout, setTimeout } from 'timers';\nimport { Driver } from './drivers';\nimport { ClientConfiguration, InsertOpts, InsertResult, Job, JobArgs, Worker } from './types';\nimport { CLIENT_CONFIGURATION_DEFAULTS } from './utils';\n\n/**\n * RiverClient is the main entry point for interacting with the River job queue.\n * It supports two usage modes:\n * - Insertion only: enqueue jobs without processing them.\n * - Worker mode: register workers and call `work()` to start polling and processing jobs.\n */\nexport default class RiverClient<D extends Driver<Tx>, Tx> {\n private readonly running: Map<string, number> = new Map();\n private stopped = false;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly driver: D;\n private readonly configuration: ClientConfiguration;\n private readonly workers: Map<string, Worker> = new Map();\n private readonly clientId: string;\n\n /**\n * Creates a new RiverClient instance.\n * @param driver - The driver implementation (e.g. PgDriver).\n * @param configuration - Client options: queues, max attempts, poll interval, and client ID.\n */\n constructor(driver: D, configuration: ClientConfiguration) {\n this.driver = driver;\n this.configuration = configuration;\n this.clientId = configuration.clientId ?? `${os.hostname()}-${process.pid}`;\n }\n\n /**\n * Verifies the driver can reach the database. Throws if the connection fails.\n */\n async verifyConnection(): Promise<void> {\n return this.driver.verifyConnection();\n }\n\n /**\n * Stops the polling loop (if running) and closes all database connections.\n * Always call this on shutdown to avoid resource leaks.\n */\n async close(): Promise<void> {\n this.stopped = true;\n if (this.pollTimer) clearTimeout(this.pollTimer);\n await this.driver.close();\n }\n\n /**\n * Inserts a single job into the specified queue.\n * @param args - Job arguments including `kind` and any job-specific fields.\n * @param opts - Insertion options. `queue` is required; `maxAttempts` defaults to the client configuration value.\n * @returns The inserted job and a `skipped` flag indicating if it was deduplicated.\n */\n async insert<T extends JobArgs>(args: T, opts: InsertOpts): Promise<InsertResult<T>> {\n const defaultOpts: InsertOpts = {\n maxAttempts: this.configuration.maxAttempts,\n ...opts,\n };\n\n return this.driver.insert(args, defaultOpts);\n }\n\n /**\n * Inserts a single job within an existing transaction. Use this when the job\n * should only be enqueued if the surrounding transaction commits.\n * @param tx - The active transaction object (type is driver-specific).\n * @param args - Job arguments including `kind` and any job-specific fields.\n * @param opts - Insertion options. `queue` is required; `maxAttempts` defaults to the client configuration value.\n * @returns The inserted job and a `skipped` flag indicating if it was deduplicated.\n */\n async insertTx<T extends JobArgs>(tx: Tx, args: T, opts: InsertOpts): Promise<InsertResult<T>> {\n const defaultOpts: InsertOpts = {\n maxAttempts: this.configuration.maxAttempts,\n ...opts,\n };\n\n return this.driver.insertTx(tx, args, defaultOpts);\n }\n\n /**\n * Inserts multiple jobs in a single transaction. If any insert fails, all are rolled back.\n * @param jobs - Array of `{ args, opts }` pairs. Each `opts` must include a `queue`.\n * @returns An array of InsertResult objects in the same order as the input.\n */\n async insertMany<T extends JobArgs>(\n jobs: { args: T; opts: InsertOpts }[],\n ): Promise<InsertResult<T>[]> {\n const jobsWithDefaults = jobs.map((job) => ({\n args: job.args,\n opts: {\n maxAttempts: this.configuration.maxAttempts,\n ...job.opts,\n },\n }));\n\n return this.driver.insertMany(jobsWithDefaults);\n }\n\n /**\n * Registers a worker for a specific job kind. When a job of this kind is claimed\n * from the queue, the worker's `work()` method will be called with the job.\n * Must be called before `work()` for the worker to receive jobs.\n * @param kind - The job kind string that this worker handles (e.g. `\"send_email\"`).\n * @param worker - The worker instance that implements the `Worker<T>` interface.\n */\n addWorker<T extends JobArgs>(kind: string, worker: Worker<T>): void {\n this.workers.set(kind, worker);\n }\n\n /**\n * Begins polling all configured queues and dispatching jobs to registered workers.\n * Each queue is polled independently with its own concurrency limit.\n * No-op if `queues` is not configured.\n */\n work(): void {\n this.stopped = false;\n this.poll();\n }\n\n private poll(): void {\n if (this.stopped) return;\n\n this.fetchAndWork().finally(() => {\n this.pollTimer = setTimeout(\n () => this.poll(),\n this.configuration.pollInterval ?? CLIENT_CONFIGURATION_DEFAULTS.pollInterval,\n );\n });\n }\n\n private async fetchAndWork(): Promise<void> {\n const queues = Object.entries(this.configuration.queues ?? {});\n\n for (const [queue, queueConfig] of queues) {\n const running = this.running.get(queue) ?? 0;\n const slots = queueConfig.concurrency - running;\n if (slots <= 0) continue;\n\n const jobs = await this.driver.getAvailableJobs(queue, slots, this.clientId);\n\n for (const job of jobs) {\n this.running.set(job.queue, (this.running.get(job.queue) ?? 0) + 1);\n this.processJob(job).finally(() => {\n this.running.set(job.queue, Math.max(0, (this.running.get(job.queue) ?? 0) - 1));\n });\n }\n }\n }\n\n private async processJob(job: Job): Promise<void> {\n const worker = this.workers.get(job.kind);\n\n if (!worker) {\n await this.driver.failJob(\n job.id,\n new Error(`No worker registered for kind: ${job.kind}`),\n job.attempt,\n job.maxAttempts,\n );\n return;\n }\n\n try {\n await worker.work(job);\n await this.driver.completeJob(job.id);\n } catch (error) {\n await this.driver.failJob(job.id, error as Error, job.attempt, job.maxAttempts);\n }\n }\n}\n"]}
@@ -39,23 +39,23 @@ declare enum JobState {
39
39
  }
40
40
 
41
41
  /**
42
- * Represents a failed job attempt, including error details and stack trace.
42
+ * Represents the error details recorded for a single failed job attempt.
43
43
  */
44
44
  interface AttemptError {
45
45
  /**
46
- * Time the error occurred
46
+ * ISO timestamp of when the error occurred.
47
47
  */
48
48
  at: string;
49
49
  /**
50
- * Attempt number when the error occurred
50
+ * The attempt number that produced this error.
51
51
  */
52
52
  attempt: number;
53
53
  /**
54
- * Stringified error or panic value
54
+ * The error message thrown by the worker.
55
55
  */
56
56
  error: string;
57
57
  /**
58
- * Stack trace from a job that panicked
58
+ * Stack trace of the thrown error, if available.
59
59
  */
60
60
  trace: string;
61
61
  }
@@ -96,9 +96,9 @@ interface InsertOpts {
96
96
  */
97
97
  tags?: string[];
98
98
  /**
99
- * Name of the job queue to insert into
99
+ * Name of the queue to insert the job into.
100
100
  */
101
- queue?: string;
101
+ queue: string;
102
102
  /**
103
103
  * Job priority (1 = highest, 4 = lowest)
104
104
  */
@@ -141,79 +141,80 @@ interface JobArgs {
141
141
  }
142
142
 
143
143
  /**
144
- * Represents a job persisted in the database, including its state, metadata, and scheduling information.
144
+ * A job record as stored in the database.
145
+ * @template T - The shape of the job's arguments.
145
146
  */
146
147
  interface Job<T extends JobArgs = JobArgs> {
147
148
  /**
148
- * Unique job ID, generated by the database
149
+ * Auto-generated unique job ID.
149
150
  */
150
151
  id: number;
151
152
  /**
152
- * Current state of the job (e.g., available, completed)
153
+ * Current lifecycle state (e.g. available, running, completed).
153
154
  */
154
155
  state: JobState;
155
156
  /**
156
- * Current attempt number, incremented each time the job is worked
157
+ * How many times this job has been attempted so far.
157
158
  */
158
159
  attempt: number;
159
160
  /**
160
- * Maximum number of attempts before the job stops retrying
161
+ * Maximum attempts allowed before the job is discarded.
161
162
  */
162
163
  maxAttempts: number;
163
164
  /**
164
- * Last time the job was worked, null if never
165
+ * Timestamp of the most recent attempt, null if never attempted.
165
166
  */
166
167
  attemptedAt: Date | null;
167
168
  /**
168
- * When the job record was created
169
+ * Timestamp of when the job record was created.
169
170
  */
170
171
  createdAt: Date;
171
172
  /**
172
- * When the job was finalized (completed or errored for last time), null if not finalized
173
+ * Timestamp of when the job was finalized (completed or discarded), null if still active.
173
174
  */
174
175
  finalizedAt: Date | null;
175
176
  /**
176
- * When the job is scheduled to become available
177
+ * Timestamp of when the job becomes eligible for processing.
177
178
  */
178
179
  scheduledAt: Date;
179
180
  /**
180
- * Job priority (1 = highest, 4 = lowest)
181
+ * Processing priority. Lower numbers run first (1 = highest, 4 = lowest).
181
182
  */
182
183
  priority: number;
183
184
  /**
184
- * Job arguments, decoded from JSON
185
+ * Typed job arguments, decoded from the JSON stored in the database.
185
186
  */
186
187
  args: T;
187
188
  /**
188
- * Worker IDs that have worked this job, null if never
189
+ * List of client IDs that have attempted this job, in order. Null if never attempted.
189
190
  */
190
191
  attemptedBy: string[] | null;
191
192
  /**
192
- * Errors for each attempt, ordered earliest to latest, null if none
193
+ * Error details for each failed attempt, ordered earliest to latest. Null if no failures.
193
194
  */
194
195
  errors: AttemptError[] | null;
195
196
  /**
196
- * Job type identifier, set at insertion
197
+ * The job kind string used to route it to the correct worker (e.g. `"send_email"`).
197
198
  */
198
199
  kind: string;
199
200
  /**
200
- * Arbitrary metadata associated with the job
201
+ * Arbitrary key-value metadata attached to the job at insertion.
201
202
  */
202
203
  metadata: Record<string, unknown>;
203
204
  /**
204
- * Name of the queue where the job will be worked
205
+ * The queue this job belongs to.
205
206
  */
206
207
  queue: string;
207
208
  /**
208
- * List of tags for grouping and categorizing jobs
209
+ * Tags for grouping or filtering jobs.
209
210
  */
210
211
  tags: string[];
211
212
  /**
212
- * Unique key for job within its kind, used for unique insertions, null if not set
213
+ * Hashed uniqueness key. Null if the job was not inserted with uniqueness options.
213
214
  */
214
215
  uniqueKey: Buffer | null;
215
216
  /**
216
- * States required for uniqueness, null if not set
217
+ * The job states considered when enforcing uniqueness. Null if not set.
217
218
  */
218
219
  uniqueStates: JobState[] | null;
219
220
  }
@@ -39,23 +39,23 @@ declare enum JobState {
39
39
  }
40
40
 
41
41
  /**
42
- * Represents a failed job attempt, including error details and stack trace.
42
+ * Represents the error details recorded for a single failed job attempt.
43
43
  */
44
44
  interface AttemptError {
45
45
  /**
46
- * Time the error occurred
46
+ * ISO timestamp of when the error occurred.
47
47
  */
48
48
  at: string;
49
49
  /**
50
- * Attempt number when the error occurred
50
+ * The attempt number that produced this error.
51
51
  */
52
52
  attempt: number;
53
53
  /**
54
- * Stringified error or panic value
54
+ * The error message thrown by the worker.
55
55
  */
56
56
  error: string;
57
57
  /**
58
- * Stack trace from a job that panicked
58
+ * Stack trace of the thrown error, if available.
59
59
  */
60
60
  trace: string;
61
61
  }
@@ -96,9 +96,9 @@ interface InsertOpts {
96
96
  */
97
97
  tags?: string[];
98
98
  /**
99
- * Name of the job queue to insert into
99
+ * Name of the queue to insert the job into.
100
100
  */
101
- queue?: string;
101
+ queue: string;
102
102
  /**
103
103
  * Job priority (1 = highest, 4 = lowest)
104
104
  */
@@ -141,79 +141,80 @@ interface JobArgs {
141
141
  }
142
142
 
143
143
  /**
144
- * Represents a job persisted in the database, including its state, metadata, and scheduling information.
144
+ * A job record as stored in the database.
145
+ * @template T - The shape of the job's arguments.
145
146
  */
146
147
  interface Job<T extends JobArgs = JobArgs> {
147
148
  /**
148
- * Unique job ID, generated by the database
149
+ * Auto-generated unique job ID.
149
150
  */
150
151
  id: number;
151
152
  /**
152
- * Current state of the job (e.g., available, completed)
153
+ * Current lifecycle state (e.g. available, running, completed).
153
154
  */
154
155
  state: JobState;
155
156
  /**
156
- * Current attempt number, incremented each time the job is worked
157
+ * How many times this job has been attempted so far.
157
158
  */
158
159
  attempt: number;
159
160
  /**
160
- * Maximum number of attempts before the job stops retrying
161
+ * Maximum attempts allowed before the job is discarded.
161
162
  */
162
163
  maxAttempts: number;
163
164
  /**
164
- * Last time the job was worked, null if never
165
+ * Timestamp of the most recent attempt, null if never attempted.
165
166
  */
166
167
  attemptedAt: Date | null;
167
168
  /**
168
- * When the job record was created
169
+ * Timestamp of when the job record was created.
169
170
  */
170
171
  createdAt: Date;
171
172
  /**
172
- * When the job was finalized (completed or errored for last time), null if not finalized
173
+ * Timestamp of when the job was finalized (completed or discarded), null if still active.
173
174
  */
174
175
  finalizedAt: Date | null;
175
176
  /**
176
- * When the job is scheduled to become available
177
+ * Timestamp of when the job becomes eligible for processing.
177
178
  */
178
179
  scheduledAt: Date;
179
180
  /**
180
- * Job priority (1 = highest, 4 = lowest)
181
+ * Processing priority. Lower numbers run first (1 = highest, 4 = lowest).
181
182
  */
182
183
  priority: number;
183
184
  /**
184
- * Job arguments, decoded from JSON
185
+ * Typed job arguments, decoded from the JSON stored in the database.
185
186
  */
186
187
  args: T;
187
188
  /**
188
- * Worker IDs that have worked this job, null if never
189
+ * List of client IDs that have attempted this job, in order. Null if never attempted.
189
190
  */
190
191
  attemptedBy: string[] | null;
191
192
  /**
192
- * Errors for each attempt, ordered earliest to latest, null if none
193
+ * Error details for each failed attempt, ordered earliest to latest. Null if no failures.
193
194
  */
194
195
  errors: AttemptError[] | null;
195
196
  /**
196
- * Job type identifier, set at insertion
197
+ * The job kind string used to route it to the correct worker (e.g. `"send_email"`).
197
198
  */
198
199
  kind: string;
199
200
  /**
200
- * Arbitrary metadata associated with the job
201
+ * Arbitrary key-value metadata attached to the job at insertion.
201
202
  */
202
203
  metadata: Record<string, unknown>;
203
204
  /**
204
- * Name of the queue where the job will be worked
205
+ * The queue this job belongs to.
205
206
  */
206
207
  queue: string;
207
208
  /**
208
- * List of tags for grouping and categorizing jobs
209
+ * Tags for grouping or filtering jobs.
209
210
  */
210
211
  tags: string[];
211
212
  /**
212
- * Unique key for job within its kind, used for unique insertions, null if not set
213
+ * Hashed uniqueness key. Null if the job was not inserted with uniqueness options.
213
214
  */
214
215
  uniqueKey: Buffer | null;
215
216
  /**
216
- * States required for uniqueness, null if not set
217
+ * The job states considered when enforcing uniqueness. Null if not set.
217
218
  */
218
219
  uniqueStates: JobState[] | null;
219
220
  }
package/dist/types.d.cts CHANGED
@@ -1,18 +1,54 @@
1
- export { A as AttemptError, I as InsertOpts, a as InsertResult, b as Job, J as JobArgs, c as JobState, U as UniqueOpts } from './insert-result-Bf0bAFvJ.cjs';
1
+ import { J as JobArgs, b as Job } from './insert-result-DWZkRtk9.cjs';
2
+ export { A as AttemptError, I as InsertOpts, a as InsertResult, c as JobState, U as UniqueOpts } from './insert-result-DWZkRtk9.cjs';
2
3
  import 'buffer';
3
4
 
4
5
  /**
5
- * Configuration options for the RiverQueue client.
6
+ * Configuration for a single queue.
7
+ */
8
+ interface QueueConfiguration {
9
+ /**
10
+ * Maximum number of jobs that can be processed simultaneously for this queue.
11
+ */
12
+ concurrency: number;
13
+ }
14
+
15
+ /**
16
+ * Configuration options for the RiverClient.
6
17
  */
7
18
  interface ClientConfiguration {
8
19
  /**
9
- * Default queue name for job insertion
20
+ * Queues to poll and their per-queue configuration (e.g. concurrency).
21
+ * Only used when `work()` is called. Keys are queue names.
10
22
  */
11
- defaultQueue?: string;
23
+ queues?: Record<string, QueueConfiguration>;
12
24
  /**
13
- * Maximum number of attempts for jobs
25
+ * Default maximum number of attempts for inserted jobs before they are discarded.
14
26
  */
15
27
  maxAttempts?: number;
28
+ /**
29
+ * How often (in milliseconds) the client polls the database for available jobs.
30
+ * Only applies when `work()` is called.
31
+ */
32
+ pollInterval?: number;
33
+ /**
34
+ * Unique identifier for this client instance, recorded in each job's `attemptedBy` list.
35
+ * Defaults to `hostname-pid` (e.g. `"web-server-12345"`).
36
+ */
37
+ clientId?: string;
38
+ }
39
+
40
+ /**
41
+ * Interface for job workers. Implement this for each job kind you want to process.
42
+ * Register instances with `client.addWorker(kind, worker)` before calling `client.work()`.
43
+ * @template T - The shape of the job's arguments.
44
+ */
45
+ interface Worker<T extends JobArgs = JobArgs> {
46
+ /**
47
+ * Called by the client when a job of the registered kind is claimed from the queue.
48
+ * Throw an error to mark the job as failed; return normally to mark it as completed.
49
+ * @param job - The claimed job including its args, metadata, and attempt info.
50
+ */
51
+ work(job: Job<T>): Promise<void>;
16
52
  }
17
53
 
18
- export type { ClientConfiguration };
54
+ export { type ClientConfiguration, Job, JobArgs, type QueueConfiguration, type Worker };
package/dist/types.d.ts CHANGED
@@ -1,18 +1,54 @@
1
- export { A as AttemptError, I as InsertOpts, a as InsertResult, b as Job, J as JobArgs, c as JobState, U as UniqueOpts } from './insert-result-Bf0bAFvJ.js';
1
+ import { J as JobArgs, b as Job } from './insert-result-DWZkRtk9.js';
2
+ export { A as AttemptError, I as InsertOpts, a as InsertResult, c as JobState, U as UniqueOpts } from './insert-result-DWZkRtk9.js';
2
3
  import 'buffer';
3
4
 
4
5
  /**
5
- * Configuration options for the RiverQueue client.
6
+ * Configuration for a single queue.
7
+ */
8
+ interface QueueConfiguration {
9
+ /**
10
+ * Maximum number of jobs that can be processed simultaneously for this queue.
11
+ */
12
+ concurrency: number;
13
+ }
14
+
15
+ /**
16
+ * Configuration options for the RiverClient.
6
17
  */
7
18
  interface ClientConfiguration {
8
19
  /**
9
- * Default queue name for job insertion
20
+ * Queues to poll and their per-queue configuration (e.g. concurrency).
21
+ * Only used when `work()` is called. Keys are queue names.
10
22
  */
11
- defaultQueue?: string;
23
+ queues?: Record<string, QueueConfiguration>;
12
24
  /**
13
- * Maximum number of attempts for jobs
25
+ * Default maximum number of attempts for inserted jobs before they are discarded.
14
26
  */
15
27
  maxAttempts?: number;
28
+ /**
29
+ * How often (in milliseconds) the client polls the database for available jobs.
30
+ * Only applies when `work()` is called.
31
+ */
32
+ pollInterval?: number;
33
+ /**
34
+ * Unique identifier for this client instance, recorded in each job's `attemptedBy` list.
35
+ * Defaults to `hostname-pid` (e.g. `"web-server-12345"`).
36
+ */
37
+ clientId?: string;
38
+ }
39
+
40
+ /**
41
+ * Interface for job workers. Implement this for each job kind you want to process.
42
+ * Register instances with `client.addWorker(kind, worker)` before calling `client.work()`.
43
+ * @template T - The shape of the job's arguments.
44
+ */
45
+ interface Worker<T extends JobArgs = JobArgs> {
46
+ /**
47
+ * Called by the client when a job of the registered kind is claimed from the queue.
48
+ * Throw an error to mark the job as failed; return normally to mark it as completed.
49
+ * @param job - The claimed job including its args, metadata, and attempt info.
50
+ */
51
+ work(job: Job<T>): Promise<void>;
16
52
  }
17
53
 
18
- export type { ClientConfiguration };
54
+ export { type ClientConfiguration, Job, JobArgs, type QueueConfiguration, type Worker };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odunlamizo/node-river",
3
- "version": "1.0.8",
3
+ "version": "1.1.1",
4
4
  "description": "Node.js library to support River integration.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",