{"_id":"@platformatic/job-queue","_rev":"9-617095f2efa87ea40f8154d21f4a897e","name":"@platformatic/job-queue","dist-tags":{"next":"0.1.0-alpha.3","latest":"0.5.0"},"versions":{"0.0.1":{"name":"@platformatic/job-queue","version":"0.0.1","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"license":"Apache-2.0","_id":"@platformatic/job-queue@0.0.1","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue#readme","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"f6c8f4982d53b155c9e446f2b8f049e093008279","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.0.1.tgz","fileCount":2,"integrity":"sha512-V0iN7hFtPp1ArFvLt7IP6chjIYcaPTlI79fC/3QBjCHXGLYHxOu5Fq1UAFhdvVcn2fFjz7aUL8mDZMLA48KINw==","signatures":[{"sig":"MEYCIQClx6MPIZPTQnglsuJ+1Nb1nJ5L+4gcB+FM2xvt3lqG+AIhAJaU3LGUIMGQxfiHMxVoKljreQW5AWfHcmo06kjNS6IK","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11850},"type":"module","_from":"file:platformatic-job-queue-0.0.1.tgz","engines":{"node":">=22.19.0"},"_npmUser":{"name":"shogun_panda","email":"paolo@cowtech.it"},"_resolved":"/private/var/folders/0m/wz05yxzd2ybbybl_lw_l0gfw0000gn/T/d8e68c7e15777772abfd665d97d47b89/platformatic-job-queue-0.0.1.tgz","_integrity":"sha512-V0iN7hFtPp1ArFvLt7IP6chjIYcaPTlI79fC/3QBjCHXGLYHxOu5Fq1UAFhdvVcn2fFjz7aUL8mDZMLA48KINw==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.8.0","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.0.1_1771337108308_0.5620381022243941","host":"s3://npm-registry-packages-npm-production"}},"0.1.0-alpha.3":{"name":"@platformatic/job-queue","version":"0.1.0-alpha.3","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/job-queue@0.1.0-alpha.3","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"6b371f7fabef855175a7f2c3f9137b200bfabec0","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.1.0-alpha.3.tgz","fileCount":59,"integrity":"sha512-RB04869p/lDYQJHdMczIISjB2Og2wSjXLSnyAoD5VK/lk55pFnTIVMh1nK36TpmyoKzvJg+b4bG5YD+nKehTJQ==","signatures":[{"sig":"MEUCIQCUy//NROz95pDVUYqLELE7i83bQ+9P/n5KwtRVvXFRUQIgCylW45Rv6PYsIQk5j15piUEoO/n2j9WU/cAqHpem6dM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fjob-queue@0.1.0-alpha.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":230386},"type":"module","_from":"file:platformatic-job-queue-0.1.0-alpha.3.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":"./dist/index.js","private":false,"scripts":{"ci":"npm run build && npm run lint && npm run test:ci","lint":"eslint --cache","test":"c8 -c test/config/c8-local.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","bench":"node benchmark/request-response.ts","build":"rm -rf dist && tsc -p tsconfig.build.json","ci:v20":"npm run lint && rm -rf dist && tsc -p tsconfig.json && node dist/scripts/postbuild.js && cd dist && node --env-file=../test/config/env --no-warnings --test --test-timeout=600000","format":"prettier -w benchmarks playground src test","test:ci":"c8 -c test/config/c8-ci.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","typecheck":"tsc -p . --noEmit","bench:redis":"REDIS_URL=${REDIS_URL:-redis://localhost:6379} node benchmark/request-response.ts","bench:valkey":"REDIS_URL=${REDIS_URL:-redis://localhost:6380} node benchmark/request-response.ts","test:docker:up":"docker-compose up -d --wait","test:docker:down":"docker-compose down"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56ac758a-0dd5-46d0-a33f-de330e57ff85"}},"_resolved":"/tmp/815b8c55100ccb333bfa6a0bb0d570f1/platformatic-job-queue-0.1.0-alpha.3.tgz","_integrity":"sha512-RB04869p/lDYQJHdMczIISjB2Og2wSjXLSnyAoD5VK/lk55pFnTIVMh1nK36TpmyoKzvJg+b4bG5YD+nKehTJQ==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.10.0","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"22.22.0","dependencies":{"iovalkey":"^0.2.0","fast-write-atomic":"^0.4.0"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"c8":"^10.1.3","eslint":"^9.0.0","semver":"^7.7.4","typescript":"^5.7.0","@types/node":"^22.0.0","neostandard":"^0.12.0","@platformatic/flame":"^1.6.0","cleaner-spec-reporter":"^1.0.3"},"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.1.0-alpha.3_1771406164516_0.46863995127472924","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@platformatic/job-queue","version":"0.1.0","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/job-queue@0.1.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"21c22594e1bcf99be044ed3dfc3f91ceb9802c1c","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.1.0.tgz","fileCount":59,"integrity":"sha512-0VCISxX+xjLx8gQKm6c/kkCkvVof7pO4Ps053YT0jeHxqv3zLbb6jzTHxFz3SJ4RVsr4iH13UgVQpU6aOpw/dw==","signatures":[{"sig":"MEQCIF9WCt+wtGxRu/cpae/whK74COsSZq6IEx0Q7CjHX7ePAiAedFEo4hL7d3bq1S9Cx5Yk2AGM0komqwSBXjWp1iiqFQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fjob-queue@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":230378},"type":"module","_from":"file:platformatic-job-queue-0.1.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":"./dist/index.js","private":false,"scripts":{"ci":"npm run build && npm run lint && npm run test:ci","lint":"eslint --cache","test":"c8 -c test/config/c8-local.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","bench":"node benchmark/request-response.ts","build":"rm -rf dist && tsc -p tsconfig.build.json","ci:v20":"npm run lint && rm -rf dist && tsc -p tsconfig.json && node dist/scripts/postbuild.js && cd dist && node --env-file=../test/config/env --no-warnings --test --test-timeout=600000","format":"prettier -w benchmarks playground src test","test:ci":"c8 -c test/config/c8-ci.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","typecheck":"tsc -p . --noEmit","bench:redis":"REDIS_URL=${REDIS_URL:-redis://localhost:6379} node benchmark/request-response.ts","bench:valkey":"REDIS_URL=${REDIS_URL:-redis://localhost:6380} node benchmark/request-response.ts","test:docker:up":"docker-compose up -d --wait","test:docker:down":"docker-compose down"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56ac758a-0dd5-46d0-a33f-de330e57ff85"}},"_resolved":"/tmp/32311b8314bbfae136444506cbe1b4dc/platformatic-job-queue-0.1.0.tgz","_integrity":"sha512-0VCISxX+xjLx8gQKm6c/kkCkvVof7pO4Ps053YT0jeHxqv3zLbb6jzTHxFz3SJ4RVsr4iH13UgVQpU6aOpw/dw==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.10.0","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"22.22.0","dependencies":{"iovalkey":"^0.2.0","fast-write-atomic":"^0.4.0"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^9.0.0","semver":"^7.7.4","typescript":"^5.7.0","@types/node":"^22.0.0","neostandard":"^0.12.0","@platformatic/flame":"^1.6.0","cleaner-spec-reporter":"^1.0.3"},"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.1.0_1771406447786_0.31816152905024886","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@platformatic/job-queue","version":"0.2.0","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/job-queue@0.2.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"3c5df99a6e7323a867620f4e452f4a2a035bc3c8","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.2.0.tgz","fileCount":63,"integrity":"sha512-0CKpKiW+9jOfY1vi3gNVIV8DFX/0H8HhWZZR960P6Oy1BGE49k3CNnnpkw1xu4J6Jyflmzfm/VyfxYk5idgLvA==","signatures":[{"sig":"MEQCIAiBDfWZP4o2r/oul6X1MSLR+ioLSBLPrs2acNFR2NQqAiBZltu4qo4gPSNiNyisdT4iSS8Da4F9lygkE6ItQH3vCA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fjob-queue@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":255310},"type":"module","_from":"file:platformatic-job-queue-0.2.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":"./dist/index.js","private":false,"scripts":{"ci":"npm run build && npm run lint && npm run test:ci","lint":"eslint --cache","test":"c8 -c test/config/c8-local.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","bench":"node benchmark/request-response.ts","build":"rm -rf dist && tsc -p tsconfig.build.json","ci:v20":"npm run lint && rm -rf dist && tsc -p tsconfig.json && node dist/scripts/postbuild.js && cd dist && node --env-file=../test/config/env --no-warnings --test --test-timeout=600000","format":"prettier -w benchmark src test","test:ci":"c8 -c test/config/c8-ci.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","typecheck":"tsc -p . --noEmit","bench:redis":"REDIS_URL=${REDIS_URL:-redis://localhost:6379} node benchmark/request-response.ts","bench:valkey":"REDIS_URL=${REDIS_URL:-redis://localhost:6380} node benchmark/request-response.ts","test:docker:up":"docker-compose up -d --wait","test:docker:down":"docker-compose down"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56ac758a-0dd5-46d0-a33f-de330e57ff85"}},"_resolved":"/tmp/2721098ee60b87ce8d2ea7896ce757f7/platformatic-job-queue-0.2.0.tgz","_integrity":"sha512-0CKpKiW+9jOfY1vi3gNVIV8DFX/0H8HhWZZR960P6Oy1BGE49k3CNnnpkw1xu4J6Jyflmzfm/VyfxYk5idgLvA==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.10.0","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"22.22.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^9.0.0","semver":"^7.7.4","typescript":"^5.7.0","@types/node":"^22.0.0","neostandard":"^0.12.0","@platformatic/flame":"^1.6.0","cleaner-spec-reporter":"^1.0.3","eslint-plugin-prettier":"^5.5.5","prettier-plugin-space-before-function-paren":"^0.0.10"},"optionalDependencies":{"iovalkey":"^0.2.0","fast-write-atomic":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.2.0_1771498822412_0.9716814840723769","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@platformatic/job-queue","version":"0.3.0","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/job-queue@0.3.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"17a23abba0d3fb416507bf63a60d13eda386d37f","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.3.0.tgz","fileCount":67,"integrity":"sha512-3VMkAZSbCkNRwJpp6FL6atPegKVKbfpbPdfU91C4433B0YBLT4uOc6hy/x9Q+PgPeVsa/jTpzH4IjTuUqJqQ5g==","signatures":[{"sig":"MEQCIBVRO3KBJZbfYEp2RehNrOhYU1ifma0eaev8ZgC4YFm/AiBp4Ra0oDkIOkeCbCXs0EAEmMb3ga6NM+K4SDxcjGcT9Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fjob-queue@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":277371},"type":"module","_from":"file:platformatic-job-queue-0.3.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":"./dist/index.js","private":false,"scripts":{"ci":"npm run build && npm run lint && npm run test:ci","lint":"eslint --cache","test":"c8 -c test/config/c8-local.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","bench":"node benchmark/request-response.ts","build":"rm -rf dist && tsc -p tsconfig.build.json","ci:v20":"npm run lint && rm -rf dist && tsc -p tsconfig.json && node dist/scripts/postbuild.js && cd dist && node --env-file=../test/config/env --no-warnings --test --test-timeout=600000","format":"prettier -w benchmark src test","test:ci":"c8 -c test/config/c8-ci.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","typecheck":"tsc -p . --noEmit","bench:redis":"REDIS_URL=${REDIS_URL:-redis://localhost:6379} node benchmark/request-response.ts","bench:valkey":"REDIS_URL=${REDIS_URL:-redis://localhost:6380} node benchmark/request-response.ts","test:docker:up":"docker-compose up -d --wait","test:docker:down":"docker-compose down"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56ac758a-0dd5-46d0-a33f-de330e57ff85"}},"_resolved":"/tmp/e5cac3bfcfc02404a5ec0e9431f41cbc/platformatic-job-queue-0.3.0.tgz","_integrity":"sha512-3VMkAZSbCkNRwJpp6FL6atPegKVKbfpbPdfU91C4433B0YBLT4uOc6hy/x9Q+PgPeVsa/jTpzH4IjTuUqJqQ5g==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.10.1","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"22.22.0","dependencies":{"pino":"^10.3.1"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^9.0.0","semver":"^7.7.4","typescript":"^5.7.0","@types/node":"^22.0.0","neostandard":"^0.12.0","@platformatic/flame":"^1.6.0","cleaner-spec-reporter":"^1.0.3","eslint-plugin-prettier":"^5.5.5","prettier-plugin-space-before-function-paren":"^0.0.10"},"optionalDependencies":{"iovalkey":"^0.2.0","fast-write-atomic":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.3.0_1771585193147_0.8083188107837196","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@platformatic/job-queue","version":"0.4.0","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/job-queue@0.4.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"cd4b85145336350dc75f176e88617a81fe1bf8fb","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.4.0.tgz","fileCount":67,"integrity":"sha512-JpbzZuo0kHyoOKmTa14qZPR4O22AebMGaMBmsqBEnOgSqWtxYzWfgDCHa1G7gaN5tFDFThDtVB958kzrA+zWXA==","signatures":[{"sig":"MEUCIAOrb9+eyxT1f5dgf0ew05MJLqnZzHHaHvH28jJPI9PWAiEAveAlx2MnDe0zRPReDKV8vZlijqJ8nQC0SL8e4Zn7ibk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fjob-queue@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":287986},"type":"module","_from":"file:platformatic-job-queue-0.4.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":"./dist/index.js","private":false,"scripts":{"ci":"npm run build && npm run lint && npm run test:ci","lint":"eslint --cache","test":"c8 -c test/config/c8-local.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","bench":"node benchmark/request-response.ts","build":"rm -rf dist && tsc -p tsconfig.build.json","ci:v20":"npm run lint && rm -rf dist && tsc -p tsconfig.json && node dist/scripts/postbuild.js && cd dist && node --env-file=../test/config/env --no-warnings --test --test-timeout=600000","format":"prettier -w benchmark src test","test:ci":"c8 -c test/config/c8-ci.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","typecheck":"tsc -p . --noEmit","bench:redis":"REDIS_URL=${REDIS_URL:-redis://localhost:6379} node benchmark/request-response.ts","bench:valkey":"REDIS_URL=${REDIS_URL:-redis://localhost:6380} node benchmark/request-response.ts","test:docker:up":"docker-compose up -d --wait","test:docker:down":"docker-compose down"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56ac758a-0dd5-46d0-a33f-de330e57ff85"}},"_resolved":"/tmp/3ddf94a8b62bd46d4e070bbb2de2f1b3/platformatic-job-queue-0.4.0.tgz","_integrity":"sha512-JpbzZuo0kHyoOKmTa14qZPR4O22AebMGaMBmsqBEnOgSqWtxYzWfgDCHa1G7gaN5tFDFThDtVB958kzrA+zWXA==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.11.0","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"22.22.0","dependencies":{"pino":"^10.3.1"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^9.0.0","semver":"^7.7.4","typescript":"^5.7.0","@types/node":"^22.0.0","neostandard":"^0.12.0","@platformatic/flame":"^1.6.0","cleaner-spec-reporter":"^1.0.3","eslint-plugin-prettier":"^5.5.5","prettier-plugin-space-before-function-paren":"^0.0.10"},"optionalDependencies":{"iovalkey":"^0.2.0","fast-write-atomic":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.4.0_1772981966784_0.37252500837133184","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@platformatic/job-queue","version":"0.4.1","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/job-queue@0.4.1","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"fcf3fdcc708b29e0136d85b9cc01c3c4deb0db2e","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.4.1.tgz","fileCount":74,"integrity":"sha512-oFXAi7DDRVGHqPzfkpcs8i3TKx3ppz4YQ1KIkNEkJWYpzglJv6Xkc/aF4tgUZBaUptEGLorwE4SsEFVGspgh0A==","signatures":[{"sig":"MEUCIGhtPF8DA/5IjPzRi0OmWwSbaytR4UOskpx1HmR2tb9uAiEAkiGlvpssYeKZRvI2E8VCyRLGSUeKTnFAWyzYZgvhR9I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fjob-queue@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":292523},"type":"module","_from":"file:platformatic-job-queue-0.4.1.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":"./dist/index.js","private":false,"scripts":{"ci":"npm run build && npm run lint && npm run test:ci","lint":"eslint --cache","test":"c8 -c test/config/c8-local.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","bench":"node benchmark/request-response.ts","build":"rm -rf dist && tsc -p tsconfig.build.json","ci:v20":"npm run lint && rm -rf dist && tsc -p tsconfig.json && node dist/scripts/postbuild.js && cd dist && node --env-file=../test/config/env --no-warnings --test --test-timeout=600000","format":"prettier -w benchmark src test","test:ci":"c8 -c test/config/c8-ci.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","typecheck":"tsc -p . --noEmit","bench:redis":"REDIS_URL=${REDIS_URL:-redis://localhost:6379} node benchmark/request-response.ts","bench:valkey":"REDIS_URL=${REDIS_URL:-redis://localhost:6380} node benchmark/request-response.ts","test:docker:up":"docker-compose up -d --wait","test:docker:down":"docker-compose down"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56ac758a-0dd5-46d0-a33f-de330e57ff85"}},"_resolved":"/tmp/4c586a61dacd8a594bde0aa72ed6d121/platformatic-job-queue-0.4.1.tgz","_integrity":"sha512-oFXAi7DDRVGHqPzfkpcs8i3TKx3ppz4YQ1KIkNEkJWYpzglJv6Xkc/aF4tgUZBaUptEGLorwE4SsEFVGspgh0A==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.12.0","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"22.22.1","dependencies":{"pino":"^10.3.1"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^9.0.0","semver":"^7.7.4","typescript":"^5.7.0","@types/node":"^22.0.0","neostandard":"^0.12.0","@platformatic/flame":"^1.6.0","cleaner-spec-reporter":"^1.0.3","eslint-plugin-prettier":"^5.5.5","prettier-plugin-space-before-function-paren":"^0.0.10"},"optionalDependencies":{"iovalkey":"^0.2.0","fast-write-atomic":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.4.1_1774358524716_0.017668450862610374","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@platformatic/job-queue","version":"0.5.0","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","_id":"@platformatic/job-queue@0.5.0","maintainers":[{"name":"mzugm","email":"mzugm@1368435.ca"},{"name":"ivan-tymoshenko","email":"ivan@tymoshenko.me"},{"name":"marcopiraccini","email":"marco.piraccini@gmail.com"},{"name":"leorossi","email":"leonardo.rossi@gmail.com"},{"name":"shogun_panda","email":"paolo@cowtech.it"},{"name":"matteo.collina","email":"hello@matteocollina.com"},{"name":"qard","email":"admin@stephenbelanger.com"},{"name":"lucamaraschi","email":"luca.maraschi@gmail.com"},{"name":"simone.sanfra","email":"simone.sanfra@gmail.com"}],"homepage":"https://github.com/platformatic/job-queue","bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"dist":{"shasum":"caca3d2ddd6f9ca7b3ab1fe5970a77d7869e60b3","tarball":"https://registry.npmjs.org/@platformatic/job-queue/-/job-queue-0.5.0.tgz","fileCount":78,"integrity":"sha512-vAD4HmVy3IdDhv74+282j6YS9N8wbziWT/T1IPBQNsQO+ObKiZNfDQFeakAIEUp0w80vFJyrgy42knxJ97ACgQ==","signatures":[{"sig":"MEUCIERmUKm07+hI98oG31VjkaRAbj43aMU9ReZmTt5ukkbTAiEAuIax37KMtTF8PTG5Zq69PdQpGW8vr13bRNqeWRtJSyM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@platformatic%2fjob-queue@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":359993},"type":"module","_from":"file:platformatic-job-queue-0.5.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":"./dist/index.js","private":false,"scripts":{"ci":"npm run build && npm run lint && npm run test:ci","lint":"eslint --cache","test":"c8 -c test/config/c8-local.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","bench":"node benchmark/request-response.ts","build":"rm -rf dist && tsc -p tsconfig.build.json","ci:v20":"npm run lint && rm -rf dist && tsc -p tsconfig.json && node dist/scripts/postbuild.js && cd dist && node --env-file=../test/config/env --no-warnings --test --test-timeout=600000","format":"prettier -w benchmark src test","test:ci":"c8 -c test/config/c8-ci.json node --test-reporter=cleaner-spec-reporter --no-warnings --test --test-timeout=60000 'test/**/*.test.ts'","typecheck":"tsc -p . --noEmit","bench:redis":"REDIS_URL=${REDIS_URL:-redis://localhost:6379} node benchmark/request-response.ts","bench:valkey":"REDIS_URL=${REDIS_URL:-redis://localhost:6380} node benchmark/request-response.ts","test:docker:up":"docker-compose up -d --wait","test:docker:down":"docker-compose down"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56ac758a-0dd5-46d0-a33f-de330e57ff85"}},"_resolved":"/tmp/5a1f884c452e0e361a5a3f1172407115/platformatic-job-queue-0.5.0.tgz","_integrity":"sha512-vAD4HmVy3IdDhv74+282j6YS9N8wbziWT/T1IPBQNsQO+ObKiZNfDQFeakAIEUp0w80vFJyrgy42knxJ97ACgQ==","repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"_npmVersion":"11.12.0","description":"A reliable job queue with deduplication, request/response support, and pluggable storage","directories":{},"_nodeVersion":"22.22.1","dependencies":{"pino":"^10.3.1"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","eslint":"^9.0.0","semver":"^7.7.4","typescript":"^5.7.0","@types/node":"^22.0.0","neostandard":"^0.12.0","@platformatic/flame":"^1.6.0","cleaner-spec-reporter":"^1.0.3","eslint-plugin-prettier":"^5.5.5","prettier-plugin-space-before-function-paren":"^0.0.10"},"optionalDependencies":{"pg":"^8.20.0","iovalkey":"^0.2.0","fast-write-atomic":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/job-queue_0.5.0_1774515498609_0.6058577406512404","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-02-17T14:05:08.181Z","modified":"2026-04-16T09:48:49.138Z","0.0.1":"2026-02-17T14:05:08.431Z","0.1.0-alpha.3":"2026-02-18T09:16:04.641Z","0.1.0":"2026-02-18T09:20:47.950Z","0.2.0":"2026-02-19T11:00:22.553Z","0.3.0":"2026-02-20T10:59:53.277Z","0.4.0":"2026-03-08T14:59:26.922Z","0.4.1":"2026-03-24T13:22:04.854Z","0.5.0":"2026-03-26T08:58:18.758Z"},"bugs":{"url":"https://github.com/platformatic/job-queue/issues"},"author":{"url":"https://platformatic.dev","name":"Platformatic Inc.","email":"oss@platformatic.dev"},"license":"Apache-2.0","homepage":"https://github.com/platformatic/job-queue","keywords":["queue","job-queue","redis","valkey","reliable-queue"],"repository":{"url":"git+https://github.com/platformatic/job-queue.git","type":"git"},"description":"A reliable job queue with deduplication, request/response support, and pluggable storage","maintainers":[{"email":"mzugm@1368435.ca","name":"mzugm"},{"email":"ivan@tymoshenko.me","name":"ivan-tymoshenko"},{"email":"marco.piraccini@gmail.com","name":"marcopiraccini"},{"email":"leonardo.rossi@gmail.com","name":"leorossi"},{"email":"paolo@cowtech.it","name":"shogun_panda"},{"email":"hello@matteocollina.com","name":"matteo.collina"},{"email":"admin@stephenbelanger.com","name":"qard"},{"email":"luca.maraschi@gmail.com","name":"lucamaraschi"}],"readme":"# @platformatic/job-queue\n\nA reliable job queue for Node.js with deduplication, request/response support, and pluggable storage backends.\n\n## Features\n\n- **Deduplication** - Prevents duplicate job processing with configurable result caching\n- **Request/Response** - Enqueue jobs and wait for results with `enqueueAndWait()`\n- **Multiple Storage Backends** - Redis/Valkey, filesystem, or in-memory\n- **Automatic Retries** - Configurable retry attempts with exponential backoff\n- **Stalled Job Recovery** - Reaper automatically recovers jobs from crashed workers\n- **Graceful Shutdown** - Complete in-flight jobs before stopping\n- **TypeScript Native** - Full type safety with generic payload and result types\n- **Node.js 22.19+** - Uses native TypeScript type stripping\n\n## Installation\n\n```bash\nnpm install @platformatic/job-queue\n```\n\n## Quick Start\n\n```typescript\nimport { Queue, MemoryStorage } from '@platformatic/job-queue'\n\n// Create a queue with in-memory storage\nconst storage = new MemoryStorage()\nconst queue = new Queue<{ email: string }, { sent: boolean }>({\n  storage,\n  concurrency: 5\n})\n\n// Register a job handler\nqueue.execute(async (job) => {\n  console.log(`Processing job ${job.id}:`, job.payload)\n  // Send email...\n  return { sent: true }\n})\n\n// Start the queue\nawait queue.start()\n\n// Enqueue jobs\nawait queue.enqueue('email-1', { email: 'user@example.com' })\n\n// Optional per-job TTL override for cached result/error\nawait queue.enqueue('email-ttl', { email: 'ttl@example.com' }, {\n  resultTTL: 5 * 60 * 1000 // 5 minutes\n})\n\n// Or wait for the result\nconst result = await queue.enqueueAndWait('email-2', { email: 'another@example.com' }, {\n  timeout: 30000,\n  resultTTL: 24 * 60 * 60 * 1000 // keep this result for 24h\n})\nconsole.log('Result:', result) // { sent: true }\n\n// Graceful shutdown\nawait queue.stop()\n```\n\n## API Reference\n\n### Queue\n\nThe main class that combines producer and consumer functionality.\n\n```typescript\nimport { Queue } from '@platformatic/job-queue'\n\nconst queue = new Queue<TPayload, TResult>(config)\n```\n\n#### Configuration\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `storage` | `Storage` | *required* | Storage backend instance |\n| `workerId` | `string` | `uuid()` | Unique identifier for this worker |\n| `concurrency` | `number` | `1` | Number of jobs to process in parallel |\n| `maxRetries` | `number` | `3` | Maximum retry attempts for failed jobs |\n| `blockTimeout` | `number` | `5` | Seconds to wait when polling for jobs |\n| `visibilityTimeout` | `number` | `30000` | Milliseconds before a processing job is considered stalled |\n| `resultTTL` | `number` | `3600000` | Milliseconds to cache job results (1 hour) |\n| `logger` | `pino.Logger` | `abstractLogger` | Logger used by queue/producer/consumer |\n| `afterExecution` | `AfterExecutionHook<TPayload, TResult>` | `undefined` | Hook called after execution and before persisting terminal state |\n| `payloadSerde` | `Serde<TPayload>` | `JsonSerde` | Custom serializer for job payloads |\n| `resultSerde` | `Serde<TResult>` | `JsonSerde` | Custom serializer for job results |\n\n#### Methods\n\n##### `queue.start(): Promise<void>`\n\nConnect to storage and start processing jobs.\n\n##### `queue.stop(): Promise<void>`\n\nGracefully stop processing. Waits for in-flight jobs to complete.\n\n##### `queue.execute(handler): void`\n\nRegister a job handler. Call before or after `start()`.\n\n```typescript\nqueue.execute(async (job) => {\n  // job.id - unique job identifier\n  // job.payload - the job data\n  // job.attempts - current attempt number (starts at 1)\n  // job.signal - AbortSignal for cancellation\n  return result\n})\n```\n\n##### `queue.enqueue(id, payload, options?): Promise<EnqueueResult>`\n\nEnqueue a job (fire-and-forget).\n\n```typescript\nconst result = await queue.enqueue('job-123', { data: 'value' }, {\n  maxAttempts: 5,\n  resultTTL: 60_000 // optional per-job TTL override (ms)\n})\n\n// result.status can be:\n// - 'queued': Job was added to the queue\n// - 'duplicate': Job with this ID already exists\n// - 'completed': Job already completed (returns cached result)\n```\n\n##### `queue.enqueueAndWait(id, payload, options?): Promise<TResult>`\n\nEnqueue a job and wait for the result.\n\n```typescript\nconst result = await queue.enqueueAndWait('job-123', payload, {\n  timeout: 30000,      // Timeout in milliseconds\n  maxAttempts: 5,      // Override default max retries\n  resultTTL: 300000    // Optional per-job TTL override (ms)\n})\n```\n\nThrows `TimeoutError` if the job doesn't complete within the timeout.\n\n`resultTTL` behavior:\n- If provided in `enqueue()` / `enqueueAndWait()`, it overrides the queue default for that job.\n- If omitted, the producer uses the queue default `resultTTL` at enqueue time.\n- For duplicate IDs, the first accepted enqueue defines the TTL for that job.\n\n`afterExecution` hook behavior:\n- Runs after a successful execution or after the final failed attempt, before terminal state is persisted.\n- Receives a mutable context object (passed by reference), including `result`/`error` and `ttl`.\n- You can update `context.ttl` to change the stored result/error TTL dynamically.\n- You can replace `context.result` / `context.error` to affect what is persisted.\n- `id` and `status` are informational; changing them has no effect on persistence.\n\n```typescript\nconst queue = new Queue<{ url: string }, { body: string; maxAgeMs?: number }>({\n  storage,\n  resultTTL: 60_000,\n  afterExecution: async (context) => {\n    if (context.status === 'completed' && context.result?.maxAgeMs) {\n      context.ttl = context.result.maxAgeMs\n    }\n  }\n})\n```\n\n##### `queue.cancel(id): Promise<CancelResult>`\n\nCancel a queued job.\n\n```typescript\nconst result = await queue.cancel('job-123')\n\n// result.status can be:\n// - 'cancelled': Job was successfully cancelled\n// - 'not_found': Job doesn't exist\n// - 'processing': Job is currently being processed (cannot cancel)\n// - 'completed': Job already completed\n```\n\n##### `queue.getResult(id): Promise<TResult | null>`\n\nGet the cached result of a completed job.\n\n##### `queue.updateResultTTL(id, ttlMs): Promise<UpdateResultTTLResult>`\n\nUpdate TTL for a terminal job payload:\n- completed jobs: updates cached result TTL\n- failed jobs: updates cached error TTL\n\n```typescript\nconst update = await queue.updateResultTTL('job-123', 5 * 60_000)\n\n// update.status can be:\n// - 'updated'\n// - 'not_found'\n// - 'not_terminal'\n// - 'missing_payload'\n```\n\n##### `queue.getStatus(id): Promise<MessageStatus | null>`\n\nGet the current status of a job.\n\n```typescript\nconst status = await queue.getStatus('job-123')\n// {\n//   id: 'job-123',\n//   state: 'completed', // 'queued' | 'processing' | 'failing' | 'completed' | 'failed'\n//   createdAt: 1234567890,\n//   attempts: 1,\n//   result?: { ... },\n//   error?: { message: 'Error message', code?: 'ERROR_CODE', stack?: '...' }\n// }\n```\n\n#### Events\n\n```typescript\n// Lifecycle events\nqueue.on('started', () => {\n  console.log('Queue started')\n})\n\nqueue.on('stopped', () => {\n  console.log('Queue stopped')\n})\n\n// Job events\nqueue.on('enqueued', (id) => {\n  console.log(`Job ${id} was enqueued`)\n})\n\nqueue.on('completed', (id, result) => {\n  console.log(`Job ${id} completed:`, result)\n})\n\nqueue.on('failed', (id, error) => {\n  console.log(`Job ${id} failed:`, error.message)\n})\n\nqueue.on('failing', (id, error, attempt) => {\n  console.log(`Job ${id} failed attempt ${attempt}, will retry:`, error.message)\n})\n\nqueue.on('requeued', (id) => {\n  console.log(`Job ${id} was returned to queue (e.g., during graceful shutdown)`)\n})\n\nqueue.on('cancelled', (id) => {\n  console.log(`Job ${id} was cancelled`)\n})\n\n// Error events\nqueue.on('error', (error) => {\n  console.error('Queue error:', error)\n})\n```\n\n### Storage Backends\n\n#### MemoryStorage\n\nIn-memory storage for development and testing.\n\n```typescript\nimport { MemoryStorage } from '@platformatic/job-queue'\n\nconst storage = new MemoryStorage()\n```\n\n#### RedisStorage\n\nProduction-ready storage using Redis or Valkey.\n\n```typescript\nimport { RedisStorage } from '@platformatic/job-queue'\n\nconst storage = new RedisStorage({\n  url: 'redis://localhost:6379',\n  keyPrefix: 'myapp:', // Optional prefix for all keys\n  logger // Optional pino-compatible logger\n})\n```\n\nFeatures:\n- Atomic operations via Lua scripts\n- Blocking dequeue with `BLMOVE`\n- Pub/sub for real-time notifications\n- Compatible with Redis 7+ and Valkey 8+\n\n#### FileStorage\n\nFilesystem-based storage for single-node deployments.\n\n```typescript\nimport { FileStorage } from '@platformatic/job-queue'\n\nconst storage = new FileStorage({\n  basePath: '/var/lib/myapp/queue'\n})\n```\n\nFeatures:\n- Atomic writes with `fast-write-atomic`\n- FIFO ordering via sequence numbers\n- `fs.watch` for real-time notifications\n- Survives process restarts\n\n### Reaper\n\nThe Reaper monitors for stalled jobs and requeues them. Use when running multiple workers.\n\n```typescript\nimport { Reaper } from '@platformatic/job-queue'\n\nconst reaper = new Reaper({\n  storage,\n  visibilityTimeout: 30000  // Same as your queue's visibilityTimeout\n})\n\nawait reaper.start()\n\nreaper.on('stalled', (id) => {\n  console.log(`Job ${id} was stalled and requeued`)\n})\n\n// On shutdown\nawait reaper.stop()\n```\n\nThe Reaper uses event-based monitoring: it subscribes to job state changes and sets per-job timers. An initial scan at startup catches any jobs that were processing before the Reaper started.\n\n#### Leader Election\n\nFor high availability, you can run multiple Reaper instances with leader election enabled. Only one instance will be active at a time, and if it fails, another will automatically take over.\n\n```typescript\nconst reaper = new Reaper({\n  storage,\n  visibilityTimeout: 30000,\n  leaderElection: {\n    enabled: true,              // Enable leader election (default: false)\n    lockTTL: 30000,             // Lock expiry in ms (default: 30s)\n    renewalInterval: 10000,     // Renew lock every 10s (default: 1/3 of TTL)\n    acquireRetryInterval: 5000  // Followers retry every 5s (default: 5s)\n  }\n})\n\nreaper.on('leadershipAcquired', () => {\n  console.log('This reaper is now the leader')\n})\n\nreaper.on('leadershipLost', () => {\n  console.log('This reaper lost leadership')\n})\n\nawait reaper.start()\n```\n\nLeader election uses Redis's `SET NX PX` pattern for atomic lock acquisition:\n- The leader acquires a lock with a TTL and renews it periodically\n- If the leader stops gracefully, it releases the lock immediately\n- If the leader crashes, the lock expires and a follower takes over\n- Only RedisStorage supports leader election; other storage backends will emit an error\n\n### Custom Serialization\n\nImplement the `Serde` interface for custom serialization:\n\n```typescript\nimport { Serde } from '@platformatic/job-queue'\nimport * as msgpack from 'msgpackr'\n\nclass MsgPackSerde<T> implements Serde<T> {\n  serialize(value: T): Buffer {\n    return msgpack.pack(value)\n  }\n\n  deserialize(buffer: Buffer): T {\n    return msgpack.unpack(buffer) as T\n  }\n}\n\nconst queue = new Queue({\n  storage,\n  payloadSerde: new MsgPackSerde(),\n  resultSerde: new MsgPackSerde()\n})\n```\n\n## Patterns\n\n### Producer/Consumer Separation\n\nRun producers and consumers as separate processes:\n\n```typescript\n// producer.ts\nimport { Queue, RedisStorage } from '@platformatic/job-queue'\n\nconst storage = new RedisStorage({ url: process.env.REDIS_URL })\nconst producer = new Queue({ storage })\n\nawait producer.start()\nawait producer.enqueue('task-1', { ... })\nawait producer.stop()\n```\n\n```typescript\n// worker.ts\nimport { Queue, RedisStorage, Reaper } from '@platformatic/job-queue'\n\nconst storage = new RedisStorage({ url: process.env.REDIS_URL })\nconst queue = new Queue({\n  storage,\n  workerId: `worker-${process.pid}`,\n  concurrency: 10\n})\n\nconst reaper = new Reaper({\n  storage,\n  visibilityTimeout: 30000\n})\n\nqueue.execute(async (job) => {\n  // Process job\n  return result\n})\n\nawait queue.start()\nawait reaper.start()\n\nprocess.on('SIGTERM', async () => {\n  await queue.stop()\n  await reaper.stop()\n})\n```\n\n### Request/Response with Timeout\n\n```typescript\ntry {\n  const result = await queue.enqueueAndWait('request-1', payload, {\n    timeout: 10000\n  })\n  console.log('Got result:', result)\n} catch (error) {\n  if (error instanceof TimeoutError) {\n    console.log('Request timed out')\n  } else if (error instanceof JobFailedError) {\n    console.log('Job failed:', error.originalError)\n  }\n}\n```\n\n### Graceful Shutdown\n\n```typescript\nconst queue = new Queue({\n  storage,\n  visibilityTimeout: 30000  // Jobs have 30s to complete\n})\n\nqueue.execute(async (job) => {\n  // Check for cancellation\n  if (job.signal.aborted) {\n    throw new Error('Job cancelled')\n  }\n\n  // Long-running work...\n  await doWork()\n\n  return result\n})\n\nawait queue.start()\n\n// Handle shutdown signals\nprocess.on('SIGTERM', async () => {\n  console.log('Shutting down...')\n  await queue.stop()  // Waits for in-flight jobs\n  process.exit(0)\n})\n```\n\n## Error Handling\n\nThe library exports typed errors for specific failure conditions:\n\n```typescript\nimport {\n  TimeoutError,      // enqueueAndWait timeout\n  MaxRetriesError,   // Job failed after all retries\n  JobNotFoundError,  // Job doesn't exist\n  JobCancelledError, // Job was cancelled\n  JobFailedError,    // Job failed with error\n  StorageError       // Storage backend error\n} from '@platformatic/job-queue'\n```\n\n## Benchmarks\n\nRun request/response benchmarks over Redis:\n\n```bash\n# With Redis\nnpm run bench:redis\n\n# With Valkey\nnpm run bench:valkey\n```\n\nSample output:\n\n```\nSingle Request Baseline (100 requests, concurrency=1)\n──────────────────────────────────────────────────\n  Requests:    100\n  Throughput:  312.50 req/s\n  Latency:\n    min:       2.15 ms\n    p50:       3.02 ms\n    p95:       4.21 ms\n    p99:       5.43 ms\n    max:       6.12 ms\n    avg:       3.20 ms\n```\n\n## Testing\n\nRun tests against different backends:\n\n```bash\n# Memory storage only\nnpm run test:memory\n\n# With Redis (requires Redis on localhost:6379)\nnpm run test:redis\n\n# With Valkey (requires Valkey on localhost:6380)\nnpm run test:valkey\n\n# All backends\nnpm test\n```\n\nStart test infrastructure with Docker:\n\n```bash\nnpm run docker:up    # Start Redis and Valkey\nnpm run docker:down  # Stop containers\n```\n\n## Requirements\n\n- Node.js 22.19.0 or later (for native TypeScript support)\n- Redis 7+ or Valkey 8+ (for RedisStorage)\n\n## License\n\nApache 2.0\n","readmeFilename":"README.md"}