{"_id":"@bymax-one/nest-cache","_rev":"11-99ce8da475f7293f05c2f5e269053d1c","name":"@bymax-one/nest-cache","dist-tags":{"latest":"1.2.2"},"versions":{"1.0.0":{"name":"@bymax-one/nest-cache","version":"1.0.0","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.0.0","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"a6ccc3521cf80f120e01f9bb7c84267dce5dc027","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.0.0.tgz","fileCount":12,"integrity":"sha512-Kz3+uiPx1MxkCYgyfzp4l2DvzhUr06Ut5lhZFbKFlRZ2VV96B7A0s9Q/4xx1ARQc5qhQM8IYUT92DjaHOp+aXA==","signatures":[{"sig":"MEUCIBX7fcxTCXWxHJQOsrxyTK9QxhbdbaqBLYKl2v/qaNUAAiEAjihW/HnyBsKoFSZ+EN+jfOaaJaAq23Re7sRYoRCVdYw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":308554},"type":"module","_from":"file:bymax-one-nest-cache-1.0.0.tgz","engines":{"node":">=24.0.0"},"exports":{".":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.mjs","require":"./dist/server/index.cjs"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.mjs","require":"./dist/shared/index.cjs"}},"scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","release":"pnpm publish --provenance","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","mutation:dry-run":"stryker run --dryRunOnly","mutation:incremental":"stryker run --incremental"},"_npmUser":{"name":"bymax.one","email":"bymaxone.core@gmail.com"},"_resolved":"/private/var/folders/zf/_zr7t0ms60x6rcwj5zxk90cr0000gn/T/3a6e294e1ed806665f9502b1b8655cc6/bymax-one-nest-cache-1.0.0.tgz","_integrity":"sha512-Kz3+uiPx1MxkCYgyfzp4l2DvzhUr06Ut5lhZFbKFlRZ2VV96B7A0s9Q/4xx1ARQc5qhQM8IYUT92DjaHOp+aXA==","repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.13.0","description":"Typed Redis cache for NestJS based on ioredis 5, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.16.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^5.10.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.0.0_1785366665272_0.17242463140270914","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@bymax-one/nest-cache","version":"1.0.1","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.0.1","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"1d2cb56a39e3401df94b25b8cbd4c260f9ef6243","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.0.1.tgz","fileCount":12,"integrity":"sha512-mn5YJb+bmxN/2h9fz+4zY7L1DypNy6kT9JEylrO4aJAUODgZcEynAgDjoxaPPhpkzmAC7jOsRJcDrP7oV0pzjg==","signatures":[{"sig":"MEUCIQD9MQGY8GOqoT8JaYI+zmPzWdilOJFTbcJu4sbtsO30zQIgPewhG2bJmsrHglfEfJ5eVrz0x2ZiVuJrrQGgyGWAAPI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":310312},"main":"./dist/server/index.cjs","type":"module","_from":"file:bymax-one-nest-cache-1.0.1.tgz","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","release":"pnpm publish --provenance","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack . --profile node16","mutation:dry-run":"stryker run --dryRunOnly","mutation:incremental":"stryker run --incremental"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"_resolved":"/tmp/ebfc72754df410edf2fc6eb11f272980/bymax-one-nest-cache-1.0.1.tgz","_integrity":"sha512-mn5YJb+bmxN/2h9fz+4zY7L1DypNy6kT9JEylrO4aJAUODgZcEynAgDjoxaPPhpkzmAC7jOsRJcDrP7oV0pzjg==","repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.16.0","description":"Typed Redis cache for NestJS based on ioredis 5, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^5.10.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.0.1_1785369171944_0.8308982242410603","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@bymax-one/nest-cache","version":"1.0.2","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.0.2","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"ce631cfcb4426a1df1241a0cd80d153b0d36603a","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.0.2.tgz","fileCount":12,"integrity":"sha512-9MVjxGWMf5MAeNq7gmyuauuNNQaR5ZGhY2L5iUn6qh7a3W1caOVaUoJCJBZ5kDlLK4Sn+M1qlBv9/shwhsDIrA==","signatures":[{"sig":"MEQCIG9nL56UTelIzX0WQkJm1Qf/atKOIQkwHhxyoKOMsNRTAiA7LmOmOocPMIJgXy8jRcpv6ShPzj2RA0R8H7w8/hvMaQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":311539},"main":"./dist/server/index.cjs","type":"module","_from":"file:bymax-one-nest-cache-1.0.2.tgz","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","release":"pnpm publish --provenance","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","mutation:dry-run":"stryker run --dryRunOnly","mutation:incremental":"stryker run --incremental"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"_resolved":"/tmp/32818e2c2f46448661e4cc0ca04fc91f/bymax-one-nest-cache-1.0.2.tgz","_integrity":"sha512-9MVjxGWMf5MAeNq7gmyuauuNNQaR5ZGhY2L5iUn6qh7a3W1caOVaUoJCJBZ5kDlLK4Sn+M1qlBv9/shwhsDIrA==","repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.16.0","description":"Typed Redis cache for NestJS based on ioredis 5, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^5.10.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.0.2_1785370719699_0.7976927564124632","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@bymax-one/nest-cache","version":"1.0.3","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.0.3","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"46b24a099bbb03207de7ea3a7b9f8e2c2cc2412f","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.0.3.tgz","fileCount":12,"integrity":"sha512-iLd8SJ2mhbZ1qJ6rwZzLDte6jGVWZPbtW0/AuBRa6iIAyDFumXelDGTVkvcxJ5Roo1MY98fYC07jYPQIg/QTIg==","signatures":[{"sig":"MEYCIQDueSi+rB1+ZVS6nGK0tTfXV9Vbpd0QWcBflXe2P74jPAIhAP0cvZZkw88GOWdYSw73ZOWChwkOF3yVylt/PNYdTLSz","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":313487},"main":"./dist/server/index.cjs","type":"module","_from":"file:bymax-one-nest-cache-1.0.3.tgz","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","release":"pnpm publish --provenance","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","check:published":"node scripts/check-published-surface.mjs","mutation:dry-run":"stryker run --dryRunOnly","mutation:incremental":"stryker run --incremental"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"_resolved":"/tmp/12c7b9e0aca6d2178cc9d9c48b00a497/bymax-one-nest-cache-1.0.3.tgz","_integrity":"sha512-iLd8SJ2mhbZ1qJ6rwZzLDte6jGVWZPbtW0/AuBRa6iIAyDFumXelDGTVkvcxJ5Roo1MY98fYC07jYPQIg/QTIg==","repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.16.0","description":"Typed Redis cache for NestJS based on ioredis 5, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^5.10.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^11.1.18","@nestjs/common":"^11.0.16","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.0.3_1785614483042_0.5843760851392525","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@bymax-one/nest-cache","version":"1.0.4","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.0.4","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"e82a55db8c6a0f1be8ee44f057a340fe7527d42e","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.0.4.tgz","fileCount":12,"integrity":"sha512-OEU2aGymvhbKQBE9GdIjLNNRKNG5FO45bgE0sdYiVCRY1luVAs+H6YJ9HYuOtI2sB+LpOaUWqyULx4GZWj4Tbg==","signatures":[{"sig":"MEUCIQCxKuURHPAiOF1O7FnbEgsTZopp3Lf34v0hfwePS+K39AIgHvFNycZA2tYd/6TB4yWkblmdeEnRxZdLbcm44gU73VY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":318897},"main":"./dist/server/index.cjs","pnpm":{"overrides":{"qs":"^6.15.2","undici":"^7.28.0","esbuild":"^0.28.1","fast-uri":"^3.1.5","js-yaml@3":"^3.15.0","js-yaml@4":"^4.3.0","protobufjs":"^7.6.5","brace-expansion@1":"^1.1.18","brace-expansion@2":"^2.1.4","brace-expansion@5":"^5.0.9"}},"type":"module","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"gitHead":"d3c272d041ef84e148ca8c593af3c2f5d1fb5b4b","scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","prepare":"husky","release":"npm publish --provenance --access public","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","prepublishOnly":"pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm test:cov:all && pnpm build && pnpm check:published","check:published":"node scripts/check-published-surface.mjs","mutation:dry-run":"stryker run --dryRunOnly","mutation:incremental":"stryker run --incremental"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.16.0","description":"Typed Redis cache for NestJS based on ioredis 5, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@10.8.1","devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^5.10.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^11.1.18","@nestjs/common":"^11.0.16","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.0.4_1785859659833_0.1840321244165073","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@bymax-one/nest-cache","version":"1.0.5","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.0.5","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"c9da0fb445ef690f0c0000ba847d427876fb9158","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.0.5.tgz","fileCount":12,"integrity":"sha512-J7mp7BPoW10MtOzFv3+ZHIYbxawnkD/FsZItNjEiudUzEKT5oIk+E/CWX1zQSi5hTZ7jAtITM1MBektv7J0QEA==","signatures":[{"sig":"MEYCIQDat3gjQ7n4Ys36b3ca8IUr7AHMXoqFPEEi0SBYkkd/IgIhAIutq+nrqGvRa/DjwEPnrG8idWC4qsSp05lMElrhxx9r","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.0.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":319674},"main":"./dist/server/index.cjs","type":"module","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"gitHead":"ab0701ff5fa47c4138ce8b1c1e03d15223cfd809","scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","prepare":"husky","release":"npm publish --provenance --access public","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","prepublishOnly":"pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm test:cov:all && pnpm build && pnpm check:published","check:published":"node scripts/check-published-surface.mjs","mutation:dry-run":"stryker run --dryRunOnly","mutation:incremental":"stryker run --incremental"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.16.0","description":"Typed Redis cache for NestJS based on ioredis 5, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@11.20.0","devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^5.10.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^11.1.18","@nestjs/common":"^11.0.16","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.0.5_1786016021955_0.3491231361046494","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@bymax-one/nest-cache","version":"1.0.6","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.0.6","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"1b62b2b189beda6f2dbd36c5d19ba2f0b226ff67","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.0.6.tgz","fileCount":12,"integrity":"sha512-9gv2B8Q5IjJJzA/B/wWZY+Aae0Z+SZEImWbb2tBxWrIT5/DUiR18X/T81s2+lW7X+rOgdVrSDNvZwVIR5+UTzA==","signatures":[{"sig":"MEQCIFGjEUmgemDfqj/voMFHXAY0e3bWVGQvSSWpki4MRjE6AiBUer6lKoAIbVzcynAwXyPeTKjMWeMmv2yqUahbOgoPyw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.0.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":324147},"main":"./dist/server/index.cjs","type":"module","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"gitHead":"5495f2bf9658d538ae4dd722d7e3320cc5c67548","scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","prepare":"husky","release":"npm publish --provenance --access public","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","check:mutants":"node scripts/check-mutation-directives.mjs","prepublishOnly":"pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm check:mutants && pnpm test:cov:all && pnpm build && pnpm check:published","check:published":"node scripts/check-published-surface.mjs","mutation:dry-run":"stryker run --dryRunOnly","mutation:incremental":"stryker run --incremental"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.16.0","description":"Typed Redis cache for NestJS based on ioredis 5, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@11.20.0","devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^5.10.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^11.1.18","@nestjs/common":"^11.0.16","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.0.6_1786194050363_0.6040417823981914","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@bymax-one/nest-cache","version":"1.1.0","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.1.0","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"e930f961f6a62d48c5f616371b8d92283f03ffbb","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.1.0.tgz","fileCount":12,"integrity":"sha512-ueNNtkd/DOZ5feSQxg0iRyAOf4v2ecyDl+pDR/DS+WWXdZhlFRXMhftjyYrTHzuuYhuK15cDSEq7BgAHZIJI8A==","signatures":[{"sig":"MEUCIHhu+BtBKzLDMmtT58m8G7NTmGn140c23Dhh3G9SxCd2AiEA+rwq+EQjGqbcXPub6TxfRnQZQA7dsgxfjsQQdhLREjM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":325686},"main":"./dist/server/index.cjs","type":"module","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"gitHead":"37be6770f675b7da8c2e8299b80d6139822f3b03","scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","prepare":"husky","release":"npm publish --provenance --access public","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","check:mutants":"node scripts/check-mutation-directives.mjs","mutation:full":"node -e \"require('node:fs').rmSync('reports/stryker-incremental.json',{force:true,recursive:true})\" && stryker run","prepublishOnly":"pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm check:mutants && pnpm test:cov:all && pnpm build && pnpm check:published","check:published":"node scripts/check-published-surface.mjs","mutation:dry-run":"stryker run --dryRunOnly"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.16.0","description":"Typed Redis cache for NestJS based on ioredis 6, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@11.20.0","devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^6.0.0","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^6.0.0","@nestjs/core":"^11.1.18","@nestjs/common":"^11.0.16","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.1.0_1786443364710_0.2743976742322818","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@bymax-one/nest-cache","version":"1.2.0","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.2.0","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"dcd881fd5815574eff170c04d735f2681a9a737e","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.2.0.tgz","fileCount":16,"integrity":"sha512-4raE25p9hr9gRUTecth5cr9tpqgY1NNBniV5gan5oRQTzZsVGP+54b/604H1rLnXEER7s9WTHvV80UmlLEStiw==","signatures":[{"sig":"MEUCIQD7rfMgc0Fh6JsxpgB9XkZAOusi1CG33D+hQLf4yGrTEQIgaMGUp9gjKbIv0iR2H3jl0dJ77lkYh0PH6mWCZlnE0a0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":482397},"main":"./dist/server/index.cjs","type":"module","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./admin":{"import":{"types":"./dist/admin/index.d.ts","default":"./dist/admin/index.mjs"},"require":{"types":"./dist/admin/index.d.cts","default":"./dist/admin/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"gitHead":"a865b9f32bb6d529e96465d03990e9a534665c0e","scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","prepare":"husky","release":"npm publish --provenance --access public","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","check:mutants":"node scripts/check-mutation-directives.mjs","mutation:full":"node -e \"require('node:fs').rmSync('reports/stryker-incremental.json',{force:true,recursive:true})\" && stryker run","prepublishOnly":"pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm check:mutants && pnpm check:admin-readonly && pnpm test:cov:all && pnpm build && pnpm check:published","check:published":"node scripts/check-published-surface.mjs","mutation:dry-run":"stryker run --dryRunOnly","check:admin-readonly":"node scripts/check-admin-readonly.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.17.0","description":"Typed Redis cache for NestJS based on ioredis 6, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.19.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"admin":["./dist/admin/index.d.cts"],"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@11.20.0","devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^6.0.0","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^6.0.0","@nestjs/core":"^11.1.18","@nestjs/common":"^11.0.16","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.2.0_1787346033231_0.4971229712245968","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@bymax-one/nest-cache","version":"1.2.1","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","_id":"@bymax-one/nest-cache@1.2.1","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"homepage":"https://github.com/bymaxone/nest-cache#readme","bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"dist":{"shasum":"f407ae2bee4e2029bec966b975a505decc34a8b9","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.2.1.tgz","fileCount":16,"integrity":"sha512-Y64U/PSbIJqd0XA6RuXtUM/EJMZWTrubkAQyY+0IYkZte95xxaQthaRrKyBFGzTlzVeHV1nm6Yv84srApXL0Jg==","signatures":[{"sig":"MEUCIC3HOEtmk6auMKu8MTxWs50dBopsfyWvnF39MU8ID3eJAiEA1Rbl1lCIGtuLhoCjBARje4HHiZxhxpZ7lrb7ojiD4F0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":487035},"main":"./dist/server/index.cjs","type":"module","types":"./dist/server/index.d.cts","module":"./dist/server/index.mjs","engines":{"node":">=24.0.0"},"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./admin":{"import":{"types":"./dist/admin/index.d.ts","default":"./dist/admin/index.mjs"},"require":{"types":"./dist/admin/index.d.cts","default":"./dist/admin/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"gitHead":"4f0aa7ccc8fa8a96da5556ff6bef615abe942d0a","scripts":{"lint":"eslint src scripts","size":"node scripts/check-size.mjs","test":"jest","build":"pnpm clean && tsup","clean":"rm -rf dist coverage","prepare":"husky","release":"npm publish --provenance --access public","lint:fix":"eslint src scripts --fix","mutation":"stryker run","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","test:cov:all":"jest --config jest.coverage.config.ts --coverage","check:exports":"attw --pack .","check:mutants":"node scripts/check-mutation-directives.mjs","mutation:full":"node -e \"require('node:fs').rmSync('reports/stryker-incremental.json',{force:true,recursive:true})\" && stryker run","prepublishOnly":"pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm check:mutants && pnpm check:admin-readonly && pnpm test:cov:all && pnpm build && pnpm check:published","check:published":"node scripts/check-published-surface.mjs","mutation:dry-run":"stryker run --dryRunOnly","check:admin-readonly":"node scripts/check-admin-readonly.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"repository":{"url":"git+https://github.com/bymaxone/nest-cache.git","type":"git"},"_npmVersion":"11.17.0","description":"Typed Redis cache for NestJS based on ioredis 6, with namespace strategy, Pub/Sub and Lua script management.","directories":{},"lint-staged":{"*.{json,md,yml,yaml}":["prettier --write"],"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"24.19.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"typesVersions":{"*":{"admin":["./dist/admin/index.d.cts"],"shared":["./dist/shared/index.d.cts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@11.20.0","devDependencies":{"jest":"^30.4.2","rxjs":"^7.8.0","tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.4","globals":"^17.6.0","ioredis":"^6.0.0","ts-jest":"^29.4.9","ts-node":"^10.9.2","prettier":"^3.8.3","@eslint/js":"^9.39.4","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24","lint-staged":"^17.2.0","@nestjs/core":"^11.1.20","ioredis-mock":"^8.13.1","@nestjs/common":"^11.1.20","testcontainers":"^12.0.1","@commitlint/cli":"^21.2.1","@nestjs/testing":"^11.1.20","reflect-metadata":"^0.2.2","eslint-plugin-import":"^2.32.0","@arethetypeswrong/cli":"^0.18.5","@stryker-mutator/core":"^9","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","@typescript-eslint/parser":"^8.59.3","@stryker-mutator/jest-runner":"^9","@commitlint/config-conventional":"^21.2.0","@typescript-eslint/eslint-plugin":"^8.59.3","eslint-import-resolver-typescript":"^4.4.4","@stryker-mutator/typescript-checker":"^9"},"peerDependencies":{"ioredis":"^6.0.0","@nestjs/core":"^11.1.18","@nestjs/common":"^11.0.16","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-cache_1.2.1_1787356739086_0.8684551826223688","host":"s3://npm-registry-packages-npm-production"}},"1.2.2":{"name":"@bymax-one/nest-cache","version":"1.2.2","description":"Typed Redis cache for NestJS based on ioredis 6, with namespace strategy, Pub/Sub and Lua script management.","author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","homepage":"https://github.com/bymaxone/nest-cache#readme","repository":{"type":"git","url":"git+https://github.com/bymaxone/nest-cache.git"},"bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"type":"module","sideEffects":false,"exports":{".":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.mjs"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./admin":{"import":{"types":"./dist/admin/index.d.ts","default":"./dist/admin/index.mjs"},"require":{"types":"./dist/admin/index.d.cts","default":"./dist/admin/index.cjs"}},"./shared":{"import":{"types":"./dist/shared/index.d.ts","default":"./dist/shared/index.mjs"},"require":{"types":"./dist/shared/index.d.cts","default":"./dist/shared/index.cjs"}},"./package.json":"./package.json"},"main":"./dist/server/index.cjs","module":"./dist/server/index.mjs","types":"./dist/server/index.d.cts","typesVersions":{"*":{"shared":["./dist/shared/index.d.cts"],"admin":["./dist/admin/index.d.cts"]}},"scripts":{"build":"pnpm clean && tsup","check:exports":"attw --pack .","check:mutants":"node scripts/check-mutation-directives.mjs","check:admin-readonly":"node scripts/check-admin-readonly.mjs","check:published":"node scripts/check-published-surface.mjs","clean":"rm -rf dist coverage","lint":"eslint src scripts","lint:fix":"eslint src scripts --fix","mutation":"stryker run","mutation:full":"node -e \"require('node:fs').rmSync('reports/stryker-incremental.json',{force:true,recursive:true})\" && stryker run","mutation:dry-run":"stryker run --dryRunOnly","prepare":"husky","prepublishOnly":"pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm check:mutants && pnpm check:admin-readonly && pnpm test:cov:all && pnpm build && pnpm check:published","release":"npm publish --provenance --access public","size":"node scripts/check-size.mjs","test":"jest","test:all":"pnpm test && pnpm test:e2e","test:cov":"jest --coverage","test:cov:all":"jest --config jest.coverage.config.ts --coverage","test:e2e":"jest --config jest.e2e.config.ts --runInBand","test:types":"tsc --noEmit -p tsconfig.typetest.json","test:watch":"jest --watch","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.server.json"},"lint-staged":{"*.{ts,tsx,js,mjs,cjs}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]},"dependencies":{},"peerDependencies":{"@nestjs/common":"^11.0.16","@nestjs/core":"^11.1.18","ioredis":"^6.0.0","reflect-metadata":"^0.2.0"},"devDependencies":{"@arethetypeswrong/cli":"^0.18.5","@commitlint/cli":"^21.2.1","@commitlint/config-conventional":"^21.2.0","@eslint/js":"^10.0.1","@nestjs/common":"^11.1.20","@nestjs/core":"^11.1.20","@nestjs/testing":"^11.1.20","@stryker-mutator/core":"^9","@stryker-mutator/jest-runner":"^9","@stryker-mutator/typescript-checker":"^9","@types/jest":"^30.0.0","@types/node":"^24","@typescript-eslint/eslint-plugin":"^8.59.3","@typescript-eslint/parser":"^8.59.3","eslint":"^10.9.1","eslint-config-prettier":"^10.1.8","eslint-import-resolver-typescript":"^4.4.5","eslint-plugin-import-x":"^4.17.1","eslint-plugin-prettier":"^5.5.5","eslint-plugin-security":"^4.0.0","globals":"^17.6.0","husky":"^9.1.7","ioredis":"^6.0.0","ioredis-mock":"^8.13.1","jest":"^30.4.2","lint-staged":"^17.2.0","prettier":"^3.8.3","reflect-metadata":"^0.2.2","rxjs":"^7.8.0","testcontainers":"^12.0.1","ts-jest":"^29.4.9","ts-node":"^10.9.2","tsup":"^8.5.1","typescript":"^5.9.3"},"keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"packageManager":"pnpm@11.20.0","engines":{"node":">=24.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"0ea1ec19d9a44e60ab22ec0807338182fe48aa0d","_id":"@bymax-one/nest-cache@1.2.2","_nodeVersion":"24.20.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-N0Sc6IbZXF4/3D11m5zyJnjfL7eLbf9RcC5jGeIZjBNSpSfNgtXJRhDWeWkQt3SC6WOoKgqEkPWSyhXkAKNVnQ==","shasum":"affe5d653f6f67284610653c0dc3ea01880cb107","tarball":"https://registry.npmjs.org/@bymax-one/nest-cache/-/nest-cache-1.2.2.tgz","fileCount":16,"unpackedSize":489521,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bymax-one%2fnest-cache@1.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDQFa90OCzDzAxOTJUohF7ssIeSv749gJ/fP5DZBE5QZwIgIPWGahMy34/+fnZD1uRIqVZlOPGjBoGXgJZnezL8R7M="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1a5ae95-d98c-48bc-a1e0-c942d0cca112"}},"directories":{},"maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nest-cache_1.2.2_1788554361462_0.668027316047739"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-29T23:11:04.879Z","modified":"2026-09-04T20:39:21.879Z","1.0.0":"2026-07-29T23:11:05.462Z","1.0.1":"2026-07-29T23:52:52.127Z","1.0.2":"2026-07-30T00:18:39.848Z","1.0.3":"2026-08-01T20:01:23.191Z","1.0.4":"2026-08-04T16:07:39.980Z","1.0.5":"2026-08-06T11:33:42.106Z","1.0.6":"2026-08-08T13:00:50.530Z","1.1.0":"2026-08-11T10:16:04.909Z","1.2.0":"2026-08-21T21:00:33.433Z","1.2.1":"2026-08-21T23:58:59.227Z","1.2.2":"2026-09-04T20:39:21.612Z"},"bugs":{"url":"https://github.com/bymaxone/nest-cache/issues"},"author":{"name":"Bymax One","email":"support@bymax.one"},"license":"MIT","homepage":"https://github.com/bymaxone/nest-cache#readme","keywords":["cache","redis","ioredis","nestjs","typescript","sentinel","cluster","pubsub","lua"],"repository":{"type":"git","url":"git+https://github.com/bymaxone/nest-cache.git"},"description":"Typed Redis cache for NestJS based on ioredis 6, with namespace strategy, Pub/Sub and Lua script management.","maintainers":[{"name":"bymax.one","email":"bymaxone.core@gmail.com"},{"name":"msalvatti","email":"msalvatti@gmail.com"}],"readme":"<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/%40bymax--one-nest--cache-000000?style=for-the-badge&logo=nestjs&logoColor=E0234E\" alt=\"@bymax-one/nest-cache\" />\n</p>\n\n<h1 align=\"center\">@bymax-one/nest-cache</h1>\n\n<p align=\"center\">\n  <strong>Typed Redis cache for NestJS</strong><br />\n  <sub>ioredis 6 · Namespacing · Pub/Sub · Lua Scripts · Multi-Tenant · Zero Runtime Dependencies</sub>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@bymax-one/nest-cache\"><img src=\"https://img.shields.io/npm/v/@bymax-one/nest-cache?style=flat-square&colorA=000000&colorB=000000\" alt=\"npm version\" /></a>\n  <a href=\"https://www.npmjs.com/package/@bymax-one/nest-cache\"><img src=\"https://img.shields.io/npm/dm/@bymax-one/nest-cache?style=flat-square&colorA=000000&colorB=000000\" alt=\"npm downloads\" /></a>\n  <a href=\"https://github.com/bymaxone/nest-cache/actions/workflows/ci.yml\"><img src=\"https://img.shields.io/github/actions/workflow/status/bymaxone/nest-cache/ci.yml?branch=main&style=flat-square&colorA=000000&label=CI\" alt=\"CI status\" /></a>\n  <a href=\"https://github.com/bymaxone/nest-cache/actions/workflows/ci.yml\"><img src=\"https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square&colorA=000000\" alt=\"coverage\" /></a>\n  <a href=\"https://github.com/bymaxone/nest-cache/blob/main/docs/mutation_testing_results.md\"><img src=\"https://img.shields.io/badge/mutation-100%25-brightgreen?style=flat-square&colorA=000000\" alt=\"mutation score\" /></a>\n  <a href=\"https://scorecard.dev/viewer/?uri=github.com/bymaxone/nest-cache\"><img src=\"https://api.scorecard.dev/projects/github.com/bymaxone/nest-cache/badge?style=flat-square\" alt=\"OpenSSF Scorecard\" /></a>\n  <a href=\"https://github.com/bymaxone/nest-cache/blob/main/LICENSE\"><img src=\"https://img.shields.io/github/license/bymaxone/nest-cache?style=flat-square&colorA=000000&colorB=000000\" alt=\"license\" /></a>\n  <a href=\"https://www.typescriptlang.org/\"><img src=\"https://img.shields.io/badge/TypeScript-strict-3178C6?style=flat-square&logo=typescript&logoColor=white\" alt=\"TypeScript\" /></a>\n  <a href=\"https://nodejs.org/\"><img src=\"https://img.shields.io/badge/Node.js-24%2B-339933?style=flat-square&logo=node.js&logoColor=white\" alt=\"Node.js\" /></a>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/bymaxone/nest-cache\">GitHub</a> ·\n  <a href=\"https://github.com/bymaxone/nest-cache/issues\">Issues</a> ·\n  <a href=\"#-quick-start\">Quick Start</a> ·\n  <a href=\"#-api-reference\">API Reference</a> ·\n  <a href=\"https://github.com/bymaxone/nest-cache-example\">Example App</a>\n</p>\n\n---\n\n## ✨ Overview\n\n`@bymax-one/nest-cache` wraps a single, correctly-managed `ioredis` connection behind a typed\nNestJS module. Instead of scattering raw Redis calls across your services, you get a namespaced,\nserializer-backed API with first-class Pub/Sub and atomic Lua scripting — and a connection whose\nlifecycle, reconnection, and graceful shutdown are handled for you.\n\nThe library has **zero direct dependencies** — `ioredis` and NestJS arrive as peer dependencies,\nso you control exact versions and the supply-chain surface stays minimal.\n\n### Why nest-cache?\n\n- **🔑 Namespaced by design** — every key is composed through a key builder (`{namespace}:{prefix}:{id}`), so tenants and features never collide. Raw, un-namespaced access is a documented anti-pattern.\n- **🧬 Typed get/set** — `get<T>` / `set<T>` go through a pluggable `ISerializer` (JSON by default). Deserialization **fails closed** — a malformed payload throws `CacheException`, never a half-decoded value.\n- **📡 Batteries included** — Pub/Sub on namespaced channels and a Lua script manager (`EVALSHA` + `NOSCRIPT` fallback) ship in the box, on top of the full string/hash/set/numeric command surface.\n- **♻️ Lifecycle done right** — singleton connection with `OnModuleInit` / `OnModuleDestroy`, bounded retry strategy, `READONLY`-failover reconnect, and a graceful `quit()` with shutdown timeout.\n- **🔌 Bring your own observability** — connection events surface through an `events.onEvent` callback; plug in [`@bymax-one/nest-logger`](https://github.com/bymaxone/nest-logger) or your metrics layer. No observability peer deps forced on you.\n\n```\npnpm add @bymax-one/nest-cache ioredis\n```\n\n---\n\n## 🔥 Features\n\n### 🧬 Typed Cache API\n\n- ✅ **Typed get/set** — `get<T>` / `set<T>` / `setNx<T>` / `mget<T>` / `mset<T>` through a pluggable serializer\n- ✅ **Full command surface** — strings, numbers (`incr`/`decr`), hashes, sets, TTL (`expire`/`ttl`/`persist`), iteration (`scan`)\n- ✅ **Pluggable serialization** — `ISerializer` contract; swap JSON for MessagePack, CBOR, or your own codec\n- ✅ **Raw string access** — `getRaw` / `setRaw` skip the serializer while keeping namespacing; `pipeline` / `getClient` are the documented escape hatches\n\n### 🔑 Isolation & Namespacing\n\n- ✅ **Automatic namespacing** — the key builder enforces tenant/feature isolation; no manual string concatenation\n- ✅ **Namespaced channels** — Pub/Sub channels are composed through the same builder as keys\n- ✅ **Surgical flush** — `flushNamespace()` scans only `{namespace}:*`, never another namespace's keys\n- ✅ **Validated at bootstrap** — an empty namespace, or one containing the key separator, fails module initialization\n\n### ⚙️ Reliability & Topology\n\n- ✅ **Multi-topology** — standalone, Sentinel, and Cluster modes from the same options shape\n- ✅ **Managed lifecycle** — singleton connection via `OnModuleInit` / `OnModuleDestroy`, graceful `quit()` with a shutdown timeout\n- ✅ **Bounded retries** — `maxRetriesPerRequest` and a reconnect policy that triggers on `READONLY` replica failover\n- ✅ **Connection events** — `connect` / `ready` / `error` / `close` / `reconnecting` / `end` surfaced via `events.onEvent`\n- ✅ **Health checks** — `isHealthy()` / `ping()` / `info()` for readiness and liveness endpoints\n\n### 🛡️ Safety\n\n- ✅ **Fail-closed serialization** — malformed payloads raise `CacheException(DESERIALIZATION_FAILED)`, never a partial value\n- ✅ **Production flush guard** — `flushNamespace()` is blocked under `NODE_ENV=production` unless explicitly allowed\n- ✅ **Secrets never echoed** — connection URLs and cached values are kept out of error `details`; previews are truncated\n- ✅ **Script bodies are registered, not interpolated** — call sites pass a name plus keys/args, never Lua source\n\n### 🧩 Developer Experience\n\n- ✅ **Dynamic module** — `forRoot()` and `forRootAsync()` via `ConfigurableModuleBuilder`; registered globally by default (`isGlobal`)\n- ✅ **Lua script manager** — register scripts up front, execute by name with transparent `NOSCRIPT` reload + retry\n- ✅ **Structured errors** — every failure is a `CacheException` with a stable `cache.*` code and an HTTP status\n- ✅ **Zero runtime dependencies** — everything is a peer dependency; `dependencies: {}`\n\n---\n\n## 📦 Subpath Exports\n\nOne package, three entry points — import only what your app needs:\n\n| Subpath    | Import                         | Purpose                                                                                                  |              Dependencies              |\n| ---------- | ------------------------------ | -------------------------------------------------------------------------------------------------------- | :------------------------------------: |\n| **Server** | `@bymax-one/nest-cache`        | `BymaxCacheModule`, `CacheService`, `PubSubService`, `ScriptManagerService`, DI tokens, `CacheException` | NestJS 11, ioredis 6, reflect-metadata |\n| **Admin**  | `@bymax-one/nest-cache/admin`  | Read-only administration — health, `INFO` statistics, keyspace listing, key inspection, value reveal     | NestJS 11, ioredis 6, the server entry |\n| **Shared** | `@bymax-one/nest-cache/shared` | Types + constants — `CACHE_ERROR_CODES`, `CacheEventName`, config types                                  |                  None                  |\n\n```\nshared (zero deps)\n     ↑\n  server  ←  admin\n```\n\n`/admin` is a **privileged** surface and is kept out of the main entry on purpose: importing it is a greppable, reviewable act, a consumer who never wires it cannot resolve a reveal service from DI by accident, and it never lands in the main bundle.\n\nThe `/shared` subpath is safe to import in isomorphic code, test helpers, CLI scripts, or shared packages that must not pull in NestJS or ioredis.\n\n---\n\n> [!TIP]\n> Prefer to learn from a working app? See the [nest-cache-example](https://github.com/bymaxone/nest-cache-example) — a full NestJS project wired with this library.\n\n## 🚀 Quick Start\n\n### 1. Install\n\n```bash\n# Using pnpm (recommended)\npnpm add @bymax-one/nest-cache ioredis\n\n# Using npm\nnpm install @bymax-one/nest-cache ioredis\n\n# Using yarn\nyarn add @bymax-one/nest-cache ioredis\n```\n\n> [!IMPORTANT]\n> `@nestjs/common`, `@nestjs/core`, and `reflect-metadata` are peer dependencies (already present in\n> any NestJS app). `ioredis` is the single functional peer — the Redis client itself.\n\n### 2. Register the Module\n\nPick the topology that matches your deployment. All four forms share the same options shape.\n\n#### Scenario 1 — Standalone (dev / single node)\n\n```typescript\nimport { Module } from '@nestjs/common'\nimport { BymaxCacheModule } from '@bymax-one/nest-cache'\n\n@Module({\n  imports: [\n    BymaxCacheModule.forRoot({\n      connection: { url: 'redis://localhost:6379' },\n      namespace: 'app'\n    })\n  ]\n})\nexport class AppModule {}\n```\n\n#### Scenario 2 — Sentinel (high availability)\n\n```typescript\nBymaxCacheModule.forRoot({\n  mode: 'sentinel',\n  sentinel: {\n    sentinels: [\n      { host: 'sentinel1.example.com', port: 26379 },\n      { host: 'sentinel2.example.com', port: 26379 }\n    ],\n    name: 'mymaster',\n    password: process.env.REDIS_PASSWORD\n  },\n  namespace: 'app'\n})\n```\n\n#### Scenario 3 — Cluster (sharded)\n\n```typescript\nBymaxCacheModule.forRoot({\n  mode: 'cluster',\n  cluster: {\n    nodes: [\n      { host: 'cluster1.example.com', port: 7000 },\n      { host: 'cluster2.example.com', port: 7001 },\n      { host: 'cluster3.example.com', port: 7002 }\n    ]\n  },\n  namespace: 'app'\n})\n```\n\n#### Scenario 4 — forRootAsync with ConfigService\n\n```typescript\nimport { Module } from '@nestjs/common'\nimport { ConfigModule, ConfigService } from '@nestjs/config'\nimport { BymaxCacheModule } from '@bymax-one/nest-cache'\n\n@Module({\n  imports: [\n    BymaxCacheModule.forRootAsync({\n      imports: [ConfigModule],\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        connection: { url: config.getOrThrow<string>('REDIS_URL') },\n        namespace: 'app',\n        events: {\n          // Plug @bymax-one/nest-logger or a metrics sink here\n          onEvent: (event, data) => console.log(`[cache] ${event}`, data)\n        }\n      })\n    })\n  ]\n})\nexport class AppModule {}\n```\n\n### 3. Inject `CacheService`\n\nThe module is global by default, so no re-import is needed in feature modules:\n\n```typescript\nimport { Injectable } from '@nestjs/common'\nimport { CacheService } from '@bymax-one/nest-cache'\n\n@Injectable()\nexport class ProfileService {\n  constructor(private readonly cache: CacheService) {}\n\n  async getProfile(userId: string): Promise<Profile | null> {\n    const cached = await this.cache.get<Profile>('user-profile', userId)\n    if (cached) return cached\n\n    const profile = await this.repo.findProfile(userId)\n    await this.cache.set('user-profile', userId, profile, 3600) // TTL in seconds\n    return profile\n  }\n}\n```\n\nKeys resolve to `app:user-profile:<userId>` — namespaced automatically.\n\n---\n\n## ⚙️ Configuration\n\n| Option              | Type                                      | Default          | Description                                                        |\n| ------------------- | ----------------------------------------- | ---------------- | ------------------------------------------------------------------ |\n| `mode`              | `'standalone' \\| 'sentinel' \\| 'cluster'` | `'standalone'`   | Redis topology                                                     |\n| `connection.url`    | `string`                                  | —                | `redis://` / `rediss://` URL (overrides discrete host/port fields) |\n| `connection.tls`    | `tls.ConnectionOptions`                   | —                | TLS options for `rediss://`                                        |\n| `namespace`         | `string`                                  | `'app'`          | Key prefix for tenant/feature isolation                            |\n| `serializer`        | `ISerializer`                             | `JsonSerializer` | Value encoding/decoding (plug MsgPack, CBOR, etc.)                 |\n| `events.onEvent`    | `(event, data) => void`                   | —                | Connection-event hook (plug a logger or metrics)                   |\n| `scripts`           | `IScriptDefinition[]`                     | `[]`             | Lua scripts to preload on init                                     |\n| `shutdownTimeoutMs` | `number`                                  | `5000`           | Graceful `quit()` timeout before forced `disconnect()`             |\n\nBoth `forRoot(options)` (synchronous) and `forRootAsync({ useFactory, inject, imports })` are supported. The module registers globally by default — pass `isGlobal: false` to scope it to the importing module.\n\n---\n\n## 🔑 Key Namespacing\n\nEvery key is composed as `{namespace}{separator}{prefix}{separator}{id}` (default separator `:`).\nCalling `cache.get('user-profile', '42')` under namespace `app` reads `app:user-profile:42`. This\nkeeps tenants and features isolated and makes `flushNamespace()` surgical. Reaching for\n`getClient()` to set raw, un-namespaced keys is supported as an escape hatch but documented as an\nanti-pattern.\n\n---\n\n## 📡 Pub/Sub\n\n```typescript\nconst unsubscribe = await pubsub.subscribe<UserEvent>('user-events', async (msg) => {\n  await handle(msg)\n})\nawait pubsub.publish<UserEvent>('user-events', { type: 'created', id: '42' })\n// ...later\nawait unsubscribe()\n```\n\nChannels are namespaced like keys. The subscriber connection is created lazily on the first\nsubscription. Redis Pub/Sub is fire-and-forget — messages published while a subscriber is offline\nare not replayed.\n\n---\n\n## 📜 Lua Scripts\n\nRegister scripts at module init, then execute them atomically by name. The manager caches the\nSHA1 and uses `EVALSHA`, transparently reloading on `NOSCRIPT`:\n\n```typescript\n// In module options:\nscripts: [{ name: 'compareAndSet', lua: '...' }]\n\n// At call site — keys are flat strings passed directly to Lua's KEYS[] table.\n// CacheService prepends the namespace via applyNamespace(), so 'lock:job'\n// becomes 'app:lock:job' in Redis. Pass the full suffix as a single string.\nconst ok = await cache.eval('compareAndSet', ['lock:job'], [expected, next])\n```\n\n---\n\n## 🔁 Custom Serializer\n\nSwap the default `JsonSerializer` with any `ISerializer` implementation — MsgPack, CBOR, or your own:\n\n```typescript\nimport { encode, decode } from '@msgpack/msgpack'\nimport type { ISerializer } from '@bymax-one/nest-cache'\n\nclass MsgPackSerializer implements ISerializer {\n  serialize<T>(value: T): string {\n    return Buffer.from(encode(value)).toString('base64')\n  }\n  deserialize<T>(raw: string): T {\n    return decode(Buffer.from(raw, 'base64')) as T\n  }\n}\n\n// In module options:\nBymaxCacheModule.forRoot({\n  connection: { url: 'redis://localhost:6379' },\n  serializer: new MsgPackSerializer()\n})\n```\n\n---\n\n## 🔗 Plug with @bymax-one/nest-logger\n\nWire connection events into your logger via the `events.onEvent` hook:\n\n```typescript\nimport { BymaxOneLogger } from '@bymax-one/nest-logger'\n\nBymaxCacheModule.forRootAsync({\n  imports: [ConfigModule, LoggerModule],\n  inject: [ConfigService, BymaxOneLogger],\n  useFactory: (config: ConfigService, logger: BymaxOneLogger) => ({\n    connection: { url: config.getOrThrow('REDIS_URL') },\n    namespace: 'app',\n    events: {\n      onEvent: (event, data) => {\n        if (event === 'error') logger.error('[cache]', data)\n        else logger.log(`[cache] ${event}`, data)\n      }\n    }\n  })\n})\n```\n\n---\n\n## ❤️ Health Check (terminus integration)\n\n```typescript\nimport { Controller, Get } from '@nestjs/common'\nimport { HealthCheck, HealthCheckService } from '@nestjs/terminus'\nimport { CacheService } from '@bymax-one/nest-cache'\n\n@Controller('health')\nexport class HealthController {\n  constructor(\n    private readonly health: HealthCheckService,\n    private readonly cache: CacheService\n  ) {}\n\n  @Get()\n  @HealthCheck()\n  check() {\n    return this.health.check([\n      () =>\n        this.cache\n          .isHealthy()\n          .then((ok) =>\n            ok ? { redis: { status: 'up' } } : Promise.reject(new Error('Redis not ready'))\n          )\n    ])\n  }\n}\n```\n\n---\n\n## 🔎 Administration surface (`/admin`)\n\nA read-only surface for an operator-facing console: is the cache answering, what is it doing, what is in it.\n\n```ts\nimport { BymaxCacheModule } from '@bymax-one/nest-cache'\nimport { BymaxCacheAdminModule } from '@bymax-one/nest-cache/admin'\n\n// `config` is a ConfigService reachable where the module is declared; see\n// Scenario 4 above for the forRootAsync form that injects it properly.\n@Module({\n  imports: [\n    BymaxCacheModule.forRoot({\n      connection: { url: config.getOrThrow<string>('REDIS_URL') },\n      namespace: 'my-app'\n    }),\n    BymaxCacheAdminModule.forRoot({\n      scopes: [\n        {\n          id: 'cache',\n          label: 'Application cache',\n          pattern: 'my-app:*',\n          isReadable: true,\n          origin: \"the application's own namespace, written through the typed API\"\n        },\n        {\n          id: 'auth',\n          label: 'Authentication',\n          pattern: 'auth:*',\n          isReadable: false,\n          origin:\n            'written by another library through the un-namespaced client, so it sits at Redis ' +\n            'root. Values are refused: this keyspace holds session records.'\n        }\n      ]\n    })\n  ]\n})\nexport class AppModule {}\n```\n\n```ts\nconstructor(\n  @Inject(CacheStatusService) private readonly status: CacheStatusService,\n  @Inject(CacheAdminService) private readonly admin: CacheAdminService\n) {}\n\nawait this.status.health()   // { status: 'up', latencyMs: 3, mode, isScanSupported, degradedAboveMs }\nawait this.status.stats()    // parsed INFO\nthis.status.config()         // resolved wiring, connection URL withheld\nthis.admin.listScopes()      // never touches the connection\nawait this.admin.listKeys('cache', { includeSize: true })\nawait this.admin.revealValue('auth', 'auth:sess:1')  // { status: 'withheld', origin }\n```\n\n### What the library owns, and what you own\n\nThe library owns the **mechanism**: validating the allowlist, scanning against it, describing keys, withholding values. The application owns **which keyspaces exist**, the `origin` prose that explains them, the routes, and the guards. A cache library cannot know that another library writes at Redis root through `getClient()`, and making it depend on that library to find out would invert two packages to save an application from stating one thing about itself.\n\n### `isReadable: false` withholds the value — and nothing else\n\nListing, types, TTLs and sizes stay available on an unreadable scope. Only the value is refused, and the refusal is returned _before_ the value is read.\n\nThis is the easy thing to get wrong, because \"unreadable\" reads like \"return nothing\" — and the unreadable scope is typically the one that holds the most interesting keys. A surface that renders it as empty tells an operator the region holds nothing while it is full, which is the same defect as a blank log page during an outage: a reading meaning _\"I may not tell you\"_ drawn identically to one meaning _\"there is nothing here\"_.\n\n### Scope patterns: a literal prefix, optionally ending in `*`\n\n`auth:*`, `my-app:*` and exact keys are accepted. `app:*:v1`, `*`, `a?b` and `a[bc]` are refused at wiring.\n\nThe restriction exists because a caller names a **key**, so the library must decide whether that key belongs to the named scope — otherwise a caller names the readable scope and passes a key from the credential-bearing one. Deciding that for arbitrary globs means reimplementing Redis's `stringmatchlen` — greedy `*` with backtracking, `[a-z]` classes, `^` negation, escapes, and the unterminated-class case where `ten[ant` matches nothing at all — and **a matcher even slightly more permissive than the server's is a silent cross-scope leak that no happy-path test would show.** With this shape, membership is exact by construction, and the E2E suite checks it differentially against a real server's own `KEYS`.\n\nDo not relax this to be helpful. Widening it later is compatible; a leak is not un-shippable.\n\n### Readings that would carry two meanings are unions, not nullables\n\n| Reading      | Type                                                      | Why                                                                                                                                                                                            |\n| ------------ | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `maxmemory`  | `unbounded \\| limited \\| unreported`                      | Redis spells \"no ceiling\" as `maxmemory:0`; read as a literal ceiling it draws a full saturation bar on the least constrained server there is — and \"unbounded\" is not \"the server didn't say\" |\n| `TTL`        | `expiring \\| persistent \\| missing`                       | `-1` and `-2` are different facts, and the key that expired between the scan and the read is the one an operator is watching                                                                   |\n| `aofEnabled` | `boolean \\| null`                                         | `false` for an absent field is a durability claim made without evidence                                                                                                                        |\n| health       | `{ up \\| degraded, latencyMs } \\| { down, reason, code }` | A latency exists if and only if the ping answered — expressed as `latencyMs: number \\| null` that is a convention a future `catch` can break; as a union it does not compile                   |\n\n`mode`, `isScanSupported` and `degradedAboveMs` sit **outside** the health union: a cluster deployment that is down should still report that scanning was never going to work.\n\n### Reading a value: `revealed` / `withheld` / `missing`\n\n`revealValue()` answers with a discriminated union, not a nullable value, because **\"I may not tell you\" and \"there is nothing here\" are different answers** and rendering them identically is the defect this whole surface exists to avoid.\n\n```ts\nimport type { CacheAdminService } from '@bymax-one/nest-cache/admin'\n\nasync function describeValue(admin: CacheAdminService, scope: string, key: string) {\n  const result = await admin.revealValue(scope, key)\n\n  switch (result.status) {\n    case 'revealed':\n      // `result.type` is the Redis type; `result.value` is shaped by it.\n      return { type: result.type, value: result.value }\n    case 'withheld':\n      // The scope declares `isReadable: false`. `origin` explains why, verbatim.\n      // Listing, types, TTLs and sizes for this key remain available.\n      return { refusedBecause: result.origin }\n    case 'missing':\n      return { gone: true }\n  }\n}\n```\n\nA withheld value is **not an authorization failure**. The caller is allowed to ask; the deployment declared the keyspace unreadable at wiring, and no credential changes that — so serving it as `403` would tell a client that some other permission would unlock it, which is false.\n\nThe revealed value is shaped by the key's type:\n\n| `value.kind`  | Shape                              | Notes                                                                                    |\n| ------------- | ---------------------------------- | ---------------------------------------------------------------------------------------- |\n| `string`      | `{ value: string }`                | Truncated to `revealStringLimit`                                                         |\n| `hash`        | `{ fields: { field, value }[] }`   | `field`, matching Redis's own vocabulary (`HSET key field value`)                        |\n| `members`     | `{ members: string[] }`            | **Sets and lists both.** The enclosing `type` says which — see the ordering caveat below |\n| `scored`      | `{ members: { member, score }[] }` | Sorted sets. `score` is a **string**: parsing to a number would round large integers     |\n| `unsupported` | `{ type: RedisKeyType }`           | Streams and module types — says so, rather than returning an empty value                 |\n\nEvery arm except `unsupported` carries `isComplete`. It is deliberately **positive polarity**: an absent boolean reads as `false`, and under a name like `truncated` that default would be the _reassuring_ answer — a value silently claiming nothing was cut.\n\n> [!IMPORTANT]\n> **List order is meaningful; set order is not**, and both arrive in the same `members` array. Nothing in the type stops a renderer from sorting either one. Sorting a set for display is fine; sorting a list is a lie about the data — an `LPUSH` queue shown alphabetically misreports what pops next. Gate any client-side ordering on `type === 'set'`.\n\n### Costs the surface does not hide\n\n- **`sampledCount` / `sampledBytes`** are sums over a capped `SCAN`, not measurements of the keyspace. `isComplete` is the fact; the names are the guard.\n- **A page may carry slightly more than `scanLimit` entries.** `SCAN` returns whole batches and the cursor has already moved past them, so the limit stops the loop rather than trimming the result — trimming would drop keys no later page could reach.\n- **Sizing is opt-in** (`includeSize`), and every pipeline batch is bounded in **commands**, not keys. Redis is single-threaded, so a pipeline converts a network cost into a server-blocking one: one flush of N keys × 3 commands blocks every other client for the whole burst, on a server someone is inspecting precisely because it is unwell.\n- **`connection.url` is never on the wire.** The config payload carries host, port and a TLS flag; the URL is never read into the admin subpath at all.\n- **`mem_fragmentation_ratio` is reported raw.** On an instance holding very little, allocator and copy-on-write overhead dominate and the figure reads far above 1 without indicating a problem — 9.07 was measured on an instance holding 1.1 MiB. Turning it into a verdict is deployment policy.\n\n### Cluster\n\nEvery scan-based operation throws `UNSUPPORTED_IN_CLUSTER`, inherited from `CacheService.getClient()` rather than restated. `isScanSupported` travels on the health payload so a console never has to re-derive that rule from `mode`.\n\n---\n\n## 🏗️ Architecture\n\nThe package runs **inside** your NestJS application as a dynamic module — not as a separate service:\n\n```\n┌─────────────────────────────────────────────────────┐\n│               Your NestJS Application               │\n│                                                     │\n│  ┌───────────────────────────────────────────────┐  │\n│  │             @bymax-one/nest-cache             │  │\n│  │                                               │  │\n│  │  CacheService ←→ ConnectionManager ←→ Redis   │  │\n│  │  PubSubService ←→ lazy subscriber conn        │  │\n│  │  ScriptManagerService ←→ EVALSHA + NOSCRIPT   │  │\n│  │  KeyBuilder → {namespace}:{prefix}:{id}       │  │\n│  └─────────────┬─────────────────┬───────────────┘  │\n│                │                 │                  │\n│        ┌───────▼──────┐  ┌───────▼──────┐           │\n│        │ ISerializer  │  │ ICacheEvents │           │\n│        │ (yours)      │  │ (yours)      │           │\n│        └──────────────┘  └──────────────┘           │\n└─────────────────────────────────────────────────────┘\n```\n\nBoth consumer-facing contracts are optional: omit `serializer` and you get `JsonSerializer`; omit `events.onEvent` and connection events are simply not forwarded anywhere.\n\nDI tokens are `Symbol`s (`BYMAX_CACHE_OPTIONS`, `BYMAX_CACHE_CONNECTION`, `BYMAX_CACHE_SCRIPT_REGISTRY`, `BYMAX_CACHE_EVENTS`, `BYMAX_CACHE_SERIALIZER`, `BYMAX_CACHE_KEY_BUILDER`); all providers are singletons. The module is built with `ConfigurableModuleBuilder` and registers globally by default via the `isGlobal` extra (which sets `DynamicModule.global`) — there is no `@Global()` decorator.\n\n### Design Principles\n\n| Principle                    | Description                                                                                                                                                       |\n| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **🔑 Structural Isolation**  | Every key and channel is composed by `KeyBuilder` — namespacing is enforced by the type of the API, not by a convention a caller can forget                       |\n| **🚪 Fail Closed**           | A payload that cannot be decoded throws; it never degrades into `undefined` or a partially-typed object, because a silently wrong cache read is worse than a miss |\n| **🔌 Interface-Driven**      | `ISerializer` and `ICacheEvents` are contracts — MessagePack, a metrics sink, or a logger is a consumer implementation, never a dependency of this package        |\n| **🪶 Singleton Connection**  | One `ioredis` client per module, owned by `ConnectionManager` with `OnModuleInit` / `OnModuleDestroy` — no `Scope.REQUEST`, no per-call connection churn          |\n| **🌳 Zero Runtime Deps**     | `\"dependencies\": {}` — every package arrives as a peer dependency, so consumers pin exact versions and the supply-chain surface stays theirs                      |\n| **🧭 Explicit Escape Hatch** | `getClient()` exists, is documented, and is an anti-pattern — un-namespaced access is possible on purpose, and named so a reviewer sees it                        |\n\n---\n\n## 🔐 Security Model\n\nA cache sits between your application and every value it has ever computed, so the security posture is about two things: one tenant's data never being reachable from another tenant's key, and a hostile or corrupt payload never being handed back as a valid object.\n\n### Namespace isolation is structural\n\nEvery key and every Pub/Sub channel is composed through `KeyBuilder`, which prepends `{namespace}{separator}` before the command reaches Redis. `CacheService.get('user-profile', '42')` can only ever read `app:user-profile:42` — there is no code path through the typed API that emits a bare key.\n\nThe namespace itself is validated at module bootstrap, not at call time:\n\n| Violation                               | Result                                                           |\n| --------------------------------------- | ---------------------------------------------------------------- |\n| Empty or whitespace-only namespace      | `CacheException(INVALID_NAMESPACE)` — the module fails to start  |\n| Namespace containing the key separator  | `CacheException(INVALID_NAMESPACE)` — prevents prefix collisions |\n| Empty `prefix` or `id` at the call site | `CacheException(INVALID_KEY)` — no key is sent to Redis          |\n\nA namespace containing the separator is rejected because `namespace: 'a:b'` and `namespace: 'a'` with prefix `b` would resolve to the same keyspace — a tenant boundary that reads as isolated but is not.\n\n### Deserialization fails closed\n\n`JsonSerializer.deserialize` throws `CacheException(DESERIALIZATION_FAILED)` on any payload that is not valid JSON. It never returns `undefined`, never returns a partial object, and never lets a corrupted entry masquerade as a valid `T`. The same contract is required of any custom `ISerializer` — fail-closed is the invariant, not the default implementation's private choice.\n\nSerialization is symmetric: a top-level `undefined`, function, or `symbol` is rejected up front, because `JSON.stringify` returns the JS value `undefined` for those **without throwing**, which would otherwise escape the try/catch and break the `string` return contract.\n\n### Secrets stay out of error payloads\n\nError `details` are built to be safe to log:\n\n- A malformed `connection.url` throws `CONNECTION_FAILED` with `reason: 'invalid connection.url'` — the URL is omitted, because it may embed a password.\n- `SERIALIZATION_FAILED` carries the encoder's message, never the value being encoded.\n- `DESERIALIZATION_FAILED` carries a `preview` of the raw payload truncated to **100 characters** with an ellipsis — enough to debug a codec mismatch, bounded so a cached record full of PII is not copied into a log line.\n\n### Destructive operations are guarded in production\n\n`flushNamespace()` throws `CacheException(FLUSH_DISABLED_IN_PRODUCTION)` when `NODE_ENV === 'production'` unless `allowFlushInProduction` is explicitly set. When it does run, it iterates with `SCAN` scoped to `{namespace}{separator}*` and removes keys with `UNLINK` (asynchronous reclaim), so it neither touches another namespace nor blocks the server on a large keyset.\n\n### Lua scripts are registered, never interpolated\n\nScripts are declared up front — through `options.scripts` or `ScriptManagerService.register(name, lua)` — and executed **by name**. A call site passes `eval(scriptName, keys, args)`; it has no way to pass a script body. Keys are namespaced before execution and arguments arrive as Redis `ARGV[]`, which Lua treats as data, so request input cannot become script source. Standalone and Sentinel use `EVALSHA` with a `NOSCRIPT` reload-and-retry; Cluster sends the full body via `EVAL`, because `EVALSHA` routes by key slot and a keyless reload would not reach the node that reported `NOSCRIPT`.\n\n### The namespace cannot widen a destructive pattern\n\n`validateOptions` rejects a namespace containing a Redis glob metacharacter (`*`, `?`, `[`, `\\`). The namespace is composed into `flushNamespace()`'s match pattern, so a metacharacter there is not cosmetic — measured against Redis 8.10.0, `*` and `?` **widen** the pattern into other keyspaces, `\\` **escapes** into a different one while sparing its own keys, and `[` opens a character class that never closes so the pattern matches **nothing** and the flush reports success having removed no keys. `]` is accepted: measured to be a literal that neither widens nor silences. This matters most when the namespace is derived from input — multi-tenant wiring using a tenant slug reads like isolation and would otherwise be one unsanitised character from a cross-tenant delete.\n\n### Cluster mode refuses commands it cannot honor safely\n\n`scan()`, `flushNamespace()`, and `getClient()` throw `UNSUPPORTED_IN_CLUSTER` under `mode: 'cluster'` rather than silently operating on one node. A partial flush that reports success is worse than an error.\n\n### Security Checklist\n\nWhen integrating `@bymax-one/nest-cache` in production, verify each of the following:\n\n- `namespace` is distinct per tenant or per application — it is the isolation boundary, and a shared value makes every other guarantee moot\n- `allowFlushInProduction` stays unset; if it is on, the reason belongs in a security review\n- `getClient()` and `pipeline()` call sites are audited — they are the only paths that bypass namespacing\n- Connection credentials come from the environment (`config.getOrThrow('REDIS_URL')`), never from a URL literal in module options in source control\n- `rediss://` (or an explicit `connection.tls`) is used for any Redis reachable off-host\n- Values that must not be readable by whoever can read Redis are encrypted by the application (or a custom `ISerializer`) before they are cached — the library stores what you hand it\n- Cached entries carry a TTL sized to your data-retention policy; namespacing bounds who can read an entry, not how long it exists\n- Custom `ISerializer` implementations throw on malformed input rather than returning a fallback value\n- If `/admin` is wired, its routes are behind the application's own authorization — the library validates scopes and withholds values, it does not authenticate anyone\n- Any admin scope whose keyspace holds credentials is declared `isReadable: false`, and the deployment understands that this withholds the **value only** — listing, types, TTLs and sizes stay visible by design\n\n---\n\n## 🛡️ Security Table\n\n| Layer                 | Implementation                                                                                                              |\n| --------------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| Tenant Isolation      | Every key and channel composed by `KeyBuilder` as `{namespace}{sep}{prefix}{sep}{id}` — no bare-key path in the API         |\n| Namespace Validation  | Empty, separator-containing, or glob-metacharacter namespace rejected at bootstrap (`INVALID_NAMESPACE`)                    |\n| Admin Scope Allowlist | Scopes declared at wiring, validated and frozen; a caller names a scope by id and can never supply a match pattern          |\n| Admin Read-Only       | The `/admin` subpath issues no mutating command — enforced by the `check:admin-readonly` build gate, not by convention      |\n| Key Validation        | Empty `prefix` / `id` rejected before the command is issued (`INVALID_KEY`)                                                 |\n| Deserialization       | Fails closed — `DESERIALIZATION_FAILED`; never a partial or wrongly-typed value                                             |\n| Serialization         | Top-level `undefined` / function / symbol rejected; the value is never echoed into `details`                                |\n| Error Payloads        | Connection URLs omitted (may embed credentials); raw payload previews truncated to 100 characters                           |\n| Destructive Ops       | `flushNamespace()` blocked under `NODE_ENV=production` unless `allowFlushInProduction`; `SCAN` + `UNLINK`, namespace-scoped |\n| Lua Execution         | Scripts registered by name; call sites pass keys/args only — request input never reaches a script body                      |\n| Cluster Safety        | `scan` / `flushNamespace` / `getClient` throw `UNSUPPORTED_IN_CLUSTER` instead of acting on a single node                   |\n| Transport             | `rediss://` sets TLS on the client; explicit `connection.tls` supported for custom CA / mTLS                                |\n| Connection Resilience | Bounded `maxRetriesPerRequest`, `READONLY`-failover reconnect, graceful `quit()` with a shutdown timeout                    |\n| Supply Chain          | `\"dependencies\": {}` — no transitive runtime packages of the library's own choosing; published with npm provenance          |\n\n> [!IMPORTANT]\n> The namespace is the isolation boundary. Anything reached through `getClient()` or `pipeline()` sits outside it — those call sites are where cross-tenant reads get introduced.\n\n---\n\n## 🧱 Tech Stack\n\n[![NestJS](https://img.shields.io/badge/NestJS-11-E0234E?style=flat-square&logo=nestjs&logoColor=white)](https://nestjs.com)\n[![ioredis](https://img.shields.io/badge/ioredis-6-DC382D?style=flat-square&logo=redis&logoColor=white)](https://github.com/redis/ioredis)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org)\n[![Node.js](https://img.shields.io/badge/Node.js-24-339933?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org)\n[![Jest](https://img.shields.io/badge/Jest-30-C21325?style=flat-square&logo=jest)](https://jestjs.io)\n[![Stryker](https://img.shields.io/badge/Stryker-Mutation_Testing-red?style=flat-square)](https://stryker-mutator.io)\n[![pnpm](https://img.shields.io/badge/pnpm-10-F69220?style=flat-square&logo=pnpm&logoColor=white)](https://pnpm.io)\n[![tsup](https://img.shields.io/badge/tsup-8-orange?style=flat-square)](https://tsup.egoist.dev)\n\n---\n\n## 🧪 Testing & Quality\n\nA cache is consulted on the hot path of every request that touches it, so the suite is held to a bar beyond \"it runs\" — every behavior is pinned so that a regression **fails a test**.\n\n- ✅ **100% line coverage** — statements, branches, functions, and lines, enforced by `jest.coverage.config.ts` as a pre-publish gate, not a target\n- ✅ **100% mutation score** — verified with [Stryker](https://stryker-mutator.io/) at `break: 100` and `ignoreStatic: false`; 441 killed, 6 timed out, **0 survived**, and [documented in full](./docs/mutation_testing_results.md)\n- ✅ **One documented equivalent** — the production source carries a single `// Stryker disable` directive, on `configurable: false` of the withheld connection accessor, genuinely equivalent because the resolved options are frozen on the way out (freezing already makes every property non-configurable); `check:mutants` proves it parses and carries its reason, so the score is an accounting rather than a number\n- ✅ **No real Redis in unit tests** — `ioredis-mock` throughout; e2e tests exercise the wired module through `@nestjs/testing` and Testcontainers against a real Redis for connection lifecycle, Pub/Sub, and Lua scripts\n- ✅ **Published-package smoke test** — `scripts/dogfood-smoke-test.mjs` validates exports, tarball shape, and a consumer install before tagging\n\n```bash\npnpm test          # unit tests (Jest)\npnpm test:e2e      # end-to-end tests (@nestjs/testing + Testcontainers)\npnpm test:cov:all  # full coverage gate (100% statements/branches/functions/lines)\npnpm mutation      # Stryker mutation testing (95% break gate)\npnpm typecheck     # tsc strict check\npnpm lint          # ESLint\n```\n\n> [!NOTE]\n> Line coverage proves a line _executed_ under test; mutation testing proves a test _would fail_ if that line were wrong. The full methodology and per-area breakdown are in [docs/mutation_testing_results.md](./docs/mutation_testing_results.md).\n\n---\n\n## 📖 API Reference\n\n### `CacheService`\n\n| Group           | Methods                                                |\n| --------------- | ------------------------------------------------------ |\n| Strings         | `get<T>` · `getRaw` · `set<T>` · `setRaw` · `setNx<T>` |\n| Delete / exists | `del` · `delMany` · `exists`                           |\n| TTL             | `ttl` · `expire` · `persist`                           |\n| Numbers         | `incr` · `decr`                                        |\n| Batch           | `mget<T>` · `mset<T>`                                  |\n| Hashes          | `hget<T>` · `hset<T>` · `hgetall<T>` · `hdel`          |\n| Sets            | `sadd` · `srem` · `smembers` · `sismember` · `scard`   |\n| Iteration       | `keys` (avoid in prod) · `scan` (cursor)               |\n| Scripts         | `eval`                                                 |\n| Escape hatch    | `pipeline` · `getClient`                               |\n| Namespace       | `flushNamespace` (prod-guarded)                        |\n| Health          | `isHealthy` · `ping` · `info`                          |\n\n### `PubSubService`\n\n`publish<T>(channel, message)` · `subscribe<T>(channel, handler)` · `psubscribe<T>(pattern, handler)`\n\n### `ScriptManagerService`\n\n`register(name, lua)` · `load(name)` · `eval(name, keys, args)`\n\n### Errors\n\n`CacheException` (extends `HttpException`) + `CACHE_ERROR_CODES` (namespaced `cache.*`).\n\n---\n\n## 🪪 Default Error Codes\n\nAll errors are instances of `CacheException` and carry a stable `code` string from `CACHE_ERROR_CODES`:\n\n| Code                                 | HTTP | When thrown                                                     |\n| ------------------------------------ | ---- | --------------------------------------------------------------- |\n| `cache.connection_failed`            | 500  | Cannot connect after retries                                    |\n| `cache.command_timeout`              | 504  | Command exceeded `commandTimeout`                               |\n| `cache.connection_lost`              | 503  | Connection dropped during an in-flight operation                |\n| `cache.deserialization_failed`       | 500  | Malformed payload in `get<T>`                                   |\n| `cache.serialization_failed`         | 500  | Unserializable value in `set<T>`                                |\n| `cache.invalid_key`                  | 400  | Empty prefix or id passed to `build` / `applyNamespace`         |\n| `cache.invalid_namespace`            | 500  | Empty or separator-containing namespace                         |\n| `cache.script_not_registered`        | 500  | `eval(name)` before `register(name)`                            |\n| `cache.script_execution_failed`      | 500  | Lua runtime error or NOSCRIPT retry failure                     |\n| `cache.script_registry_missing`      | 500  | `eval` called when no `ScriptManagerService` is wired           |\n| `cache.flush_disabled_in_production` | 403  | `flushNamespace()` in prod without `allowFlushInProduction`     |\n| `cache.unsupported_in_cluster`       | 500  | `scan`, `flushNamespace`, or `getClient` called in cluster mode |\n| `cache.cluster_misconfigured`        | 500  | `mode: 'cluster'` without `cluster.nodes`                       |\n| `cache.sentinel_misconfigured`       | 500  | `mode: 'sentinel'` without `sentinel.sentinels`/`name`          |\n| `cache.shutdown_timeout`             | 500  | `quit()` exceeded `shutdownTimeoutMs`                           |\n\nFull catalog and HTTP status mapping: [`docs/technical_specification.md §12`](./docs/technical_specification.md).\n\n---\n\n## 🚫 What This Library Does NOT Do\n\nReliable atomic primitives are in scope; opinionated policies are not. By design, the following are out of scope:\n\n- ❌ **Rate limiting** — compose `incr` + `expire`, a custom Lua script, or a future `@bymax-one/nest-rate-limit`\n- ❌ **Distributed locks** — `setNx` + a release script covers the common case; a future `@bymax-one/nest-lock` would own the edge cases\n- ❌ **BullMQ wiring** — [`@bymax-one/nest-queue`](https://github.com/bymaxone/nest-queue) owns its own connection\n- ❌ **Cache-aside / read-through patterns** — that policy belongs in your repositories, not in the client\n- ❌ **Compression and at-rest encryption** — implement a custom `ISerializer`\n- ❌ **Tag-based invalidation** — namespaces and prefixes are the invalidation unit\n- ❌ **Redis Streams** — a different consumption model than Pub/Sub; out of scope for this package\n\nSee §13 of the [technical specification](./docs/technical_specification.md) for the rationale.\n\n---\n\n## 🤝 Contributing\n\nContributions are welcome. Development follows the Bymax coding standards:\n\n- TypeScript strict (`noImplicitAny`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`)\n- TDD with a 100% coverage gate and a 95% mutation break threshold\n- Conventional Commits enforced by commitlint + husky\n- No direct dependencies — peer deps only\n- All boolean identifiers prefixed with `is / has / should / can`\n\n```bash\n# Clone the repository\ngit clone https://github.com/bymaxone/nest-cache.git\ncd nest-cache\n\n# Install dependencies\npnpm install\n\n# Run tests\npnpm test\n\n# Build\npnpm build\n\n# Type check\npnpm typecheck\n```\n\nRun the full gate before opening a PR:\n\n```bash\npnpm typecheck && pnpm lint && pnpm test:cov:all && pnpm build && pnpm size\n```\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) for the full process.\n\n---\n\n## 🔒 Security Policy\n\nIf you discover a security vulnerability, please **do not** open a public issue. Instead, email us at **support@bymax.one** with details. We take security seriously and will respond promptly.\n\nSee [SECURITY.md](./SECURITY.md) for the private reporting process, supported versions, and the threat model (cache poisoning, key injection, unsafe deserialization, production flush guard, Lua injection).\n\n---\n\n## 📄 License\n\n[MIT](./LICENSE) © [Bymax One](https://github.com/bymaxone)\n\n---\n\n<p align=\"center\">\n  <sub>Built with ❤️ by <a href=\"https://github.com/bymaxone\">Bymax One</a></sub>\n</p>\n","readmeFilename":"README.md"}