@odunlamizo/node-river 1.1.0 → 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/README.md CHANGED
@@ -19,7 +19,7 @@ const driver = new PgDriver({ connectionString: process.env.DATABASE_URL! });
19
19
  const client = new RiverClient(driver, {
20
20
  queues: {
21
21
  default: { concurrency: 10 },
22
- emails: { concurrency: 50 },
22
+ emails: { concurrency: 50 },
23
23
  },
24
24
  maxAttempts: 3,
25
25
  });
@@ -37,7 +37,7 @@ const result = await client.insert(
37
37
  { kind: 'send_email', to: 'user@example.com' },
38
38
  { queue: 'emails' },
39
39
  );
40
- console.log(result.job); // inserted Job record
40
+ console.log(result.job); // inserted Job record
41
41
  console.log(result.skipped); // true if deduplicated
42
42
 
43
43
  // Multiple jobs in one transaction (all succeed or all roll back)
@@ -89,7 +89,7 @@ await client.insert(
89
89
  Implement the `Worker<T>` interface for each job kind, register it with `addWorker`, then call `work()`. Each queue is polled independently at the configured concurrency limit.
90
90
 
91
91
  ```ts
92
- import { Worker, Job } from '@odunlamizo/node-river';
92
+ import { Worker, Job } from '@odunlamizo/node-river/types';
93
93
 
94
94
  interface SendEmailArgs {
95
95
  kind: 'send_email';
@@ -120,12 +120,12 @@ process.on('SIGTERM', async () => {
120
120
 
121
121
  ## Configuration
122
122
 
123
- | Option | Type | Description |
124
- |---|---|---|
125
- | `queues` | `Record<string, { concurrency: number }>` | Queues to poll and their concurrency limits. Required for `work()`. |
126
- | `maxAttempts` | `number` | Default max attempts for inserted jobs. |
127
- | `pollInterval` | `number` | Milliseconds between polls. Defaults to `1000`. |
128
- | `clientId` | `string` | Unique ID for this client instance. Defaults to `hostname-pid`. |
123
+ | Option | Type | Description |
124
+ | -------------- | ----------------------------------------- | ------------------------------------------------------------------- |
125
+ | `queues` | `Record<string, { concurrency: number }>` | Queues to poll and their concurrency limits. Required for `work()`. |
126
+ | `maxAttempts` | `number` | Default max attempts for inserted jobs. |
127
+ | `pollInterval` | `number` | Milliseconds between polls. Defaults to `1000`. |
128
+ | `clientId` | `string` | Unique ID for this client instance. Defaults to `hostname-pid`. |
129
129
 
130
130
  ## License
131
131
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/constants.ts","../src/client.ts"],"names":["os","process","clearTimeout","setTimeout"],"mappings":"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","file":"index.cjs","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 } from './types';\nimport Worker from './types/worker';\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"]}
1
+ {"version":3,"sources":["../src/utils/constants.ts","../src/client.ts"],"names":["os","process","clearTimeout","setTimeout"],"mappings":"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","file":"index.cjs","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"]}
package/dist/index.d.cts CHANGED
@@ -1,22 +1,8 @@
1
1
  import { D as Driver } from './driver-BhanG0yg.cjs';
2
- import { ClientConfiguration } from './types.cjs';
3
- import { J as JobArgs, b as Job, I as InsertOpts, a as InsertResult } from './insert-result-DWZkRtk9.cjs';
2
+ import { ClientConfiguration, Worker } from './types.cjs';
3
+ import { J as JobArgs, I as InsertOpts, a as InsertResult } from './insert-result-DWZkRtk9.cjs';
4
4
  import 'buffer';
5
5
 
6
- /**
7
- * Interface for job workers. Implement this for each job kind you want to process.
8
- * Register instances with `client.addWorker(kind, worker)` before calling `client.work()`.
9
- * @template T - The shape of the job's arguments.
10
- */
11
- interface Worker<T extends JobArgs = JobArgs> {
12
- /**
13
- * Called by the client when a job of the registered kind is claimed from the queue.
14
- * Throw an error to mark the job as failed; return normally to mark it as completed.
15
- * @param job - The claimed job including its args, metadata, and attempt info.
16
- */
17
- work(job: Job<T>): Promise<void>;
18
- }
19
-
20
6
  /**
21
7
  * RiverClient is the main entry point for interacting with the River job queue.
22
8
  * It supports two usage modes:
package/dist/index.d.ts CHANGED
@@ -1,22 +1,8 @@
1
1
  import { D as Driver } from './driver-DQ6PVYVO.js';
2
- import { ClientConfiguration } from './types.js';
3
- import { J as JobArgs, b as Job, I as InsertOpts, a as InsertResult } from './insert-result-DWZkRtk9.js';
2
+ import { ClientConfiguration, Worker } from './types.js';
3
+ import { J as JobArgs, I as InsertOpts, a as InsertResult } from './insert-result-DWZkRtk9.js';
4
4
  import 'buffer';
5
5
 
6
- /**
7
- * Interface for job workers. Implement this for each job kind you want to process.
8
- * Register instances with `client.addWorker(kind, worker)` before calling `client.work()`.
9
- * @template T - The shape of the job's arguments.
10
- */
11
- interface Worker<T extends JobArgs = JobArgs> {
12
- /**
13
- * Called by the client when a job of the registered kind is claimed from the queue.
14
- * Throw an error to mark the job as failed; return normally to mark it as completed.
15
- * @param job - The claimed job including its args, metadata, and attempt info.
16
- */
17
- work(job: Job<T>): Promise<void>;
18
- }
19
-
20
6
  /**
21
7
  * RiverClient is the main entry point for interacting with the River job queue.
22
8
  * It supports two usage modes:
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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 } from './types';\nimport Worker from './types/worker';\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"]}
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"]}
package/dist/types.d.cts CHANGED
@@ -1,4 +1,5 @@
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-DWZkRtk9.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
  /**
@@ -36,4 +37,18 @@ interface ClientConfiguration {
36
37
  clientId?: string;
37
38
  }
38
39
 
39
- export type { ClientConfiguration, QueueConfiguration };
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>;
52
+ }
53
+
54
+ export { type ClientConfiguration, Job, JobArgs, type QueueConfiguration, type Worker };
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
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-DWZkRtk9.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
  /**
@@ -36,4 +37,18 @@ interface ClientConfiguration {
36
37
  clientId?: string;
37
38
  }
38
39
 
39
- export type { ClientConfiguration, QueueConfiguration };
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>;
52
+ }
53
+
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.1.0",
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",