@boringnode/queue 0.0.1-alpha.2 → 0.0.1-alpha.4

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/drivers/redis_adapter.ts"],"sourcesContent":["import { Redis, type RedisOptions } from 'ioredis'\nimport type { Adapter, AcquiredJob } from '#contracts/adapter'\nimport type { JobData } from '#types/main'\n\nconst redisKey = 'jobs'\ntype RedisConfig = Redis | RedisOptions\n\n/**\n * Lua script for atomic job acquisition.\n * 1. Check and process delayed jobs\n * 2. Pop from pending queue\n * 3. Add to active hash with worker info\n * 4. Return job data\n */\nconst ACQUIRE_JOB_SCRIPT = `\n local pending_key = KEYS[1]\n local active_key = KEYS[2]\n local delayed_key = KEYS[3]\n local worker_id = ARGV[1]\n local now = ARGV[2]\n\n -- First, process delayed jobs\n local ready_jobs = redis.call('ZRANGEBYSCORE', delayed_key, 0, now)\n if #ready_jobs > 0 then\n for i = 1, #ready_jobs do\n local job_data = ready_jobs[i]\n local job = cjson.decode(job_data)\n local priority = job.priority or 5\n local timestamp = tonumber(now)\n local score = priority * 10000000000000 + timestamp\n redis.call('ZADD', pending_key, score, job_data)\n redis.call('ZREM', delayed_key, job_data)\n end\n end\n\n -- Pop highest priority job (lowest score)\n local result = redis.call('ZPOPMIN', pending_key)\n if not result or #result == 0 then\n return nil\n end\n\n local job_data = result[1]\n local job = cjson.decode(job_data)\n\n -- Store in active hash: jobId -> {workerId, acquiredAt, data}\n local active_data = cjson.encode({\n workerId = worker_id,\n acquiredAt = tonumber(now),\n data = job\n })\n redis.call('HSET', active_key, job.id, active_data)\n\n -- Return job with acquiredAt\n job.acquiredAt = tonumber(now)\n return cjson.encode(job)\n`\n\n/**\n * Lua script for completing a job.\n * Removes the job from active hash.\n */\nconst COMPLETE_JOB_SCRIPT = `\n local active_key = KEYS[1]\n local job_id = ARGV[1]\n\n redis.call('HDEL', active_key, job_id)\n return 1\n`\n\n/**\n * Lua script for failing a job permanently.\n * Removes from active hash.\n */\nconst FAIL_JOB_SCRIPT = `\n local active_key = KEYS[1]\n local job_id = ARGV[1]\n\n redis.call('HDEL', active_key, job_id)\n return 1\n`\n\n/**\n * Lua script for retrying a job.\n * 1. Get job from active hash\n * 2. Remove from active hash\n * 3. Increment attempts\n * 4. Add back to pending (or delayed if retryAt is set)\n */\nconst RETRY_JOB_SCRIPT = `\n local active_key = KEYS[1]\n local pending_key = KEYS[2]\n local delayed_key = KEYS[3]\n local job_id = ARGV[1]\n local retry_at = tonumber(ARGV[2])\n local now = tonumber(ARGV[3])\n\n -- Get job from active hash\n local active_data = redis.call('HGET', active_key, job_id)\n if not active_data then\n return 0\n end\n\n local active = cjson.decode(active_data)\n local job = active.data\n\n -- Remove from active\n redis.call('HDEL', active_key, job_id)\n\n -- Increment attempts\n job.attempts = (job.attempts or 0) + 1\n\n local job_data = cjson.encode(job)\n\n -- Add back to pending or delayed\n if retry_at and retry_at > now then\n redis.call('ZADD', delayed_key, retry_at, job_data)\n else\n local priority = job.priority or 5\n local score = priority * 10000000000000 + now\n redis.call('ZADD', pending_key, score, job_data)\n end\n\n return 1\n`\n\n/**\n * Create a new Redis adapter factory.\n * Accepts either a Redis instance or Redis options.\n *\n * When passing options, the adapter will create and manage\n * the connection lifecycle (closing it on destroy).\n *\n * When passing a Redis instance, the caller is responsible for\n * managing the connection lifecycle.\n */\nexport function redis(config?: RedisConfig) {\n return () => {\n if (config instanceof Redis) {\n return new RedisAdapter(config, false)\n }\n\n const options: RedisOptions = {\n host: 'localhost',\n port: 6379,\n keyPrefix: 'boringnode::queue::',\n db: 0,\n ...config,\n }\n\n const connection = new Redis(options)\n return new RedisAdapter(connection, true)\n }\n}\n\nexport class RedisAdapter implements Adapter {\n readonly #connection: Redis\n readonly #ownsConnection: boolean\n #workerId: string = ''\n\n constructor(connection: Redis, ownsConnection: boolean = false) {\n this.#connection = connection\n this.#ownsConnection = ownsConnection\n }\n\n setWorkerId(workerId: string): void {\n this.#workerId = workerId\n }\n\n async destroy(): Promise<void> {\n if (this.#ownsConnection) {\n await this.#connection.quit()\n }\n }\n\n pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const now = Date.now()\n const pendingKey = `${redisKey}::${queue}`\n const activeKey = `${redisKey}::${queue}::active`\n const delayedKey = `${redisKey}::delayed::${queue}`\n\n const result = await this.#connection.eval(\n ACQUIRE_JOB_SCRIPT,\n 3,\n pendingKey,\n activeKey,\n delayedKey,\n this.#workerId,\n now.toString()\n )\n\n if (!result) {\n return null\n }\n\n return JSON.parse(result as string)\n }\n\n async popAndWait(queue: string, timeout: number): Promise<AcquiredJob | null> {\n // First try immediate pop\n const immediate = await this.popFrom(queue)\n if (immediate) {\n return immediate\n }\n\n // Wait for new job using BZPOPMIN on pending queue\n const pendingKey = `${redisKey}::${queue}`\n const activeKey = `${redisKey}::${queue}::active`\n const now = Date.now()\n\n // BZPOPMIN returns [key, member, score] or null\n const result = await this.#connection.bzpopmin(pendingKey, timeout / 1000)\n\n if (!result) {\n return null\n }\n\n const [, jobData] = result\n const job = JSON.parse(jobData)\n\n // Store in active hash\n const activeData = JSON.stringify({\n workerId: this.#workerId,\n acquiredAt: now,\n data: job,\n })\n await this.#connection.hset(activeKey, job.id, activeData)\n\n return {\n ...job,\n acquiredAt: now,\n }\n }\n\n async completeJob(jobId: string, queue: string): Promise<void> {\n const activeKey = `${redisKey}::${queue}::active`\n\n await this.#connection.eval(COMPLETE_JOB_SCRIPT, 1, activeKey, jobId)\n }\n\n async failJob(jobId: string, queue: string, _error?: Error): Promise<void> {\n const activeKey = `${redisKey}::${queue}::active`\n\n await this.#connection.eval(FAIL_JOB_SCRIPT, 1, activeKey, jobId)\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const now = Date.now()\n const activeKey = `${redisKey}::${queue}::active`\n const pendingKey = `${redisKey}::${queue}`\n const delayedKey = `${redisKey}::delayed::${queue}`\n\n await this.#connection.eval(\n RETRY_JOB_SCRIPT,\n 3,\n activeKey,\n pendingKey,\n delayedKey,\n jobId,\n retryAt ? retryAt.getTime().toString() : '0',\n now.toString()\n )\n }\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n async pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n const executeAt = Date.now() + delay\n const delayedKey = `${redisKey}::delayed::${queue}`\n\n await this.#connection.zadd(delayedKey, executeAt, JSON.stringify(jobData))\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<void> {\n const priority = jobData.priority ?? 5\n\n // Use priority as primary score, add timestamp for FIFO order within same priority\n // Date.now() precision is sufficient but perfect FIFO within the same millisecond is not guaranteed\n const timestamp = Date.now()\n const score = priority * 1e13 + timestamp\n\n await this.#connection.zadd(`${redisKey}::${queue}`, score, JSON.stringify(jobData))\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(queue: string): Promise<number> {\n return this.#connection.zcard(`${redisKey}::${queue}`)\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/drivers/redis_adapter.ts"],"sourcesContent":["import { Redis, type RedisOptions } from 'ioredis'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type { JobData } from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\nimport { calculateScore } from '../utils.js'\n\nconst redisKey = 'jobs'\ntype RedisConfig = Redis | RedisOptions\n\n/**\n * Lua script for atomic job acquisition.\n * 1. Check and process delayed jobs\n * 2. Pop from pending queue\n * 3. Add to active hash with worker info\n * 4. Return job data\n */\nconst ACQUIRE_JOB_SCRIPT = `\n local pending_key = KEYS[1]\n local active_key = KEYS[2]\n local delayed_key = KEYS[3]\n local worker_id = ARGV[1]\n local now = ARGV[2]\n\n -- First, process delayed jobs\n local ready_jobs = redis.call('ZRANGEBYSCORE', delayed_key, 0, now)\n if #ready_jobs > 0 then\n for i = 1, #ready_jobs do\n local job_data = ready_jobs[i]\n local job = cjson.decode(job_data)\n -- Score = priority * 1e13 + timestamp\n -- Lower score = higher priority, FIFO within same priority\n local priority = job.priority or 5\n local timestamp = tonumber(now)\n local score = priority * 10000000000000 + timestamp\n redis.call('ZADD', pending_key, score, job_data)\n redis.call('ZREM', delayed_key, job_data)\n end\n end\n\n -- Pop highest priority job (lowest score)\n local result = redis.call('ZPOPMIN', pending_key)\n if not result or #result == 0 then\n return nil\n end\n\n local job_data = result[1]\n local job = cjson.decode(job_data)\n\n -- Store in active hash: jobId -> {workerId, acquiredAt, data}\n local active_data = cjson.encode({\n workerId = worker_id,\n acquiredAt = tonumber(now),\n data = job\n })\n redis.call('HSET', active_key, job.id, active_data)\n\n -- Return job with acquiredAt\n job.acquiredAt = tonumber(now)\n return cjson.encode(job)\n`\n\n/**\n * Lua script for completing a job.\n * Removes the job from active hash.\n */\nconst COMPLETE_JOB_SCRIPT = `\n local active_key = KEYS[1]\n local job_id = ARGV[1]\n\n redis.call('HDEL', active_key, job_id)\n return 1\n`\n\n/**\n * Lua script for failing a job permanently.\n * Removes from active hash.\n */\nconst FAIL_JOB_SCRIPT = `\n local active_key = KEYS[1]\n local job_id = ARGV[1]\n\n redis.call('HDEL', active_key, job_id)\n return 1\n`\n\n/**\n * Lua script for retrying a job.\n * 1. Get job from active hash\n * 2. Remove from active hash\n * 3. Increment attempts\n * 4. Add back to pending (or delayed if retryAt is set)\n */\nconst RETRY_JOB_SCRIPT = `\n local active_key = KEYS[1]\n local pending_key = KEYS[2]\n local delayed_key = KEYS[3]\n local job_id = ARGV[1]\n local retry_at = tonumber(ARGV[2])\n local now = tonumber(ARGV[3])\n\n -- Get job from active hash\n local active_data = redis.call('HGET', active_key, job_id)\n if not active_data then\n return 0\n end\n\n local active = cjson.decode(active_data)\n local job = active.data\n\n -- Remove from active\n redis.call('HDEL', active_key, job_id)\n\n -- Increment attempts\n job.attempts = (job.attempts or 0) + 1\n\n local job_data = cjson.encode(job)\n\n -- Add back to pending or delayed\n if retry_at and retry_at > now then\n redis.call('ZADD', delayed_key, retry_at, job_data)\n else\n -- Score = priority * 1e13 + timestamp\n -- Lower score = higher priority, FIFO within same priority\n local priority = job.priority or 5\n local score = priority * 10000000000000 + now\n redis.call('ZADD', pending_key, score, job_data)\n end\n\n return 1\n`\n\n/**\n * Lua script for recovering stalled jobs.\n * Scans the active hash for jobs that have been active too long.\n * - Jobs within maxStalledCount: move back to pending with incremented stalledCount\n * - Jobs exceeding maxStalledCount: remove permanently (fail)\n * Returns the number of recovered jobs (not including failed ones).\n */\nconst RECOVER_STALLED_JOBS_SCRIPT = `\n local active_key = KEYS[1]\n local pending_key = KEYS[2]\n local now = tonumber(ARGV[1])\n local stalled_threshold = tonumber(ARGV[2])\n local max_stalled_count = tonumber(ARGV[3])\n\n local recovered = 0\n local stalled_cutoff = now - stalled_threshold\n\n -- Get all active jobs\n local active_jobs = redis.call('HGETALL', active_key)\n\n -- HGETALL returns [field1, value1, field2, value2, ...]\n for i = 1, #active_jobs, 2 do\n local job_id = active_jobs[i]\n local active_data = active_jobs[i + 1]\n local active = cjson.decode(active_data)\n\n -- Check if job is stalled\n if active.acquiredAt < stalled_cutoff then\n local job = active.data\n local current_stalled_count = job.stalledCount or 0\n\n -- Remove from active hash\n redis.call('HDEL', active_key, job_id)\n\n -- Check if job has exceeded max stalled count\n if current_stalled_count >= max_stalled_count then\n -- Job failed permanently, just remove (already done above)\n else\n -- Recover: increment stalledCount and put back in pending\n job.stalledCount = current_stalled_count + 1\n local job_data = cjson.encode(job)\n -- Score = priority * 1e13 + timestamp\n -- Lower score = higher priority, FIFO within same priority\n local priority = job.priority or 5\n local score = priority * 10000000000000 + now\n redis.call('ZADD', pending_key, score, job_data)\n recovered = recovered + 1\n end\n end\n end\n\n return recovered\n`\n\n/**\n * Create a new Redis adapter factory.\n * Accepts either a Redis instance or Redis options.\n *\n * When passing options, the adapter will create and manage\n * the connection lifecycle (closing it on destroy).\n *\n * When passing a Redis instance, the caller is responsible for\n * managing the connection lifecycle.\n */\nexport function redis(config?: RedisConfig) {\n return () => {\n if (config instanceof Redis) {\n return new RedisAdapter(config, false)\n }\n\n const options: RedisOptions = {\n host: 'localhost',\n port: 6379,\n keyPrefix: 'boringnode::queue::',\n db: 0,\n ...config,\n }\n\n const connection = new Redis(options)\n return new RedisAdapter(connection, true)\n }\n}\n\nexport class RedisAdapter implements Adapter {\n readonly #connection: Redis\n readonly #ownsConnection: boolean\n #workerId: string = ''\n\n constructor(connection: Redis, ownsConnection: boolean = false) {\n this.#connection = connection\n this.#ownsConnection = ownsConnection\n }\n\n setWorkerId(workerId: string): void {\n this.#workerId = workerId\n }\n\n async destroy(): Promise<void> {\n if (this.#ownsConnection) {\n await this.#connection.quit()\n }\n }\n\n pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const now = Date.now()\n const pendingKey = `${redisKey}::${queue}`\n const activeKey = `${redisKey}::${queue}::active`\n const delayedKey = `${redisKey}::delayed::${queue}`\n\n const result = await this.#connection.eval(\n ACQUIRE_JOB_SCRIPT,\n 3,\n pendingKey,\n activeKey,\n delayedKey,\n this.#workerId,\n now.toString()\n )\n\n if (!result) {\n return null\n }\n\n return JSON.parse(result as string)\n }\n\n async completeJob(jobId: string, queue: string): Promise<void> {\n const activeKey = `${redisKey}::${queue}::active`\n\n await this.#connection.eval(COMPLETE_JOB_SCRIPT, 1, activeKey, jobId)\n }\n\n async failJob(jobId: string, queue: string, _error?: Error): Promise<void> {\n const activeKey = `${redisKey}::${queue}::active`\n\n await this.#connection.eval(FAIL_JOB_SCRIPT, 1, activeKey, jobId)\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const now = Date.now()\n const activeKey = `${redisKey}::${queue}::active`\n const pendingKey = `${redisKey}::${queue}`\n const delayedKey = `${redisKey}::delayed::${queue}`\n\n await this.#connection.eval(\n RETRY_JOB_SCRIPT,\n 3,\n activeKey,\n pendingKey,\n delayedKey,\n jobId,\n retryAt ? retryAt.getTime().toString() : '0',\n now.toString()\n )\n }\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n async pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n const executeAt = Date.now() + delay\n const delayedKey = `${redisKey}::delayed::${queue}`\n\n await this.#connection.zadd(delayedKey, executeAt, JSON.stringify(jobData))\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<void> {\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const timestamp = Date.now()\n const score = calculateScore(priority, timestamp)\n\n await this.#connection.zadd(`${redisKey}::${queue}`, score, JSON.stringify(jobData))\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(queue: string): Promise<number> {\n return this.#connection.zcard(`${redisKey}::${queue}`)\n }\n\n async recoverStalledJobs(\n queue: string,\n stalledThreshold: number,\n maxStalledCount: number\n ): Promise<number> {\n const now = Date.now()\n const activeKey = `${redisKey}::${queue}::active`\n const pendingKey = `${redisKey}::${queue}`\n\n const recovered = await this.#connection.eval(\n RECOVER_STALLED_JOBS_SCRIPT,\n 2,\n activeKey,\n pendingKey,\n now.toString(),\n stalledThreshold.toString(),\n maxStalledCount.toString()\n )\n\n return recovered as number\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,8 @@
1
- import { A as Adapter, b as JobData, a as AcquiredJob } from '../../job-Bd_c2lFK.js';
1
+ import { A as Adapter, J as JobData, a as AcquiredJob } from '../../index-C3_tlebh.js';
2
2
 
3
+ /**
4
+ * Create a sync adapter factory.
5
+ */
3
6
  declare function sync(): () => SyncAdapter;
4
7
  /**
5
8
  * Sync adapter executes jobs immediately when pushed.
@@ -9,9 +12,9 @@ declare class SyncAdapter implements Adapter {
9
12
  #private;
10
13
  setWorkerId(_workerId: string): void;
11
14
  push(jobData: JobData): Promise<void>;
12
- pushOn(_queue: string, jobData: JobData): Promise<void>;
15
+ pushOn(queue: string, jobData: JobData): Promise<void>;
13
16
  pushLater(jobData: JobData, delay: number): Promise<void>;
14
- pushLaterOn(_queue: string, jobData: JobData, delay: number): Promise<void>;
17
+ pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
15
18
  size(): Promise<number>;
16
19
  sizeOf(_queue: string): Promise<number>;
17
20
  pop(): Promise<AcquiredJob | null>;
@@ -19,6 +22,7 @@ declare class SyncAdapter implements Adapter {
19
22
  completeJob(_jobId: string, _queue: string): Promise<void>;
20
23
  failJob(_jobId: string, _queue: string, _error?: Error): Promise<void>;
21
24
  retryJob(_jobId: string, _queue: string, _retryAt?: Date): Promise<void>;
25
+ recoverStalledJobs(_queue: string, _stalledThreshold: number, _maxStalledCount: number): Promise<number>;
22
26
  destroy(): Promise<void>;
23
27
  }
24
28
 
@@ -1,5 +1,12 @@
1
+ import {
2
+ Locator,
3
+ QueueManager
4
+ } from "../../chunk-CD45GT6E.js";
5
+ import {
6
+ DEFAULT_PRIORITY
7
+ } from "../../chunk-HMGNQSSG.js";
8
+
1
9
  // src/drivers/sync_adapter.ts
2
- import { Locator } from "#src/locator";
3
10
  function sync() {
4
11
  return () => new SyncAdapter();
5
12
  }
@@ -9,15 +16,15 @@ var SyncAdapter = class {
9
16
  push(jobData) {
10
17
  return this.pushOn("default", jobData);
11
18
  }
12
- pushOn(_queue, jobData) {
13
- return this.#execute(jobData.name, jobData.payload);
19
+ pushOn(queue, jobData) {
20
+ return this.#execute(jobData.name, jobData.payload, queue);
14
21
  }
15
22
  pushLater(jobData, delay) {
16
23
  return this.pushLaterOn("default", jobData, delay);
17
24
  }
18
- pushLaterOn(_queue, jobData, delay) {
25
+ pushLaterOn(queue, jobData, delay) {
19
26
  setTimeout(() => {
20
- void this.#execute(jobData.name, jobData.payload);
27
+ void this.#execute(jobData.name, jobData.payload, queue);
21
28
  }, delay);
22
29
  return Promise.resolve();
23
30
  }
@@ -42,15 +49,28 @@ var SyncAdapter = class {
42
49
  retryJob(_jobId, _queue, _retryAt) {
43
50
  return Promise.resolve();
44
51
  }
52
+ recoverStalledJobs(_queue, _stalledThreshold, _maxStalledCount) {
53
+ return Promise.resolve(0);
54
+ }
45
55
  destroy() {
46
56
  return Promise.resolve();
47
57
  }
48
- async #execute(jobName, payload) {
58
+ async #execute(jobName, payload, queue = "default") {
49
59
  const JobClass = Locator.get(jobName);
50
60
  if (!JobClass) {
51
61
  throw new Error(`Job class ${jobName} not found.`);
52
62
  }
53
- const jobInstance = new JobClass(payload);
63
+ const context = Object.freeze({
64
+ jobId: `sync-${Date.now()}`,
65
+ name: jobName,
66
+ attempt: 1,
67
+ queue,
68
+ priority: DEFAULT_PRIORITY,
69
+ acquiredAt: /* @__PURE__ */ new Date(),
70
+ stalledCount: 0
71
+ });
72
+ const jobFactory = QueueManager.getJobFactory();
73
+ const jobInstance = jobFactory ? await jobFactory(JobClass, payload, context) : new JobClass(payload, context);
54
74
  await jobInstance.execute();
