queue-jobs-worker 1.0.2 → 1.0.3

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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage/redis.adapter.ts","../src/storage/postgres.adapter.ts","../src/storage/mysql.adapter.ts","../src/core/job.ts","../src/core/backoff.ts","../src/core/id.ts","../src/core/worker.ts","../src/core/queue.ts","../src/storage/in-memory.adapter.ts","../src/events/emitter.ts","../src/core/client.ts","../src/index.ts"],"names":["rowToJob","CREATE_JOBS_TABLE","CREATE_RATE_TABLE","randomUUID","job","RedisStorageAdapter","PostgreSQLStorageAdapter","MySQLStorageAdapter"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * RedisStorageAdapter (node-redis v4/v5)\n *\n * Key layout — prefix: \"qjw:\"\n * ─────────────────────────────────────────────────────────────────────────\n * qjw:job:{id} → Hash — all job fields (flat strings)\n * qjw:queue:{name}:waiting → Sorted Set score = -priority\n * (ZPOPMIN → highest priority first)\n * qjw:queue:{name}:delayed → Sorted Set score = runAt epoch-ms\n * qjw:queue:{name}:active → Set\n * qjw:queue:{name}:completed → Set\n * qjw:queue:{name}:dead → Set\n * qjw:rate:{name} → String sliding-window counter\n * qjw:rate:{name}:ts → String window-start epoch-ms\n * ─────────────────────────────────────────────────────────────────────────\n *\n * Atomic claim\n * ─────────────────────────────────────────────────────────────────────────\n * A Lua script executed by Redis guarantees that two concurrent workers\n * cannot claim the same job.\n * ─────────────────────────────────────────────────────────────────────────\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus, JobAttempt } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy import — redis is an optional peer dependency\n// ---------------------------------------------------------------------------\n\n// Type-only import — runtime import is deferred inside loadRedis().\nimport type { RedisClientType } from \"redis\";\nimport type * as RedisModule from \"redis\";\n\nasync function loadRedis(): Promise<typeof RedisModule> {\n try {\n return await import(\"redis\");\n } catch {\n throw new Error(\n 'RedisStorageAdapter requires the \"redis\" package (node-redis v4+).\\n' +\n \"Install it: npm install redis\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Key helpers\n// ---------------------------------------------------------------------------\n\nconst PREFIX = \"qjw:\";\n\nconst k = {\n job: (id: string) => `${PREFIX}job:${id}`,\n waiting: (q: string) => `${PREFIX}queue:${q}:waiting`,\n delayed: (q: string) => `${PREFIX}queue:${q}:delayed`,\n active: (q: string) => `${PREFIX}queue:${q}:active`,\n completed: (q: string) => `${PREFIX}queue:${q}:completed`,\n dead: (q: string) => `${PREFIX}queue:${q}:dead`,\n rateCount: (q: string) => `${PREFIX}rate:${q}`,\n rateTs: (q: string) => `${PREFIX}rate:${q}:ts`,\n};\n\n// ---------------------------------------------------------------------------\n// Lua — atomic claim\n//\n// KEYS[1] waiting sorted set\n// KEYS[2] delayed sorted set\n// KEYS[3] active set\n// ARGV[1] lock ID (worker id)\n// ARGV[2] lockExpiresAt ISO string\n// ARGV[3] now epoch-ms (string)\n// ARGV[4] PREFIX (passed as arg to keep the script host-agnostic)\n//\n// Returns claimed job ID or empty string.\n// ---------------------------------------------------------------------------\n\nconst CLAIM_LUA = `\nlocal prefix = ARGV[4]\nlocal now_ms = tonumber(ARGV[3])\n\n-- Promote due delayed jobs into the waiting set.\nlocal due = redis.call('ZRANGEBYSCORE', KEYS[2], '-inf', now_ms)\nfor _, jid in ipairs(due) do\n local pri_raw = redis.call('HGET', prefix .. 'job:' .. jid, 'priority')\n local pri = tonumber(pri_raw) or 0\n redis.call('ZADD', KEYS[1], -pri, jid)\n redis.call('ZREM', KEYS[2], jid)\n redis.call('HSET', prefix .. 'job:' .. jid, 'status', 'waiting')\nend\n\n-- Pop the top-priority job.\nlocal items = redis.call('ZPOPMIN', KEYS[1], 1)\nif #items == 0 then return '' end\nlocal job_id = items[1]\n\n-- Lock it.\nredis.call('SADD', KEYS[3], job_id)\nredis.call('HSET', prefix .. 'job:' .. job_id,\n 'status', 'active',\n 'lockId', ARGV[1],\n 'lockExpiresAt', ARGV[2],\n 'updatedAt', ARGV[2]\n)\nreturn job_id\n`;\n\n// ---------------------------------------------------------------------------\n// Lua — atomic stalled-job recovery (per job, compare-and-swap)\n//\n// The non-atomic read→decide→write pattern in the old recoverStalledJobs()\n// had a TOCTOU race: a worker could complete or renew its lock between the\n// read pipeline and the write pipeline, causing recovery to overwrite a valid\n// active job back to \"waiting\".\n//\n// This script closes that window by performing the validity re-check and the\n// state update as a single indivisible Redis command. The caller passes the\n// lockExpiresAt and lockId it observed; the script re-reads those fields and\n// aborts if they differ (compare-and-swap). This is the same pattern used\n// by BullMQ and Redlock for stale-lock detection.\n//\n// KEYS[1] job hash key e.g. \"qjw:job:<id>\"\n// KEYS[2] active set e.g. \"qjw:queue:<name>:active\"\n// KEYS[3] waiting sorted set e.g. \"qjw:queue:<name>:waiting\"\n// ARGV[1] job ID — member value for SREM / ZADD\n// ARGV[2] expected lockExpiresAt — caller's observed value\n// ARGV[3] expected lockId — caller's observed value\n// ARGV[4] recovery timestamp (ISO) — written to updatedAt\n// ARGV[5] priority score (string) — ZADD score (-priority → ZPOPMIN highest first)\n//\n// Returns 1 when the job was recovered, 0 when the read was stale (skipped).\n// ---------------------------------------------------------------------------\n\nconst RECOVER_STALLED_LUA = `\nlocal job_key = KEYS[1]\nlocal active_set = KEYS[2]\nlocal waiting_zset = KEYS[3]\n\nlocal job_id = ARGV[1]\nlocal expected_exp = ARGV[2]\nlocal expected_lock = ARGV[3]\nlocal recovery_ts = ARGV[4]\nlocal priority_score = tonumber(ARGV[5])\n\n-- Re-read the three guard fields in one atomic HMGET.\nlocal fields = redis.call('HMGET', job_key, 'lockExpiresAt', 'lockId', 'status')\nlocal current_exp = fields[1] or ''\nlocal current_lock = fields[2] or ''\nlocal current_status = fields[3] or ''\n\n-- Guard 1: job must still be active.\n-- If the worker already completed, failed, or moved to DLQ, skip.\nif current_status ~= 'active' then return 0 end\n\n-- Guard 2: lockId must not have changed.\n-- A different worker may have claimed the job after the original lock expired\n-- and before this script runs.\nif current_lock ~= expected_lock then return 0 end\n\n-- Guard 3: lockExpiresAt must be exactly what the caller observed.\n-- If the worker renewed its lock the timestamp will be later than observed;\n-- the CAS mismatch catches that without needing ISO→epoch conversion in Lua.\nif current_exp ~= expected_exp then return 0 end\n\n-- All guards passed — recover atomically.\nredis.call('HSET', job_key,\n 'status', 'waiting',\n 'lockId', '',\n 'lockExpiresAt', '',\n 'updatedAt', recovery_ts\n)\nredis.call('SREM', active_set, job_id)\nredis.call('ZADD', waiting_zset, priority_score, job_id)\nreturn 1\n`;\n\n// ---------------------------------------------------------------------------\n// Serialisation helpers\n// ---------------------------------------------------------------------------\n\ntype FlatHash = Record<string, string>;\n\nfunction jobToHash(job: JobData<unknown>): FlatHash {\n return {\n id: job.id,\n queue: job.queue,\n type: job.type,\n payload: JSON.stringify(job.payload),\n status: job.status,\n attemptsMade: String(job.attemptsMade),\n maxAttempts: String(job.maxAttempts),\n retryDelay: String(job.retryDelay),\n backoff: job.backoff,\n timeout: String(job.timeout),\n priority: String(job.priority),\n runAt: job.runAt,\n ...(job.cron !== undefined ? { cron: job.cron } : {}),\n attempts: JSON.stringify(job.attempts),\n lockId: job.lockId ?? \"\",\n lockExpiresAt: job.lockExpiresAt ?? \"\",\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n completedAt: job.completedAt ?? \"\",\n failedAt: job.failedAt ?? \"\",\n };\n}\n\nfunction hashToJob<TPayload>(h: FlatHash): JobData<TPayload> {\n const job: JobData<TPayload> = {\n id: h[\"id\"] ?? \"\",\n queue: h[\"queue\"] ?? \"\",\n type: h[\"type\"] ?? \"\",\n payload: JSON.parse(h[\"payload\"] ?? \"null\") as TPayload,\n status: (h[\"status\"] ?? \"waiting\") as JobStatus,\n attemptsMade: Number(h[\"attemptsMade\"] ?? 0),\n maxAttempts: Number(h[\"maxAttempts\"] ?? 1),\n retryDelay: Number(h[\"retryDelay\"] ?? 1000),\n backoff: (h[\"backoff\"] ?? \"exponential\") as JobData<TPayload>[\"backoff\"],\n timeout: Number(h[\"timeout\"] ?? 30000),\n priority: Number(h[\"priority\"] ?? 0),\n runAt: h[\"runAt\"] ?? new Date().toISOString(),\n attempts: JSON.parse(h[\"attempts\"] ?? \"[]\") as JobAttempt[],\n lockId: h[\"lockId\"] || null,\n lockExpiresAt: h[\"lockExpiresAt\"] || null,\n createdAt: h[\"createdAt\"] ?? new Date().toISOString(),\n updatedAt: h[\"updatedAt\"] ?? new Date().toISOString(),\n completedAt: h[\"completedAt\"] || null,\n failedAt: h[\"failedAt\"] || null,\n };\n if (h[\"cron\"]) job.cron = h[\"cron\"];\n return job;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class RedisStorageAdapter implements StorageAdapter {\n private client!: RedisClientType;\n private readonly url: string;\n\n constructor(connectionString: string) {\n this.url = connectionString;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const { createClient } = await loadRedis();\n\n this.client = createClient({ url: this.url }) as RedisClientType;\n\n // Surface connection errors as exceptions (node-redis v4 hides them otherwise).\n this.client.on(\"error\", () => {\n // Handled by the connect() rejection below on first connect.\n // Subsequent errors are emitted on the client; callers should listen if needed.\n });\n\n await this.client.connect();\n\n // Verify the connection is healthy.\n const pong = await this.client.ping();\n if (pong !== \"PONG\") {\n throw new Error(\n \"RedisStorageAdapter: PING returned unexpected response. Connection may be unhealthy.\",\n );\n }\n }\n\n async close(): Promise<void> {\n if (this.client) {\n await this.client.quit();\n }\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n const now = new Date().toISOString();\n const runAtMs = new Date(input.runAt).getTime();\n const isDelayed = runAtMs > Date.now();\n\n const job: JobData<TPayload> = {\n id: input.id,\n queue: input.queue,\n type: input.type,\n payload: input.payload,\n status: isDelayed ? \"delayed\" : \"waiting\",\n attemptsMade: 0,\n maxAttempts: input.maxAttempts,\n retryDelay: input.retryDelay,\n backoff: input.backoff,\n timeout: input.timeout,\n priority: input.priority,\n runAt: input.runAt,\n ...(input.cron !== undefined ? { cron: input.cron } : {}),\n attempts: [],\n lockId: null,\n lockExpiresAt: null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n failedAt: null,\n };\n\n const key = k.job(input.id);\n\n // Idempotent — return existing job if already present.\n const exists = await this.client.exists(key);\n if (exists) {\n const hash = await this.client.hGetAll(key);\n return hashToJob<TPayload>(hash);\n }\n\n // Persist in a pipeline.\n const multi = this.client.multi();\n multi.hSet(key, jobToHash(job as JobData<unknown>));\n\n if (isDelayed) {\n multi.zAdd(k.delayed(input.queue), { score: runAtMs, value: input.id });\n } else {\n // Negative priority score → ZPOPMIN yields highest priority first.\n multi.zAdd(k.waiting(input.queue), { score: -input.priority, value: input.id });\n }\n\n await multi.exec();\n return job;\n }\n\n // -------------------------------------------------------------------------\n // Claim (atomic Lua)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const nowMs = new Date(now).getTime();\n const lockExpiresAt = new Date(nowMs + lockDuration).toISOString();\n\n // node-redis v4 eval signature:\n // client.eval(script, { keys, arguments })\n const jobId = (await this.client.eval(CLAIM_LUA, {\n keys: [k.waiting(queue), k.delayed(queue), k.active(queue)],\n arguments: [lockId, lockExpiresAt, String(nowMs), PREFIX],\n })) as string;\n\n if (!jobId) return null;\n\n const hash = await this.client.hGetAll(k.job(jobId));\n if (!hash || Object.keys(hash).length === 0) return null;\n\n return hashToJob<TPayload>(hash);\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string): Promise<void> {\n const now = new Date().toISOString();\n const hash = await this.client.hGetAll(k.job(jobId));\n if (!hash) return;\n\n const queue = hash[\"queue\"] ?? \"\";\n const multi = this.client.multi();\n multi.hSet(k.job(jobId), {\n status: \"completed\",\n lockId: \"\",\n lockExpiresAt: \"\",\n completedAt: now,\n updatedAt: now,\n });\n multi.sRem(k.active(queue), jobId);\n multi.sAdd(k.completed(queue), jobId);\n await multi.exec();\n }\n\n // -------------------------------------------------------------------------\n // Requeue\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const now = new Date().toISOString();\n const hash = await this.client.hGetAll(k.job(input.jobId));\n if (!hash) return;\n\n const queue = hash[\"queue\"] ?? \"\";\n const attempts: JobAttempt[] = JSON.parse(hash[\"attempts\"] ?? \"[]\") as JobAttempt[];\n\n attempts.push({\n attempt: input.attemptNumber,\n startedAt: hash[\"updatedAt\"] ?? now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n const multi = this.client.multi();\n multi.hSet(k.job(input.jobId), {\n status: \"waiting\",\n attemptsMade: String(input.attemptNumber),\n attempts: JSON.stringify(attempts),\n runAt: input.runAt,\n lockId: \"\",\n lockExpiresAt: \"\",\n updatedAt: now,\n });\n multi.sRem(k.active(queue), input.jobId);\n // Put back in delayed set so the claim script can promote it when due.\n multi.zAdd(k.delayed(queue), {\n score: new Date(input.runAt).getTime(),\n value: input.jobId,\n });\n await multi.exec();\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const now = new Date().toISOString();\n const hash = await this.client.hGetAll(k.job(input.jobId));\n if (!hash) return;\n\n const queue = hash[\"queue\"] ?? \"\";\n const attempts: JobAttempt[] = JSON.parse(hash[\"attempts\"] ?? \"[]\") as JobAttempt[];\n\n attempts.push({\n attempt: input.attemptNumber,\n startedAt: hash[\"updatedAt\"] ?? now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n const multi = this.client.multi();\n multi.hSet(k.job(input.jobId), {\n status: \"dead\",\n attemptsMade: String(input.attemptNumber),\n attempts: JSON.stringify(attempts),\n lockId: \"\",\n lockExpiresAt: \"\",\n failedAt: now,\n updatedAt: now,\n });\n multi.sRem(k.active(queue), input.jobId);\n multi.sAdd(k.dead(queue), input.jobId);\n await multi.exec();\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string): Promise<void> {\n const now = new Date().toISOString();\n // Set lockExpiresAt to now (already-expired) rather than clearing it to an\n // empty string. recoverStalledJobs() skips entries where lockExpiresAt is\n // falsy, so an empty string would leave the job permanently stuck.\n await this.client.hSet(k.job(jobId), {\n lockId: \"\",\n lockExpiresAt: now,\n updatedAt: now,\n });\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs (atomic compare-and-swap per job via Lua)\n //\n // Previous implementation: two-phase read pipeline → write pipeline.\n // Race condition: a worker could complete or renew its lock between the two\n // phases, causing recovery to overwrite a legitimately-active job.\n //\n // Fix (issue #6): for each candidate job the RECOVER_STALLED_LUA script\n // re-reads lockExpiresAt, lockId, and status atomically and only applies\n // the recovery if all three still match what was observed in the read phase\n // (compare-and-swap). If the worker renewed or completed the job in the\n // window between the read and the Lua call, the CAS mismatch causes the\n // script to return 0 and the job is left untouched.\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const nowMs = new Date(now).getTime();\n const activeIds = await this.client.sMembers(k.active(queue));\n if (activeIds.length === 0) return [];\n\n // Phase 1 — read candidate fields.\n // We fetch lockExpiresAt, lockId, and priority for all active jobs in one\n // pipeline. These values become the \"expected\" snapshot passed to the Lua\n // CAS script. If any of these values change before the Lua executes, the\n // script will detect the mismatch and skip that job.\n const fetchPipeline = this.client.multi();\n for (const jobId of activeIds) {\n fetchPipeline.hmGet(k.job(jobId), [\"lockExpiresAt\", \"lockId\", \"priority\"]);\n }\n const fetchResults = await fetchPipeline.exec();\n\n // Phase 2 — per-job atomic CAS via Lua.\n // Each eval call is an independent atomic unit in Redis; no pipeline is\n // needed here because the script itself guarantees atomicity per job.\n const recovered: string[] = [];\n const evalPromises: Promise<unknown>[] = [];\n\n for (let i = 0; i < activeIds.length; i++) {\n const jobId = activeIds[i] as string;\n const fields = fetchResults[i] as unknown as\n [string | null, string | null, string | null] | null;\n if (!fields) continue;\n\n const [lockExpiresAt, lockId, priorityStr] = fields;\n\n // Pre-filter: skip jobs whose lock has not yet expired according to the\n // snapshot. This avoids unnecessary Lua round-trips for healthy jobs.\n if (!lockExpiresAt) continue;\n if (new Date(lockExpiresAt).getTime() > nowMs) continue;\n\n const priority = Number(priorityStr ?? \"0\");\n const priorityScore = String(-priority); // negative → ZPOPMIN yields highest first\n\n // The Lua script performs a CAS: it re-reads lockExpiresAt, lockId, and\n // status atomically, aborts if anything has changed, and only then\n // applies the recovery. Returns 1 on recovery, 0 on stale skip.\n const p = this.client\n .eval(RECOVER_STALLED_LUA, {\n keys: [k.job(jobId), k.active(queue), k.waiting(queue)],\n arguments: [jobId, lockExpiresAt, lockId ?? \"\", now, priorityScore],\n })\n .then((result) => {\n if (result === 1) recovered.push(jobId);\n });\n\n evalPromises.push(p);\n }\n\n // Wait for all CAS scripts to finish before returning.\n await Promise.all(evalPromises);\n\n return recovered;\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n const hash = await this.client.hGetAll(k.job(jobId));\n if (!hash || Object.keys(hash).length === 0) return null;\n return hashToJob<TPayload>(hash);\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n\n let ids: string[] = [];\n\n if (queue && status) {\n switch (status) {\n case \"waiting\":\n ids = await this.client.zRange(k.waiting(queue), 0, -1);\n break;\n case \"delayed\":\n ids = await this.client.zRange(k.delayed(queue), 0, -1);\n break;\n case \"active\":\n ids = await this.client.sMembers(k.active(queue));\n break;\n case \"completed\":\n ids = await this.client.sMembers(k.completed(queue));\n break;\n case \"dead\":\n ids = await this.client.sMembers(k.dead(queue));\n break;\n default:\n ids = [];\n }\n } else {\n // Scan all job keys — suitable for dev/small datasets only.\n for await (const key of this.client.scanIterator({\n MATCH: `${PREFIX}job:*`,\n COUNT: 500,\n })) {\n const keyStr: string = Array.isArray(key) ? String(key[0]) : String(key);\n ids.push(keyStr.replace(`${PREFIX}job:`, \"\"));\n }\n }\n\n const results: JobData<TPayload>[] = [];\n const page = ids.slice(offset, offset + limit);\n\n for (const id of page) {\n const job = await this.getJob<TPayload>(id);\n if (!job) continue;\n if (queue && job.queue !== queue) continue;\n if (status && job.status !== status) continue;\n results.push(job);\n }\n\n return results;\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const [waiting, delayed, active, completed, dead] = await Promise.all([\n this.client.zCard(k.waiting(queue)),\n this.client.zCard(k.delayed(queue)),\n this.client.sCard(k.active(queue)),\n this.client.sCard(k.completed(queue)),\n this.client.sCard(k.dead(queue)),\n ]);\n\n return { waiting, delayed, active, completed, dead };\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n const ck = k.rateCount(queue);\n const tk = k.rateTs(queue);\n\n const windowStart = await this.client.get(tk);\n const ttlSec = Math.ceil(windowMs / 1000);\n\n if (!windowStart || nowMs - Number(windowStart) >= windowMs) {\n // Fresh window.\n const multi = this.client.multi();\n multi.set(ck, \"1\");\n multi.expire(ck, ttlSec);\n multi.set(tk, String(nowMs));\n multi.expire(tk, ttlSec);\n await multi.exec();\n return true;\n }\n\n const count = await this.client.incr(ck);\n if (count > max) {\n await this.client.decr(ck);\n return false;\n }\n\n return true;\n }\n}\n","/**\n * PostgreSQLStorageAdapter (pg / node-postgres)\n *\n * Table prefix: \"qjw_\"\n *\n * Tables created on init (if they do not exist):\n * qjw_jobs — persists every job and its full state\n * qjw_rate_limits — sliding-window rate-limit counters\n *\n * Atomicity\n * ─────────────────────────────────────────────────────────────────────────\n * Claim uses SELECT … FOR UPDATE SKIP LOCKED inside a transaction so\n * concurrent workers never receive the same row.\n * ─────────────────────────────────────────────────────────────────────────\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy import — pg is an optional peer dependency\n// ---------------------------------------------------------------------------\n\n// These type-only imports are used solely for type annotations.\n// The actual runtime import is deferred inside loadPg() to keep pg optional.\nimport type { Pool as PgPool, PoolClient as PgPoolClient } from \"pg\";\nimport type * as PgModule from \"pg\";\n\nasync function loadPg(): Promise<typeof PgModule> {\n try {\n return await import(\"pg\");\n } catch {\n throw new Error(\n 'PostgreSQLStorageAdapter requires the \"pg\" package (node-postgres).\\n' +\n \"Install it: npm install pg\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// DDL\n// ---------------------------------------------------------------------------\n\nconst CREATE_JOBS_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_jobs (\n id TEXT NOT NULL PRIMARY KEY,\n queue TEXT NOT NULL,\n type TEXT NOT NULL,\n payload JSONB NOT NULL DEFAULT '{}',\n status TEXT NOT NULL DEFAULT 'waiting',\n attempts_made INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n retry_delay INTEGER NOT NULL DEFAULT 1000,\n backoff TEXT NOT NULL DEFAULT 'exponential',\n timeout INTEGER NOT NULL DEFAULT 30000,\n priority INTEGER NOT NULL DEFAULT 0,\n run_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n cron TEXT,\n attempts JSONB NOT NULL DEFAULT '[]',\n lock_id TEXT,\n lock_expires_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n completed_at TIMESTAMPTZ,\n failed_at TIMESTAMPTZ\n);\nCREATE INDEX IF NOT EXISTS qjw_jobs_claim_idx\n ON qjw_jobs (queue, status, run_at, priority DESC)\n WHERE status IN ('waiting', 'delayed');\n`;\n\nconst CREATE_RATE_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_rate_limits (\n queue TEXT NOT NULL PRIMARY KEY,\n count INTEGER NOT NULL DEFAULT 0,\n window_start BIGINT NOT NULL DEFAULT 0\n);\n`;\n\n// ---------------------------------------------------------------------------\n// Row → JobData\n// ---------------------------------------------------------------------------\n\ninterface JobRow {\n id: string;\n queue: string;\n type: string;\n payload: unknown;\n status: string;\n attempts_made: number;\n max_attempts: number;\n retry_delay: number;\n backoff: string;\n timeout: number;\n priority: number;\n run_at: Date;\n cron: string | null;\n attempts: unknown;\n lock_id: string | null;\n lock_expires_at: Date | null;\n created_at: Date;\n updated_at: Date;\n completed_at: Date | null;\n failed_at: Date | null;\n}\n\nfunction rowToJob<TPayload>(row: JobRow): JobData<TPayload> {\n const job: JobData<TPayload> = {\n id: row.id,\n queue: row.queue,\n type: row.type,\n payload: row.payload as TPayload,\n status: row.status as JobStatus,\n attemptsMade: row.attempts_made,\n maxAttempts: row.max_attempts,\n retryDelay: row.retry_delay,\n backoff: row.backoff as JobData<TPayload>[\"backoff\"],\n timeout: row.timeout,\n priority: row.priority,\n runAt: row.run_at.toISOString(),\n attempts: row.attempts as JobData<TPayload>[\"attempts\"],\n lockId: row.lock_id,\n lockExpiresAt: row.lock_expires_at ? row.lock_expires_at.toISOString() : null,\n createdAt: row.created_at.toISOString(),\n updatedAt: row.updated_at.toISOString(),\n completedAt: row.completed_at ? row.completed_at.toISOString() : null,\n failedAt: row.failed_at ? row.failed_at.toISOString() : null,\n };\n if (row.cron) job.cron = row.cron;\n return job;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class PostgreSQLStorageAdapter implements StorageAdapter {\n private pool!: PgPool;\n private readonly connectionString: string;\n\n constructor(connectionString: string) {\n this.connectionString = connectionString;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const { Pool } = await loadPg();\n\n this.pool = new Pool({ connectionString: this.connectionString });\n\n // Verify connectivity.\n const client: PgPoolClient = await this.pool.connect();\n try {\n await client.query(\"SELECT 1\");\n\n // Auto-create tables (idempotent).\n await client.query(CREATE_JOBS_TABLE);\n await client.query(CREATE_RATE_TABLE);\n } finally {\n client.release();\n }\n }\n\n async close(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n }\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n const res = await this.pool.query<JobRow>(\n `INSERT INTO qjw_jobs\n (id, queue, type, payload, status, attempts_made, max_attempts,\n retry_delay, backoff, timeout, priority, run_at, cron,\n attempts, lock_id, lock_expires_at, created_at, updated_at,\n completed_at, failed_at)\n VALUES ($1,$2,$3,$4,\n CASE WHEN $5::timestamptz > NOW() THEN 'delayed' ELSE 'waiting' END,\n 0,$6,$7,$8,$9,$10,$5,$11,\n '[]'::jsonb, NULL, NULL, NOW(), NOW(), NULL, NULL)\n ON CONFLICT (id) DO NOTHING\n RETURNING *`,\n [\n input.id,\n input.queue,\n input.type,\n JSON.stringify(input.payload),\n input.runAt,\n input.maxAttempts,\n input.retryDelay,\n input.backoff,\n input.timeout,\n input.priority,\n input.cron ?? null,\n ],\n );\n\n if (res.rows.length > 0) {\n return rowToJob<TPayload>(res.rows[0] as JobRow);\n }\n\n // Row already exists — return it.\n const existing = await this.pool.query<JobRow>(\"SELECT * FROM qjw_jobs WHERE id = $1\", [\n input.id,\n ]);\n return rowToJob<TPayload>(existing.rows[0] as JobRow);\n }\n\n // -------------------------------------------------------------------------\n // Claim (SELECT … FOR UPDATE SKIP LOCKED)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const lockExpiresAt = new Date(new Date(now).getTime() + lockDuration).toISOString();\n\n const client: PgPoolClient = await this.pool.connect();\n try {\n await client.query(\"BEGIN\");\n\n const res = await client.query<JobRow>(\n `SELECT * FROM qjw_jobs\n WHERE queue = $1\n AND status IN ('waiting', 'delayed')\n AND run_at <= $2::timestamptz\n AND (lock_expires_at IS NULL OR lock_expires_at <= $2::timestamptz)\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED`,\n [queue, now],\n );\n\n if (res.rows.length === 0) {\n await client.query(\"ROLLBACK\");\n return null;\n }\n\n const row = res.rows[0] as JobRow;\n\n await client.query(\n `UPDATE qjw_jobs\n SET status = 'active', lock_id = $1, lock_expires_at = $2, updated_at = NOW()\n WHERE id = $3`,\n [lockId, lockExpiresAt, row.id],\n );\n\n await client.query(\"COMMIT\");\n\n // Return the fresh row.\n const updated = await this.pool.query<JobRow>(\"SELECT * FROM qjw_jobs WHERE id = $1\", [\n row.id,\n ]);\n return rowToJob<TPayload>(updated.rows[0] as JobRow);\n } catch (err) {\n await client.query(\"ROLLBACK\");\n throw err;\n } finally {\n client.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string): Promise<void> {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'completed', lock_id = NULL, lock_expires_at = NULL,\n completed_at = NOW(), updated_at = NOW()\n WHERE id = $1`,\n [jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Requeue\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = {\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n };\n\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'waiting',\n attempts_made = attempts_made + 1,\n attempts = attempts || $1::jsonb,\n run_at = $2::timestamptz,\n lock_id = NULL,\n lock_expires_at = NULL,\n updated_at = NOW()\n WHERE id = $3`,\n [JSON.stringify([attempt]), input.runAt, input.jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = {\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n };\n\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'dead',\n attempts_made = attempts_made + 1,\n attempts = attempts || $1::jsonb,\n lock_id = NULL,\n lock_expires_at = NULL,\n failed_at = NOW(),\n updated_at = NOW()\n WHERE id = $2`,\n [JSON.stringify([attempt]), input.jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string): Promise<void> {\n // Set lock_expires_at to NOW() (already-expired) rather than NULL.\n // recoverStalledJobs() uses WHERE lock_expires_at <= $now — NULL comparisons\n // in SQL always evaluate to NULL (not true), so a NULL value would leave the\n // job permanently stuck in active status.\n await this.pool.query(\n `UPDATE qjw_jobs\n SET lock_id = NULL, lock_expires_at = NOW(), updated_at = NOW()\n WHERE id = $1`,\n [jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const res = await this.pool.query<{ id: string }>(\n `UPDATE qjw_jobs\n SET status = 'waiting', lock_id = NULL, lock_expires_at = NULL, updated_at = NOW()\n WHERE queue = $1\n AND status = 'active'\n AND lock_expires_at <= $2::timestamptz\n RETURNING id`,\n [queue, now],\n );\n return res.rows.map((r) => r.id);\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n const res = await this.pool.query<JobRow>(\"SELECT * FROM qjw_jobs WHERE id = $1\", [jobId]);\n if (res.rows.length === 0) return null;\n return rowToJob<TPayload>(res.rows[0] as JobRow);\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n let idx = 1;\n\n if (queue !== undefined) {\n conditions.push(`queue = $${idx++}`);\n params.push(queue);\n }\n if (status !== undefined) {\n conditions.push(`status = $${idx++}`);\n params.push(status);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n params.push(limit, offset);\n\n const res = await this.pool.query<JobRow>(\n `SELECT * FROM qjw_jobs ${where}\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT $${idx++} OFFSET $${idx}`,\n params,\n );\n\n return res.rows.map((r) => rowToJob<TPayload>(r));\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const res = await this.pool.query<{ status: string; count: string }>(\n \"SELECT status, COUNT(*)::int AS count FROM qjw_jobs WHERE queue = $1 GROUP BY status\",\n [queue],\n );\n\n const counts: Record<JobStatus, number> = {\n waiting: 0,\n active: 0,\n completed: 0,\n delayed: 0,\n dead: 0,\n };\n\n for (const row of res.rows) {\n if (row.status in counts) {\n counts[row.status as JobStatus] = Number(row.count);\n }\n }\n\n return counts;\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n\n const res = await this.pool.query<{ count: number; window_start: string }>(\n \"SELECT count, window_start FROM qjw_rate_limits WHERE queue = $1\",\n [queue],\n );\n\n if (res.rows.length === 0 || nowMs - Number(res.rows[0]?.window_start ?? 0) >= windowMs) {\n // New window — upsert with count = 1.\n await this.pool.query(\n `INSERT INTO qjw_rate_limits (queue, count, window_start)\n VALUES ($1, 1, $2)\n ON CONFLICT (queue) DO UPDATE\n SET count = 1, window_start = EXCLUDED.window_start`,\n [queue, nowMs],\n );\n return true;\n }\n\n const current = res.rows[0]!.count;\n if (current >= max) return false;\n\n await this.pool.query(\"UPDATE qjw_rate_limits SET count = count + 1 WHERE queue = $1\", [queue]);\n return true;\n }\n}\n","/**\n * MySQLStorageAdapter (mysql2/promise)\n *\n * Table prefix: \"qjw_\"\n *\n * Tables created on init (if they do not exist):\n * qjw_jobs — full job state\n * qjw_rate_limits — sliding-window rate-limit counters\n *\n * Atomicity\n * ─────────────────────────────────────────────────────────────────────────\n * Claim uses SELECT … FOR UPDATE SKIP LOCKED inside a transaction —\n * concurrent workers safely skip rows that are already being claimed.\n * ─────────────────────────────────────────────────────────────────────────\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy import — mysql2 is an optional peer dependency\n// ---------------------------------------------------------------------------\n\n// Type-only imports — runtime import is deferred inside loadMySQL2().\nimport type { Pool as MySQLPool, PoolConnection as MySQLConnection } from \"mysql2/promise\";\nimport type { RowDataPacket } from \"mysql2/promise\";\nimport type * as MySQL2Module from \"mysql2/promise\";\n\nasync function loadMySQL2(): Promise<typeof MySQL2Module> {\n try {\n return await import(\"mysql2/promise\");\n } catch {\n throw new Error(\n 'MySQLStorageAdapter requires the \"mysql2\" package.\\n' + \"Install it: npm install mysql2\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// DDL\n// ---------------------------------------------------------------------------\n\nconst CREATE_JOBS_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_jobs (\n id VARCHAR(36) NOT NULL PRIMARY KEY,\n queue VARCHAR(255) NOT NULL,\n type VARCHAR(255) NOT NULL,\n payload JSON NOT NULL,\n status VARCHAR(20) NOT NULL DEFAULT 'waiting',\n attempts_made INT NOT NULL DEFAULT 0,\n max_attempts INT NOT NULL DEFAULT 3,\n retry_delay INT NOT NULL DEFAULT 1000,\n backoff VARCHAR(20) NOT NULL DEFAULT 'exponential',\n timeout INT NOT NULL DEFAULT 30000,\n priority INT NOT NULL DEFAULT 0,\n run_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n cron VARCHAR(255),\n attempts JSON NOT NULL,\n lock_id VARCHAR(255),\n lock_expires_at DATETIME(3),\n created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),\n completed_at DATETIME(3),\n failed_at DATETIME(3),\n INDEX qjw_jobs_claim_idx (queue, status, run_at, priority)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n`;\n\nconst CREATE_RATE_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_rate_limits (\n queue VARCHAR(255) NOT NULL PRIMARY KEY,\n count INT NOT NULL DEFAULT 0,\n window_start BIGINT NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n`;\n\n// ---------------------------------------------------------------------------\n// Row → JobData\n// ---------------------------------------------------------------------------\n\ninterface JobRow extends RowDataPacket {\n id: string;\n queue: string;\n type: string;\n payload: unknown;\n status: string;\n attempts_made: number;\n max_attempts: number;\n retry_delay: number;\n backoff: string;\n timeout: number;\n priority: number;\n run_at: Date;\n cron: string | null;\n attempts: unknown;\n lock_id: string | null;\n lock_expires_at: Date | null;\n created_at: Date;\n updated_at: Date;\n completed_at: Date | null;\n failed_at: Date | null;\n}\n\nfunction rowToJob<TPayload>(row: JobRow): JobData<TPayload> {\n // mysql2 returns JSON columns as parsed objects when supportBigNumbers/parseJson is on.\n const payload =\n typeof row.payload === \"string\"\n ? (JSON.parse(row.payload) as TPayload)\n : (row.payload as TPayload);\n\n const attempts =\n typeof row.attempts === \"string\" ? JSON.parse(row.attempts) : (row.attempts ?? []);\n\n const job: JobData<TPayload> = {\n id: row.id,\n queue: row.queue,\n type: row.type,\n payload,\n status: row.status as JobStatus,\n attemptsMade: Number(row.attempts_made),\n maxAttempts: Number(row.max_attempts),\n retryDelay: Number(row.retry_delay),\n backoff: row.backoff as JobData<TPayload>[\"backoff\"],\n timeout: Number(row.timeout),\n priority: Number(row.priority),\n runAt: row.run_at instanceof Date ? row.run_at.toISOString() : String(row.run_at),\n attempts: attempts as JobData<TPayload>[\"attempts\"],\n lockId: row.lock_id,\n lockExpiresAt: row.lock_expires_at instanceof Date ? row.lock_expires_at.toISOString() : null,\n createdAt:\n row.created_at instanceof Date ? row.created_at.toISOString() : String(row.created_at),\n updatedAt:\n row.updated_at instanceof Date ? row.updated_at.toISOString() : String(row.updated_at),\n completedAt: row.completed_at instanceof Date ? row.completed_at.toISOString() : null,\n failedAt: row.failed_at instanceof Date ? row.failed_at.toISOString() : null,\n };\n\n if (row.cron) job.cron = row.cron;\n return job;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class MySQLStorageAdapter implements StorageAdapter {\n private pool!: MySQLPool;\n private readonly connectionString: string;\n\n constructor(connectionString: string) {\n this.connectionString = connectionString;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const mysql2 = await loadMySQL2();\n\n this.pool = mysql2.createPool({\n uri: this.connectionString,\n waitForConnections: true,\n connectionLimit: 10,\n queueLimit: 0,\n });\n\n // Verify connectivity and auto-create tables.\n const conn: MySQLConnection = await this.pool.getConnection();\n try {\n await conn.query(\"SELECT 1\");\n await conn.query(CREATE_JOBS_TABLE);\n await conn.query(CREATE_RATE_TABLE);\n } finally {\n conn.release();\n }\n }\n\n async close(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n }\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n const runAtMs = new Date(input.runAt).getTime();\n const isDelayed = runAtMs > Date.now();\n const status = isDelayed ? \"delayed\" : \"waiting\";\n\n await this.pool.query(\n `INSERT IGNORE INTO qjw_jobs\n (id, queue, type, payload, status, attempts_made, max_attempts,\n retry_delay, backoff, timeout, priority, run_at, cron,\n attempts, lock_id, lock_expires_at, created_at, updated_at,\n completed_at, failed_at)\n VALUES (?,?,?,?,?,0,?,?,?,?,?,?,?,?,NULL,NULL,NOW(3),NOW(3),NULL,NULL)`,\n [\n input.id,\n input.queue,\n input.type,\n JSON.stringify(input.payload),\n status,\n input.maxAttempts,\n input.retryDelay,\n input.backoff,\n input.timeout,\n input.priority,\n input.runAt,\n input.cron ?? null,\n JSON.stringify([]),\n ],\n );\n\n const rows = await this.pool.query<JobRow[]>(\"SELECT * FROM qjw_jobs WHERE id = ?\", [input.id]);\n const row = (rows[0] as unknown as JobRow[])[0] as JobRow;\n return rowToJob<TPayload>(row);\n }\n\n // -------------------------------------------------------------------------\n // Claim (transaction + SELECT … FOR UPDATE SKIP LOCKED)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const lockExpiresAt = new Date(new Date(now).getTime() + lockDuration)\n .toISOString()\n .slice(0, 23)\n .replace(\"T\", \" \");\n const nowMysql = new Date(now).toISOString().slice(0, 23).replace(\"T\", \" \");\n\n const conn: MySQLConnection = await this.pool.getConnection();\n try {\n await conn.beginTransaction();\n\n const [rows] = await conn.query<JobRow[]>(\n `SELECT * FROM qjw_jobs\n WHERE queue = ?\n AND status IN ('waiting', 'delayed')\n AND run_at <= ?\n AND (lock_expires_at IS NULL OR lock_expires_at <= ?)\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED`,\n [queue, nowMysql, nowMysql],\n );\n\n if (!rows || rows.length === 0) {\n await conn.rollback();\n return null;\n }\n\n const row = rows[0] as JobRow;\n\n await conn.query(\n `UPDATE qjw_jobs\n SET status = 'active', lock_id = ?, lock_expires_at = ?, updated_at = NOW(3)\n WHERE id = ?`,\n [lockId, lockExpiresAt, row.id],\n );\n\n await conn.commit();\n\n // Re-fetch the updated row.\n const [updated] = await this.pool.query<JobRow[]>(\"SELECT * FROM qjw_jobs WHERE id = ?\", [\n row.id,\n ]);\n return rowToJob<TPayload>((updated as unknown as JobRow[])[0] as JobRow);\n } catch (err) {\n await conn.rollback();\n throw err;\n } finally {\n conn.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string): Promise<void> {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'completed', lock_id = NULL, lock_expires_at = NULL,\n completed_at = NOW(3), updated_at = NOW(3)\n WHERE id = ?`,\n [jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Requeue\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = JSON.stringify({\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n // JSON_ARRAY_APPEND appends to the attempts JSON array atomically.\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'waiting',\n attempts_made = attempts_made + 1,\n attempts = JSON_ARRAY_APPEND(attempts, '$', CAST(? AS JSON)),\n run_at = ?,\n lock_id = NULL,\n lock_expires_at = NULL,\n updated_at = NOW(3)\n WHERE id = ?`,\n [attempt, input.runAt, input.jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = JSON.stringify({\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'dead',\n attempts_made = attempts_made + 1,\n attempts = JSON_ARRAY_APPEND(attempts, '$', CAST(? AS JSON)),\n lock_id = NULL,\n lock_expires_at = NULL,\n failed_at = NOW(3),\n updated_at = NOW(3)\n WHERE id = ?`,\n [attempt, input.jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string): Promise<void> {\n // Set lock_expires_at to NOW(3) (already-expired) rather than NULL.\n // recoverStalledJobs() uses WHERE lock_expires_at <= ? — NULL comparisons\n // in SQL always evaluate to NULL (not true), so a NULL value would leave the\n // job permanently stuck in active status.\n await this.pool.query(\n `UPDATE qjw_jobs\n SET lock_id = NULL, lock_expires_at = NOW(3), updated_at = NOW(3)\n WHERE id = ?`,\n [jobId],\n );\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs (atomic — SELECT … FOR UPDATE inside transaction)\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const nowMysql = new Date(now).toISOString().slice(0, 23).replace(\"T\", \" \");\n\n const conn: MySQLConnection = await this.pool.getConnection();\n try {\n await conn.beginTransaction();\n\n // Lock the matching rows so no other worker can claim them concurrently.\n const [rows] = await conn.query<RowDataPacket[]>(\n `SELECT id FROM qjw_jobs\n WHERE queue = ?\n AND status = 'active'\n AND lock_expires_at <= ?\n FOR UPDATE SKIP LOCKED`,\n [queue, nowMysql],\n );\n\n const ids = (rows as RowDataPacket[]).map((r) => String(r[\"id\"]));\n\n if (ids.length > 0) {\n await conn.query(\n `UPDATE qjw_jobs\n SET status = 'waiting', lock_id = NULL, lock_expires_at = NULL, updated_at = NOW(3)\n WHERE id IN (?)`,\n [ids],\n );\n }\n\n await conn.commit();\n return ids;\n } catch (err) {\n await conn.rollback();\n throw err;\n } finally {\n conn.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n const [rows] = await this.pool.query<JobRow[]>(\"SELECT * FROM qjw_jobs WHERE id = ?\", [jobId]);\n const list = rows as unknown as JobRow[];\n if (!list || list.length === 0) return null;\n return rowToJob<TPayload>(list[0] as JobRow);\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (queue !== undefined) {\n conditions.push(\"queue = ?\");\n params.push(queue);\n }\n if (status !== undefined) {\n conditions.push(\"status = ?\");\n params.push(status);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n params.push(limit, offset);\n\n const [rows] = await this.pool.query<JobRow[]>(\n `SELECT * FROM qjw_jobs ${where}\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT ? OFFSET ?`,\n params,\n );\n\n return (rows as unknown as JobRow[]).map((r) => rowToJob<TPayload>(r));\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n \"SELECT status, COUNT(*) AS count FROM qjw_jobs WHERE queue = ? GROUP BY status\",\n [queue],\n );\n\n const counts: Record<JobStatus, number> = {\n waiting: 0,\n active: 0,\n completed: 0,\n delayed: 0,\n dead: 0,\n };\n\n for (const row of rows as RowDataPacket[]) {\n const s = String(row[\"status\"]);\n if (s in counts) counts[s as JobStatus] = Number(row[\"count\"]);\n }\n\n return counts;\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n\n const [rows] = await this.pool.query<RowDataPacket[]>(\n \"SELECT count, window_start FROM qjw_rate_limits WHERE queue = ?\",\n [queue],\n );\n\n const existing = (rows as RowDataPacket[])[0];\n\n if (!existing || nowMs - Number(existing[\"window_start\"]) >= windowMs) {\n await this.pool.query(\n `INSERT INTO qjw_rate_limits (queue, count, window_start) VALUES (?,1,?)\n ON DUPLICATE KEY UPDATE count = 1, window_start = VALUES(window_start)`,\n [queue, nowMs],\n );\n return true;\n }\n\n if (Number(existing[\"count\"]) >= max) return false;\n\n await this.pool.query(\"UPDATE qjw_rate_limits SET count = count + 1 WHERE queue = ?\", [queue]);\n return true;\n }\n}\n","/**\n * Job\n *\n * A rich wrapper around the raw JobData record stored in the storage layer.\n * Exposes read-only accessors for all fields and a small set of helpers used\n * by Queue and Worker internals.\n *\n * The Job instance is passed directly to user-supplied Processor functions.\n * Users should never mutate the internal data object — all mutations happen\n * through the storage adapter.\n */\n\nimport type { JobData, JobStatus, JobAttempt, BackoffStrategy } from \"../types/job.types.js\";\n\nexport class Job<TPayload = unknown> {\n /** @internal Raw data record — treat as immutable outside storage layer. */\n readonly _data: JobData<TPayload>;\n\n constructor(data: JobData<TPayload>) {\n this._data = data;\n }\n\n // -------------------------------------------------------------------------\n // Identity\n // -------------------------------------------------------------------------\n\n /** Unique, stable job identifier. */\n get id(): string {\n return this._data.id;\n }\n\n /** Name of the queue this job belongs to. */\n get queue(): string {\n return this._data.queue;\n }\n\n /** Application-defined job type (matches the registered processor). */\n get type(): string {\n return this._data.type;\n }\n\n // -------------------------------------------------------------------------\n // Payload\n // -------------------------------------------------------------------------\n\n /**\n * User-supplied job payload.\n * Never log this value — it may contain sensitive data.\n */\n get data(): TPayload {\n return this._data.payload;\n }\n\n // -------------------------------------------------------------------------\n // Status & attempts\n // -------------------------------------------------------------------------\n\n /** Current lifecycle status. */\n get status(): JobStatus {\n return this._data.status;\n }\n\n /** Number of attempts already executed (0 = not yet started). */\n get attemptsMade(): number {\n return this._data.attemptsMade;\n }\n\n /** Maximum allowed attempts. */\n get maxAttempts(): number {\n return this._data.maxAttempts;\n }\n\n /** How many attempts remain (including the current one). */\n get attemptsRemaining(): number {\n return Math.max(0, this._data.maxAttempts - this._data.attemptsMade);\n }\n\n /** Ordered list of past attempt records. */\n get attemptHistory(): readonly JobAttempt[] {\n return this._data.attempts;\n }\n\n // -------------------------------------------------------------------------\n // Retry configuration\n // -------------------------------------------------------------------------\n\n /** Base delay in ms between retry attempts. */\n get retryDelay(): number {\n return this._data.retryDelay;\n }\n\n /** Backoff strategy applied on retry. */\n get backoff(): BackoffStrategy {\n return this._data.backoff;\n }\n\n // -------------------------------------------------------------------------\n // Execution config\n // -------------------------------------------------------------------------\n\n /** Per-attempt timeout in ms. */\n get timeout(): number {\n return this._data.timeout;\n }\n\n /** Processing priority — higher numbers are processed first. */\n get priority(): number {\n return this._data.priority;\n }\n\n // -------------------------------------------------------------------------\n // Scheduling\n // -------------------------------------------------------------------------\n\n /** ISO timestamp when this job is eligible to run. */\n get runAt(): string {\n return this._data.runAt;\n }\n\n /** Cron expression for recurring jobs, or undefined. */\n get cron(): string | undefined {\n return this._data.cron;\n }\n\n // -------------------------------------------------------------------------\n // Lock\n // -------------------------------------------------------------------------\n\n /** ID of the worker currently holding the lock, or null. */\n get lockId(): string | null {\n return this._data.lockId;\n }\n\n /** ISO timestamp when the current lock expires, or null. */\n get lockExpiresAt(): string | null {\n return this._data.lockExpiresAt;\n }\n\n // -------------------------------------------------------------------------\n // Timestamps\n // -------------------------------------------------------------------------\n\n /** ISO timestamp when the job was first enqueued. */\n get createdAt(): string {\n return this._data.createdAt;\n }\n\n /** ISO timestamp of the last status change. */\n get updatedAt(): string {\n return this._data.updatedAt;\n }\n\n /** ISO timestamp when the job completed successfully, or null. */\n get completedAt(): string | null {\n return this._data.completedAt;\n }\n\n /** ISO timestamp when the job was moved to the DLQ, or null. */\n get failedAt(): string | null {\n return this._data.failedAt;\n }\n\n // -------------------------------------------------------------------------\n // Derived helpers\n // -------------------------------------------------------------------------\n\n /** Returns true when the job has been successfully processed. */\n isCompleted(): boolean {\n return this._data.status === \"completed\";\n }\n\n /** Returns true when the job is in the Dead Letter Queue. */\n isDead(): boolean {\n return this._data.status === \"dead\";\n }\n\n /** Returns true when the job is currently being processed by a worker. */\n isActive(): boolean {\n return this._data.status === \"active\";\n }\n\n /** Returns true when the job is waiting to be claimed. */\n isWaiting(): boolean {\n return this._data.status === \"waiting\";\n }\n\n /** Returns true when the job is scheduled for a future time. */\n isDelayed(): boolean {\n return this._data.status === \"delayed\";\n }\n\n // -------------------------------------------------------------------------\n // Debug representation (intentionally omits payload)\n // -------------------------------------------------------------------------\n\n toJSON(): Record<string, unknown> {\n return {\n id: this._data.id,\n queue: this._data.queue,\n type: this._data.type,\n status: this._data.status,\n attemptsMade: this._data.attemptsMade,\n maxAttempts: this._data.maxAttempts,\n priority: this._data.priority,\n runAt: this._data.runAt,\n createdAt: this._data.createdAt,\n updatedAt: this._data.updatedAt,\n completedAt: this._data.completedAt,\n failedAt: this._data.failedAt,\n // Payload intentionally excluded to prevent accidental logging.\n };\n }\n\n toString(): string {\n return `Job(${this._data.id}, type=${this._data.type}, status=${this._data.status}, attempt=${this._data.attemptsMade}/${this._data.maxAttempts})`;\n }\n}\n","/**\n * Backoff strategies\n *\n * Calculates the delay (in ms) before a failed job is re-queued.\n *\n * - fixed — always uses `baseDelay`\n * - linear — baseDelay × attemptNumber\n * - exponential — baseDelay × 2^(attemptNumber - 1), capped at MAX_DELAY\n */\n\nimport type { BackoffStrategy } from \"../types/job.types.js\";\n\n/** Maximum delay cap for exponential backoff (10 minutes). */\nconst MAX_DELAY_MS = 10 * 60 * 1_000;\n\n/**\n * Returns the retry delay in milliseconds for the given attempt.\n *\n * @param strategy - Backoff strategy name.\n * @param baseDelay - Base delay in ms (from job / queue / client config).\n * @param attemptNumber - The 1-based attempt number that just failed.\n */\nexport function calculateBackoff(\n strategy: BackoffStrategy,\n baseDelay: number,\n attemptNumber: number,\n): number {\n const attempt = Math.max(1, attemptNumber);\n\n switch (strategy) {\n case \"fixed\":\n return baseDelay;\n\n case \"linear\":\n return baseDelay * attempt;\n\n case \"exponential\": {\n const delay = baseDelay * Math.pow(2, attempt - 1);\n return Math.min(delay, MAX_DELAY_MS);\n }\n\n default: {\n // Exhaustive guard — TypeScript will catch unhandled variants at\n // compile time; this is a runtime safety net.\n const _exhaustive: never = strategy;\n void _exhaustive;\n return baseDelay;\n }\n }\n}\n\n/**\n * Returns the ISO timestamp at which a job should next become eligible,\n * given the current time and the calculated delay.\n */\nexport function nextRunAt(delayMs: number, fromDate: Date = new Date()): string {\n return new Date(fromDate.getTime() + delayMs).toISOString();\n}\n","/**\n * Job ID generation\n *\n * Uses the built-in `crypto.randomUUID()` available in Node.js >= 14.17.\n * No external dependency needed.\n */\n\nimport { randomUUID } from \"node:crypto\";\n\n/** Generate a new unique job ID. */\nexport function generateJobId(): string {\n return randomUUID();\n}\n","/**\n * Worker\n *\n * Claims jobs from a queue and executes registered processors concurrently.\n *\n * Key guarantees:\n * - At-most `concurrency` jobs run simultaneously.\n * - Job claiming is delegated to the storage adapter's atomic `claim()` call.\n * - Failed jobs are requeued (with backoff) or moved to the DLQ.\n * - Stalled jobs (expired locks from crashed workers) are recovered\n * periodically.\n * - Graceful shutdown: stop claiming, wait for active jobs, release any\n * locks that could not be finished within shutdownTimeout.\n * - Cron jobs: re-enqueued immediately after each successful execution.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { Cron } from \"croner\";\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type { WorkerOptions, WorkerStatus, Processor } from \"../types/worker.types.js\";\nimport type { QueueOptions } from \"../types/queue.types.js\";\nimport type { ClientDefaults } from \"../types/client.types.js\";\nimport { Job } from \"./job.js\";\nimport { calculateBackoff, nextRunAt } from \"./backoff.js\";\nimport { generateJobId } from \"./id.js\";\nimport type { QueueEventEmitter } from \"../events/emitter.js\";\n\ntype ResolvedDefaults = Required<ClientDefaults>;\n\n// ---------------------------------------------------------------------------\n// Resolved config helper\n// ---------------------------------------------------------------------------\n\nfunction resolveConfig(\n workerOptions: WorkerOptions,\n queueOptions: QueueOptions,\n defaults: ResolvedDefaults,\n) {\n return {\n concurrency: workerOptions.concurrency ?? queueOptions.concurrency ?? defaults.concurrency,\n shutdownTimeout: workerOptions.shutdownTimeout ?? 30_000,\n pollInterval: queueOptions.pollInterval ?? defaults.pollInterval,\n stalledInterval: queueOptions.stalledInterval ?? defaults.stalledInterval,\n lockDuration: queueOptions.lockDuration ?? defaults.lockDuration,\n rateLimit: queueOptions.rateLimit ?? defaults.rateLimit,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Worker\n// ---------------------------------------------------------------------------\n\nexport class Worker {\n /** Unique identifier for this worker instance. */\n readonly id: string;\n\n private readonly queueName: string;\n private readonly storage: StorageAdapter;\n private readonly emitter: QueueEventEmitter;\n private readonly processors: Map<string, Processor<unknown>>;\n private readonly config: ReturnType<typeof resolveConfig>;\n\n private _status: WorkerStatus = \"idle\";\n private activeCount = 0;\n\n /**\n * Tracks job IDs currently being processed so we can release their locks\n * when graceful shutdown times out before they finish.\n */\n private readonly activeJobIds = new Set<string>();\n\n private pollTimer: NodeJS.Timeout | null = null;\n private stalledTimer: NodeJS.Timeout | null = null;\n\n /** Resolves when all active jobs finish during shutdown. */\n private drainResolve: (() => void) | null = null;\n\n constructor(\n queueName: string,\n storage: StorageAdapter,\n emitter: QueueEventEmitter,\n processors: Map<string, Processor<unknown>>,\n workerOptions: WorkerOptions,\n queueOptions: QueueOptions,\n defaults: ResolvedDefaults,\n ) {\n this.id = `worker:${queueName}:${randomUUID()}`;\n this.queueName = queueName;\n this.storage = storage;\n this.emitter = emitter;\n this.processors = processors;\n this.config = resolveConfig(workerOptions, queueOptions, defaults);\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n\n get status(): WorkerStatus {\n return this._status;\n }\n\n /** Start polling for jobs. */\n start(): void {\n if (this._status !== \"idle\" && this._status !== \"stopped\") {\n return;\n }\n\n this._status = \"running\";\n this.emitter.emit(\"worker:started\", this.id);\n this.emitter.emit(\"worker:status\", this.id, this._status);\n\n this.schedulePoll();\n this.scheduleStallCheck();\n }\n\n /**\n * Gracefully stop the worker.\n *\n * 1. Stop accepting new jobs.\n * 2. Wait up to `shutdownTimeout` ms for active jobs to finish.\n * 3. Release locks on any jobs that did not finish in time so another\n * worker can reclaim them.\n * 4. Emit stopped event.\n */\n async stop(): Promise<void> {\n if (this._status === \"stopped\" || this._status === \"stopping\") {\n return;\n }\n\n this._status = \"stopping\";\n this.emitter.emit(\"worker:status\", this.id, this._status);\n\n // Cancel timers.\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n if (this.stalledTimer) {\n clearTimeout(this.stalledTimer);\n this.stalledTimer = null;\n }\n\n // Wait for active jobs to finish, but no longer than shutdownTimeout.\n if (this.activeCount > 0) {\n await Promise.race([\n new Promise<void>((resolve) => {\n this.drainResolve = resolve;\n }),\n new Promise<void>((resolve) => setTimeout(resolve, this.config.shutdownTimeout)),\n ]);\n }\n\n // Release locks for any jobs that did not finish in time.\n // releaseLock() sets lockExpiresAt to an already-expired timestamp so that\n // recoverStalledJobs() on any worker will immediately pick them up and\n // return them to \"waiting\" — rather than leaving them stuck in \"active\"\n // forever (which would happen if lockExpiresAt were cleared to null/empty,\n // since every adapter's stalled-job check requires a non-null expired value).\n if (this.activeJobIds.size > 0) {\n await Promise.all(\n Array.from(this.activeJobIds).map((jobId) =>\n this.storage.releaseLock(jobId).catch(() => {\n // Best-effort — storage may already be unavailable during shutdown.\n }),\n ),\n );\n }\n\n this._status = \"stopped\";\n this.emitter.emit(\"worker:stopped\", this.id);\n this.emitter.emit(\"worker:status\", this.id, this._status);\n }\n\n // -------------------------------------------------------------------------\n // Poll loop\n // -------------------------------------------------------------------------\n\n private schedulePoll(): void {\n if (this._status !== \"running\") return;\n\n this.pollTimer = setTimeout(() => {\n void this.poll();\n }, this.config.pollInterval);\n }\n\n private async poll(): Promise<void> {\n if (this._status !== \"running\") return;\n\n try {\n // Fill up to concurrency limit.\n while (this._status === \"running\" && this.activeCount < this.config.concurrency) {\n const claimed = await this.claimNext();\n if (!claimed) break; // No eligible jobs right now.\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emitter.emit(\"queue:error\", this.queueName, error);\n this.emitter.emit(\"worker:error\", this.id, error);\n }\n\n this.schedulePoll();\n }\n\n // -------------------------------------------------------------------------\n // Claim & execute\n // -------------------------------------------------------------------------\n\n private async claimNext(): Promise<boolean> {\n const now = new Date().toISOString();\n\n // Attempt to claim a job before touching the rate-limit counter.\n // The counter must only be consumed when a job is actually claimed for\n // processing — polling an empty queue must not burn quota (issue #4).\n const raw = await this.storage.claim({\n queue: this.queueName,\n lockId: this.id,\n lockDuration: this.config.lockDuration,\n now,\n });\n\n if (!raw) return false;\n\n // A job was claimed. Now enforce the rate limit. If the limit has been\n // reached we immediately release the lock so the job is recoverable and\n // return false — the poll loop will stop trying until the next cycle.\n if (this.config.rateLimit) {\n const allowed = await this.storage.checkAndIncrementRateLimit(\n this.queueName,\n this.config.rateLimit.max,\n this.config.rateLimit.duration,\n now,\n );\n if (!allowed) {\n await this.storage.releaseLock(raw.id);\n return false;\n }\n }\n\n const job = new Job(raw);\n this.activeCount += 1;\n this.activeJobIds.add(job.id);\n\n // Fire-and-forget — errors are caught inside executeJob.\n void this.executeJob(job);\n\n return true;\n }\n\n private async executeJob(job: Job<unknown>): Promise<void> {\n this.emitter.emit(\"job:started\", job._data);\n\n const processor = this.processors.get(job.type);\n\n if (!processor) {\n // No processor registered → treat as a permanent failure.\n const error = new Error(\n `No processor registered for job type \"${job.type}\" in queue \"${this.queueName}\"`,\n );\n await this.handleFailure(job, error);\n return;\n }\n\n let timeoutHandle: NodeJS.Timeout | null = null;\n\n try {\n await new Promise<void>((resolve, reject) => {\n // Enforce per-attempt timeout.\n timeoutHandle = setTimeout(() => {\n reject(new Error(`Job timed out after ${job.timeout}ms`));\n }, job.timeout);\n\n Promise.resolve(processor(job)).then(resolve, reject);\n });\n\n // Success path.\n if (timeoutHandle) clearTimeout(timeoutHandle);\n await this.storage.complete(job.id);\n\n const completedData = (await this.storage.getJob(job.id)) ?? job._data;\n this.emitter.emit(\"job:completed\", completedData);\n\n // Re-enqueue cron job for its next run.\n if (job.cron) {\n await this.enqueueCronNext(job);\n }\n } catch (err) {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n const error = err instanceof Error ? err : new Error(String(err));\n await this.handleFailure(job, error);\n } finally {\n this.activeJobIds.delete(job.id);\n this.activeCount -= 1;\n // Signal drain waiter if we've reached zero active jobs.\n if (this.activeCount === 0 && this.drainResolve) {\n this.drainResolve();\n this.drainResolve = null;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Cron — re-enqueue the next occurrence after a successful run\n // -------------------------------------------------------------------------\n\n private async enqueueCronNext(job: Job<unknown>): Promise<void> {\n try {\n // Resolve the next scheduled run using the croner package.\n // croner is a required dependency — if the expression is invalid or has\n // no future occurrences we emit a worker:error and skip re-enqueue\n // rather than silently falling back to a 1-minute interval.\n let nextDate: Date | null;\n\n try {\n const cronInstance = new Cron(job.cron as string);\n nextDate = cronInstance.nextRun();\n } catch (cronErr) {\n // Cron expression is invalid or croner itself threw.\n const error =\n cronErr instanceof Error\n ? cronErr\n : new Error(\n `croner failed to initialize for expression \"${job.cron as string}\": ${String(cronErr)}`,\n );\n this.emitter.emit(\"worker:error\", this.id, error);\n return; // Do not re-enqueue — invalid expression should not produce a job.\n }\n\n if (nextDate === null) {\n // The cron schedule has no future occurrences (e.g. a bounded expression\n // that has already elapsed). Emitting an error lets operators know the\n // job will not recur rather than silently dropping it.\n const error = new Error(\n `Cron expression \"${job.cron as string}\" has no future occurrences — job \"${job.id}\" will not be re-enqueued`,\n );\n this.emitter.emit(\"worker:error\", this.id, error);\n return;\n }\n\n const runAt = nextDate.toISOString();\n\n await this.storage.enqueue({\n id: generateJobId(),\n queue: this.queueName,\n type: job.type,\n payload: job._data.payload,\n maxAttempts: job.maxAttempts,\n retryDelay: job.retryDelay,\n backoff: job.backoff,\n timeout: job.timeout,\n priority: job.priority,\n runAt,\n cron: job.cron,\n });\n } catch (err) {\n // Cron re-enqueue failures must not crash the worker.\n const error = err instanceof Error ? err : new Error(String(err));\n this.emitter.emit(\"worker:error\", this.id, error);\n }\n }\n\n // -------------------------------------------------------------------------\n // Failure handling — requeue or DLQ\n // -------------------------------------------------------------------------\n\n private async handleFailure(job: Job<unknown>, error: Error): Promise<void> {\n const attemptNumber = job.attemptsMade + 1; // the attempt that just failed\n const hasMore = attemptNumber < job.maxAttempts;\n\n this.emitter.emit(\"job:failed\", job._data, error);\n\n if (hasMore) {\n const delayMs = calculateBackoff(job.backoff, job.retryDelay, attemptNumber);\n const runAt = nextRunAt(delayMs);\n\n await this.storage.requeue({\n jobId: job.id,\n runAt,\n error: error.message,\n attemptNumber,\n ...(error.stack !== undefined && { stack: error.stack }),\n });\n\n const updated = await this.storage.getJob(job.id);\n if (updated) {\n this.emitter.emit(\"job:retrying\", updated, error, runAt);\n }\n } else {\n // No attempts remaining → Dead Letter Queue.\n await this.storage.moveToDlq({\n jobId: job.id,\n error: error.message,\n attemptNumber,\n ...(error.stack !== undefined && { stack: error.stack }),\n });\n\n const dead = await this.storage.getJob(job.id);\n if (dead) {\n this.emitter.emit(\"job:dead\", dead, error);\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Stalled-job recovery\n // -------------------------------------------------------------------------\n\n private scheduleStallCheck(): void {\n if (this._status !== \"running\") return;\n\n this.stalledTimer = setTimeout(() => {\n void this.recoverStalledJobs();\n }, this.config.stalledInterval);\n }\n\n private async recoverStalledJobs(): Promise<void> {\n if (this._status !== \"running\") return;\n\n try {\n const now = new Date().toISOString();\n const recovered = await this.storage.recoverStalledJobs(this.queueName, now);\n\n for (const jobId of recovered) {\n this.emitter.emit(\"job:stalled\", jobId);\n this.emitter.emit(\"job:recovered\", jobId);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emitter.emit(\"worker:error\", this.id, error);\n }\n\n this.scheduleStallCheck();\n }\n}\n","/**\n * Queue\n *\n * Represents an independent stream of jobs. Applications use a Queue to:\n * - Enqueue jobs (with optional scheduling, priority, retry config).\n * - Register processors for job types.\n * - Create workers that consume jobs from this queue.\n * - Query job state and counts.\n *\n * Queue-level configuration overrides client defaults.\n * Job-level options override queue configuration where applicable.\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type { JobOptions, JobStatus } from \"../types/job.types.js\";\nimport type { QueueOptions } from \"../types/queue.types.js\";\nimport type { WorkerOptions, Processor } from \"../types/worker.types.js\";\nimport type { ClientDefaults } from \"../types/client.types.js\";\nimport { Job } from \"./job.js\";\nimport { Worker } from \"./worker.js\";\nimport { generateJobId } from \"./id.js\";\nimport type { QueueEventEmitter } from \"../events/emitter.js\";\n\ntype ResolvedDefaults = Required<ClientDefaults>;\n\n// ---------------------------------------------------------------------------\n// Resolved queue-level config\n// ---------------------------------------------------------------------------\n\nfunction mergeQueueConfig(\n options: QueueOptions | undefined,\n defaults: ResolvedDefaults,\n): Required<QueueOptions> {\n return {\n concurrency: options?.concurrency ?? defaults.concurrency,\n attempts: options?.attempts ?? defaults.attempts,\n retryDelay: options?.retryDelay ?? defaults.retryDelay,\n backoff: options?.backoff ?? defaults.backoff,\n timeout: options?.timeout ?? defaults.timeout,\n rateLimit: options?.rateLimit ?? defaults.rateLimit ?? undefined,\n pollInterval: options?.pollInterval ?? defaults.pollInterval,\n stalledInterval: options?.stalledInterval ?? defaults.stalledInterval,\n lockDuration: options?.lockDuration ?? defaults.lockDuration,\n } as Required<QueueOptions>;\n}\n\n// ---------------------------------------------------------------------------\n// Queue\n// ---------------------------------------------------------------------------\n\nexport class Queue<TPayload = unknown> {\n /** The name of this queue — unique within a QueueClient. */\n readonly name: string;\n\n private readonly storage: StorageAdapter;\n private readonly emitter: QueueEventEmitter;\n private readonly resolvedConfig: Required<QueueOptions>;\n private readonly clientDefaults: ResolvedDefaults;\n\n /** Registered processors keyed by job type. */\n private readonly processors = new Map<string, Processor<unknown>>();\n\n /** Active worker instances created by this queue. */\n private readonly workers: Worker[] = [];\n\n constructor(\n name: string,\n storage: StorageAdapter,\n emitter: QueueEventEmitter,\n options: QueueOptions | undefined,\n defaults: ResolvedDefaults,\n ) {\n this.name = name;\n this.storage = storage;\n this.emitter = emitter;\n this.clientDefaults = defaults;\n this.resolvedConfig = mergeQueueConfig(options, defaults);\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n /**\n * Add a new job to the queue.\n *\n * @param type - Job type string, must match a registered processor.\n * @param payload - Arbitrary serialisable payload (never logged by default).\n * @param options - Per-job overrides (attempts, delay, priority, etc.).\n */\n async enqueue(type: string, payload: TPayload, options?: JobOptions): Promise<Job<TPayload>> {\n const cfg = this.resolvedConfig;\n const now = new Date();\n\n // Resolve runAt from schedule options.\n let runAt: Date;\n if (options?.schedule?.runAt !== undefined) {\n runAt = new Date(options.schedule.runAt);\n } else if (options?.schedule?.delay !== undefined) {\n runAt = new Date(now.getTime() + options.schedule.delay);\n } else {\n runAt = now;\n }\n\n const raw = await this.storage.enqueue<TPayload>({\n id: generateJobId(),\n queue: this.name,\n type,\n payload,\n maxAttempts: options?.attempts ?? cfg.attempts,\n retryDelay: options?.retryDelay ?? cfg.retryDelay,\n backoff: options?.backoff ?? cfg.backoff,\n timeout: options?.timeout ?? cfg.timeout,\n priority: options?.priority ?? 0,\n runAt: runAt.toISOString(),\n ...(options?.schedule?.cron !== undefined && { cron: options.schedule.cron }),\n });\n\n const job = new Job<TPayload>(raw);\n this.emitter.emit(\"job:enqueued\", raw);\n return job;\n }\n\n // -------------------------------------------------------------------------\n // Processor registration\n // -------------------------------------------------------------------------\n\n /**\n * Register a processor function for a given job type.\n *\n * Only one processor per type per queue is supported.\n * Registering a second processor for the same type replaces the first.\n */\n process<P = TPayload>(type: string, processor: Processor<P>): void {\n this.processors.set(type, processor as Processor<unknown>);\n }\n\n // -------------------------------------------------------------------------\n // Worker creation\n // -------------------------------------------------------------------------\n\n /**\n * Create and start a Worker that consumes jobs from this queue.\n *\n * Worker-level `concurrency` overrides queue-level concurrency.\n */\n createWorker(options?: WorkerOptions): Worker {\n const worker = new Worker(\n this.name,\n this.storage,\n this.emitter,\n this.processors,\n options ?? {},\n this.resolvedConfig,\n this.clientDefaults,\n );\n\n this.workers.push(worker);\n worker.start();\n return worker;\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n /** Fetch a single job by its ID. Returns null if not found or wrong queue. */\n async getJob(jobId: string): Promise<Job<TPayload> | null> {\n const raw = await this.storage.getJob<TPayload>(jobId);\n if (!raw || raw.queue !== this.name) return null;\n return new Job<TPayload>(raw);\n }\n\n /** Fetch jobs from this queue, optionally filtered by status. */\n async getJobs(status?: JobStatus, limit = 100, offset = 0): Promise<Job<TPayload>[]> {\n const raws = await this.storage.getJobs<TPayload>({\n queue: this.name,\n ...(status !== undefined && { status }),\n limit,\n offset,\n });\n return raws.map((r) => new Job<TPayload>(r));\n }\n\n /** Get job counts by status for this queue. */\n async getJobCounts(): Promise<Record<JobStatus, number>> {\n return this.storage.getJobCounts(this.name);\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n /**\n * Gracefully stop all workers attached to this queue.\n * Called automatically by QueueClient.close().\n */\n async close(): Promise<void> {\n await Promise.all(this.workers.map((w) => w.stop()));\n }\n\n /** Return all active worker instances on this queue. */\n getWorkers(): readonly Worker[] {\n return this.workers;\n }\n}\n","/**\n * InMemoryStorageAdapter\n *\n * A fully-featured, in-process storage adapter backed by a plain Map.\n * Intended for development, testing, and single-process scenarios.\n * Data is NOT persisted between process restarts.\n *\n * All \"atomic\" operations are synchronous under the hood — safe within a\n * single Node.js event-loop tick.\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Rate-limit window bucket\n// ---------------------------------------------------------------------------\n\ninterface RateLimitBucket {\n count: number;\n windowStart: number;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class InMemoryStorageAdapter implements StorageAdapter {\n private readonly jobs = new Map<string, JobData<unknown>>();\n private readonly rateLimitBuckets = new Map<string, RateLimitBucket>();\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n // Nothing to set up for in-memory storage.\n }\n\n async close(): Promise<void> {\n this.jobs.clear();\n this.rateLimitBuckets.clear();\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n // Idempotent: return existing job if already stored with the same id.\n if (this.jobs.has(input.id)) {\n return this.jobs.get(input.id) as JobData<TPayload>;\n }\n\n const now = new Date().toISOString();\n const runAt = input.runAt;\n const isDelayed = new Date(runAt).getTime() > Date.now();\n\n const job: JobData<TPayload> = {\n id: input.id,\n queue: input.queue,\n type: input.type,\n payload: input.payload,\n status: isDelayed ? \"delayed\" : \"waiting\",\n attemptsMade: 0,\n maxAttempts: input.maxAttempts,\n retryDelay: input.retryDelay,\n backoff: input.backoff,\n timeout: input.timeout,\n priority: input.priority,\n runAt,\n ...(input.cron !== undefined && { cron: input.cron }),\n attempts: [],\n lockId: null,\n lockExpiresAt: null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n failedAt: null,\n };\n\n this.jobs.set(job.id, job as JobData<unknown>);\n return job;\n }\n\n // -------------------------------------------------------------------------\n // Claim (atomic within a single event-loop tick)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const nowMs = new Date(now).getTime();\n\n // Collect eligible jobs: same queue, waiting/delayed, runAt <= now,\n // and no valid lock held.\n const eligible: JobData<unknown>[] = [];\n\n for (const job of this.jobs.values()) {\n if (job.queue !== queue) continue;\n if (job.status !== \"waiting\" && job.status !== \"delayed\") continue;\n if (new Date(job.runAt).getTime() > nowMs) continue;\n if (job.lockId !== null && job.lockExpiresAt !== null) {\n const lockExpiry = new Date(job.lockExpiresAt).getTime();\n if (lockExpiry > nowMs) continue; // still locked\n }\n eligible.push(job);\n }\n\n if (eligible.length === 0) return null;\n\n // Sort: higher priority first; break ties by earliest runAt then createdAt.\n eligible.sort((a, b) => {\n if (b.priority !== a.priority) return b.priority - a.priority;\n const runAtDiff = new Date(a.runAt).getTime() - new Date(b.runAt).getTime();\n if (runAtDiff !== 0) return runAtDiff;\n return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();\n });\n\n const job = eligible[0];\n if (!job) return null;\n\n const lockExpiresAt = new Date(nowMs + lockDuration).toISOString();\n\n job.status = \"active\";\n job.lockId = lockId;\n job.lockExpiresAt = lockExpiresAt;\n job.updatedAt = now;\n\n return job as JobData<TPayload>;\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string): Promise<void> {\n const job = this.requireJob(jobId);\n const now = new Date().toISOString();\n\n job.status = \"completed\";\n job.lockId = null;\n job.lockExpiresAt = null;\n job.completedAt = now;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Requeue (retry)\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const job = this.requireJob(input.jobId);\n const now = new Date().toISOString();\n\n // Record this attempt in history using the canonical attempt number from input.\n job.attempts.push({\n attempt: input.attemptNumber,\n startedAt: job.updatedAt,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined && { stack: input.stack }),\n });\n\n job.attemptsMade = input.attemptNumber;\n job.status = \"waiting\";\n job.runAt = input.runAt;\n job.lockId = null;\n job.lockExpiresAt = null;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const job = this.requireJob(input.jobId);\n const now = new Date().toISOString();\n\n job.attempts.push({\n attempt: input.attemptNumber,\n startedAt: job.updatedAt,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined && { stack: input.stack }),\n });\n\n job.attemptsMade = input.attemptNumber;\n job.status = \"dead\";\n job.lockId = null;\n job.lockExpiresAt = null;\n job.failedAt = now;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string): Promise<void> {\n const job = this.jobs.get(jobId);\n if (!job) return;\n\n const now = new Date().toISOString();\n job.lockId = null;\n // Set lockExpiresAt to now (already-expired) so recoverStalledJobs() will\n // immediately pick up the job — its lock-expired check requires a non-null,\n // expired timestamp, not null.\n job.lockExpiresAt = now;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs\n //\n // Fix (issue #6): snapshot lockId and lockExpiresAt before the eligibility\n // check, then re-validate both values at write time. In a single-process\n // scenario all operations are synchronous within one event-loop tick, so\n // the race is theoretical — but the guard makes the adapter consistent with\n // the Redis CAS semantics and protects against any future async paths.\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const nowMs = new Date(now).getTime();\n const recovered: string[] = [];\n\n for (const job of this.jobs.values()) {\n if (job.queue !== queue) continue;\n if (job.status !== \"active\") continue;\n if (job.lockExpiresAt === null) continue;\n\n const lockExpiry = new Date(job.lockExpiresAt).getTime();\n if (lockExpiry > nowMs) continue;\n\n // Snapshot the guard fields observed at decision time.\n const snapshotLockId = job.lockId;\n const snapshotLockExpiresAt = job.lockExpiresAt;\n\n // Re-validate before writing (compare-and-swap).\n // If another operation (complete, requeue, releaseLock) ran between the\n // check above and this point, one of these will differ and we skip.\n if (job.status !== \"active\") continue;\n if (job.lockId !== snapshotLockId) continue;\n if (job.lockExpiresAt !== snapshotLockExpiresAt) continue;\n\n // Lock expired and state is unchanged — recover.\n job.status = \"waiting\";\n job.lockId = null;\n job.lockExpiresAt = null;\n job.updatedAt = now;\n recovered.push(job.id);\n }\n\n return recovered;\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n return (this.jobs.get(jobId) as JobData<TPayload>) ?? null;\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n const results: JobData<unknown>[] = [];\n\n for (const job of this.jobs.values()) {\n if (queue !== undefined && job.queue !== queue) continue;\n if (status !== undefined && job.status !== status) continue;\n results.push(job);\n }\n\n // Sort by priority desc, then createdAt asc.\n results.sort((a, b) => {\n if (b.priority !== a.priority) return b.priority - a.priority;\n return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();\n });\n\n return results.slice(offset, offset + limit) as JobData<TPayload>[];\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const counts: Record<JobStatus, number> = {\n waiting: 0,\n active: 0,\n completed: 0,\n delayed: 0,\n dead: 0,\n };\n\n for (const job of this.jobs.values()) {\n if (job.queue !== queue) continue;\n counts[job.status] += 1;\n }\n\n return counts;\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n const existing = this.rateLimitBuckets.get(queue);\n\n if (!existing || nowMs - existing.windowStart >= windowMs) {\n // Start a fresh window.\n this.rateLimitBuckets.set(queue, { count: 1, windowStart: nowMs });\n return true;\n }\n\n if (existing.count >= max) {\n return false; // Rate limit reached.\n }\n\n existing.count += 1;\n return true;\n }\n\n // -------------------------------------------------------------------------\n // Internal helpers\n // -------------------------------------------------------------------------\n\n private requireJob(jobId: string): JobData<unknown> {\n const job = this.jobs.get(jobId);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n return job;\n }\n}\n","/**\n * QueueEventEmitter\n *\n * A strongly-typed, lightweight event emitter built on top of Node.js\n * EventEmitter. All queue/worker/job lifecycle events flow through here.\n *\n * Using typed generics ensures that each event name maps to its exact\n * argument tuple — no runtime guessing.\n */\n\nimport { EventEmitter } from \"node:events\";\nimport type { QueueEvents } from \"../types/events.types.js\";\n\n// ---------------------------------------------------------------------------\n// Typed emitter\n// ---------------------------------------------------------------------------\n\ntype EventKey = keyof QueueEvents;\ntype EventArgs<K extends EventKey> = QueueEvents[K];\n\nexport class QueueEventEmitter {\n private readonly emitter: EventEmitter;\n\n constructor() {\n this.emitter = new EventEmitter();\n // Prevent Node.js from printing \"MaxListenersExceededWarning\" in tests\n // where many listeners are registered.\n this.emitter.setMaxListeners(100);\n }\n\n // -------------------------------------------------------------------------\n // Subscribe\n // -------------------------------------------------------------------------\n\n on<K extends EventKey>(event: K, listener: (...args: EventArgs<K>) => void): this {\n this.emitter.on(event as string, listener as (...a: unknown[]) => void);\n return this;\n }\n\n once<K extends EventKey>(event: K, listener: (...args: EventArgs<K>) => void): this {\n this.emitter.once(event as string, listener as (...a: unknown[]) => void);\n return this;\n }\n\n off<K extends EventKey>(event: K, listener: (...args: EventArgs<K>) => void): this {\n this.emitter.off(event as string, listener as (...a: unknown[]) => void);\n return this;\n }\n\n // ---------------------------------------------------------------------------\n // Publish\n // ---------------------------------------------------------------------------\n\n emit<K extends EventKey>(event: K, ...args: EventArgs<K>): boolean {\n return this.emitter.emit(event as string, ...args);\n }\n\n // ---------------------------------------------------------------------------\n // Utility\n // ---------------------------------------------------------------------------\n\n removeAllListeners(event?: EventKey): this {\n if (event) {\n this.emitter.removeAllListeners(event as string);\n } else {\n this.emitter.removeAllListeners();\n }\n return this;\n }\n\n listenerCount(event: EventKey): number {\n return this.emitter.listenerCount(event as string);\n }\n}\n","/**\n * QueueClient\n *\n * Primary entry point for the queue-jobs-worker package.\n *\n * Usage:\n *\n * // In-memory (dev / tests)\n * const client = new QueueClient();\n * // init() is optional for \"memory\" but always safe to call\n *\n * // Redis\n * const client = new QueueClient({ dialect: \"redis\", connectionString: \"redis://localhost:6379\" });\n * await client.init(); // connects, pings Redis\n *\n * // PostgreSQL\n * const client = new QueueClient({ dialect: \"postgres\", connectionString: \"postgresql://...\" });\n * await client.init(); // connects, auto-creates qjw_ tables if missing\n *\n * // MySQL\n * const client = new QueueClient({ dialect: \"mysql\", connectionString: \"mysql://...\" });\n * await client.init(); // connects, auto-creates qjw_ tables if missing\n */\n\nimport type { QueueClientOptions, ClientDefaults } from \"../types/client.types.js\";\nimport type { QueueOptions } from \"../types/queue.types.js\";\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type { QueueEvents } from \"../types/events.types.js\";\nimport { Queue } from \"./queue.js\";\nimport { InMemoryStorageAdapter } from \"../storage/in-memory.adapter.js\";\nimport { QueueEventEmitter } from \"../events/emitter.js\";\n\n// ---------------------------------------------------------------------------\n// Hard defaults\n// ---------------------------------------------------------------------------\n\nconst HARD_DEFAULTS: Required<ClientDefaults> = {\n attempts: 3,\n retryDelay: 1_000,\n backoff: \"exponential\",\n timeout: 30_000,\n concurrency: 10,\n rateLimit: undefined as unknown as Required<ClientDefaults>[\"rateLimit\"],\n pollInterval: 1_000,\n stalledInterval: 30_000,\n lockDuration: 60_000,\n};\n\n// ---------------------------------------------------------------------------\n// QueueClient\n// ---------------------------------------------------------------------------\n\nexport class QueueClient {\n /** @internal — mutable so withAdapter() can replace it */\n _storage: StorageAdapter;\n\n private readonly emitter: QueueEventEmitter;\n private readonly defaults: Required<ClientDefaults>;\n private readonly dialect: string;\n private readonly connectionString: string | undefined;\n private readonly queues = new Map<string, Queue<unknown>>();\n private initialised = false;\n private closed = false;\n\n constructor(options: QueueClientOptions = {}) {\n this.defaults = { ...HARD_DEFAULTS, ...options.defaults };\n this.dialect = options.dialect ?? \"memory\";\n this.connectionString = options.connectionString;\n\n // Memory adapter is built eagerly (no I/O needed).\n // External adapters are built eagerly too, but require init() before use.\n this._storage = this.buildMemoryOrEagerAdapter();\n this.emitter = new QueueEventEmitter();\n }\n\n // -------------------------------------------------------------------------\n // Eager adapter construction (memory only; external adapters built in init)\n // -------------------------------------------------------------------------\n\n private buildMemoryOrEagerAdapter(): StorageAdapter {\n if (this.dialect === \"memory\") {\n return new InMemoryStorageAdapter();\n }\n // For external dialects we return a sentinel that will be replaced in init().\n // This lets the constructor stay synchronous.\n return new InMemoryStorageAdapter(); // replaced in init()\n }\n\n // -------------------------------------------------------------------------\n // Initialisation\n // -------------------------------------------------------------------------\n\n /**\n * Initialise the client and storage adapter.\n *\n * **Must be called** before creating queues when using Redis, PostgreSQL,\n * or MySQL.\n *\n * What each adapter does:\n * - **memory** — no-op (always safe to call)\n * - **redis** — connects, sends PING, verifies PONG\n * - **postgres** — connects, runs `SELECT 1`, auto-creates `qjw_` tables\n * - **mysql** — connects, runs `SELECT 1`, auto-creates `qjw_` tables\n *\n * Idempotent — safe to call multiple times.\n */\n async init(): Promise<void> {\n if (this.initialised) return;\n\n // Build the real adapter (lazy import keeps optional peer deps optional).\n this._storage = await this.resolveAdapter();\n\n await this._storage.initialize();\n this.initialised = true;\n }\n\n /** @deprecated Use `init()`. */\n async initialize(): Promise<void> {\n return this.init();\n }\n\n private async resolveAdapter(): Promise<StorageAdapter> {\n const cs = this.connectionString;\n\n switch (this.dialect) {\n case \"memory\":\n return new InMemoryStorageAdapter();\n\n case \"redis\": {\n if (!cs) throw new Error('dialect \"redis\" requires a connectionString.');\n const { RedisStorageAdapter } = await import(\"../storage/redis.adapter.js\");\n return new RedisStorageAdapter(cs);\n }\n\n case \"postgres\": {\n if (!cs) throw new Error('dialect \"postgres\" requires a connectionString.');\n const { PostgreSQLStorageAdapter } = await import(\"../storage/postgres.adapter.js\");\n return new PostgreSQLStorageAdapter(cs);\n }\n\n case \"mysql\": {\n if (!cs) throw new Error('dialect \"mysql\" requires a connectionString.');\n const { MySQLStorageAdapter } = await import(\"../storage/mysql.adapter.js\");\n return new MySQLStorageAdapter(cs);\n }\n\n default:\n throw new Error(`Unknown storage dialect: \"${this.dialect}\"`);\n }\n }\n\n // -------------------------------------------------------------------------\n // Queue management\n // -------------------------------------------------------------------------\n\n /**\n * Create a named queue with optional per-queue configuration.\n *\n * For external dialects (redis/postgres/mysql), call `await client.init()`\n * first.\n */\n createQueue<TPayload = unknown>(name: string, options?: QueueOptions): Queue<TPayload> {\n if (this.closed) throw new Error(\"QueueClient has been closed.\");\n if (this.queues.has(name)) {\n throw new Error(`A queue named \"${name}\" already exists on this client.`);\n }\n\n // Auto-init for memory (harmless no-op).\n if (!this.initialised && this.dialect === \"memory\") {\n void this.init();\n }\n\n const queue = new Queue<TPayload>(name, this._storage, this.emitter, options, this.defaults);\n this.queues.set(name, queue as Queue<unknown>);\n return queue;\n }\n\n /** Returns the queue with the given name, or `undefined`. */\n getQueue<TPayload = unknown>(name: string): Queue<TPayload> | undefined {\n return this.queues.get(name) as Queue<TPayload> | undefined;\n }\n\n /** Returns the queue with the given name, or throws. */\n requireQueue<TPayload = unknown>(name: string): Queue<TPayload> {\n const queue = this.getQueue<TPayload>(name);\n if (!queue) {\n throw new Error(`Queue \"${name}\" not found. Did you call createQueue(\"${name}\")?`);\n }\n return queue;\n }\n\n /** Names of all queues registered on this client. */\n get queueNames(): string[] {\n return Array.from(this.queues.keys());\n }\n\n /** `true` after `init()` has completed successfully. */\n get isInitialised(): boolean {\n return this.initialised;\n }\n\n // -------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------\n\n on<K extends keyof QueueEvents>(event: K, listener: (...args: QueueEvents[K]) => void): this {\n this.emitter.on(event, listener);\n return this;\n }\n\n once<K extends keyof QueueEvents>(event: K, listener: (...args: QueueEvents[K]) => void): this {\n this.emitter.once(event, listener);\n return this;\n }\n\n off<K extends keyof QueueEvents>(event: K, listener: (...args: QueueEvents[K]) => void): this {\n this.emitter.off(event, listener);\n return this;\n }\n\n // -------------------------------------------------------------------------\n // Graceful shutdown\n // -------------------------------------------------------------------------\n\n /**\n * Stop all workers, close all queues, and release the storage connection.\n * Safe to call multiple times.\n */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n\n await Promise.all(Array.from(this.queues.values()).map((q) => q.close()));\n await this._storage.close();\n this.emitter.removeAllListeners();\n }\n\n // -------------------------------------------------------------------------\n // Advanced: custom StorageAdapter\n // -------------------------------------------------------------------------\n\n /**\n * Create a QueueClient with a fully custom StorageAdapter.\n *\n * `init()` will call `adapter.initialize()`.\n *\n * @example\n * const client = QueueClient.withAdapter(myAdapter);\n * await client.init();\n */\n static withAdapter(\n adapter: StorageAdapter,\n options: Omit<QueueClientOptions, \"dialect\" | \"connectionString\"> = {},\n ): QueueClient {\n const client = new QueueClient(options);\n client._storage = adapter;\n return client;\n }\n}\n","/**\n * queue-jobs-worker\n *\n * Reliable job queue and background worker system for Node.js.\n *\n * Quick start:\n *\n * import { QueueClient } from \"queue-jobs-worker\";\n *\n * // In-memory (dev / tests)\n * const client = new QueueClient();\n *\n * // Redis\n * const client = new QueueClient({ dialect: \"redis\", connectionString: \"redis://localhost:6379\" });\n * await client.init();\n *\n * // PostgreSQL\n * const client = new QueueClient({ dialect: \"postgres\", connectionString: \"postgresql://...\" });\n * await client.init();\n *\n * // MySQL\n * const client = new QueueClient({ dialect: \"mysql\", connectionString: \"mysql://...\" });\n * await client.init();\n *\n * @module queue-jobs-worker\n */\n\n// Core\nexport { QueueClient } from \"./core/client.js\";\nexport { Queue } from \"./core/queue.js\";\nexport { Job } from \"./core/job.js\";\nexport { Worker } from \"./core/worker.js\";\n\n// Storage adapters\nexport { InMemoryStorageAdapter } from \"./storage/in-memory.adapter.js\";\nexport { RedisStorageAdapter } from \"./storage/redis.adapter.js\";\nexport { PostgreSQLStorageAdapter } from \"./storage/postgres.adapter.js\";\nexport { MySQLStorageAdapter } from \"./storage/mysql.adapter.js\";\n\n// Event emitter\nexport { QueueEventEmitter } from \"./events/emitter.js\";\n\n// Utilities\nexport { calculateBackoff, nextRunAt } from \"./core/backoff.js\";\nexport { generateJobId } from \"./core/id.js\";\n\n// All public types\nexport type {\n JobStatus,\n JobAttempt,\n JobSchedule,\n JobOptions,\n BackoffStrategy,\n JobData,\n RateLimitOptions,\n QueueOptions,\n Processor,\n WorkerOptions,\n WorkerStatus,\n StorageAdapter,\n EnqueueInput,\n ClaimInput,\n ClaimResult,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n StorageDialect,\n ClientDefaults,\n QueueClientOptions,\n QueueEvents,\n} from \"./types/index.js\";\n"]}
1
+ {"version":3,"sources":["../src/storage/redis.adapter.ts","../src/storage/postgres.adapter.ts","../src/storage/mysql.adapter.ts","../src/core/job.ts","../src/core/backoff.ts","../src/core/id.ts","../src/core/worker.ts","../src/core/queue.ts","../src/storage/in-memory.adapter.ts","../src/events/emitter.ts","../src/core/client.ts","../src/index.ts"],"names":["rowToJob","CREATE_JOBS_TABLE","CREATE_RATE_TABLE","randomUUID","job","RedisStorageAdapter","PostgreSQLStorageAdapter","MySQLStorageAdapter"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * RedisStorageAdapter (node-redis v4/v5)\n *\n * Key layout — prefix: \"qjw:\"\n * ─────────────────────────────────────────────────────────────────────────\n * qjw:job:{id} → Hash — all job fields (flat strings)\n * qjw:queue:{name}:waiting → Sorted Set score = -priority\n * (ZPOPMIN → highest priority first)\n * qjw:queue:{name}:delayed → Sorted Set score = runAt epoch-ms\n * qjw:queue:{name}:active → Set\n * qjw:queue:{name}:completed → Set\n * qjw:queue:{name}:dead → Set\n * qjw:rate:{name} → String sliding-window counter\n * qjw:rate:{name}:ts → String window-start epoch-ms\n * ─────────────────────────────────────────────────────────────────────────\n *\n * Atomic claim\n * ─────────────────────────────────────────────────────────────────────────\n * A Lua script executed by Redis guarantees that two concurrent workers\n * cannot claim the same job.\n * ─────────────────────────────────────────────────────────────────────────\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus, JobAttempt } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy import — redis is an optional peer dependency\n// ---------------------------------------------------------------------------\n\n// Type-only import — runtime import is deferred inside loadRedis().\nimport type { RedisClientType } from \"redis\";\nimport type * as RedisModule from \"redis\";\n\nasync function loadRedis(): Promise<typeof RedisModule> {\n try {\n return await import(\"redis\");\n } catch {\n throw new Error(\n 'RedisStorageAdapter requires the \"redis\" package (node-redis v4+).\\n' +\n \"Install it: npm install redis\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Key helpers\n// ---------------------------------------------------------------------------\n\nconst PREFIX = \"qjw:\";\n\nconst k = {\n job: (id: string) => `${PREFIX}job:${id}`,\n waiting: (q: string) => `${PREFIX}queue:${q}:waiting`,\n delayed: (q: string) => `${PREFIX}queue:${q}:delayed`,\n active: (q: string) => `${PREFIX}queue:${q}:active`,\n completed: (q: string) => `${PREFIX}queue:${q}:completed`,\n dead: (q: string) => `${PREFIX}queue:${q}:dead`,\n rateCount: (q: string) => `${PREFIX}rate:${q}`,\n rateTs: (q: string) => `${PREFIX}rate:${q}:ts`,\n};\n\n// ---------------------------------------------------------------------------\n// Lua — atomic claim\n//\n// KEYS[1] waiting sorted set\n// KEYS[2] delayed sorted set\n// KEYS[3] active set\n// ARGV[1] lock ID (worker id)\n// ARGV[2] lockExpiresAt ISO string\n// ARGV[3] now epoch-ms (string)\n// ARGV[4] PREFIX (passed as arg to keep the script host-agnostic)\n//\n// Returns claimed job ID or empty string.\n// ---------------------------------------------------------------------------\n\nconst CLAIM_LUA = `\nlocal prefix = ARGV[4]\nlocal now_ms = tonumber(ARGV[3])\n\n-- Promote due delayed jobs into the waiting set.\nlocal due = redis.call('ZRANGEBYSCORE', KEYS[2], '-inf', now_ms)\nfor _, jid in ipairs(due) do\n local pri_raw = redis.call('HGET', prefix .. 'job:' .. jid, 'priority')\n local pri = tonumber(pri_raw) or 0\n redis.call('ZADD', KEYS[1], -pri, jid)\n redis.call('ZREM', KEYS[2], jid)\n redis.call('HSET', prefix .. 'job:' .. jid, 'status', 'waiting')\nend\n\n-- Pop the top-priority job.\nlocal items = redis.call('ZPOPMIN', KEYS[1], 1)\nif #items == 0 then return '' end\nlocal job_id = items[1]\n\n-- Lock it.\nredis.call('SADD', KEYS[3], job_id)\nredis.call('HSET', prefix .. 'job:' .. job_id,\n 'status', 'active',\n 'lockId', ARGV[1],\n 'lockExpiresAt', ARGV[2],\n 'updatedAt', ARGV[2]\n)\nreturn job_id\n`;\n\n// ---------------------------------------------------------------------------\n// Lua — atomic stalled-job recovery (per job, compare-and-swap)\n//\n// The non-atomic read→decide→write pattern in the old recoverStalledJobs()\n// had a TOCTOU race: a worker could complete or renew its lock between the\n// read pipeline and the write pipeline, causing recovery to overwrite a valid\n// active job back to \"waiting\".\n//\n// This script closes that window by performing the validity re-check and the\n// state update as a single indivisible Redis command. The caller passes the\n// lockExpiresAt and lockId it observed; the script re-reads those fields and\n// aborts if they differ (compare-and-swap). This is the same pattern used\n// by BullMQ and Redlock for stale-lock detection.\n//\n// KEYS[1] job hash key e.g. \"qjw:job:<id>\"\n// KEYS[2] active set e.g. \"qjw:queue:<name>:active\"\n// KEYS[3] waiting sorted set e.g. \"qjw:queue:<name>:waiting\"\n// ARGV[1] job ID — member value for SREM / ZADD\n// ARGV[2] expected lockExpiresAt — caller's observed value\n// ARGV[3] expected lockId — caller's observed value\n// ARGV[4] recovery timestamp (ISO) — written to updatedAt\n// ARGV[5] priority score (string) — ZADD score (-priority → ZPOPMIN highest first)\n//\n// Returns 1 when the job was recovered, 0 when the read was stale (skipped).\n// ---------------------------------------------------------------------------\n\nconst RECOVER_STALLED_LUA = `\nlocal job_key = KEYS[1]\nlocal active_set = KEYS[2]\nlocal waiting_zset = KEYS[3]\n\nlocal job_id = ARGV[1]\nlocal expected_exp = ARGV[2]\nlocal expected_lock = ARGV[3]\nlocal recovery_ts = ARGV[4]\nlocal priority_score = tonumber(ARGV[5])\n\n-- Re-read the three guard fields in one atomic HMGET.\nlocal fields = redis.call('HMGET', job_key, 'lockExpiresAt', 'lockId', 'status')\nlocal current_exp = fields[1] or ''\nlocal current_lock = fields[2] or ''\nlocal current_status = fields[3] or ''\n\n-- Guard 1: job must still be active.\n-- If the worker already completed, failed, or moved to DLQ, skip.\nif current_status ~= 'active' then return 0 end\n\n-- Guard 2: lockId must not have changed.\n-- A different worker may have claimed the job after the original lock expired\n-- and before this script runs.\nif current_lock ~= expected_lock then return 0 end\n\n-- Guard 3: lockExpiresAt must be exactly what the caller observed.\n-- If the worker renewed its lock the timestamp will be later than observed;\n-- the CAS mismatch catches that without needing ISO→epoch conversion in Lua.\nif current_exp ~= expected_exp then return 0 end\n\n-- All guards passed — recover atomically.\nredis.call('HSET', job_key,\n 'status', 'waiting',\n 'lockId', '',\n 'lockExpiresAt', '',\n 'updatedAt', recovery_ts\n)\nredis.call('SREM', active_set, job_id)\nredis.call('ZADD', waiting_zset, priority_score, job_id)\nreturn 1\n`;\n\n// ---------------------------------------------------------------------------\n// Lua — atomic lock renewal\n//\n// KEYS[1] job hash key e.g. \"qjw:job:<id>\"\n// ARGV[1] lock ID (expected current owner)\n// ARGV[2] new lockExpiresAt (ISO)\n// ARGV[3] now (ISO string)\n//\n// Returns 1 when lock was renewed, 0 when ownership lost or job not active.\n// ---------------------------------------------------------------------------\n\nconst RENEW_LOCK_LUA = `\nlocal job_key = KEYS[1]\nlocal lock_id = ARGV[1]\nlocal new_exp = ARGV[2]\nlocal now_iso = ARGV[3]\n\nlocal fields = redis.call('HMGET', job_key, 'status', 'lockId', 'lockExpiresAt')\nlocal status = fields[1] or ''\nlocal current_lock = fields[2] or ''\nlocal current_exp = fields[3] or ''\n\nif status ~= 'active' then return 0 end\nif current_lock ~= lock_id then return 0 end\nif current_exp == '' or current_exp <= now_iso then return 0 end\n\nredis.call('HSET', job_key, 'lockExpiresAt', new_exp, 'updatedAt', now_iso)\nreturn 1\n`;\n\n// ---------------------------------------------------------------------------\n// Serialisation helpers\n// ---------------------------------------------------------------------------\n\ntype FlatHash = Record<string, string>;\n\nfunction jobToHash(job: JobData<unknown>): FlatHash {\n return {\n id: job.id,\n queue: job.queue,\n type: job.type,\n payload: JSON.stringify(job.payload),\n status: job.status,\n attemptsMade: String(job.attemptsMade),\n maxAttempts: String(job.maxAttempts),\n retryDelay: String(job.retryDelay),\n backoff: job.backoff,\n timeout: String(job.timeout),\n priority: String(job.priority),\n runAt: job.runAt,\n ...(job.cron !== undefined ? { cron: job.cron } : {}),\n attempts: JSON.stringify(job.attempts),\n lockId: job.lockId ?? \"\",\n lockExpiresAt: job.lockExpiresAt ?? \"\",\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n completedAt: job.completedAt ?? \"\",\n failedAt: job.failedAt ?? \"\",\n };\n}\n\nfunction hashToJob<TPayload>(h: FlatHash): JobData<TPayload> {\n const job: JobData<TPayload> = {\n id: h[\"id\"] ?? \"\",\n queue: h[\"queue\"] ?? \"\",\n type: h[\"type\"] ?? \"\",\n payload: JSON.parse(h[\"payload\"] ?? \"null\") as TPayload,\n status: (h[\"status\"] ?? \"waiting\") as JobStatus,\n attemptsMade: Number(h[\"attemptsMade\"] ?? 0),\n maxAttempts: Number(h[\"maxAttempts\"] ?? 1),\n retryDelay: Number(h[\"retryDelay\"] ?? 1000),\n backoff: (h[\"backoff\"] ?? \"exponential\") as JobData<TPayload>[\"backoff\"],\n timeout: Number(h[\"timeout\"] ?? 30000),\n priority: Number(h[\"priority\"] ?? 0),\n runAt: h[\"runAt\"] ?? new Date().toISOString(),\n attempts: JSON.parse(h[\"attempts\"] ?? \"[]\") as JobAttempt[],\n lockId: h[\"lockId\"] || null,\n lockExpiresAt: h[\"lockExpiresAt\"] || null,\n createdAt: h[\"createdAt\"] ?? new Date().toISOString(),\n updatedAt: h[\"updatedAt\"] ?? new Date().toISOString(),\n completedAt: h[\"completedAt\"] || null,\n failedAt: h[\"failedAt\"] || null,\n };\n if (h[\"cron\"]) job.cron = h[\"cron\"];\n return job;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class RedisStorageAdapter implements StorageAdapter {\n private client!: RedisClientType;\n private readonly url: string;\n\n constructor(connectionString: string) {\n this.url = connectionString;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const { createClient } = await loadRedis();\n\n this.client = createClient({ url: this.url }) as RedisClientType;\n\n // Surface connection errors as exceptions (node-redis v4 hides them otherwise).\n this.client.on(\"error\", () => {\n // Handled by the connect() rejection below on first connect.\n // Subsequent errors are emitted on the client; callers should listen if needed.\n });\n\n await this.client.connect();\n\n // Verify the connection is healthy.\n const pong = await this.client.ping();\n if (pong !== \"PONG\") {\n throw new Error(\n \"RedisStorageAdapter: PING returned unexpected response. Connection may be unhealthy.\",\n );\n }\n }\n\n async close(): Promise<void> {\n if (this.client) {\n await this.client.quit();\n }\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n const now = new Date().toISOString();\n const runAtMs = new Date(input.runAt).getTime();\n const isDelayed = runAtMs > Date.now();\n\n const job: JobData<TPayload> = {\n id: input.id,\n queue: input.queue,\n type: input.type,\n payload: input.payload,\n status: isDelayed ? \"delayed\" : \"waiting\",\n attemptsMade: 0,\n maxAttempts: input.maxAttempts,\n retryDelay: input.retryDelay,\n backoff: input.backoff,\n timeout: input.timeout,\n priority: input.priority,\n runAt: input.runAt,\n ...(input.cron !== undefined ? { cron: input.cron } : {}),\n attempts: [],\n lockId: null,\n lockExpiresAt: null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n failedAt: null,\n };\n\n const key = k.job(input.id);\n\n // Idempotent — return existing job if already present.\n const exists = await this.client.exists(key);\n if (exists) {\n const hash = await this.client.hGetAll(key);\n return hashToJob<TPayload>(hash);\n }\n\n // Persist in a pipeline.\n const multi = this.client.multi();\n multi.hSet(key, jobToHash(job as JobData<unknown>));\n\n if (isDelayed) {\n multi.zAdd(k.delayed(input.queue), { score: runAtMs, value: input.id });\n } else {\n // Negative priority score → ZPOPMIN yields highest priority first.\n multi.zAdd(k.waiting(input.queue), { score: -input.priority, value: input.id });\n }\n\n await multi.exec();\n return job;\n }\n\n // -------------------------------------------------------------------------\n // Claim (atomic Lua)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const nowMs = new Date(now).getTime();\n const lockExpiresAt = new Date(nowMs + lockDuration).toISOString();\n\n // node-redis v4 eval signature:\n // client.eval(script, { keys, arguments })\n const jobId = (await this.client.eval(CLAIM_LUA, {\n keys: [k.waiting(queue), k.delayed(queue), k.active(queue)],\n arguments: [lockId, lockExpiresAt, String(nowMs), PREFIX],\n })) as string;\n\n if (!jobId) return null;\n\n const hash = await this.client.hGetAll(k.job(jobId));\n if (!hash || Object.keys(hash).length === 0) return null;\n\n return hashToJob<TPayload>(hash);\n }\n\n // -------------------------------------------------------------------------\n // Renew lock\n // -------------------------------------------------------------------------\n\n async renewLock(jobId: string, lockId: string, lockDuration: number): Promise<boolean> {\n const nowMs = Date.now();\n const nowIso = new Date(nowMs).toISOString();\n const newExpiresAt = new Date(nowMs + lockDuration).toISOString();\n\n const res = await this.client.eval(RENEW_LOCK_LUA, {\n keys: [k.job(jobId)],\n arguments: [lockId, newExpiresAt, nowIso],\n });\n\n return Number(res) === 1;\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string, lockId?: string): Promise<void> {\n const now = new Date().toISOString();\n const hash = await this.client.hGetAll(k.job(jobId));\n if (!hash || Object.keys(hash).length === 0) return;\n\n if (lockId !== undefined) {\n if (hash[\"status\"] !== \"active\" || hash[\"lockId\"] !== lockId) return;\n }\n\n const queue = hash[\"queue\"] ?? \"\";\n const multi = this.client.multi();\n multi.hSet(k.job(jobId), {\n status: \"completed\",\n lockId: \"\",\n lockExpiresAt: \"\",\n completedAt: now,\n updatedAt: now,\n });\n multi.sRem(k.active(queue), jobId);\n multi.sAdd(k.completed(queue), jobId);\n await multi.exec();\n }\n\n // -------------------------------------------------------------------------\n // Requeue\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const now = new Date().toISOString();\n const hash = await this.client.hGetAll(k.job(input.jobId));\n if (!hash || Object.keys(hash).length === 0) return;\n\n if (input.lockId !== undefined) {\n if (hash[\"status\"] !== \"active\" || hash[\"lockId\"] !== input.lockId) return;\n }\n\n const queue = hash[\"queue\"] ?? \"\";\n const attempts: JobAttempt[] = JSON.parse(hash[\"attempts\"] ?? \"[]\") as JobAttempt[];\n\n attempts.push({\n attempt: input.attemptNumber,\n startedAt: hash[\"updatedAt\"] ?? now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n const multi = this.client.multi();\n multi.hSet(k.job(input.jobId), {\n status: \"waiting\",\n attemptsMade: String(input.attemptNumber),\n attempts: JSON.stringify(attempts),\n runAt: input.runAt,\n lockId: \"\",\n lockExpiresAt: \"\",\n updatedAt: now,\n });\n multi.sRem(k.active(queue), input.jobId);\n // Put back in delayed set so the claim script can promote it when due.\n multi.zAdd(k.delayed(queue), {\n score: new Date(input.runAt).getTime(),\n value: input.jobId,\n });\n await multi.exec();\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const now = new Date().toISOString();\n const hash = await this.client.hGetAll(k.job(input.jobId));\n if (!hash || Object.keys(hash).length === 0) return;\n\n if (input.lockId !== undefined) {\n if (hash[\"status\"] !== \"active\" || hash[\"lockId\"] !== input.lockId) return;\n }\n\n const queue = hash[\"queue\"] ?? \"\";\n const attempts: JobAttempt[] = JSON.parse(hash[\"attempts\"] ?? \"[]\") as JobAttempt[];\n\n attempts.push({\n attempt: input.attemptNumber,\n startedAt: hash[\"updatedAt\"] ?? now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n const multi = this.client.multi();\n multi.hSet(k.job(input.jobId), {\n status: \"dead\",\n attemptsMade: String(input.attemptNumber),\n attempts: JSON.stringify(attempts),\n lockId: \"\",\n lockExpiresAt: \"\",\n failedAt: now,\n updatedAt: now,\n });\n multi.sRem(k.active(queue), input.jobId);\n multi.sAdd(k.dead(queue), input.jobId);\n await multi.exec();\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string, lockId?: string): Promise<void> {\n const hash = await this.client.hGetAll(k.job(jobId));\n if (!hash || Object.keys(hash).length === 0) return;\n\n if (lockId !== undefined) {\n if (hash[\"status\"] !== \"active\" || hash[\"lockId\"] !== lockId) return;\n }\n\n const now = new Date().toISOString();\n // Set lockExpiresAt to now (already-expired) rather than clearing it to an\n // empty string. recoverStalledJobs() skips entries where lockExpiresAt is\n // falsy, so an empty string would leave the job permanently stuck.\n await this.client.hSet(k.job(jobId), {\n lockId: \"\",\n lockExpiresAt: now,\n updatedAt: now,\n });\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs (atomic compare-and-swap per job via Lua)\n //\n // Previous implementation: two-phase read pipeline → write pipeline.\n // Race condition: a worker could complete or renew its lock between the two\n // phases, causing recovery to overwrite a legitimately-active job.\n //\n // Fix (issue #6): for each candidate job the RECOVER_STALLED_LUA script\n // re-reads lockExpiresAt, lockId, and status atomically and only applies\n // the recovery if all three still match what was observed in the read phase\n // (compare-and-swap). If the worker renewed or completed the job in the\n // window between the read and the Lua call, the CAS mismatch causes the\n // script to return 0 and the job is left untouched.\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const nowMs = new Date(now).getTime();\n const activeIds = await this.client.sMembers(k.active(queue));\n if (activeIds.length === 0) return [];\n\n // Phase 1 — read candidate fields.\n // We fetch lockExpiresAt, lockId, and priority for all active jobs in one\n // pipeline. These values become the \"expected\" snapshot passed to the Lua\n // CAS script. If any of these values change before the Lua executes, the\n // script will detect the mismatch and skip that job.\n const fetchPipeline = this.client.multi();\n for (const jobId of activeIds) {\n fetchPipeline.hmGet(k.job(jobId), [\"lockExpiresAt\", \"lockId\", \"priority\"]);\n }\n const fetchResults = await fetchPipeline.exec();\n\n // Phase 2 — per-job atomic CAS via Lua.\n // Each eval call is an independent atomic unit in Redis; no pipeline is\n // needed here because the script itself guarantees atomicity per job.\n const recovered: string[] = [];\n const evalPromises: Promise<unknown>[] = [];\n\n for (let i = 0; i < activeIds.length; i++) {\n const jobId = activeIds[i] as string;\n const fields = fetchResults[i] as unknown as\n [string | null, string | null, string | null] | null;\n if (!fields) continue;\n\n const [lockExpiresAt, lockId, priorityStr] = fields;\n\n // Pre-filter: skip jobs whose lock has not yet expired according to the\n // snapshot. This avoids unnecessary Lua round-trips for healthy jobs.\n if (!lockExpiresAt) continue;\n if (new Date(lockExpiresAt).getTime() > nowMs) continue;\n\n const priority = Number(priorityStr ?? \"0\");\n const priorityScore = String(-priority); // negative → ZPOPMIN yields highest first\n\n // The Lua script performs a CAS: it re-reads lockExpiresAt, lockId, and\n // status atomically, aborts if anything has changed, and only then\n // applies the recovery. Returns 1 on recovery, 0 on stale skip.\n const p = this.client\n .eval(RECOVER_STALLED_LUA, {\n keys: [k.job(jobId), k.active(queue), k.waiting(queue)],\n arguments: [jobId, lockExpiresAt, lockId ?? \"\", now, priorityScore],\n })\n .then((result) => {\n if (result === 1) recovered.push(jobId);\n });\n\n evalPromises.push(p);\n }\n\n // Wait for all CAS scripts to finish before returning.\n await Promise.all(evalPromises);\n\n return recovered;\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n const hash = await this.client.hGetAll(k.job(jobId));\n if (!hash || Object.keys(hash).length === 0) return null;\n return hashToJob<TPayload>(hash);\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n\n let ids: string[] = [];\n\n if (queue && status) {\n switch (status) {\n case \"waiting\":\n ids = await this.client.zRange(k.waiting(queue), 0, -1);\n break;\n case \"delayed\":\n ids = await this.client.zRange(k.delayed(queue), 0, -1);\n break;\n case \"active\":\n ids = await this.client.sMembers(k.active(queue));\n break;\n case \"completed\":\n ids = await this.client.sMembers(k.completed(queue));\n break;\n case \"dead\":\n ids = await this.client.sMembers(k.dead(queue));\n break;\n default:\n ids = [];\n }\n } else {\n // Scan all job keys — suitable for dev/small datasets only.\n for await (const key of this.client.scanIterator({\n MATCH: `${PREFIX}job:*`,\n COUNT: 500,\n })) {\n const keyStr: string = Array.isArray(key) ? String(key[0]) : String(key);\n ids.push(keyStr.replace(`${PREFIX}job:`, \"\"));\n }\n }\n\n const results: JobData<TPayload>[] = [];\n const page = ids.slice(offset, offset + limit);\n\n for (const id of page) {\n const job = await this.getJob<TPayload>(id);\n if (!job) continue;\n if (queue && job.queue !== queue) continue;\n if (status && job.status !== status) continue;\n results.push(job);\n }\n\n return results;\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const [waiting, delayed, active, completed, dead] = await Promise.all([\n this.client.zCard(k.waiting(queue)),\n this.client.zCard(k.delayed(queue)),\n this.client.sCard(k.active(queue)),\n this.client.sCard(k.completed(queue)),\n this.client.sCard(k.dead(queue)),\n ]);\n\n return { waiting, delayed, active, completed, dead };\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n const ck = k.rateCount(queue);\n const tk = k.rateTs(queue);\n\n const windowStart = await this.client.get(tk);\n const ttlSec = Math.ceil(windowMs / 1000);\n\n if (!windowStart || nowMs - Number(windowStart) >= windowMs) {\n // Fresh window.\n const multi = this.client.multi();\n multi.set(ck, \"1\");\n multi.expire(ck, ttlSec);\n multi.set(tk, String(nowMs));\n multi.expire(tk, ttlSec);\n await multi.exec();\n return true;\n }\n\n const count = await this.client.incr(ck);\n if (count > max) {\n await this.client.decr(ck);\n return false;\n }\n\n return true;\n }\n}\n","/**\n * PostgreSQLStorageAdapter (pg / node-postgres)\n *\n * Table prefix: \"qjw_\"\n *\n * Tables created on init (if they do not exist):\n * qjw_jobs — persists every job and its full state\n * qjw_rate_limits — sliding-window rate-limit counters\n *\n * Atomicity\n * ─────────────────────────────────────────────────────────────────────────\n * Claim uses SELECT … FOR UPDATE SKIP LOCKED inside a transaction so\n * concurrent workers never receive the same row.\n * ─────────────────────────────────────────────────────────────────────────\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy import — pg is an optional peer dependency\n// ---------------------------------------------------------------------------\n\n// These type-only imports are used solely for type annotations.\n// The actual runtime import is deferred inside loadPg() to keep pg optional.\nimport type { Pool as PgPool, PoolClient as PgPoolClient } from \"pg\";\nimport type * as PgModule from \"pg\";\n\nasync function loadPg(): Promise<typeof PgModule> {\n try {\n return await import(\"pg\");\n } catch {\n throw new Error(\n 'PostgreSQLStorageAdapter requires the \"pg\" package (node-postgres).\\n' +\n \"Install it: npm install pg\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// DDL\n// ---------------------------------------------------------------------------\n\nconst CREATE_JOBS_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_jobs (\n id TEXT NOT NULL PRIMARY KEY,\n queue TEXT NOT NULL,\n type TEXT NOT NULL,\n payload JSONB NOT NULL DEFAULT '{}',\n status TEXT NOT NULL DEFAULT 'waiting',\n attempts_made INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n retry_delay INTEGER NOT NULL DEFAULT 1000,\n backoff TEXT NOT NULL DEFAULT 'exponential',\n timeout INTEGER NOT NULL DEFAULT 30000,\n priority INTEGER NOT NULL DEFAULT 0,\n run_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n cron TEXT,\n attempts JSONB NOT NULL DEFAULT '[]',\n lock_id TEXT,\n lock_expires_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n completed_at TIMESTAMPTZ,\n failed_at TIMESTAMPTZ\n);\nCREATE INDEX IF NOT EXISTS qjw_jobs_claim_idx\n ON qjw_jobs (queue, status, run_at, priority DESC)\n WHERE status IN ('waiting', 'delayed');\n`;\n\nconst CREATE_RATE_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_rate_limits (\n queue TEXT NOT NULL PRIMARY KEY,\n count INTEGER NOT NULL DEFAULT 0,\n window_start BIGINT NOT NULL DEFAULT 0\n);\n`;\n\n// ---------------------------------------------------------------------------\n// Row → JobData\n// ---------------------------------------------------------------------------\n\ninterface JobRow {\n id: string;\n queue: string;\n type: string;\n payload: unknown;\n status: string;\n attempts_made: number;\n max_attempts: number;\n retry_delay: number;\n backoff: string;\n timeout: number;\n priority: number;\n run_at: Date;\n cron: string | null;\n attempts: unknown;\n lock_id: string | null;\n lock_expires_at: Date | null;\n created_at: Date;\n updated_at: Date;\n completed_at: Date | null;\n failed_at: Date | null;\n}\n\nfunction rowToJob<TPayload>(row: JobRow): JobData<TPayload> {\n const job: JobData<TPayload> = {\n id: row.id,\n queue: row.queue,\n type: row.type,\n payload: row.payload as TPayload,\n status: row.status as JobStatus,\n attemptsMade: row.attempts_made,\n maxAttempts: row.max_attempts,\n retryDelay: row.retry_delay,\n backoff: row.backoff as JobData<TPayload>[\"backoff\"],\n timeout: row.timeout,\n priority: row.priority,\n runAt: row.run_at.toISOString(),\n attempts: row.attempts as JobData<TPayload>[\"attempts\"],\n lockId: row.lock_id,\n lockExpiresAt: row.lock_expires_at ? row.lock_expires_at.toISOString() : null,\n createdAt: row.created_at.toISOString(),\n updatedAt: row.updated_at.toISOString(),\n completedAt: row.completed_at ? row.completed_at.toISOString() : null,\n failedAt: row.failed_at ? row.failed_at.toISOString() : null,\n };\n if (row.cron) job.cron = row.cron;\n return job;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class PostgreSQLStorageAdapter implements StorageAdapter {\n private pool!: PgPool;\n private readonly connectionString: string;\n\n constructor(connectionString: string) {\n this.connectionString = connectionString;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const { Pool } = await loadPg();\n\n this.pool = new Pool({ connectionString: this.connectionString });\n\n // Verify connectivity.\n const client: PgPoolClient = await this.pool.connect();\n try {\n await client.query(\"SELECT 1\");\n\n // Auto-create tables (idempotent).\n await client.query(CREATE_JOBS_TABLE);\n await client.query(CREATE_RATE_TABLE);\n } finally {\n client.release();\n }\n }\n\n async close(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n }\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n const res = await this.pool.query<JobRow>(\n `INSERT INTO qjw_jobs\n (id, queue, type, payload, status, attempts_made, max_attempts,\n retry_delay, backoff, timeout, priority, run_at, cron,\n attempts, lock_id, lock_expires_at, created_at, updated_at,\n completed_at, failed_at)\n VALUES ($1,$2,$3,$4,\n CASE WHEN $5::timestamptz > NOW() THEN 'delayed' ELSE 'waiting' END,\n 0,$6,$7,$8,$9,$10,$5,$11,\n '[]'::jsonb, NULL, NULL, NOW(), NOW(), NULL, NULL)\n ON CONFLICT (id) DO NOTHING\n RETURNING *`,\n [\n input.id,\n input.queue,\n input.type,\n JSON.stringify(input.payload),\n input.runAt,\n input.maxAttempts,\n input.retryDelay,\n input.backoff,\n input.timeout,\n input.priority,\n input.cron ?? null,\n ],\n );\n\n if (res.rows.length > 0) {\n return rowToJob<TPayload>(res.rows[0] as JobRow);\n }\n\n // Row already exists — return it.\n const existing = await this.pool.query<JobRow>(\"SELECT * FROM qjw_jobs WHERE id = $1\", [\n input.id,\n ]);\n return rowToJob<TPayload>(existing.rows[0] as JobRow);\n }\n\n // -------------------------------------------------------------------------\n // Claim (SELECT … FOR UPDATE SKIP LOCKED)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const lockExpiresAt = new Date(new Date(now).getTime() + lockDuration).toISOString();\n\n const client: PgPoolClient = await this.pool.connect();\n try {\n await client.query(\"BEGIN\");\n\n const res = await client.query<JobRow>(\n `SELECT * FROM qjw_jobs\n WHERE queue = $1\n AND status IN ('waiting', 'delayed')\n AND run_at <= $2::timestamptz\n AND (lock_expires_at IS NULL OR lock_expires_at <= $2::timestamptz)\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED`,\n [queue, now],\n );\n\n if (res.rows.length === 0) {\n await client.query(\"ROLLBACK\");\n return null;\n }\n\n const row = res.rows[0] as JobRow;\n\n await client.query(\n `UPDATE qjw_jobs\n SET status = 'active', lock_id = $1, lock_expires_at = $2, updated_at = NOW()\n WHERE id = $3`,\n [lockId, lockExpiresAt, row.id],\n );\n\n await client.query(\"COMMIT\");\n\n // Return the fresh row.\n const updated = await this.pool.query<JobRow>(\"SELECT * FROM qjw_jobs WHERE id = $1\", [\n row.id,\n ]);\n return rowToJob<TPayload>(updated.rows[0] as JobRow);\n } catch (err) {\n await client.query(\"ROLLBACK\");\n throw err;\n } finally {\n client.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Renew lock\n // -------------------------------------------------------------------------\n\n async renewLock(jobId: string, lockId: string, lockDuration: number): Promise<boolean> {\n const newLockExpiresAt = new Date(Date.now() + lockDuration).toISOString();\n const res = await this.pool.query(\n `UPDATE qjw_jobs\n SET lock_expires_at = $1::timestamptz,\n updated_at = NOW()\n WHERE id = $2 AND status = 'active' AND lock_id = $3 AND lock_expires_at > NOW()`,\n [newLockExpiresAt, jobId, lockId],\n );\n return (res.rowCount ?? 0) > 0;\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string, lockId?: string): Promise<void> {\n if (lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'completed', lock_id = NULL, lock_expires_at = NULL,\n completed_at = NOW(), updated_at = NOW()\n WHERE id = $1 AND status = 'active' AND lock_id = $2`,\n [jobId, lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'completed', lock_id = NULL, lock_expires_at = NULL,\n completed_at = NOW(), updated_at = NOW()\n WHERE id = $1`,\n [jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Requeue\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = {\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n };\n\n if (input.lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'waiting',\n attempts_made = $1,\n attempts = attempts || $2::jsonb,\n run_at = $3::timestamptz,\n lock_id = NULL,\n lock_expires_at = NULL,\n updated_at = NOW()\n WHERE id = $4 AND status = 'active' AND lock_id = $5`,\n [input.attemptNumber, JSON.stringify([attempt]), input.runAt, input.jobId, input.lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'waiting',\n attempts_made = $1,\n attempts = attempts || $2::jsonb,\n run_at = $3::timestamptz,\n lock_id = NULL,\n lock_expires_at = NULL,\n updated_at = NOW()\n WHERE id = $4`,\n [input.attemptNumber, JSON.stringify([attempt]), input.runAt, input.jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = {\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n };\n\n if (input.lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'dead',\n attempts_made = $1,\n attempts = attempts || $2::jsonb,\n lock_id = NULL,\n lock_expires_at = NULL,\n failed_at = NOW(),\n updated_at = NOW()\n WHERE id = $3 AND status = 'active' AND lock_id = $4`,\n [input.attemptNumber, JSON.stringify([attempt]), input.jobId, input.lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'dead',\n attempts_made = $1,\n attempts = attempts || $2::jsonb,\n lock_id = NULL,\n lock_expires_at = NULL,\n failed_at = NOW(),\n updated_at = NOW()\n WHERE id = $3`,\n [input.attemptNumber, JSON.stringify([attempt]), input.jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string, lockId?: string): Promise<void> {\n if (lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET lock_id = NULL, lock_expires_at = NOW(), updated_at = NOW()\n WHERE id = $1 AND status = 'active' AND lock_id = $2`,\n [jobId, lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET lock_id = NULL, lock_expires_at = NOW(), updated_at = NOW()\n WHERE id = $1`,\n [jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const res = await this.pool.query<{ id: string }>(\n `UPDATE qjw_jobs\n SET status = 'waiting', lock_id = NULL, lock_expires_at = NULL, updated_at = NOW()\n WHERE queue = $1\n AND status = 'active'\n AND lock_expires_at <= $2::timestamptz\n RETURNING id`,\n [queue, now],\n );\n return res.rows.map((r) => r.id);\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n const res = await this.pool.query<JobRow>(\"SELECT * FROM qjw_jobs WHERE id = $1\", [jobId]);\n if (res.rows.length === 0) return null;\n return rowToJob<TPayload>(res.rows[0] as JobRow);\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n let idx = 1;\n\n if (queue !== undefined) {\n conditions.push(`queue = $${idx++}`);\n params.push(queue);\n }\n if (status !== undefined) {\n conditions.push(`status = $${idx++}`);\n params.push(status);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n params.push(limit, offset);\n\n const res = await this.pool.query<JobRow>(\n `SELECT * FROM qjw_jobs ${where}\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT $${idx++} OFFSET $${idx}`,\n params,\n );\n\n return res.rows.map((r) => rowToJob<TPayload>(r));\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const res = await this.pool.query<{ status: string; count: string }>(\n \"SELECT status, COUNT(*)::int AS count FROM qjw_jobs WHERE queue = $1 GROUP BY status\",\n [queue],\n );\n\n const counts: Record<JobStatus, number> = {\n waiting: 0,\n active: 0,\n completed: 0,\n delayed: 0,\n dead: 0,\n };\n\n for (const row of res.rows) {\n if (row.status in counts) {\n counts[row.status as JobStatus] = Number(row.count);\n }\n }\n\n return counts;\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n\n const res = await this.pool.query<{ count: number; window_start: string }>(\n \"SELECT count, window_start FROM qjw_rate_limits WHERE queue = $1\",\n [queue],\n );\n\n if (res.rows.length === 0 || nowMs - Number(res.rows[0]?.window_start ?? 0) >= windowMs) {\n // New window — upsert with count = 1.\n await this.pool.query(\n `INSERT INTO qjw_rate_limits (queue, count, window_start)\n VALUES ($1, 1, $2)\n ON CONFLICT (queue) DO UPDATE\n SET count = 1, window_start = EXCLUDED.window_start`,\n [queue, nowMs],\n );\n return true;\n }\n\n const current = res.rows[0]!.count;\n if (current >= max) return false;\n\n await this.pool.query(\"UPDATE qjw_rate_limits SET count = count + 1 WHERE queue = $1\", [queue]);\n return true;\n }\n}\n","/**\n * MySQLStorageAdapter (mysql2/promise)\n *\n * Table prefix: \"qjw_\"\n *\n * Tables created on init (if they do not exist):\n * qjw_jobs — full job state\n * qjw_rate_limits — sliding-window rate-limit counters\n *\n * Atomicity\n * ─────────────────────────────────────────────────────────────────────────\n * Claim uses SELECT … FOR UPDATE SKIP LOCKED inside a transaction —\n * concurrent workers safely skip rows that are already being claimed.\n * ─────────────────────────────────────────────────────────────────────────\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy import — mysql2 is an optional peer dependency\n// ---------------------------------------------------------------------------\n\n// Type-only imports — runtime import is deferred inside loadMySQL2().\nimport type { Pool as MySQLPool, PoolConnection as MySQLConnection } from \"mysql2/promise\";\nimport type { RowDataPacket, ResultSetHeader } from \"mysql2/promise\";\nimport type * as MySQL2Module from \"mysql2/promise\";\n\nasync function loadMySQL2(): Promise<typeof MySQL2Module> {\n try {\n return await import(\"mysql2/promise\");\n } catch {\n throw new Error(\n 'MySQLStorageAdapter requires the \"mysql2\" package.\\n' + \"Install it: npm install mysql2\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// DDL\n// ---------------------------------------------------------------------------\n\nconst CREATE_JOBS_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_jobs (\n id VARCHAR(36) NOT NULL PRIMARY KEY,\n queue VARCHAR(255) NOT NULL,\n type VARCHAR(255) NOT NULL,\n payload JSON NOT NULL,\n status VARCHAR(20) NOT NULL DEFAULT 'waiting',\n attempts_made INT NOT NULL DEFAULT 0,\n max_attempts INT NOT NULL DEFAULT 3,\n retry_delay INT NOT NULL DEFAULT 1000,\n backoff VARCHAR(20) NOT NULL DEFAULT 'exponential',\n timeout INT NOT NULL DEFAULT 30000,\n priority INT NOT NULL DEFAULT 0,\n run_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n cron VARCHAR(255),\n attempts JSON NOT NULL,\n lock_id VARCHAR(255),\n lock_expires_at DATETIME(3),\n created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),\n completed_at DATETIME(3),\n failed_at DATETIME(3),\n INDEX qjw_jobs_claim_idx (queue, status, run_at, priority)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n`;\n\nconst CREATE_RATE_TABLE = `\nCREATE TABLE IF NOT EXISTS qjw_rate_limits (\n queue VARCHAR(255) NOT NULL PRIMARY KEY,\n count INT NOT NULL DEFAULT 0,\n window_start BIGINT NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n`;\n\n// ---------------------------------------------------------------------------\n// Row → JobData\n// ---------------------------------------------------------------------------\n\ninterface JobRow extends RowDataPacket {\n id: string;\n queue: string;\n type: string;\n payload: unknown;\n status: string;\n attempts_made: number;\n max_attempts: number;\n retry_delay: number;\n backoff: string;\n timeout: number;\n priority: number;\n run_at: Date;\n cron: string | null;\n attempts: unknown;\n lock_id: string | null;\n lock_expires_at: Date | null;\n created_at: Date;\n updated_at: Date;\n completed_at: Date | null;\n failed_at: Date | null;\n}\n\nfunction rowToJob<TPayload>(row: JobRow): JobData<TPayload> {\n // mysql2 returns JSON columns as parsed objects when supportBigNumbers/parseJson is on.\n const payload =\n typeof row.payload === \"string\"\n ? (JSON.parse(row.payload) as TPayload)\n : (row.payload as TPayload);\n\n const attempts =\n typeof row.attempts === \"string\" ? JSON.parse(row.attempts) : (row.attempts ?? []);\n\n const job: JobData<TPayload> = {\n id: row.id,\n queue: row.queue,\n type: row.type,\n payload,\n status: row.status as JobStatus,\n attemptsMade: Number(row.attempts_made),\n maxAttempts: Number(row.max_attempts),\n retryDelay: Number(row.retry_delay),\n backoff: row.backoff as JobData<TPayload>[\"backoff\"],\n timeout: Number(row.timeout),\n priority: Number(row.priority),\n runAt: row.run_at instanceof Date ? row.run_at.toISOString() : String(row.run_at),\n attempts: attempts as JobData<TPayload>[\"attempts\"],\n lockId: row.lock_id,\n lockExpiresAt: row.lock_expires_at instanceof Date ? row.lock_expires_at.toISOString() : null,\n createdAt:\n row.created_at instanceof Date ? row.created_at.toISOString() : String(row.created_at),\n updatedAt:\n row.updated_at instanceof Date ? row.updated_at.toISOString() : String(row.updated_at),\n completedAt: row.completed_at instanceof Date ? row.completed_at.toISOString() : null,\n failedAt: row.failed_at instanceof Date ? row.failed_at.toISOString() : null,\n };\n\n if (row.cron) job.cron = row.cron;\n return job;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class MySQLStorageAdapter implements StorageAdapter {\n private pool!: MySQLPool;\n private readonly connectionString: string;\n\n constructor(connectionString: string) {\n this.connectionString = connectionString;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n const mysql2 = await loadMySQL2();\n\n this.pool = mysql2.createPool({\n uri: this.connectionString,\n waitForConnections: true,\n connectionLimit: 10,\n queueLimit: 0,\n });\n\n // Verify connectivity and auto-create tables.\n const conn: MySQLConnection = await this.pool.getConnection();\n try {\n await conn.query(\"SELECT 1\");\n await conn.query(CREATE_JOBS_TABLE);\n await conn.query(CREATE_RATE_TABLE);\n } finally {\n conn.release();\n }\n }\n\n async close(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n }\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n const runAtMs = new Date(input.runAt).getTime();\n const isDelayed = runAtMs > Date.now();\n const status = isDelayed ? \"delayed\" : \"waiting\";\n\n await this.pool.query(\n `INSERT IGNORE INTO qjw_jobs\n (id, queue, type, payload, status, attempts_made, max_attempts,\n retry_delay, backoff, timeout, priority, run_at, cron,\n attempts, lock_id, lock_expires_at, created_at, updated_at,\n completed_at, failed_at)\n VALUES (?,?,?,?,?,0,?,?,?,?,?,?,?,?,NULL,NULL,NOW(3),NOW(3),NULL,NULL)`,\n [\n input.id,\n input.queue,\n input.type,\n JSON.stringify(input.payload),\n status,\n input.maxAttempts,\n input.retryDelay,\n input.backoff,\n input.timeout,\n input.priority,\n input.runAt,\n input.cron ?? null,\n JSON.stringify([]),\n ],\n );\n\n const rows = await this.pool.query<JobRow[]>(\"SELECT * FROM qjw_jobs WHERE id = ?\", [input.id]);\n const row = (rows[0] as unknown as JobRow[])[0] as JobRow;\n return rowToJob<TPayload>(row);\n }\n\n // -------------------------------------------------------------------------\n // Claim (transaction + SELECT … FOR UPDATE SKIP LOCKED)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const lockExpiresAt = new Date(new Date(now).getTime() + lockDuration)\n .toISOString()\n .slice(0, 23)\n .replace(\"T\", \" \");\n const nowMysql = new Date(now).toISOString().slice(0, 23).replace(\"T\", \" \");\n\n const conn: MySQLConnection = await this.pool.getConnection();\n try {\n await conn.beginTransaction();\n\n const [rows] = await conn.query<JobRow[]>(\n `SELECT * FROM qjw_jobs\n WHERE queue = ?\n AND status IN ('waiting', 'delayed')\n AND run_at <= ?\n AND (lock_expires_at IS NULL OR lock_expires_at <= ?)\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED`,\n [queue, nowMysql, nowMysql],\n );\n\n if (!rows || rows.length === 0) {\n await conn.rollback();\n return null;\n }\n\n const row = rows[0] as JobRow;\n\n await conn.query(\n `UPDATE qjw_jobs\n SET status = 'active', lock_id = ?, lock_expires_at = ?, updated_at = NOW(3)\n WHERE id = ?`,\n [lockId, lockExpiresAt, row.id],\n );\n\n await conn.commit();\n\n // Re-fetch the updated row.\n const [updated] = await this.pool.query<JobRow[]>(\"SELECT * FROM qjw_jobs WHERE id = ?\", [\n row.id,\n ]);\n return rowToJob<TPayload>((updated as unknown as JobRow[])[0] as JobRow);\n } catch (err) {\n await conn.rollback();\n throw err;\n } finally {\n conn.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Renew lock\n // -------------------------------------------------------------------------\n\n async renewLock(jobId: string, lockId: string, lockDuration: number): Promise<boolean> {\n const newLockExpiresAt = new Date(Date.now() + lockDuration)\n .toISOString()\n .slice(0, 23)\n .replace(\"T\", \" \");\n const [result] = await this.pool.query<ResultSetHeader>(\n `UPDATE qjw_jobs\n SET lock_expires_at = ?, updated_at = NOW(3)\n WHERE id = ? AND status = 'active' AND lock_id = ? AND lock_expires_at > NOW(3)`,\n [newLockExpiresAt, jobId, lockId],\n );\n return result.affectedRows > 0;\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string, lockId?: string): Promise<void> {\n if (lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'completed', lock_id = NULL, lock_expires_at = NULL,\n completed_at = NOW(3), updated_at = NOW(3)\n WHERE id = ? AND status = 'active' AND lock_id = ?`,\n [jobId, lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'completed', lock_id = NULL, lock_expires_at = NULL,\n completed_at = NOW(3), updated_at = NOW(3)\n WHERE id = ?`,\n [jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Requeue\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = JSON.stringify({\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n if (input.lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'waiting',\n attempts_made = attempts_made + 1,\n attempts = JSON_ARRAY_APPEND(attempts, '$', CAST(? AS JSON)),\n run_at = ?,\n lock_id = NULL,\n lock_expires_at = NULL,\n updated_at = NOW(3)\n WHERE id = ? AND status = 'active' AND lock_id = ?`,\n [attempt, input.runAt, input.jobId, input.lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'waiting',\n attempts_made = attempts_made + 1,\n attempts = JSON_ARRAY_APPEND(attempts, '$', CAST(? AS JSON)),\n run_at = ?,\n lock_id = NULL,\n lock_expires_at = NULL,\n updated_at = NOW(3)\n WHERE id = ?`,\n [attempt, input.runAt, input.jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const now = new Date().toISOString();\n const attempt = JSON.stringify({\n attempt: input.attemptNumber,\n startedAt: now,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined ? { stack: input.stack } : {}),\n });\n\n if (input.lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'dead',\n attempts_made = attempts_made + 1,\n attempts = JSON_ARRAY_APPEND(attempts, '$', CAST(? AS JSON)),\n lock_id = NULL,\n lock_expires_at = NULL,\n failed_at = NOW(3),\n updated_at = NOW(3)\n WHERE id = ? AND status = 'active' AND lock_id = ?`,\n [attempt, input.jobId, input.lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET status = 'dead',\n attempts_made = attempts_made + 1,\n attempts = JSON_ARRAY_APPEND(attempts, '$', CAST(? AS JSON)),\n lock_id = NULL,\n lock_expires_at = NULL,\n failed_at = NOW(3),\n updated_at = NOW(3)\n WHERE id = ?`,\n [attempt, input.jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string, lockId?: string): Promise<void> {\n if (lockId !== undefined) {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET lock_id = NULL, lock_expires_at = NOW(3), updated_at = NOW(3)\n WHERE id = ? AND status = 'active' AND lock_id = ?`,\n [jobId, lockId],\n );\n } else {\n await this.pool.query(\n `UPDATE qjw_jobs\n SET lock_id = NULL, lock_expires_at = NOW(3), updated_at = NOW(3)\n WHERE id = ?`,\n [jobId],\n );\n }\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs (atomic — SELECT … FOR UPDATE inside transaction)\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const nowMysql = new Date(now).toISOString().slice(0, 23).replace(\"T\", \" \");\n\n const conn: MySQLConnection = await this.pool.getConnection();\n try {\n await conn.beginTransaction();\n\n // Lock the matching rows so no other worker can claim them concurrently.\n const [rows] = await conn.query<RowDataPacket[]>(\n `SELECT id FROM qjw_jobs\n WHERE queue = ?\n AND status = 'active'\n AND lock_expires_at <= ?\n FOR UPDATE SKIP LOCKED`,\n [queue, nowMysql],\n );\n\n const ids = (rows as RowDataPacket[]).map((r) => String(r[\"id\"]));\n\n if (ids.length > 0) {\n await conn.query(\n `UPDATE qjw_jobs\n SET status = 'waiting', lock_id = NULL, lock_expires_at = NULL, updated_at = NOW(3)\n WHERE id IN (?)`,\n [ids],\n );\n }\n\n await conn.commit();\n return ids;\n } catch (err) {\n await conn.rollback();\n throw err;\n } finally {\n conn.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n const [rows] = await this.pool.query<JobRow[]>(\"SELECT * FROM qjw_jobs WHERE id = ?\", [jobId]);\n const list = rows as unknown as JobRow[];\n if (!list || list.length === 0) return null;\n return rowToJob<TPayload>(list[0] as JobRow);\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (queue !== undefined) {\n conditions.push(\"queue = ?\");\n params.push(queue);\n }\n if (status !== undefined) {\n conditions.push(\"status = ?\");\n params.push(status);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n params.push(limit, offset);\n\n const [rows] = await this.pool.query<JobRow[]>(\n `SELECT * FROM qjw_jobs ${where}\n ORDER BY priority DESC, run_at ASC, created_at ASC\n LIMIT ? OFFSET ?`,\n params,\n );\n\n return (rows as unknown as JobRow[]).map((r) => rowToJob<TPayload>(r));\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const [rows] = await this.pool.query<RowDataPacket[]>(\n \"SELECT status, COUNT(*) AS count FROM qjw_jobs WHERE queue = ? GROUP BY status\",\n [queue],\n );\n\n const counts: Record<JobStatus, number> = {\n waiting: 0,\n active: 0,\n completed: 0,\n delayed: 0,\n dead: 0,\n };\n\n for (const row of rows as RowDataPacket[]) {\n const s = String(row[\"status\"]);\n if (s in counts) counts[s as JobStatus] = Number(row[\"count\"]);\n }\n\n return counts;\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n\n const [rows] = await this.pool.query<RowDataPacket[]>(\n \"SELECT count, window_start FROM qjw_rate_limits WHERE queue = ?\",\n [queue],\n );\n\n const existing = (rows as RowDataPacket[])[0];\n\n if (!existing || nowMs - Number(existing[\"window_start\"]) >= windowMs) {\n await this.pool.query(\n `INSERT INTO qjw_rate_limits (queue, count, window_start) VALUES (?,1,?)\n ON DUPLICATE KEY UPDATE count = 1, window_start = VALUES(window_start)`,\n [queue, nowMs],\n );\n return true;\n }\n\n if (Number(existing[\"count\"]) >= max) return false;\n\n await this.pool.query(\"UPDATE qjw_rate_limits SET count = count + 1 WHERE queue = ?\", [queue]);\n return true;\n }\n}\n","/**\n * Job\n *\n * A rich wrapper around the raw JobData record stored in the storage layer.\n * Exposes read-only accessors for all fields and a small set of helpers used\n * by Queue and Worker internals.\n *\n * The Job instance is passed directly to user-supplied Processor functions.\n * Users should never mutate the internal data object — all mutations happen\n * through the storage adapter.\n */\n\nimport type { JobData, JobStatus, JobAttempt, BackoffStrategy } from \"../types/job.types.js\";\n\nexport class Job<TPayload = unknown> {\n /** @internal Raw data record — treat as immutable outside storage layer. */\n readonly _data: JobData<TPayload>;\n\n constructor(data: JobData<TPayload>) {\n this._data = data;\n }\n\n // -------------------------------------------------------------------------\n // Identity\n // -------------------------------------------------------------------------\n\n /** Unique, stable job identifier. */\n get id(): string {\n return this._data.id;\n }\n\n /** Name of the queue this job belongs to. */\n get queue(): string {\n return this._data.queue;\n }\n\n /** Application-defined job type (matches the registered processor). */\n get type(): string {\n return this._data.type;\n }\n\n // -------------------------------------------------------------------------\n // Payload\n // -------------------------------------------------------------------------\n\n /**\n * User-supplied job payload.\n * Never log this value — it may contain sensitive data.\n */\n get data(): TPayload {\n return this._data.payload;\n }\n\n // -------------------------------------------------------------------------\n // Status & attempts\n // -------------------------------------------------------------------------\n\n /** Current lifecycle status. */\n get status(): JobStatus {\n return this._data.status;\n }\n\n /** Number of attempts already executed (0 = not yet started). */\n get attemptsMade(): number {\n return this._data.attemptsMade;\n }\n\n /** Maximum allowed attempts. */\n get maxAttempts(): number {\n return this._data.maxAttempts;\n }\n\n /** How many attempts remain (including the current one). */\n get attemptsRemaining(): number {\n return Math.max(0, this._data.maxAttempts - this._data.attemptsMade);\n }\n\n /** Ordered list of past attempt records. */\n get attemptHistory(): readonly JobAttempt[] {\n return this._data.attempts;\n }\n\n // -------------------------------------------------------------------------\n // Retry configuration\n // -------------------------------------------------------------------------\n\n /** Base delay in ms between retry attempts. */\n get retryDelay(): number {\n return this._data.retryDelay;\n }\n\n /** Backoff strategy applied on retry. */\n get backoff(): BackoffStrategy {\n return this._data.backoff;\n }\n\n // -------------------------------------------------------------------------\n // Execution config\n // -------------------------------------------------------------------------\n\n /** Per-attempt timeout in ms. */\n get timeout(): number {\n return this._data.timeout;\n }\n\n /** Processing priority — higher numbers are processed first. */\n get priority(): number {\n return this._data.priority;\n }\n\n // -------------------------------------------------------------------------\n // Scheduling\n // -------------------------------------------------------------------------\n\n /** ISO timestamp when this job is eligible to run. */\n get runAt(): string {\n return this._data.runAt;\n }\n\n /** Cron expression for recurring jobs, or undefined. */\n get cron(): string | undefined {\n return this._data.cron;\n }\n\n // -------------------------------------------------------------------------\n // Lock\n // -------------------------------------------------------------------------\n\n /** ID of the worker currently holding the lock, or null. */\n get lockId(): string | null {\n return this._data.lockId;\n }\n\n /** ISO timestamp when the current lock expires, or null. */\n get lockExpiresAt(): string | null {\n return this._data.lockExpiresAt;\n }\n\n // -------------------------------------------------------------------------\n // Timestamps\n // -------------------------------------------------------------------------\n\n /** ISO timestamp when the job was first enqueued. */\n get createdAt(): string {\n return this._data.createdAt;\n }\n\n /** ISO timestamp of the last status change. */\n get updatedAt(): string {\n return this._data.updatedAt;\n }\n\n /** ISO timestamp when the job completed successfully, or null. */\n get completedAt(): string | null {\n return this._data.completedAt;\n }\n\n /** ISO timestamp when the job was moved to the DLQ, or null. */\n get failedAt(): string | null {\n return this._data.failedAt;\n }\n\n // -------------------------------------------------------------------------\n // Derived helpers\n // -------------------------------------------------------------------------\n\n /** Returns true when the job has been successfully processed. */\n isCompleted(): boolean {\n return this._data.status === \"completed\";\n }\n\n /** Returns true when the job is in the Dead Letter Queue. */\n isDead(): boolean {\n return this._data.status === \"dead\";\n }\n\n /** Returns true when the job is currently being processed by a worker. */\n isActive(): boolean {\n return this._data.status === \"active\";\n }\n\n /** Returns true when the job is waiting to be claimed. */\n isWaiting(): boolean {\n return this._data.status === \"waiting\";\n }\n\n /** Returns true when the job is scheduled for a future time. */\n isDelayed(): boolean {\n return this._data.status === \"delayed\";\n }\n\n // -------------------------------------------------------------------------\n // Debug representation (intentionally omits payload)\n // -------------------------------------------------------------------------\n\n toJSON(): Record<string, unknown> {\n return {\n id: this._data.id,\n queue: this._data.queue,\n type: this._data.type,\n status: this._data.status,\n attemptsMade: this._data.attemptsMade,\n maxAttempts: this._data.maxAttempts,\n priority: this._data.priority,\n runAt: this._data.runAt,\n createdAt: this._data.createdAt,\n updatedAt: this._data.updatedAt,\n completedAt: this._data.completedAt,\n failedAt: this._data.failedAt,\n // Payload intentionally excluded to prevent accidental logging.\n };\n }\n\n toString(): string {\n return `Job(${this._data.id}, type=${this._data.type}, status=${this._data.status}, attempt=${this._data.attemptsMade}/${this._data.maxAttempts})`;\n }\n}\n","/**\n * Backoff strategies\n *\n * Calculates the delay (in ms) before a failed job is re-queued.\n *\n * - fixed — always uses `baseDelay`\n * - linear — baseDelay × attemptNumber\n * - exponential — baseDelay × 2^(attemptNumber - 1), capped at MAX_DELAY\n */\n\nimport type { BackoffStrategy } from \"../types/job.types.js\";\n\n/** Maximum delay cap for exponential backoff (10 minutes). */\nconst MAX_DELAY_MS = 10 * 60 * 1_000;\n\n/**\n * Returns the retry delay in milliseconds for the given attempt.\n *\n * @param strategy - Backoff strategy name.\n * @param baseDelay - Base delay in ms (from job / queue / client config).\n * @param attemptNumber - The 1-based attempt number that just failed.\n */\nexport function calculateBackoff(\n strategy: BackoffStrategy,\n baseDelay: number,\n attemptNumber: number,\n): number {\n const attempt = Math.max(1, attemptNumber);\n\n switch (strategy) {\n case \"fixed\":\n return baseDelay;\n\n case \"linear\":\n return baseDelay * attempt;\n\n case \"exponential\": {\n const delay = baseDelay * Math.pow(2, attempt - 1);\n return Math.min(delay, MAX_DELAY_MS);\n }\n\n default: {\n // Exhaustive guard — TypeScript will catch unhandled variants at\n // compile time; this is a runtime safety net.\n const _exhaustive: never = strategy;\n void _exhaustive;\n return baseDelay;\n }\n }\n}\n\n/**\n * Returns the ISO timestamp at which a job should next become eligible,\n * given the current time and the calculated delay.\n */\nexport function nextRunAt(delayMs: number, fromDate: Date = new Date()): string {\n return new Date(fromDate.getTime() + delayMs).toISOString();\n}\n","/**\n * Job ID generation\n *\n * Uses the built-in `crypto.randomUUID()` available in Node.js >= 14.17.\n * No external dependency needed.\n */\n\nimport { randomUUID } from \"node:crypto\";\n\n/** Generate a new unique job ID. */\nexport function generateJobId(): string {\n return randomUUID();\n}\n","/**\n * Worker\n *\n * Claims jobs from a queue and executes registered processors concurrently.\n *\n * Key guarantees:\n * - At-most `concurrency` jobs run simultaneously.\n * - Job claiming is delegated to the storage adapter's atomic `claim()` call.\n * - Failed jobs are requeued (with backoff) or moved to the DLQ.\n * - Stalled jobs (expired locks from crashed workers) are recovered\n * periodically.\n * - Graceful shutdown: stop claiming, wait for active jobs, release any\n * locks that could not be finished within shutdownTimeout.\n * - Cron jobs: re-enqueued immediately after each successful execution.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { Cron } from \"croner\";\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type { WorkerOptions, WorkerStatus, Processor } from \"../types/worker.types.js\";\nimport type { QueueOptions } from \"../types/queue.types.js\";\nimport type { ClientDefaults } from \"../types/client.types.js\";\nimport { Job } from \"./job.js\";\nimport { calculateBackoff, nextRunAt } from \"./backoff.js\";\nimport { generateJobId } from \"./id.js\";\nimport type { QueueEventEmitter } from \"../events/emitter.js\";\n\ntype ResolvedDefaults = Required<ClientDefaults>;\n\n// ---------------------------------------------------------------------------\n// Resolved config helper\n// ---------------------------------------------------------------------------\n\nfunction resolveConfig(\n workerOptions: WorkerOptions,\n queueOptions: QueueOptions,\n defaults: ResolvedDefaults,\n) {\n return {\n concurrency: workerOptions.concurrency ?? queueOptions.concurrency ?? defaults.concurrency,\n shutdownTimeout: workerOptions.shutdownTimeout ?? 30_000,\n pollInterval: queueOptions.pollInterval ?? defaults.pollInterval,\n stalledInterval: queueOptions.stalledInterval ?? defaults.stalledInterval,\n lockDuration: queueOptions.lockDuration ?? defaults.lockDuration,\n rateLimit: queueOptions.rateLimit ?? defaults.rateLimit,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Worker\n// ---------------------------------------------------------------------------\n\nexport class Worker {\n /** Unique identifier for this worker instance. */\n readonly id: string;\n\n private readonly queueName: string;\n private readonly storage: StorageAdapter;\n private readonly emitter: QueueEventEmitter;\n private readonly processors: Map<string, Processor<unknown>>;\n private readonly config: ReturnType<typeof resolveConfig>;\n\n private _status: WorkerStatus = \"idle\";\n private activeCount = 0;\n\n /**\n * Tracks job IDs currently being processed so we can release their locks\n * when graceful shutdown times out before they finish.\n */\n private readonly activeJobIds = new Set<string>();\n\n private pollTimer: NodeJS.Timeout | null = null;\n private stalledTimer: NodeJS.Timeout | null = null;\n\n /** Resolves when all active jobs finish during shutdown. */\n private drainResolve: (() => void) | null = null;\n\n constructor(\n queueName: string,\n storage: StorageAdapter,\n emitter: QueueEventEmitter,\n processors: Map<string, Processor<unknown>>,\n workerOptions: WorkerOptions,\n queueOptions: QueueOptions,\n defaults: ResolvedDefaults,\n ) {\n this.id = `worker:${queueName}:${randomUUID()}`;\n this.queueName = queueName;\n this.storage = storage;\n this.emitter = emitter;\n this.processors = processors;\n this.config = resolveConfig(workerOptions, queueOptions, defaults);\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n\n get status(): WorkerStatus {\n return this._status;\n }\n\n /** Start polling for jobs. */\n start(): void {\n if (this._status !== \"idle\" && this._status !== \"stopped\") {\n return;\n }\n\n this._status = \"running\";\n this.emitter.emit(\"worker:started\", this.id);\n this.emitter.emit(\"worker:status\", this.id, this._status);\n\n this.schedulePoll();\n this.scheduleStallCheck();\n }\n\n /**\n * Gracefully stop the worker.\n *\n * 1. Stop accepting new jobs.\n * 2. Wait up to `shutdownTimeout` ms for active jobs to finish.\n * 3. Release locks on any jobs that did not finish in time so another\n * worker can reclaim them.\n * 4. Emit stopped event.\n */\n async stop(): Promise<void> {\n if (this._status === \"stopped\" || this._status === \"stopping\") {\n return;\n }\n\n this._status = \"stopping\";\n this.emitter.emit(\"worker:status\", this.id, this._status);\n\n // Cancel timers.\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n if (this.stalledTimer) {\n clearTimeout(this.stalledTimer);\n this.stalledTimer = null;\n }\n\n // Wait for active jobs to finish, but no longer than shutdownTimeout.\n if (this.activeCount > 0) {\n await Promise.race([\n new Promise<void>((resolve) => {\n this.drainResolve = resolve;\n }),\n new Promise<void>((resolve) => setTimeout(resolve, this.config.shutdownTimeout)),\n ]);\n }\n\n // Release locks for any jobs that did not finish in time.\n // releaseLock() sets lockExpiresAt to an already-expired timestamp so that\n // recoverStalledJobs() on any worker will immediately pick them up and\n // return them to \"waiting\" — rather than leaving them stuck in \"active\"\n // forever (which would happen if lockExpiresAt were cleared to null/empty,\n // since every adapter's stalled-job check requires a non-null expired value).\n if (this.activeJobIds.size > 0) {\n await Promise.all(\n Array.from(this.activeJobIds).map((jobId) =>\n this.storage.releaseLock(jobId, this.id).catch(() => {\n // Best-effort — storage may already be unavailable during shutdown.\n }),\n ),\n );\n }\n\n this._status = \"stopped\";\n this.emitter.emit(\"worker:stopped\", this.id);\n this.emitter.emit(\"worker:status\", this.id, this._status);\n }\n\n // -------------------------------------------------------------------------\n // Poll loop\n // -------------------------------------------------------------------------\n\n private schedulePoll(): void {\n if (this._status !== \"running\") return;\n\n this.pollTimer = setTimeout(() => {\n void this.poll();\n }, this.config.pollInterval);\n }\n\n private async poll(): Promise<void> {\n if (this._status !== \"running\") return;\n\n try {\n // Fill up to concurrency limit.\n while (this._status === \"running\" && this.activeCount < this.config.concurrency) {\n const claimed = await this.claimNext();\n if (!claimed) break; // No eligible jobs right now.\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emitter.emit(\"queue:error\", this.queueName, error);\n this.emitter.emit(\"worker:error\", this.id, error);\n }\n\n this.schedulePoll();\n }\n\n // -------------------------------------------------------------------------\n // Claim & execute\n // -------------------------------------------------------------------------\n\n private async claimNext(): Promise<boolean> {\n const now = new Date().toISOString();\n\n // Attempt to claim a job before touching the rate-limit counter.\n // The counter must only be consumed when a job is actually claimed for\n // processing — polling an empty queue must not burn quota (issue #4).\n const raw = await this.storage.claim({\n queue: this.queueName,\n lockId: this.id,\n lockDuration: this.config.lockDuration,\n now,\n });\n\n if (!raw) return false;\n\n // A job was claimed. Now enforce the rate limit. If the limit has been\n // reached we immediately release the lock so the job is recoverable and\n // return false — the poll loop will stop trying until the next cycle.\n if (this.config.rateLimit) {\n const allowed = await this.storage.checkAndIncrementRateLimit(\n this.queueName,\n this.config.rateLimit.max,\n this.config.rateLimit.duration,\n now,\n );\n if (!allowed) {\n await this.storage.releaseLock(raw.id, this.id);\n return false;\n }\n }\n\n const job = new Job(raw);\n this.activeCount += 1;\n this.activeJobIds.add(job.id);\n\n // Fire-and-forget — errors are caught inside executeJob.\n void this.executeJob(job);\n\n return true;\n }\n\n private async executeJob(job: Job<unknown>): Promise<void> {\n this.emitter.emit(\"job:started\", job._data);\n\n const processor = this.processors.get(job.type);\n\n if (!processor) {\n // No processor registered → treat as a permanent failure.\n const error = new Error(\n `No processor registered for job type \"${job.type}\" in queue \"${this.queueName}\"`,\n );\n await this.handleFailure(job, error);\n return;\n }\n\n const abortController = new AbortController();\n let timeoutHandle: NodeJS.Timeout | null = null;\n let lockRenewTimer: NodeJS.Timeout | null = null;\n\n const lockRenewInterval = Math.max(100, Math.floor(this.config.lockDuration / 2));\n lockRenewTimer = setInterval(async () => {\n try {\n const renewed = await this.storage.renewLock(job.id, this.id, this.config.lockDuration);\n if (!renewed && lockRenewTimer) {\n clearInterval(lockRenewTimer);\n lockRenewTimer = null;\n }\n } catch {\n // ignore transient renewal errors\n }\n }, lockRenewInterval);\n\n try {\n await new Promise<void>((resolve, reject) => {\n // Enforce per-attempt timeout.\n timeoutHandle = setTimeout(() => {\n const timeoutError = new Error(`Job timed out after ${job.timeout}ms`);\n abortController.abort(timeoutError);\n reject(timeoutError);\n }, job.timeout);\n\n Promise.resolve(processor(job, abortController.signal)).then(resolve, reject);\n });\n\n // Success path.\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (lockRenewTimer) {\n clearInterval(lockRenewTimer);\n lockRenewTimer = null;\n }\n await this.storage.complete(job.id, this.id);\n\n const completedData = (await this.storage.getJob(job.id)) ?? job._data;\n this.emitter.emit(\"job:completed\", completedData);\n\n // Re-enqueue cron job for its next run.\n if (job.cron) {\n await this.enqueueCronNext(job);\n }\n } catch (err) {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (lockRenewTimer) {\n clearInterval(lockRenewTimer);\n lockRenewTimer = null;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n await this.handleFailure(job, error);\n } finally {\n if (lockRenewTimer) {\n clearInterval(lockRenewTimer);\n lockRenewTimer = null;\n }\n this.activeJobIds.delete(job.id);\n this.activeCount -= 1;\n // Signal drain waiter if we've reached zero active jobs.\n if (this.activeCount === 0 && this.drainResolve) {\n this.drainResolve();\n this.drainResolve = null;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Cron — re-enqueue the next occurrence after a successful run\n // -------------------------------------------------------------------------\n\n private async enqueueCronNext(job: Job<unknown>): Promise<void> {\n try {\n // Resolve the next scheduled run using the croner package.\n // croner is a required dependency — if the expression is invalid or has\n // no future occurrences we emit a worker:error and skip re-enqueue\n // rather than silently falling back to a 1-minute interval.\n let nextDate: Date | null;\n\n try {\n const cronInstance = new Cron(job.cron as string);\n nextDate = cronInstance.nextRun();\n } catch (cronErr) {\n // Cron expression is invalid or croner itself threw.\n const error =\n cronErr instanceof Error\n ? cronErr\n : new Error(\n `croner failed to initialize for expression \"${job.cron as string}\": ${String(cronErr)}`,\n );\n this.emitter.emit(\"worker:error\", this.id, error);\n return; // Do not re-enqueue — invalid expression should not produce a job.\n }\n\n if (nextDate === null) {\n // The cron schedule has no future occurrences (e.g. a bounded expression\n // that has already elapsed). Emitting an error lets operators know the\n // job will not recur rather than silently dropping it.\n const error = new Error(\n `Cron expression \"${job.cron as string}\" has no future occurrences — job \"${job.id}\" will not be re-enqueued`,\n );\n this.emitter.emit(\"worker:error\", this.id, error);\n return;\n }\n\n const runAt = nextDate.toISOString();\n\n await this.storage.enqueue({\n id: generateJobId(),\n queue: this.queueName,\n type: job.type,\n payload: job._data.payload,\n maxAttempts: job.maxAttempts,\n retryDelay: job.retryDelay,\n backoff: job.backoff,\n timeout: job.timeout,\n priority: job.priority,\n runAt,\n cron: job.cron,\n });\n } catch (err) {\n // Cron re-enqueue failures must not crash the worker.\n const error = err instanceof Error ? err : new Error(String(err));\n this.emitter.emit(\"worker:error\", this.id, error);\n }\n }\n\n // -------------------------------------------------------------------------\n // Failure handling — requeue or DLQ\n // -------------------------------------------------------------------------\n\n private async handleFailure(job: Job<unknown>, error: Error): Promise<void> {\n const attemptNumber = job.attemptsMade + 1; // the attempt that just failed\n const hasMore = attemptNumber < job.maxAttempts;\n\n this.emitter.emit(\"job:failed\", job._data, error);\n\n if (hasMore) {\n const delayMs = calculateBackoff(job.backoff, job.retryDelay, attemptNumber);\n const runAt = nextRunAt(delayMs);\n\n await this.storage.requeue({\n jobId: job.id,\n runAt,\n error: error.message,\n attemptNumber,\n lockId: this.id,\n ...(error.stack !== undefined && { stack: error.stack }),\n });\n\n const updated = await this.storage.getJob(job.id);\n if (updated) {\n this.emitter.emit(\"job:retrying\", updated, error, runAt);\n }\n } else {\n // No attempts remaining → Dead Letter Queue.\n await this.storage.moveToDlq({\n jobId: job.id,\n error: error.message,\n attemptNumber,\n lockId: this.id,\n ...(error.stack !== undefined && { stack: error.stack }),\n });\n\n const dead = await this.storage.getJob(job.id);\n if (dead) {\n this.emitter.emit(\"job:dead\", dead, error);\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Stalled-job recovery\n // -------------------------------------------------------------------------\n\n private scheduleStallCheck(): void {\n if (this._status !== \"running\") return;\n\n this.stalledTimer = setTimeout(() => {\n void this.recoverStalledJobs();\n }, this.config.stalledInterval);\n }\n\n private async recoverStalledJobs(): Promise<void> {\n if (this._status !== \"running\") return;\n\n try {\n const now = new Date().toISOString();\n const recovered = await this.storage.recoverStalledJobs(this.queueName, now);\n\n for (const jobId of recovered) {\n this.emitter.emit(\"job:stalled\", jobId);\n this.emitter.emit(\"job:recovered\", jobId);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emitter.emit(\"worker:error\", this.id, error);\n }\n\n this.scheduleStallCheck();\n }\n}\n","/**\n * Queue\n *\n * Represents an independent stream of jobs. Applications use a Queue to:\n * - Enqueue jobs (with optional scheduling, priority, retry config).\n * - Register processors for job types.\n * - Create workers that consume jobs from this queue.\n * - Query job state and counts.\n *\n * Queue-level configuration overrides client defaults.\n * Job-level options override queue configuration where applicable.\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type { JobOptions, JobStatus } from \"../types/job.types.js\";\nimport type { QueueOptions } from \"../types/queue.types.js\";\nimport type { WorkerOptions, Processor } from \"../types/worker.types.js\";\nimport type { ClientDefaults } from \"../types/client.types.js\";\nimport { Job } from \"./job.js\";\nimport { Worker } from \"./worker.js\";\nimport { generateJobId } from \"./id.js\";\nimport type { QueueEventEmitter } from \"../events/emitter.js\";\n\ntype ResolvedDefaults = Required<ClientDefaults>;\n\n// ---------------------------------------------------------------------------\n// Resolved queue-level config\n// ---------------------------------------------------------------------------\n\nfunction mergeQueueConfig(\n options: QueueOptions | undefined,\n defaults: ResolvedDefaults,\n): Required<QueueOptions> {\n return {\n concurrency: options?.concurrency ?? defaults.concurrency,\n attempts: options?.attempts ?? defaults.attempts,\n retryDelay: options?.retryDelay ?? defaults.retryDelay,\n backoff: options?.backoff ?? defaults.backoff,\n timeout: options?.timeout ?? defaults.timeout,\n rateLimit: options?.rateLimit ?? defaults.rateLimit ?? undefined,\n pollInterval: options?.pollInterval ?? defaults.pollInterval,\n stalledInterval: options?.stalledInterval ?? defaults.stalledInterval,\n lockDuration: options?.lockDuration ?? defaults.lockDuration,\n } as Required<QueueOptions>;\n}\n\n// ---------------------------------------------------------------------------\n// Queue\n// ---------------------------------------------------------------------------\n\nexport class Queue<TPayload = unknown> {\n /** The name of this queue — unique within a QueueClient. */\n readonly name: string;\n\n private readonly storage: StorageAdapter;\n private readonly emitter: QueueEventEmitter;\n private readonly resolvedConfig: Required<QueueOptions>;\n private readonly clientDefaults: ResolvedDefaults;\n\n /** Registered processors keyed by job type. */\n private readonly processors = new Map<string, Processor<unknown>>();\n\n /** Active worker instances created by this queue. */\n private readonly workers: Worker[] = [];\n\n constructor(\n name: string,\n storage: StorageAdapter,\n emitter: QueueEventEmitter,\n options: QueueOptions | undefined,\n defaults: ResolvedDefaults,\n ) {\n this.name = name;\n this.storage = storage;\n this.emitter = emitter;\n this.clientDefaults = defaults;\n this.resolvedConfig = mergeQueueConfig(options, defaults);\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n /**\n * Add a new job to the queue.\n *\n * @param type - Job type string, must match a registered processor.\n * @param payload - Arbitrary serialisable payload (never logged by default).\n * @param options - Per-job overrides (attempts, delay, priority, etc.).\n */\n async enqueue(type: string, payload: TPayload, options?: JobOptions): Promise<Job<TPayload>> {\n const cfg = this.resolvedConfig;\n const now = new Date();\n\n // Resolve runAt from schedule options.\n let runAt: Date;\n if (options?.schedule?.runAt !== undefined) {\n runAt = new Date(options.schedule.runAt);\n } else if (options?.schedule?.delay !== undefined) {\n runAt = new Date(now.getTime() + options.schedule.delay);\n } else {\n runAt = now;\n }\n\n const raw = await this.storage.enqueue<TPayload>({\n id: generateJobId(),\n queue: this.name,\n type,\n payload,\n maxAttempts: options?.attempts ?? cfg.attempts,\n retryDelay: options?.retryDelay ?? cfg.retryDelay,\n backoff: options?.backoff ?? cfg.backoff,\n timeout: options?.timeout ?? cfg.timeout,\n priority: options?.priority ?? 0,\n runAt: runAt.toISOString(),\n ...(options?.schedule?.cron !== undefined && { cron: options.schedule.cron }),\n });\n\n const job = new Job<TPayload>(raw);\n this.emitter.emit(\"job:enqueued\", raw);\n return job;\n }\n\n // -------------------------------------------------------------------------\n // Processor registration\n // -------------------------------------------------------------------------\n\n /**\n * Register a processor function for a given job type.\n *\n * Only one processor per type per queue is supported.\n * Registering a second processor for the same type replaces the first.\n */\n process<P = TPayload>(type: string, processor: Processor<P>): void {\n this.processors.set(type, processor as Processor<unknown>);\n }\n\n // -------------------------------------------------------------------------\n // Worker creation\n // -------------------------------------------------------------------------\n\n /**\n * Create and start a Worker that consumes jobs from this queue.\n *\n * Worker-level `concurrency` overrides queue-level concurrency.\n */\n createWorker(options?: WorkerOptions): Worker {\n const worker = new Worker(\n this.name,\n this.storage,\n this.emitter,\n this.processors,\n options ?? {},\n this.resolvedConfig,\n this.clientDefaults,\n );\n\n this.workers.push(worker);\n worker.start();\n return worker;\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n /** Fetch a single job by its ID. Returns null if not found or wrong queue. */\n async getJob(jobId: string): Promise<Job<TPayload> | null> {\n const raw = await this.storage.getJob<TPayload>(jobId);\n if (!raw || raw.queue !== this.name) return null;\n return new Job<TPayload>(raw);\n }\n\n /** Fetch jobs from this queue, optionally filtered by status. */\n async getJobs(status?: JobStatus, limit = 100, offset = 0): Promise<Job<TPayload>[]> {\n const raws = await this.storage.getJobs<TPayload>({\n queue: this.name,\n ...(status !== undefined && { status }),\n limit,\n offset,\n });\n return raws.map((r) => new Job<TPayload>(r));\n }\n\n /** Get job counts by status for this queue. */\n async getJobCounts(): Promise<Record<JobStatus, number>> {\n return this.storage.getJobCounts(this.name);\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n /**\n * Gracefully stop all workers attached to this queue.\n * Called automatically by QueueClient.close().\n */\n async close(): Promise<void> {\n await Promise.all(this.workers.map((w) => w.stop()));\n }\n\n /** Return all active worker instances on this queue. */\n getWorkers(): readonly Worker[] {\n return this.workers;\n }\n}\n","/**\n * InMemoryStorageAdapter\n *\n * A fully-featured, in-process storage adapter backed by a plain Map.\n * Intended for development, testing, and single-process scenarios.\n * Data is NOT persisted between process restarts.\n *\n * All \"atomic\" operations are synchronous under the hood — safe within a\n * single Node.js event-loop tick.\n */\n\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type {\n EnqueueInput,\n ClaimInput,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n} from \"../types/storage.types.js\";\nimport type { JobData, JobStatus } from \"../types/job.types.js\";\n\n// ---------------------------------------------------------------------------\n// Rate-limit window bucket\n// ---------------------------------------------------------------------------\n\ninterface RateLimitBucket {\n count: number;\n windowStart: number;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport class InMemoryStorageAdapter implements StorageAdapter {\n private readonly jobs = new Map<string, JobData<unknown>>();\n private readonly rateLimitBuckets = new Map<string, RateLimitBucket>();\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async initialize(): Promise<void> {\n // Nothing to set up for in-memory storage.\n }\n\n async close(): Promise<void> {\n this.jobs.clear();\n this.rateLimitBuckets.clear();\n }\n\n // -------------------------------------------------------------------------\n // Enqueue\n // -------------------------------------------------------------------------\n\n async enqueue<TPayload = unknown>(input: EnqueueInput<TPayload>): Promise<JobData<TPayload>> {\n // Idempotent: return existing job if already stored with the same id.\n if (this.jobs.has(input.id)) {\n return this.jobs.get(input.id) as JobData<TPayload>;\n }\n\n const now = new Date().toISOString();\n const runAt = input.runAt;\n const isDelayed = new Date(runAt).getTime() > Date.now();\n\n const job: JobData<TPayload> = {\n id: input.id,\n queue: input.queue,\n type: input.type,\n payload: input.payload,\n status: isDelayed ? \"delayed\" : \"waiting\",\n attemptsMade: 0,\n maxAttempts: input.maxAttempts,\n retryDelay: input.retryDelay,\n backoff: input.backoff,\n timeout: input.timeout,\n priority: input.priority,\n runAt,\n ...(input.cron !== undefined && { cron: input.cron }),\n attempts: [],\n lockId: null,\n lockExpiresAt: null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n failedAt: null,\n };\n\n this.jobs.set(job.id, job as JobData<unknown>);\n return job;\n }\n\n // -------------------------------------------------------------------------\n // Claim (atomic within a single event-loop tick)\n // -------------------------------------------------------------------------\n\n async claim<TPayload = unknown>(input: ClaimInput): Promise<JobData<TPayload> | null> {\n const { queue, lockId, lockDuration, now } = input;\n const nowMs = new Date(now).getTime();\n\n // Collect eligible jobs: same queue, waiting/delayed, runAt <= now,\n // and no valid lock held.\n const eligible: JobData<unknown>[] = [];\n\n for (const job of this.jobs.values()) {\n if (job.queue !== queue) continue;\n if (job.status !== \"waiting\" && job.status !== \"delayed\") continue;\n if (new Date(job.runAt).getTime() > nowMs) continue;\n if (job.lockId !== null && job.lockExpiresAt !== null) {\n const lockExpiry = new Date(job.lockExpiresAt).getTime();\n if (lockExpiry > nowMs) continue; // still locked\n }\n eligible.push(job);\n }\n\n if (eligible.length === 0) return null;\n\n // Sort: higher priority first; break ties by earliest runAt then createdAt.\n eligible.sort((a, b) => {\n if (b.priority !== a.priority) return b.priority - a.priority;\n const runAtDiff = new Date(a.runAt).getTime() - new Date(b.runAt).getTime();\n if (runAtDiff !== 0) return runAtDiff;\n return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();\n });\n\n const job = eligible[0];\n if (!job) return null;\n\n const lockExpiresAt = new Date(nowMs + lockDuration).toISOString();\n\n job.status = \"active\";\n job.lockId = lockId;\n job.lockExpiresAt = lockExpiresAt;\n job.updatedAt = now;\n\n return job as JobData<TPayload>;\n }\n\n // -------------------------------------------------------------------------\n // Renew lock\n // -------------------------------------------------------------------------\n\n async renewLock(jobId: string, lockId: string, lockDuration: number): Promise<boolean> {\n const job = this.jobs.get(jobId);\n if (!job) return false;\n if (job.status !== \"active\" || job.lockId !== lockId) return false;\n\n const nowMs = Date.now();\n if (job.lockExpiresAt !== null && new Date(job.lockExpiresAt).getTime() <= nowMs) {\n return false; // Lock already expired\n }\n\n job.lockExpiresAt = new Date(nowMs + lockDuration).toISOString();\n job.updatedAt = new Date(nowMs).toISOString();\n return true;\n }\n\n // -------------------------------------------------------------------------\n // Complete\n // -------------------------------------------------------------------------\n\n async complete(jobId: string, lockId?: string): Promise<void> {\n const job = this.jobs.get(jobId);\n if (!job) return;\n if (lockId !== undefined && (job.status !== \"active\" || job.lockId !== lockId)) {\n return;\n }\n const now = new Date().toISOString();\n\n job.status = \"completed\";\n job.lockId = null;\n job.lockExpiresAt = null;\n job.completedAt = now;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Requeue (retry)\n // -------------------------------------------------------------------------\n\n async requeue(input: RequeueInput): Promise<void> {\n const job = this.jobs.get(input.jobId);\n if (!job) return;\n if (input.lockId !== undefined && (job.status !== \"active\" || job.lockId !== input.lockId)) {\n return;\n }\n const now = new Date().toISOString();\n\n // Record this attempt in history using the canonical attempt number from input.\n job.attempts.push({\n attempt: input.attemptNumber,\n startedAt: job.updatedAt,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined && { stack: input.stack }),\n });\n\n job.attemptsMade = input.attemptNumber;\n job.status = \"waiting\";\n job.runAt = input.runAt;\n job.lockId = null;\n job.lockExpiresAt = null;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Move to DLQ\n // -------------------------------------------------------------------------\n\n async moveToDlq(input: MoveToDlqInput): Promise<void> {\n const job = this.jobs.get(input.jobId);\n if (!job) return;\n if (input.lockId !== undefined && (job.status !== \"active\" || job.lockId !== input.lockId)) {\n return;\n }\n const now = new Date().toISOString();\n\n job.attempts.push({\n attempt: input.attemptNumber,\n startedAt: job.updatedAt,\n finishedAt: now,\n error: input.error,\n ...(input.stack !== undefined && { stack: input.stack }),\n });\n\n job.attemptsMade = input.attemptNumber;\n job.status = \"dead\";\n job.lockId = null;\n job.lockExpiresAt = null;\n job.failedAt = now;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Release lock\n // -------------------------------------------------------------------------\n\n async releaseLock(jobId: string, lockId?: string): Promise<void> {\n const job = this.jobs.get(jobId);\n if (!job) return;\n if (lockId !== undefined && (job.status !== \"active\" || job.lockId !== lockId)) {\n return;\n }\n\n const now = new Date().toISOString();\n job.lockId = null;\n // Set lockExpiresAt to now (already-expired) so recoverStalledJobs() will\n // immediately pick up the job — its lock-expired check requires a non-null,\n // expired timestamp, not null.\n job.lockExpiresAt = now;\n job.updatedAt = now;\n }\n\n // -------------------------------------------------------------------------\n // Recover stalled jobs\n //\n // Fix (issue #6): snapshot lockId and lockExpiresAt before the eligibility\n // check, then re-validate both values at write time. In a single-process\n // scenario all operations are synchronous within one event-loop tick, so\n // the race is theoretical — but the guard makes the adapter consistent with\n // the Redis CAS semantics and protects against any future async paths.\n // -------------------------------------------------------------------------\n\n async recoverStalledJobs(queue: string, now: string): Promise<string[]> {\n const nowMs = new Date(now).getTime();\n const recovered: string[] = [];\n\n for (const job of this.jobs.values()) {\n if (job.queue !== queue) continue;\n if (job.status !== \"active\") continue;\n if (job.lockExpiresAt === null) continue;\n\n const lockExpiry = new Date(job.lockExpiresAt).getTime();\n if (lockExpiry > nowMs) continue;\n\n // Snapshot the guard fields observed at decision time.\n const snapshotLockId = job.lockId;\n const snapshotLockExpiresAt = job.lockExpiresAt;\n\n // Re-validate before writing (compare-and-swap).\n // If another operation (complete, requeue, releaseLock) ran between the\n // check above and this point, one of these will differ and we skip.\n if (job.status !== \"active\") continue;\n if (job.lockId !== snapshotLockId) continue;\n if (job.lockExpiresAt !== snapshotLockExpiresAt) continue;\n\n // Lock expired and state is unchanged — recover.\n job.status = \"waiting\";\n job.lockId = null;\n job.lockExpiresAt = null;\n job.updatedAt = now;\n recovered.push(job.id);\n }\n\n return recovered;\n }\n\n // -------------------------------------------------------------------------\n // Reads\n // -------------------------------------------------------------------------\n\n async getJob<TPayload = unknown>(jobId: string): Promise<JobData<TPayload> | null> {\n return (this.jobs.get(jobId) as JobData<TPayload>) ?? null;\n }\n\n async getJobs<TPayload = unknown>(filter: GetJobsFilter): Promise<JobData<TPayload>[]> {\n const { queue, status, limit = 100, offset = 0 } = filter;\n const results: JobData<unknown>[] = [];\n\n for (const job of this.jobs.values()) {\n if (queue !== undefined && job.queue !== queue) continue;\n if (status !== undefined && job.status !== status) continue;\n results.push(job);\n }\n\n // Sort by priority desc, then createdAt asc.\n results.sort((a, b) => {\n if (b.priority !== a.priority) return b.priority - a.priority;\n return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();\n });\n\n return results.slice(offset, offset + limit) as JobData<TPayload>[];\n }\n\n async getJobCounts(queue: string): Promise<Record<JobStatus, number>> {\n const counts: Record<JobStatus, number> = {\n waiting: 0,\n active: 0,\n completed: 0,\n delayed: 0,\n dead: 0,\n };\n\n for (const job of this.jobs.values()) {\n if (job.queue !== queue) continue;\n counts[job.status] += 1;\n }\n\n return counts;\n }\n\n // -------------------------------------------------------------------------\n // Rate limiting\n // -------------------------------------------------------------------------\n\n async checkAndIncrementRateLimit(\n queue: string,\n max: number,\n windowMs: number,\n now: string,\n ): Promise<boolean> {\n const nowMs = new Date(now).getTime();\n const existing = this.rateLimitBuckets.get(queue);\n\n if (!existing || nowMs - existing.windowStart >= windowMs) {\n // Start a fresh window.\n this.rateLimitBuckets.set(queue, { count: 1, windowStart: nowMs });\n return true;\n }\n\n if (existing.count >= max) {\n return false; // Rate limit reached.\n }\n\n existing.count += 1;\n return true;\n }\n\n // -------------------------------------------------------------------------\n // Internal helpers\n // -------------------------------------------------------------------------\n\n private requireJob(jobId: string): JobData<unknown> {\n const job = this.jobs.get(jobId);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n return job;\n }\n}\n","/**\n * QueueEventEmitter\n *\n * A strongly-typed, lightweight event emitter built on top of Node.js\n * EventEmitter. All queue/worker/job lifecycle events flow through here.\n *\n * Using typed generics ensures that each event name maps to its exact\n * argument tuple — no runtime guessing.\n */\n\nimport { EventEmitter } from \"node:events\";\nimport type { QueueEvents } from \"../types/events.types.js\";\n\n// ---------------------------------------------------------------------------\n// Typed emitter\n// ---------------------------------------------------------------------------\n\ntype EventKey = keyof QueueEvents;\ntype EventArgs<K extends EventKey> = QueueEvents[K];\n\nexport class QueueEventEmitter {\n private readonly emitter: EventEmitter;\n\n constructor() {\n this.emitter = new EventEmitter();\n // Prevent Node.js from printing \"MaxListenersExceededWarning\" in tests\n // where many listeners are registered.\n this.emitter.setMaxListeners(100);\n }\n\n // -------------------------------------------------------------------------\n // Subscribe\n // -------------------------------------------------------------------------\n\n on<K extends EventKey>(event: K, listener: (...args: EventArgs<K>) => void): this {\n this.emitter.on(event as string, listener as (...a: unknown[]) => void);\n return this;\n }\n\n once<K extends EventKey>(event: K, listener: (...args: EventArgs<K>) => void): this {\n this.emitter.once(event as string, listener as (...a: unknown[]) => void);\n return this;\n }\n\n off<K extends EventKey>(event: K, listener: (...args: EventArgs<K>) => void): this {\n this.emitter.off(event as string, listener as (...a: unknown[]) => void);\n return this;\n }\n\n // ---------------------------------------------------------------------------\n // Publish\n // ---------------------------------------------------------------------------\n\n emit<K extends EventKey>(event: K, ...args: EventArgs<K>): boolean {\n return this.emitter.emit(event as string, ...args);\n }\n\n // ---------------------------------------------------------------------------\n // Utility\n // ---------------------------------------------------------------------------\n\n removeAllListeners(event?: EventKey): this {\n if (event) {\n this.emitter.removeAllListeners(event as string);\n } else {\n this.emitter.removeAllListeners();\n }\n return this;\n }\n\n listenerCount(event: EventKey): number {\n return this.emitter.listenerCount(event as string);\n }\n}\n","/**\n * QueueClient\n *\n * Primary entry point for the queue-jobs-worker package.\n *\n * Usage:\n *\n * // In-memory (dev / tests)\n * const client = new QueueClient();\n * // init() is optional for \"memory\" but always safe to call\n *\n * // Redis\n * const client = new QueueClient({ dialect: \"redis\", connectionString: \"redis://localhost:6379\" });\n * await client.init(); // connects, pings Redis\n *\n * // PostgreSQL\n * const client = new QueueClient({ dialect: \"postgres\", connectionString: \"postgresql://...\" });\n * await client.init(); // connects, auto-creates qjw_ tables if missing\n *\n * // MySQL\n * const client = new QueueClient({ dialect: \"mysql\", connectionString: \"mysql://...\" });\n * await client.init(); // connects, auto-creates qjw_ tables if missing\n */\n\nimport type { QueueClientOptions, ClientDefaults } from \"../types/client.types.js\";\nimport type { QueueOptions } from \"../types/queue.types.js\";\nimport type { StorageAdapter } from \"../types/storage.types.js\";\nimport type { QueueEvents } from \"../types/events.types.js\";\nimport { Queue } from \"./queue.js\";\nimport { InMemoryStorageAdapter } from \"../storage/in-memory.adapter.js\";\nimport { QueueEventEmitter } from \"../events/emitter.js\";\n\n// ---------------------------------------------------------------------------\n// Hard defaults\n// ---------------------------------------------------------------------------\n\nconst HARD_DEFAULTS: Required<ClientDefaults> = {\n attempts: 3,\n retryDelay: 1_000,\n backoff: \"exponential\",\n timeout: 30_000,\n concurrency: 10,\n rateLimit: undefined as unknown as Required<ClientDefaults>[\"rateLimit\"],\n pollInterval: 1_000,\n stalledInterval: 30_000,\n lockDuration: 60_000,\n};\n\n// ---------------------------------------------------------------------------\n// QueueClient\n// ---------------------------------------------------------------------------\n\nexport class QueueClient {\n /** @internal — mutable so withAdapter() can replace it */\n _storage: StorageAdapter;\n\n private readonly emitter: QueueEventEmitter;\n private readonly defaults: Required<ClientDefaults>;\n private readonly dialect: string;\n private readonly connectionString: string | undefined;\n private readonly queues = new Map<string, Queue<unknown>>();\n private initialised = false;\n private closed = false;\n\n constructor(options: QueueClientOptions = {}) {\n this.defaults = { ...HARD_DEFAULTS, ...options.defaults };\n this.dialect = options.dialect ?? \"memory\";\n this.connectionString = options.connectionString;\n\n // Memory adapter is built eagerly (no I/O needed).\n // External adapters are built eagerly too, but require init() before use.\n this._storage = this.buildMemoryOrEagerAdapter();\n this.emitter = new QueueEventEmitter();\n }\n\n // -------------------------------------------------------------------------\n // Eager adapter construction (memory only; external adapters built in init)\n // -------------------------------------------------------------------------\n\n private buildMemoryOrEagerAdapter(): StorageAdapter {\n if (this.dialect === \"memory\") {\n return new InMemoryStorageAdapter();\n }\n // For external dialects we return a sentinel that will be replaced in init().\n // This lets the constructor stay synchronous.\n return new InMemoryStorageAdapter(); // replaced in init()\n }\n\n // -------------------------------------------------------------------------\n // Initialisation\n // -------------------------------------------------------------------------\n\n /**\n * Initialise the client and storage adapter.\n *\n * **Must be called** before creating queues when using Redis, PostgreSQL,\n * or MySQL.\n *\n * What each adapter does:\n * - **memory** — no-op (always safe to call)\n * - **redis** — connects, sends PING, verifies PONG\n * - **postgres** — connects, runs `SELECT 1`, auto-creates `qjw_` tables\n * - **mysql** — connects, runs `SELECT 1`, auto-creates `qjw_` tables\n *\n * Idempotent — safe to call multiple times.\n */\n async init(): Promise<void> {\n if (this.initialised) return;\n\n // Build the real adapter (lazy import keeps optional peer deps optional).\n this._storage = await this.resolveAdapter();\n\n await this._storage.initialize();\n this.initialised = true;\n }\n\n /** @deprecated Use `init()`. */\n async initialize(): Promise<void> {\n return this.init();\n }\n\n private async resolveAdapter(): Promise<StorageAdapter> {\n const cs = this.connectionString;\n\n switch (this.dialect) {\n case \"memory\":\n return new InMemoryStorageAdapter();\n\n case \"redis\": {\n if (!cs) throw new Error('dialect \"redis\" requires a connectionString.');\n const { RedisStorageAdapter } = await import(\"../storage/redis.adapter.js\");\n return new RedisStorageAdapter(cs);\n }\n\n case \"postgres\": {\n if (!cs) throw new Error('dialect \"postgres\" requires a connectionString.');\n const { PostgreSQLStorageAdapter } = await import(\"../storage/postgres.adapter.js\");\n return new PostgreSQLStorageAdapter(cs);\n }\n\n case \"mysql\": {\n if (!cs) throw new Error('dialect \"mysql\" requires a connectionString.');\n const { MySQLStorageAdapter } = await import(\"../storage/mysql.adapter.js\");\n return new MySQLStorageAdapter(cs);\n }\n\n default:\n throw new Error(`Unknown storage dialect: \"${this.dialect}\"`);\n }\n }\n\n // -------------------------------------------------------------------------\n // Queue management\n // -------------------------------------------------------------------------\n\n /**\n * Create a named queue with optional per-queue configuration.\n *\n * For external dialects (redis/postgres/mysql), call `await client.init()`\n * first.\n */\n createQueue<TPayload = unknown>(name: string, options?: QueueOptions): Queue<TPayload> {\n if (this.closed) throw new Error(\"QueueClient has been closed.\");\n if (this.queues.has(name)) {\n throw new Error(`A queue named \"${name}\" already exists on this client.`);\n }\n\n // Auto-init for memory (harmless no-op).\n if (!this.initialised && this.dialect === \"memory\") {\n void this.init();\n }\n\n const queue = new Queue<TPayload>(name, this._storage, this.emitter, options, this.defaults);\n this.queues.set(name, queue as Queue<unknown>);\n return queue;\n }\n\n /** Returns the queue with the given name, or `undefined`. */\n getQueue<TPayload = unknown>(name: string): Queue<TPayload> | undefined {\n return this.queues.get(name) as Queue<TPayload> | undefined;\n }\n\n /** Returns the queue with the given name, or throws. */\n requireQueue<TPayload = unknown>(name: string): Queue<TPayload> {\n const queue = this.getQueue<TPayload>(name);\n if (!queue) {\n throw new Error(`Queue \"${name}\" not found. Did you call createQueue(\"${name}\")?`);\n }\n return queue;\n }\n\n /** Names of all queues registered on this client. */\n get queueNames(): string[] {\n return Array.from(this.queues.keys());\n }\n\n /** `true` after `init()` has completed successfully. */\n get isInitialised(): boolean {\n return this.initialised;\n }\n\n // -------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------\n\n on<K extends keyof QueueEvents>(event: K, listener: (...args: QueueEvents[K]) => void): this {\n this.emitter.on(event, listener);\n return this;\n }\n\n once<K extends keyof QueueEvents>(event: K, listener: (...args: QueueEvents[K]) => void): this {\n this.emitter.once(event, listener);\n return this;\n }\n\n off<K extends keyof QueueEvents>(event: K, listener: (...args: QueueEvents[K]) => void): this {\n this.emitter.off(event, listener);\n return this;\n }\n\n // -------------------------------------------------------------------------\n // Graceful shutdown\n // -------------------------------------------------------------------------\n\n /**\n * Stop all workers, close all queues, and release the storage connection.\n * Safe to call multiple times.\n */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n\n await Promise.all(Array.from(this.queues.values()).map((q) => q.close()));\n await this._storage.close();\n this.emitter.removeAllListeners();\n }\n\n // -------------------------------------------------------------------------\n // Advanced: custom StorageAdapter\n // -------------------------------------------------------------------------\n\n /**\n * Create a QueueClient with a fully custom StorageAdapter.\n *\n * `init()` will call `adapter.initialize()`.\n *\n * @example\n * const client = QueueClient.withAdapter(myAdapter);\n * await client.init();\n */\n static withAdapter(\n adapter: StorageAdapter,\n options: Omit<QueueClientOptions, \"dialect\" | \"connectionString\"> = {},\n ): QueueClient {\n const client = new QueueClient(options);\n client._storage = adapter;\n return client;\n }\n}\n","/**\n * queue-jobs-worker\n *\n * Reliable job queue and background worker system for Node.js.\n *\n * Quick start:\n *\n * import { QueueClient } from \"queue-jobs-worker\";\n *\n * // In-memory (dev / tests)\n * const client = new QueueClient();\n *\n * // Redis\n * const client = new QueueClient({ dialect: \"redis\", connectionString: \"redis://localhost:6379\" });\n * await client.init();\n *\n * // PostgreSQL\n * const client = new QueueClient({ dialect: \"postgres\", connectionString: \"postgresql://...\" });\n * await client.init();\n *\n * // MySQL\n * const client = new QueueClient({ dialect: \"mysql\", connectionString: \"mysql://...\" });\n * await client.init();\n *\n * @module queue-jobs-worker\n */\n\n// Core\nexport { QueueClient } from \"./core/client.js\";\nexport { Queue } from \"./core/queue.js\";\nexport { Job } from \"./core/job.js\";\nexport { Worker } from \"./core/worker.js\";\n\n// Storage adapters\nexport { InMemoryStorageAdapter } from \"./storage/in-memory.adapter.js\";\nexport { RedisStorageAdapter } from \"./storage/redis.adapter.js\";\nexport { PostgreSQLStorageAdapter } from \"./storage/postgres.adapter.js\";\nexport { MySQLStorageAdapter } from \"./storage/mysql.adapter.js\";\n\n// Event emitter\nexport { QueueEventEmitter } from \"./events/emitter.js\";\n\n// Utilities\nexport { calculateBackoff, nextRunAt } from \"./core/backoff.js\";\nexport { generateJobId } from \"./core/id.js\";\n\n// All public types\nexport type {\n JobStatus,\n JobAttempt,\n JobSchedule,\n JobOptions,\n BackoffStrategy,\n JobData,\n RateLimitOptions,\n QueueOptions,\n Processor,\n WorkerOptions,\n WorkerStatus,\n StorageAdapter,\n EnqueueInput,\n ClaimInput,\n ClaimResult,\n RequeueInput,\n MoveToDlqInput,\n GetJobsFilter,\n StorageDialect,\n ClientDefaults,\n QueueClientOptions,\n QueueEvents,\n} from \"./types/index.js\";\n"]}