55
75
  }
56
76
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/drivers/sync_adapter.ts"],"sourcesContent":["import { Locator } from '#src/locator'\nimport type { Adapter, AcquiredJob } from '#contracts/adapter'\nimport type { JobData } from '#types/main'\n\nexport function sync() {\n return () => new SyncAdapter()\n}\n\n/**\n * Sync adapter executes jobs immediately when pushed.\n * Pop/complete/fail/retry are not supported as jobs are executed synchronously.\n */\nexport class SyncAdapter implements Adapter {\n setWorkerId(_workerId: string): void {}\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n pushOn(_queue: string, jobData: JobData): Promise<void> {\n return this.#execute(jobData.name, jobData.payload)\n }\n\n pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n pushLaterOn(_queue: string, jobData: JobData, delay: number): Promise<void> {\n setTimeout(() => {\n void this.#execute(jobData.name, jobData.payload)\n }, delay)\n\n return Promise.resolve()\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(_queue: string): Promise<number> {\n return Promise.resolve(0)\n }\n\n pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n popFrom(_queue: string): Promise<AcquiredJob | null> {\n throw new Error('SyncAdapter does not support pop - jobs are executed immediately on push')\n }\n\n completeJob(_jobId: string, _queue: string): Promise<void> {\n return Promise.resolve()\n }\n\n failJob(_jobId: string, _queue: string, _error?: Error): Promise<void> {\n return Promise.resolve()\n }\n\n retryJob(_jobId: string, _queue: string, _retryAt?: Date): Promise<void> {\n return Promise.resolve()\n }\n\n destroy(): Promise<void> {\n return Promise.resolve()\n }\n\n async #execute(jobName: string, payload: any): Promise<any> {\n const JobClass = Locator.get(jobName)\n\n if (!JobClass) {\n throw new Error(`Job class ${jobName} not found.`)\n }\n\n const jobInstance = new JobClass(payload)\n await jobInstance.execute()\n }\n}\n"],"mappings":";AAAA,SAAS,eAAe;AAIjB,SAAS,OAAO;AACrB,SAAO,MAAM,IAAI,YAAY;AAC/B;AAMO,IAAM,cAAN,MAAqC;AAAA,EAC1C,YAAY,WAAyB;AAAA,EAAC;AAAA,EAEtC,KAAK,SAAiC;AACpC,WAAO,KAAK,OAAO,WAAW,OAAO;AAAA,EACvC;AAAA,EAEA,OAAO,QAAgB,SAAiC;AACtD,WAAO,KAAK,SAAS,QAAQ,MAAM,QAAQ,OAAO;AAAA,EACpD;AAAA,EAEA,UAAU,SAAkB,OAA8B;AACxD,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,YAAY,QAAgB,SAAkB,OAA8B;AAC1E,eAAW,MAAM;AACf,WAAK,KAAK,SAAS,QAAQ,MAAM,QAAQ,OAAO;AAAA,IAClD,GAAG,KAAK;AAER,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,OAAwB;AACtB,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,OAAO,QAAiC;AACtC,WAAO,QAAQ,QAAQ,CAAC;AAAA,EAC1B;AAAA,EAEA,MAAmC;AACjC,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,QAAQ,QAA6C;AACnD,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AAAA,EAEA,YAAY,QAAgB,QAA+B;AACzD,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,QAAQ,QAAgB,QAAgB,QAA+B;AACrE,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,SAAS,QAAgB,QAAgB,UAAgC;AACvE,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,UAAyB;AACvB,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,SAAS,SAAiB,SAA4B;AAC1D,UAAM,WAAW,QAAQ,IAAI,OAAO;AAEpC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,aAAa,OAAO,aAAa;AAAA,IACnD;AAEA,UAAM,cAAc,IAAI,SAAS,OAAO;AACxC,UAAM,YAAY,QAAQ;AAAA,EAC5B;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/drivers/sync_adapter.ts"],"sourcesContent":["import { Locator } from '../locator.js'\nimport { QueueManager } from '../queue_manager.js'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type { JobContext, JobData } from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\n\n/**\n * Create a sync adapter factory.\n */\nexport function sync() {\n return () => new SyncAdapter()\n}\n\n/**\n * Sync adapter executes jobs immediately when pushed.\n * Pop/complete/fail/retry are not supported as jobs are executed synchronously.\n */\nexport class SyncAdapter implements Adapter {\n setWorkerId(_workerId: string): void {}\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n pushOn(queue: string, jobData: JobData): Promise<void> {\n return this.#execute(jobData.name, jobData.payload, queue)\n }\n\n pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n setTimeout(() => {\n void this.#execute(jobData.name, jobData.payload, queue)\n }, delay)\n\n return Promise.resolve()\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(_queue: string): Promise<number> {\n return Promise.resolve(0)\n }\n\n pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n popFrom(_queue: string): Promise<AcquiredJob | null> {\n throw new Error('SyncAdapter does not support pop - jobs are executed immediately on push')\n }\n\n completeJob(_jobId: string, _queue: string): Promise<void> {\n return Promise.resolve()\n }\n\n failJob(_jobId: string, _queue: string, _error?: Error): Promise<void> {\n return Promise.resolve()\n }\n\n retryJob(_jobId: string, _queue: string, _retryAt?: Date): Promise<void> {\n return Promise.resolve()\n }\n\n recoverStalledJobs(\n _queue: string,\n _stalledThreshold: number,\n _maxStalledCount: number\n ): Promise<number> {\n // SyncAdapter has no stalled jobs - jobs are executed immediately\n return Promise.resolve(0)\n }\n\n destroy(): Promise<void> {\n return Promise.resolve()\n }\n\n async #execute(jobName: string, payload: any, queue: string = 'default'): Promise<any> {\n const JobClass = Locator.get(jobName)\n\n if (!JobClass) {\n throw new Error(`Job class ${jobName} not found.`)\n }\n\n const context: JobContext = Object.freeze({\n jobId: `sync-${Date.now()}`,\n name: jobName,\n attempt: 1,\n queue,\n priority: DEFAULT_PRIORITY,\n acquiredAt: new Date(),\n stalledCount: 0,\n })\n\n const jobFactory = QueueManager.getJobFactory()\n const jobInstance = jobFactory\n ? await jobFactory(JobClass, payload, context)\n : new JobClass(payload, context)\n\n await jobInstance.execute()\n }\n}\n"],"mappings":";;;;;;;;;AASO,SAAS,OAAO;AACrB,SAAO,MAAM,IAAI,YAAY;AAC/B;AAMO,IAAM,cAAN,MAAqC;AAAA,EAC1C,YAAY,WAAyB;AAAA,EAAC;AAAA,EAEtC,KAAK,SAAiC;AACpC,WAAO,KAAK,OAAO,WAAW,OAAO;AAAA,EACvC;AAAA,EAEA,OAAO,OAAe,SAAiC;AACrD,WAAO,KAAK,SAAS,QAAQ,MAAM,QAAQ,SAAS,KAAK;AAAA,EAC3D;AAAA,EAEA,UAAU,SAAkB,OAA8B;AACxD,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,YAAY,OAAe,SAAkB,OAA8B;AACzE,eAAW,MAAM;AACf,WAAK,KAAK,SAAS,QAAQ,MAAM,QAAQ,SAAS,KAAK;AAAA,IACzD,GAAG,KAAK;AAER,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,OAAwB;AACtB,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,OAAO,QAAiC;AACtC,WAAO,QAAQ,QAAQ,CAAC;AAAA,EAC1B;AAAA,EAEA,MAAmC;AACjC,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,QAAQ,QAA6C;AACnD,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AAAA,EAEA,YAAY,QAAgB,QAA+B;AACzD,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,QAAQ,QAAgB,QAAgB,QAA+B;AACrE,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,SAAS,QAAgB,QAAgB,UAAgC;AACvE,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,mBACE,QACA,mBACA,kBACiB;AAEjB,WAAO,QAAQ,QAAQ,CAAC;AAAA,EAC1B;AAAA,EAEA,UAAyB;AACvB,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,SAAS,SAAiB,SAAc,QAAgB,WAAyB;AACrF,UAAM,WAAW,QAAQ,IAAI,OAAO;AAEpC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,aAAa,OAAO,aAAa;AAAA,IACnD;AAEA,UAAM,UAAsB,OAAO,OAAO;AAAA,MACxC,OAAO,QAAQ,KAAK,IAAI,CAAC;AAAA,MACzB,MAAM;AAAA,MACN,SAAS;AAAA,MACT;AAAA,MACA,UAAU;AAAA,MACV,YAAY,oBAAI,KAAK;AAAA,MACrB,cAAc;AAAA,IAChB,CAAC;AAED,UAAM,aAAa,aAAa,cAAc;AAC9C,UAAM,cAAc,aAChB,MAAM,WAAW,UAAU,SAAS,OAAO,IAC3C,IAAI,SAAS,SAAS,OAAO;AAEjC,UAAM,YAAY,QAAQ;AAAA,EAC5B;AACF;","names":[]}
@@ -0,0 +1 @@
1
+ export { a as AcquiredJob, A as Adapter, n as AdapterFactory, j as BackoffConfig, B as BackoffStrategy, D as Duration, d as JobClass, i as JobContext, J as JobData, b as JobFactory, h as JobOptions, L as Logger, k as QueueConfig, Q as QueueManagerConfig, R as RetryConfig, m as WorkerConfig, W as WorkerCycle } from '../../index-C3_tlebh.js';
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +1 @@
1
- export { k as AdapterFactory, h as BackoffConfig, B as BackoffStrategy, D as Duration, g as JobClass, b as JobData, d as JobOptions, i as QueueConfig, Q as QueueManagerConfig, R as RetryConfig, j as WorkerConfig, W as WorkerCycle } from '../../job-Bd_c2lFK.js';
1
+ export { n as AdapterFactory, j as BackoffConfig, B as BackoffStrategy, D as Duration, d as JobClass, i as JobContext, J as JobData, b as JobFactory, h as JobOptions, L as Logger, k as QueueConfig, Q as QueueManagerConfig, R as RetryConfig, m as WorkerConfig, W as WorkerCycle } from '../../index-C3_tlebh.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@boringnode/queue",
3
3
  "description": "A simple and efficient queue system for Node.js applications",
4
- "version": "0.0.1-alpha.2",
4
+ "version": "0.0.1-alpha.4",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
7
7
  "files": [
@@ -11,27 +11,19 @@
11
11
  ".": "./build/index.js",
12
12
  "./drivers/*": "./build/src/drivers/*.js",
13
13
  "./contracts/*": "./build/src/contracts/*.js",
14
- "./types/*": "./build/src/types/*.js"
15
- },
16
- "imports": {
17
- "#src/*": "./src/*.ts",
18
- "#drivers/*": "./src/drivers/*.ts",
19
- "#lease_managers/*": "./src/lease_managers/*.ts",
20
- "#contracts/*": "./src/contracts/*.ts",
21
- "#strategies/*": "./src/strategies/*.ts",
22
- "#types/*": "./src/types/*.ts"
14
+ "./types": "./build/src/types/index.js"
23
15
  },
24
16
  "scripts": {
25
- "benchmark": "node benchmark/run.ts",
26
- "benchmark:quick": "node benchmark/run.ts --quick",
27
- "benchmark:full": "node benchmark/run.ts --full",
17
+ "benchmark": "node --import=@poppinss/ts-exec benchmark/run.ts",
18
+ "benchmark:quick": "node --import=@poppinss/ts-exec benchmark/run.ts --quick",
19
+ "benchmark:full": "node --import=@poppinss/ts-exec benchmark/run.ts --full",
28
20
  "build": "yarn clean && tsup-node",
29
21
  "clean": "del-cli build",
30
22
  "format": "prettier --write .",
31
23
  "lint": "eslint .",
32
24
  "prepublishOnly": "yarn build",
33
25
  "release": "yarn dlx release-it",
34
- "test": "c8 node --enable-source-maps bin/test.ts",
26
+ "test": "c8 node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts",
35
27
  "typecheck": "tsc --noEmit"
36
28
  },
37
29
  "dependencies": {
@@ -41,11 +33,12 @@
41
33
  "devDependencies": {
42
34
  "@adonisjs/eslint-config": "^2.1.2",
43
35
  "@adonisjs/prettier-config": "^1.4.5",
44
- "@adonisjs/tsconfig": "^1.4.1",
36
+ "@adonisjs/tsconfig": "^2.0.0-next.3",
45
37
  "@japa/assert": "^4.1.1",
46
38
  "@japa/expect-type": "^2.0.3",
47
39
  "@japa/file-system": "^2.3.2",
48
40
  "@japa/runner": "^4.4.0",
41
+ "@poppinss/ts-exec": "^1.4.1",
49
42
  "@types/better-sqlite3": "^7.6.13",
50
43
  "@types/node": "^24.3.1",
51
44
  "@types/pg": "^8",
@@ -63,6 +56,18 @@
63
56
  "tsup": "^8.5.1",
64
57
  "typescript": "^5.9.2"
65
58
  },
59
+ "peerDependencies": {
60
+ "ioredis": "^5.0.0",
61
+ "knex": "^3.0.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "ioredis": {
65
+ "optional": true
66
+ },
67
+ "knex": {
68
+ "optional": true
69
+ }
70
+ },
66
71
  "author": "Romain Lanz <romain.lanz@pm.me>",
67
72
  "license": "MIT",
68
73
  "keywords": [
@@ -1,149 +0,0 @@
1
- declare class BackoffStrategy$1 {
2
- #private;
3
- constructor(config: BackoffConfig);
4
- calculateDelay(attempt: number): number;
5
- getNextRetryAt(attempt: number): Date;
6
- getConfig(): Readonly<BackoffConfig>;
7
- }
8
- declare function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>): () => BackoffStrategy$1;
9
- declare function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>): () => BackoffStrategy$1;
10
- declare function fixedBackoff(delay?: Duration): () => BackoffStrategy$1;
11
- declare function customBackoff(config: BackoffConfig): () => BackoffStrategy$1;
12
-
13
- type Duration = number | string;
14
- interface JobData {
15
- id: string;
16
- name: string;
17
- payload: any;
18
- attempts: number;
19
- priority?: number;
20
- nextRetryAt?: Date;
21
- }
22
- interface JobOptions {
23
- queue?: string;
24
- adapter?: string | (() => Adapter);
25
- maxRetries?: number;
26
- priority?: number;
27
- retry?: RetryConfig;
28
- timeout?: Duration;
29
- failOnTimeout?: boolean;
30
- }
31
- type JobClass<T extends Job = Job> = (new (payload: any) => T) & {
32
- options?: JobOptions;
33
- };
34
- interface RetryConfig {
35
- maxRetries?: number;
36
- backoff?: () => BackoffStrategy$1;
37
- }
38
- type BackoffStrategy = 'exponential' | 'linear' | 'fixed';
39
- interface BackoffConfig {
40
- strategy: BackoffStrategy;
41
- baseDelay: Duration;
42
- maxDelay?: Duration;
43
- multiplier?: number;
44
- jitter?: boolean;
45
- }
46
- interface QueueConfig {
47
- adapter?: string;
48
- retry?: any;
49
- }
50
- interface WorkerConfig {
51
- concurrency?: number;
52
- pollingInterval?: Duration;
53
- leaseTimeout?: Duration;
54
- renewalInterval?: Duration;
55
- timeout?: Duration;
56
- }
57
- type WorkerCycle = {
58
- type: 'started';
59
- queue: string;
60
- job: any;
61
- } | {
62
- type: 'completed';
63
- queue: string;
64
- job: any;
65
- } | {
66
- type: 'idle';
67
- suggestedDelay: Duration;
68
- } | {
69
- type: 'error';
70
- error: Error;
71
- suggestedDelay: Duration;
72
- };
73
- type AdapterFactory<T extends Adapter = Adapter> = () => T;
74
- interface QueueManagerConfig {
75
- default: string;
76
- adapters: Record<string, AdapterFactory>;
77
- retry?: RetryConfig;
78
- queues?: Record<string, QueueConfig>;
79
- worker?: WorkerConfig;
80
- locations: string[];
81
- }
82
-
83
- interface AcquiredJob extends JobData {
84
- acquiredAt: number;
85
- }
86
- interface Adapter {
87
- /**
88
- * Set the worker ID for this adapter instance.
89
- * Required before calling pop methods when consuming jobs.
90
- */
91
- setWorkerId(workerId: string): void;
92
- /**
93
- * Pop the next available job from the default queue.
94
- * The driver handles locking internally.
95
- */
96
- pop(): Promise<AcquiredJob | null>;
97
- /**
98
- * Pop the next available job from a specific queue.
99
- * The driver handles locking internally.
100
- */
101
- popFrom(queue: string): Promise<AcquiredJob | null>;
102
- /**
103
- * Blocking pop that waits for a job to be available.
104
- * Supported by Redis adapter.
105
- */
106
- popAndWait?(queue: string, timeout: number): Promise<AcquiredJob | null>;
107
- /**
108
- * Mark a job as completed and remove it from active set.
109
- */
110
- completeJob(jobId: string, queue: string): Promise<void>;
111
- /**
112
- * Mark a job as failed permanently.
113
- */
114
- failJob(jobId: string, queue: string, error?: Error): Promise<void>;
115
- /**
116
- * Retry a job - move back to pending queue with incremented attempts.
117
- */
118
- retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void>;
119
- push(jobData: JobData): Promise<void>;
120
- pushOn(queue: string, jobData: JobData): Promise<void>;
121
- pushLater(jobData: JobData, delay: number): Promise<void>;
122
- pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
123
- size(): Promise<number>;
124
- sizeOf(queue: string): Promise<number>;
125
- destroy(): Promise<void>;
126
- }
127
-
128
- declare class JobDispatcher<T> {
129
- #private;
130
- constructor(name: string, payload: T);
131
- toQueue(queue: string): this;
132
- in(delay: Duration): this;
133
- priority(priority: number): this;
134
- with(adapter: string | (() => Adapter)): this;
135
- run(): Promise<`${string}-${string}-${string}-${string}-${string}`>;
136
- then(onFulfilled?: (value: string) => any, onRejected?: (reason: any) => any): Promise<any>;
137
- }
138
-
139
- declare abstract class Job<Payload = any> {
140
- #private;
141
- static options: JobOptions;
142
- get payload(): Payload;
143
- constructor(payload: Payload);
144
- static dispatch<T extends Job>(this: new (payload: any) => T, payload: T extends Job<infer P> ? P : never): JobDispatcher<T extends Job<infer P> ? P : never>;
145
- abstract execute(signal?: AbortSignal): Promise<void>;
146
- failed?(error: Error): Promise<void>;
147
- }
148
-
149
- export { type Adapter as A, type BackoffStrategy as B, type Duration as D, Job as J, type QueueManagerConfig as Q, type RetryConfig as R, type WorkerCycle as W, type AcquiredJob as a, type JobData as b, customBackoff as c, type JobOptions as d, exponentialBackoff as e, fixedBackoff as f, type JobClass as g, type BackoffConfig as h, type QueueConfig as i, type WorkerConfig as j, type AdapterFactory as k, linearBackoff as l };