{"_id":"@bratislava/log-nest","_rev":"6-6ab2c692ea80f3e86c2f48b9defc755f","name":"@bratislava/log-nest","dist-tags":{"latest":"2.0.0"},"versions":{"0.0.1":{"name":"@bratislava/log-nest","version":"0.0.1","keywords":["nestjs","logging","logfmt","loki","grafana","error-handling"],"author":{"name":"Department of Innovation and Technology of the City of Bratislava"},"license":"EUPL-1.2","_id":"@bratislava/log-nest@0.0.1","maintainers":[{"name":"marekbodingerba","email":"marek.bodinger@bratislava.sk"},{"name":"inovacie.bratislava","email":"inovacie.bratislava@gmail.com"},{"name":"mpinter","email":"mpinter.pinto@gmail.com"},{"name":"erehulka","email":"erik.rehulka@gmail.com"},{"name":"pavel.filo","email":"pavel.filo@bratislava.sk"},{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},{"name":"ty-ci","email":"jakubicka.adam@gmail.com"}],"homepage":"https://github.com/bratislava/log-nest#readme","bugs":{"url":"https://github.com/bratislava/log-nest/issues"},"dist":{"shasum":"41e6cb1ddc7bc87a3a7d263e57efa0792c3f8f07","tarball":"https://registry.npmjs.org/@bratislava/log-nest/-/log-nest-0.0.1.tgz","fileCount":31,"integrity":"sha512-YQkNlMH1a4IlkUv4z+klgwXOwkuIKkoaQjRhkoFrUvDA/YYMxAN0GudapO3T8ZCZLVI4JFuRM54zWPIInJK3Xw==","signatures":[{"sig":"MEYCIQC6BNcKvOf62if7zr3GmnzA2v/JwqMWI7DCutFguJ4BRgIhAMyui1i5unjxf8iapOd+7MWTPLQl6Dm0v0vX64Hzdydy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":91636},"main":"dist/index.js","types":"dist/index.d.ts","volta":{"npm":"11.13.0","node":"24.16.0"},"engines":{"npm":">=11.13.0","node":">=24.16.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"900f6ffc5daf22ab7d9c4d1e07cc8d38b302a584","private":false,"scripts":{"lint":"eslint \"{src,test}/**/*.{ts,js}\"","test":"jest","build":"rimraf dist && tsc -p tsconfig.build.json","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run build","lint:fix":"eslint \"{src,test}/**/*.{ts,js}\" --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","lint:report":"eslint -f json -o eslint-report.json \"{src,test}/**/*.{ts,js}\""},"_npmUser":{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},"overrides":{"minimatch":"10.2.6"},"repository":{"url":"git+https://github.com/bratislava/log-nest.git","type":"git"},"_npmVersion":"11.13.0","description":"Structured logging and error handling for NestJS, with single-line logfmt output for Grafana Loki.","directories":{},"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"30.4.2","axios":"1.18.1","rimraf":"6.1.3","express":"5.2.1","ts-jest":"29.4.11","ts-node":"10.9.2","prettier":"3.9.6","typescript":"6.0.3","@types/jest":"30.0.0","@types/node":"24.12.2","@nestjs/common":"11.1.29","@types/express":"5.0.6","reflect-metadata":"0.2.2","@bratislava/eslint-config-nest":"0.19.1"},"peerDependencies":{"axios":"^1.0.0","express":"^5.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"peerDependenciesMeta":{"axios":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/log-nest_0.0.1_1789030501192_0.5934234482850582","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@bratislava/log-nest","version":"1.0.0","keywords":["nestjs","logging","logfmt","loki","grafana","error-handling"],"author":{"name":"Department of Innovation and Technology of the City of Bratislava"},"license":"EUPL-1.2","_id":"@bratislava/log-nest@1.0.0","maintainers":[{"name":"marekbodingerba","email":"marek.bodinger@bratislava.sk"},{"name":"inovacie.bratislava","email":"inovacie.bratislava@gmail.com"},{"name":"mpinter","email":"mpinter.pinto@gmail.com"},{"name":"erehulka","email":"erik.rehulka@gmail.com"},{"name":"pavel.filo","email":"pavel.filo@bratislava.sk"},{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},{"name":"ty-ci","email":"jakubicka.adam@gmail.com"}],"homepage":"https://github.com/bratislava/log-nest#readme","bugs":{"url":"https://github.com/bratislava/log-nest/issues"},"dist":{"shasum":"7e19cba385bf065e8663e4c63d72e4234eb03cd5","tarball":"https://registry.npmjs.org/@bratislava/log-nest/-/log-nest-1.0.0.tgz","fileCount":55,"integrity":"sha512-fHU6aMy5JJxh1wgw7NzhWExfXNPGZJA9AGZZCmnaUaE1/4df5QTKFdS+M/2A900G06JOVg+jREKkan+aAjrIRQ==","signatures":[{"sig":"MEYCIQCIt/i2haAA6L0stxabvQCMIIsogRNR0zMZ+i3kmoI8yAIhAN/Qz7oTnmdnP+SKtmLJPmvrF8F8s9FZ93YYdPjpWLwo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIHUE9aUyNfkQ+DzFH3YoknZEiR/bRsoY+qahAw0sA0f3AiAvVTe/kZ7J5MHjlIMQ2sFGuRPsletqrWeuzxinoMqDUw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bratislava%2flog-nest@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":145573},"main":"dist/index.js","types":"dist/index.d.ts","volta":{"npm":"11.13.0","node":"24.16.0"},"engines":{"npm":">=11.13.0","node":">=24.16.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"1b25ad17a9d1307b0288817218d0af8e00075889","private":false,"scripts":{"lint":"eslint \"{src,test}/**/*.{ts,js}\"","test":"jest","build":"rimraf dist && tsc -p tsconfig.build.json","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run build","lint:fix":"eslint \"{src,test}/**/*.{ts,js}\" --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","lint:report":"eslint -f json -o eslint-report.json \"{src,test}/**/*.{ts,js}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:95d2ce6f-1df0-4927-abe7-6fb0193aa440"}},"overrides":{"minimatch":"10.2.6"},"repository":{"url":"git+https://github.com/bratislava/log-nest.git","type":"git"},"_npmVersion":"12.1.0","description":"Structured logging and error handling for NestJS, with single-line logfmt output for Grafana Loki.","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"30.4.2","axios":"1.18.1","rimraf":"6.1.3","express":"5.2.1","ts-jest":"29.4.11","ts-node":"10.9.2","prettier":"3.9.6","supertest":"7.2.2","typescript":"6.0.3","@types/jest":"30.0.0","@types/node":"24.12.2","@nestjs/core":"11.1.24","@nestjs/common":"11.1.29","@types/express":"5.0.6","@nestjs/testing":"11.2.3","@types/supertest":"6.0.3","reflect-metadata":"0.2.2","@nestjs/platform-express":"11.2.3","@bratislava/eslint-config-nest":"0.19.1"},"peerDependencies":{"axios":"^1.0.0","express":"^5.0.0","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"peerDependenciesMeta":{"axios":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/log-nest_1.0.0_1790171583370_0.42422879518975876","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@bratislava/log-nest","version":"1.0.1","keywords":["nestjs","logging","logfmt","loki","grafana","error-handling"],"author":{"name":"Department of Innovation and Technology of the City of Bratislava"},"license":"EUPL-1.2","_id":"@bratislava/log-nest@1.0.1","maintainers":[{"name":"marekbodingerba","email":"marek.bodinger@bratislava.sk"},{"name":"inovacie.bratislava","email":"inovacie.bratislava@gmail.com"},{"name":"mpinter","email":"mpinter.pinto@gmail.com"},{"name":"erehulka","email":"erik.rehulka@gmail.com"},{"name":"pavel.filo","email":"pavel.filo@bratislava.sk"},{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},{"name":"ty-ci","email":"jakubicka.adam@gmail.com"}],"homepage":"https://github.com/bratislava/log-nest#readme","bugs":{"url":"https://github.com/bratislava/log-nest/issues"},"dist":{"shasum":"6a8056be7c0b07af08626058a1b26947038b23db","tarball":"https://registry.npmjs.org/@bratislava/log-nest/-/log-nest-1.0.1.tgz","fileCount":55,"integrity":"sha512-8StSwL2CqkGrvy47bJb8hHjB7BBuB3VpJI1jrxGSznp/lSoxsY3UgRn0ZA+Ms26sCuABIztZDAFAIRMU0Cl6Xg==","signatures":[{"sig":"MEQCICrX2bkuNnZwGSvafMVdJKZF3Q68eKv+H1DscP5TkrYXAiAcSsaFe0oJOlJSzAnETSrMSpMORH1IHOyvSBjYRqUpVg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIAlRiGisX95BFSk7nls+oLRWju9BItmN3Um38eDXxviaAiB/2suCSMS0UjQBcaNm9wk/RpANxC1b78qWybtjz7y9JQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bratislava%2flog-nest@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":145615},"main":"dist/index.js","types":"dist/index.d.ts","volta":{"npm":"11.13.0","node":"24.16.0"},"engines":{"npm":">=11.13.0","node":">=24.16.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"bf51b9c1d54dc17633d5e362ec162e6bc213090e","private":false,"scripts":{"lint":"eslint \"{src,test}/**/*.{ts,js}\"","test":"jest","build":"rimraf dist && tsc -p tsconfig.build.json","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run build","lint:fix":"eslint \"{src,test}/**/*.{ts,js}\" --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","lint:report":"eslint -f json -o eslint-report.json \"{src,test}/**/*.{ts,js}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:95d2ce6f-1df0-4927-abe7-6fb0193aa440"}},"overrides":{"minimatch":"10.2.6"},"repository":{"url":"git+https://github.com/bratislava/log-nest.git","type":"git"},"_npmVersion":"12.1.0","description":"Structured logging and error handling for NestJS, with single-line logfmt output for Grafana Loki.","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"30.4.2","axios":"1.18.1","rimraf":"6.1.3","express":"5.2.1","ts-jest":"29.4.11","ts-node":"10.9.2","prettier":"3.9.6","supertest":"7.2.2","typescript":"6.0.3","@types/jest":"30.0.0","@types/node":"24.12.2","@nestjs/core":"11.1.24","@nestjs/common":"11.1.29","@types/express":"5.0.6","@nestjs/testing":"11.2.3","@types/supertest":"6.0.3","reflect-metadata":"0.2.2","@nestjs/platform-express":"11.2.3","@bratislava/eslint-config-nest":"0.19.1"},"peerDependencies":{"axios":"^1.0.0","express":"^5.0.0","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"peerDependenciesMeta":{"axios":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/log-nest_1.0.1_1790177099822_0.6049562964601689","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@bratislava/log-nest","version":"1.1.0","keywords":["nestjs","logging","logfmt","loki","grafana","error-handling"],"author":{"name":"Department of Innovation and Technology of the City of Bratislava"},"license":"EUPL-1.2","_id":"@bratislava/log-nest@1.1.0","maintainers":[{"name":"marekbodingerba","email":"marek.bodinger@bratislava.sk"},{"name":"inovacie.bratislava","email":"inovacie.bratislava@gmail.com"},{"name":"mpinter","email":"mpinter.pinto@gmail.com"},{"name":"erehulka","email":"erik.rehulka@gmail.com"},{"name":"pavel.filo","email":"pavel.filo@bratislava.sk"},{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},{"name":"ty-ci","email":"jakubicka.adam@gmail.com"}],"homepage":"https://github.com/bratislava/log-nest#readme","bugs":{"url":"https://github.com/bratislava/log-nest/issues"},"dist":{"shasum":"ca5102a9a43e0c2ab3ca792e29911ffc7dd4cdaa","tarball":"https://registry.npmjs.org/@bratislava/log-nest/-/log-nest-1.1.0.tgz","fileCount":55,"integrity":"sha512-5suH96bxq0PLu+17ekyWIeuRlrw0nkyjaozvwbuR4EXGDsu3bDA43J6cv49riaeniheZw658FUe/CEUYi/5YZQ==","signatures":[{"sig":"MEUCIHjcoAzX8ITweX/8XL6yBJz2Zz6g8FqxAyzzngv6FHaNAiEAudsBvR7FbhoXgRmst9FntEORooPZqnAhbMdWUYce7hs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIGWTMJ/gg2EV5ggOh6eKKMRbr0V5ohRDX36RxJRc3UF9AiBNsW74VYCHFYX3El4g0aF1AIJ6+zbQTL0z48+7WXFJUQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bratislava%2flog-nest@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":146392},"main":"dist/index.js","types":"dist/index.d.ts","volta":{"npm":"11.13.0","node":"24.16.0"},"engines":{"npm":">=11.13.0","node":">=24.16.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"dd1d0e52141ca66fa10064e7ada36d9a637d59a1","private":false,"scripts":{"lint":"eslint \"{src,test}/**/*.{ts,js}\"","test":"jest","build":"rimraf dist && tsc -p tsconfig.build.json","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run build","lint:fix":"eslint \"{src,test}/**/*.{ts,js}\" --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","lint:report":"eslint -f json -o eslint-report.json \"{src,test}/**/*.{ts,js}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:95d2ce6f-1df0-4927-abe7-6fb0193aa440"}},"overrides":{"minimatch":"10.2.6"},"repository":{"url":"git+https://github.com/bratislava/log-nest.git","type":"git"},"_npmVersion":"12.1.0","description":"Structured logging and error handling for NestJS, with single-line logfmt output for Grafana Loki.","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"30.4.2","axios":"1.18.1","rimraf":"6.1.3","express":"5.2.1","ts-jest":"29.4.11","ts-node":"10.9.2","prettier":"3.9.6","supertest":"7.2.2","typescript":"6.0.3","@types/jest":"30.0.0","@types/node":"24.12.2","@nestjs/core":"11.1.24","@nestjs/common":"11.1.29","@types/express":"5.0.6","@nestjs/testing":"11.2.3","@types/supertest":"6.0.3","reflect-metadata":"0.2.2","@nestjs/platform-express":"11.2.3","@bratislava/eslint-config-nest":"0.19.1"},"peerDependencies":{"axios":"^1.0.0","express":"^5.0.0","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"peerDependenciesMeta":{"axios":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/log-nest_1.1.0_1790252196809_0.6682968295372211","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@bratislava/log-nest","version":"1.2.0","keywords":["nestjs","logging","logfmt","loki","grafana","error-handling"],"author":{"name":"Department of Innovation and Technology of the City of Bratislava"},"license":"EUPL-1.2","_id":"@bratislava/log-nest@1.2.0","maintainers":[{"name":"marekbodingerba","email":"marek.bodinger@bratislava.sk"},{"name":"inovacie.bratislava","email":"inovacie.bratislava@gmail.com"},{"name":"mpinter","email":"mpinter.pinto@gmail.com"},{"name":"erehulka","email":"erik.rehulka@gmail.com"},{"name":"pavel.filo","email":"pavel.filo@bratislava.sk"},{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},{"name":"ty-ci","email":"jakubicka.adam@gmail.com"}],"homepage":"https://github.com/bratislava/log-nest#readme","bugs":{"url":"https://github.com/bratislava/log-nest/issues"},"dist":{"shasum":"e47f2b1feef523ee2aab4b5cd0c27b8d15f546ef","tarball":"https://registry.npmjs.org/@bratislava/log-nest/-/log-nest-1.2.0.tgz","fileCount":55,"integrity":"sha512-QCAYrZp/CFErlzXN5M9qvZROhA3LUEVBTDPg50aZ8a6CGXJ4EXq92uvKet4WRbD8kA1aGsvdyEoclgOTxT6oCQ==","signatures":[{"sig":"MEYCIQDHlEPe+1kitJFM5vt87HqYcYxtSyg6hbMNfKkAkssB7AIhAPBNStAEr+9q+l0W7zPbjwp9YsH+0rnq+vGh9eOx2Wyn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIFxfBke5PV8Io9a5jPpczfh4Q1rIO/UR+4hL3QCOksLSAiEA3nG+oT9pdcbiVFrq8lrnZkfMGX399inQWmVE6zuNkII=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bratislava%2flog-nest@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":149412},"main":"dist/index.js","types":"dist/index.d.ts","volta":{"npm":"11.13.0","node":"24.16.0"},"engines":{"npm":">=11.13.0","node":">=24.16.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"6e12fe2624bbf9dcd2fdd2042717b196eec60c8d","private":false,"scripts":{"lint":"eslint \"{src,test}/**/*.{ts,js}\"","test":"vitest run","build":"rimraf dist && tsc -p tsconfig.build.json","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run build","lint:fix":"eslint \"{src,test}/**/*.{ts,js}\" --fix","typecheck":"tsc --noEmit","test:watch":"vitest","lint:report":"eslint -f json -o eslint-report.json \"{src,test}/**/*.{ts,js}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"95d2ce6f-1df0-4927-abe7-6fb0193aa440"}},"overrides":{"minimatch":"10.2.6"},"repository":{"url":"git+https://github.com/bratislava/log-nest.git","type":"git"},"_npmVersion":"12.2.0","description":"Structured logging and error handling for NestJS, with single-line logfmt output for Grafana Loki.","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"8.3.1","axios":"1.18.1","rimraf":"6.1.3","vitest":"5.0.1","express":"5.2.1","prettier":"3.9.6","supertest":"7.2.2","typescript":"6.0.3","@types/node":"24.12.2","@nestjs/core":"11.1.24","@nestjs/common":"11.1.29","@types/express":"5.0.6","@types/supertest":"6.0.3","reflect-metadata":"0.2.2","@vitest/eslint-plugin":"1.6.27","@nestjs/platform-express":"11.2.3","@bratislava/eslint-config-nest":"0.19.1"},"peerDependencies":{"axios":"^1.0.0","express":"^5.0.0","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.0"},"peerDependenciesMeta":{"axios":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/log-nest_1.2.0_1790939183622_0.30377358939156074","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"_id":"@bratislava/log-nest@2.0.0","bugs":{"url":"https://github.com/bratislava/log-nest/issues"},"dist":{"shasum":"62b9e8aa7ed43dc6965b89a89ad5a61c8d1a6528","tarball":"https://registry.npmjs.org/@bratislava/log-nest/-/log-nest-2.0.0.tgz","fileCount":50,"integrity":"sha512-igsTmj2DvStsu5q58SlubYUtQjpYnaV9lkUaSXbgGdDby4Sphzchyz0ozyHeDKTIG6p6NID1ft2oPevsQ1rLtA==","signatures":[{"sig":"MEUCIQDxOmRYxgWMKmWik79DzqC42cXULqZPhnTQthsAvIbr5QIgW+V70gInNu3gyhMgYPtCBLRoHmy+S+LkMY9SSvPzDXE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD0DOac6VQkEWo5GL5F1eBaRH6Rp2D2zh8uaWhXRTAwagIhAKmNYgWrGTYcThUaJRFIKbrDuLc5apa1r14rVAAlQA0o"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bratislava%2flog-nest@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":119937},"main":"dist/index.js","name":"@bratislava/log-nest","type":"module","types":"dist/index.d.ts","volta":{"npm":"11.13.0","node":"24.16.0"},"author":{"name":"Department of Innovation and Technology of the City of Bratislava"},"engines":{"node":">=24.16.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"cc5802c7982864d4a0a57fdbff9499d1984f7030","license":"EUPL-1.2","private":false,"scripts":{"lint":"eslint \"{src,test}/**/*.{ts,js}\"","test":"vitest run","build":"tsc --noEmit -p tsconfig.build.json && tsdown","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run build","lint:fix":"eslint \"{src,test}/**/*.{ts,js}\" --fix","typecheck":"tsc --noEmit","test:watch":"vitest","lint:report":"eslint -f json -o eslint-report.json \"{src,test}/**/*.{ts,js}\""},"version":"2.0.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"95d2ce6f-1df0-4927-abe7-6fb0193aa440"}},"homepage":"https://github.com/bratislava/log-nest#readme","keywords":["nestjs","logging","logfmt","loki","grafana","error-handling"],"overrides":{"minimatch":"10.2.6"},"repository":{"url":"git+https://github.com/bratislava/log-nest.git","type":"git"},"_npmVersion":"12.2.0","description":"Structured logging and error handling for NestJS, with single-line logfmt output for Grafana Loki.","directories":{},"maintainers":[{"name":"marekbodingerba","email":"marek.bodinger@bratislava.sk"},{"name":"inovacie.bratislava","email":"inovacie.bratislava@gmail.com"},{"name":"mpinter","email":"mpinter.pinto@gmail.com"},{"name":"erehulka","email":"erik.rehulka@gmail.com"},{"name":"pavel.filo","email":"pavel.filo@bratislava.sk"},{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},{"name":"ty-ci","email":"jakubicka.adam@gmail.com"}],"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"8.3.1","axios":"1.18.1","tsdown":"0.23.0","vitest":"5.0.1","express":"5.2.1","publint":"0.3.24","prettier":"3.9.6","supertest":"7.2.2","typescript":"6.0.3","@types/node":"24.12.2","@nestjs/core":"12.1.2","@nestjs/common":"12.1.2","@types/express":"5.0.6","@types/supertest":"6.0.3","reflect-metadata":"0.2.2","@vitest/eslint-plugin":"1.6.27","@arethetypeswrong/core":"0.18.5","@nestjs/platform-express":"12.1.2","@bratislava/eslint-config-nest":"0.19.1"},"peerDependencies":{"axios":"^1.0.0","express":"^5.0.0","@nestjs/core":"^12.0.0","@nestjs/common":"^12.0.0","reflect-metadata":"^0.2.0"},"peerDependenciesMeta":{"axios":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/log-nest_2.0.0_1790939515704_0.7150480983255321"}}},"time":{"created":"2026-09-10T08:55:01.009Z","modified":"2026-10-02T11:11:56.157Z","0.0.1":"2026-09-10T08:55:01.321Z","1.0.0":"2026-09-23T13:53:03.457Z","1.0.1":"2026-09-23T15:24:59.911Z","1.1.0":"2026-09-24T12:16:36.914Z","1.2.0":"2026-10-02T11:06:23.710Z","2.0.0":"2026-10-02T11:11:55.783Z"},"bugs":{"url":"https://github.com/bratislava/log-nest/issues"},"author":{"name":"Department of Innovation and Technology of the City of Bratislava"},"license":"EUPL-1.2","homepage":"https://github.com/bratislava/log-nest#readme","keywords":["nestjs","logging","logfmt","loki","grafana","error-handling"],"repository":{"url":"git+https://github.com/bratislava/log-nest.git","type":"git"},"description":"Structured logging and error handling for NestJS, with single-line logfmt output for Grafana Loki.","maintainers":[{"name":"marekbodingerba","email":"marek.bodinger@bratislava.sk"},{"name":"inovacie.bratislava","email":"inovacie.bratislava@gmail.com"},{"name":"mpinter","email":"mpinter.pinto@gmail.com"},{"name":"erehulka","email":"erik.rehulka@gmail.com"},{"name":"pavel.filo","email":"pavel.filo@bratislava.sk"},{"name":"lukas.babula","email":"lukas.babula@bratislava.sk"},{"name":"ty-ci","email":"jakubicka.adam@gmail.com"}],"readme":"<p align=\"center\">\n  <img src=\"assets/log-nest-logo-251x256.png\" alt=\"log-nest\" width=\"128\" />\n</p>\n\n<h1 align=\"center\">log-nest</h1>\n\n<p align=\"center\"><i>/lɒx nɛs/</i></p>\n\n[![npm](https://img.shields.io/npm/v/@bratislava/log-nest)](https://www.npmjs.com/package/@bratislava/log-nest)\n\nShared NestJS logging and error-handling infrastructure. Made to be used by the NestJS applications of the city\nof [Bratislava](https://github.com/bratislava).\n\n## Motivation\n\nOur backends log to [Grafana Loki](https://grafana.com/oss/loki/), and multi-line output (stack traces, pretty-printed\nJSON) breaks up into separate, unrelated log entries there. So the core rule of this package is: **one event = one\nline** of space-separated `key=\"value\"` pairs ([logfmt](https://brandur.org/logfmt)), which Loki parses into queryable\nlabels.\n\nEverything else follows from that:\n\n- `LineLoggerService` formats every log call, including stack traces, as a single logfmt line.\n- `ErrorFactoryService` builds exceptions that carry structured metadata: a machine-readable `errorName` for querying,\n  an `alert` flag for Grafana alerting, and log-only context the client must never see.\n- The exception filters and `AppLoggerMiddleware` cooperate to split each error into its two audiences: the sanitized\n  JSON response goes to the client, while the full picture (cause chain, `console` context, stack) goes to the log.\n- Code running outside the request-handling chain (cron jobs, startup tasks, event handlers) is covered too: the\n  `@HandleErrors` decorator logs errors the exception filters would never see, and registering `LineLoggerService`\n  as the NestJS logger keeps the framework's own output logfmt as well.\n\n## Installation\n\n`npm i @bratislava/log-nest`\n\nRequires NestJS 12 and Node 24. The package is ESM-only; CommonJS apps load it through `require(esm)`.\n\n## Using the library\n\n### Quick start\n\nRegister the module once at the app root. `alertReporting` is the app-specific list of error-enum values that should\nraise a Grafana alert when thrown (`ErrorEnum.BAD_GATEWAY_AUTH_ERROR` should always be on it). Keep the list in a\nseparate file, it doubles as the app's single overview of which errors alert:\n\n```ts\n// alert-reporting.ts\nimport {ErrorEnum} from '@bratislava/log-nest'\n\nexport const alertReporting: readonly string[] = [\n  ErrorEnum.BAD_GATEWAY_AUTH_ERROR,\n  ErrorEnum.DATABASE_ERROR,\n  // ...whatever else should page you\n]\n```\n\n```ts\n// app.module.ts\nimport {MiddlewareConsumer, Module, NestModule} from '@nestjs/common'\nimport {\n  AppLoggerMiddleware,\n  NestLoggingModule,\n  LogSanitizationModule,\n} from '@bratislava/log-nest'\n\nimport {alertReporting} from './alert-reporting'\n\n@Module({\n  imports: [\n    NestLoggingModule.forRoot({alertReporting}),\n    LogSanitizationModule.forRoot(), // registers redactors + the allowlist for @LogRedact/@LogAllowList\n  ],\n})\nexport class AppModule implements NestModule {\n  configure(consumer: MiddlewareConsumer): void {\n    consumer.apply(AppLoggerMiddleware).forRoutes('*')\n  }\n}\n```\n\n`AppLoggerMiddleware` depends on both `LogRedactionService` and `LogAllowListService`, so\n`LogSanitizationModule.forRoot()` must be imported even if you don't configure any redactors or allowlist yet. Omitting\n`allowShape` denies by default: nothing is logged until an `allowShape` here or a per-route `@LogAllowList` explicitly\nallows it.\n\nThen wire the logger and the global exception filters in `main.ts`:\n\n```ts\n// main.ts\nimport {NestFactory} from '@nestjs/core'\nimport {\n  ErrorFilter,\n  HttpExceptionFilter,\n  LineLoggerService,\n  UnknownExceptionFilter,\n} from '@bratislava/log-nest'\n\nconst app = await NestFactory.create(AppModule, {\n  logger: new LineLoggerService(),\n})\n// Order matters: later filters take precedence, and HttpException extends\n// Error. Swap ErrorFilter/HttpExceptionFilter and ErrorFilter would swallow\n// HttpExceptions too.\napp.useGlobalFilters(\n  new UnknownExceptionFilter(),\n  new ErrorFilter(),\n  new HttpExceptionFilter(),\n)\nawait app.listen(3000)\n```\n\nThat's the whole setup. See [Motivation](#motivation) for how the pieces cooperate.\n\n### Throwing errors: `ErrorFactoryService`\n\n`NestLoggingModule` provides and globally exports `ErrorFactoryService`, so it can be injected anywhere without\nre-importing. Each factory method takes an `options` object and *returns* an `HttpException`.\n\n```ts\n\n@Injectable()\nexport class FormsService {\n  constructor(private readonly errorFactoryService: ErrorFactoryService) {\n  }\n\n  async getForm(id: string): Promise<Form> {\n    const form = await this.repository.find(id)\n    if (!form) {\n      throw this.errorFactoryService.NotFoundException({\n        errorEnum: ErrorEnum.NOT_FOUND_ERROR,\n        message: 'Form not found.', // sent to the client\n        console: {formId: id},    // logged only, stripped from the response\n      })\n    }\n    return form\n  }\n}\n```\n\n- `errorEnum` is stored as `errorName` on the exception and drives alerting: if it is listed in `alertReporting`, the\n  produced log line carries `alert=1`.\n- `console` (a string or object) and the `error` cause are attached to the log entry only. The client never sees them.\n  The cause's stack is chained onto the exception's stack.\n- `ErrorResponseEnum` holds a default client-facing message for every base error code, so the common idiom is pairing\n  the two: `errorEnum: ErrorEnum.NOT_FOUND_ERROR, message: ErrorResponseEnum.NOT_FOUND_ERROR`.\n\n**App-specific error enums.** The error factory service is generic over the enum union. Register your app's union once\nvia declaration merging, and every bare `ErrorFactoryService` injection (no generic) is typed with it automatically:\n\n```ts\n// types.d.ts (anywhere loaded by tsc, e.g. next to main.ts)\nenum UserErrorEnum {\n  USER_NOT_VERIFIED = 'USER_NOT_VERIFIED',\n}\n\ntype AppErrorEnums = ErrorEnum | UserErrorEnum\n\ndeclare module '@bratislava/log-nest' {\n  interface LogNestErrorEnumRegistry {\n    errorEnum: AppErrorEnums\n  }\n}\n```\n\n```ts\n\n@Injectable()\nexport class UserService {\n  constructor(private readonly errorFactoryService: ErrorFactoryService) {\n  }\n\n  verify(user: User): void {\n    if (!user.verified) {\n      throw this.errorFactoryService.ForbiddenException({\n        errorEnum: UserErrorEnum.USER_NOT_VERIFIED,\n        message: 'User is not verified.',\n      })\n    }\n  }\n}\n```\n\nAn explicit `ErrorFactoryService<SomeNarrowerEnum>` still works and overrides the registry. Use it if you'd rather not\ndeclare a global registry augmentation at all.\n\n**Wrapping downstream (axios) failures.** `fromAxiosError` maps an `AxiosError` to the right exception:\n\n- `503` + `Retry-After` becomes `ServiceUnavailableException`,\n- `401`/`403` becomes `BadGatewayException` with `BAD_GATEWAY_AUTH_ERROR` (alert-worthy, see\n  [Quick start](#quick-start))\n- anything else a plain `BadGatewayException`.\n\nPer-status overrides let you remap specific downstream statuses:\n\n```ts\ntry {\n  await axios.get(`${host}/documents/${id}`)\n} catch (error) {\n  if (isAxiosError(error)) {\n    throw this.errorFactoryService.fromAxiosError(error, {\n      message: 'Document service request failed.',\n      console: {documentId: id},\n      statusOverrides: {\n        // downstream 404 -> our 404 instead of the default 502\n        404: {\n          status: 404,\n          errorEnum: ErrorEnum.NOT_FOUND_ERROR,\n          message: 'Document not found.',\n        },\n      },\n    })\n  }\n  // handle other error types here\n  throw error\n}\n```\n\n### Logging: `LineLoggerService`\n\nA standard NestJS `LoggerService`. Besides `app.useLogger`, use it directly with a context label:\n\n```ts\n\n@Injectable()\nexport class FormsService {\n  private readonly logger = new LineLoggerService(FormsService.name)\n\n  create(form: Form): void {\n    this.logger.log('Form created', {formId: form.id, slug: form.slug})\n    // process=\"[Nest]\" processPID=\"42\" datetime=\"…\" severity=\"LOG\" \n    // context=\"FormsService\" message=\"Form created\" formId=\"…\" slug=\"…\"\n  }\n}\n```\n\nStrings already in logfmt shape pass through untouched; everything else is serialized with the logfmt helpers, which are\nalso exported for direct use:\n`toLogfmt(value)`, `errorToLogfmt(error)`, and `escapeForLogfmt(string)`.\n\n**Auto-named via DI.** Instead of passing `ClassName.name` yourself, inject `LineLoggerService` as a constructor\ndependency of any `@Injectable()` class, and it derives its context from that class automatically:\n\n```ts\n\n@Injectable()\nexport class FormsService {\n  constructor(private readonly logger: LineLoggerService) {\n  }\n\n  create(form: Form): void {\n    this.logger.log('Form created', {formId: form.id}) // context=\"FormsService\"\n  }\n}\n```\n\nThis only works when Nest constructs the class through its own DI container. A class built manually via a custom\n`useFactory` provider won't get a meaningful context this way, so keep using `new LineLoggerService(ClassName.name)`\nthere.\n\nTwo details are worth knowing:\n\n- The second constructor parameter disables ANSI colors: `new LineLoggerService(context, false)`. By default, every\n  line is wrapped in color escape codes.\n- When serializing an object, the `console` field is flattened: its subfields become top-level logfmt pairs on the line,\n  so name them as you want to query them in Loki.\n\n### Request logging: `AppLoggerMiddleware`\n\nThe middleware is a **mandatory** part of the setup, not an optional logging add-on. The exception filters deliberately\nnever send a response themselves. The middleware's send-hook is what strips the log-only metadata from the error body.\nOn a route without the middleware, an error therefore produces no response at all and the client waits until timeout.\nRegister it on every route, as shown in [Quick start](#quick-start).\n\nThe middleware emits one logfmt line per handled request, containing\n`method`, `originalUrl`, `statusCode`, `responseTime` (ms), `userAgent`, `ip`, `userId`, the JSON-serialized\n`request-body`, and the `response-data`. The line's severity is what makes alerting work:\n\n| Condition                       | Severity |\n|---------------------------------|----------|\n| `statusCode` ≥ 500 or `alert=1` | `error`  |\n| `statusCode` ≥ 400              | `warn`   |\n| otherwise                       | `log`    |\n\n**The log/response split.** On errors, the client gets the sanitized response (`statusCode`, `status`, `errorName`,\n`message`) while the log-only metadata (`alert`, `console`, cause chain, stack) ends up on the log line. This is how\n`console` and `error` from [`ErrorFactoryService`](#throwing-errors-errorfactoryservice) stay log-only.\n\n> [!NOTE]\n> With no `allowShape` configured anywhere, **nothing ends up in `request-body`/`response-data`**. The app-wide default\ndenies everything until something allows it. A per-route `@LogAllowList(...)` (below) is enough on its own to allow that\nroute's fields, with no `LogSanitizationModule.forRoot({allowShape})` baseline required. That baseline is still the only\nlever for anything that never reaches a handler (guard rejections, 404s), since there's no per-route decorator to fall\nback on there. Either way, `@LogAllowList(...)` only ever *widens* whatever baseline is set globally, never narrows it.\n\nAlso note that `userId` is best-effort: the JWT payload from the `Authorization` header is decoded **without signature\nverification**, purely for log correlation. Never treat it as authenticated.\n\n### Filtering logged data: `@LogAllowList`\n\n> [!WARNING]\n> `@LogAllowList`/`@LogRedact` are not a silver bullet - they only ever filter `request-body`/`response-data`. Every \n> other field on the line (`method`, `originalUrl`, `userAgent`, `ip`, `userId`, `statusCode`, `responseTime`) is logged\n> verbatim, always, with no filtering or redaction applied. In particular:\n>\n> - **`originalUrl` includes the full path and query string, unfiltered.** Don't put anything sensitive there (a\n>   route like `/reset-password/:token`) - URLs get logged by browsers, proxies, and routers everywhere regardless of\n>   this library, so that's a rule worth following independently of it.\n> - **`userId` is only as safe as the JWT's `sub` claim.** Logging it is normally fine (it's pseudonymous - useless\n>   without separate access to the user database), but only if `sub` is genuinely an opaque ID, not something\n>   directly identifying like an email.\n> - **Manual logging is entirely out of scope.** `console`/`error` passed to `ErrorFactoryService`, and any direct\n>   `LineLoggerService`/`logger.log(...)` call anywhere in your app, are programmer-supplied content this package\n>   never inspects. Don't log PII there directly.\n>\n> This package gives you a deliberate, targeted way to log more of `request-body`/`response-data` while staying\n> safe - not a blanket PII scrubber for everything your app logs.\n\n`@LogAllowList(shape)` restricts which keys of `request-body`/`response-data` `AppLoggerMiddleware` is allowed to log,\non top of the app-wide default from `LogSanitizationModule.forRoot({allowShape})`. A shape is a tree: `true` keeps a\nwhole subtree as-is, and a nested object recurses key-by-key. Anything not mentioned is dropped. Each level only ever\n**widens** what's allowed; an endpoint or controller can't narrow the app-wide default below what it already allows.\n\n```ts\n\n@Controller('users')\n@LogAllowList({id: true}) // controller level: every endpoint here may at least log `id`\nexport class UserController {\n  @Get(':id')\n  @LogAllowList({email: true}) // endpoint level: adds `email` on top of the controller's `id`\n  async getUser(@Param('id') id: string): Promise<User> {\n    return this.userService.findById(id)\n    // logged response-data: { id, email } - every other field is dropped\n  }\n}\n```\n\n`@LogAllowList` works on both methods (endpoint level) and classes (controller level, applied to every method on the\nclass).\n\nBy default, a disallowed key is **omitted** entirely. Pass `onDisallowed: 'redact'` to `LogSanitizationModule.forRoot()`\nto keep the key but replace its value with a fixed placeholder instead - useful when you'd rather see that a field\nexisted than have it silently disappear:\n\n```ts\nLogSanitizationModule.forRoot({allowShape: {id: true}, onDisallowed: 'redact'})\n```\n\n```\n// { id: 1, secret: 'shh' } logs as:\nresponse-data=\"{\\\"id\\\":\\\"1\\\",\\\"secret\\\":\\\"[REDACTED]\\\"}\"\n```\n\n### Redacting logged data: `@LogRedact`\n\nWhere `@LogAllowList` is *structural* (which keys survive at all), `@LogRedact` is *content-based*: it masks matching\npatterns (emails, IDs, ...) inside whatever `@LogAllowList` leaves behind, on the resulting value's string leaves.\nRegister named redactors once via `LogSanitizationModule.forRoot({redactors})`, then reference them by name.\n`emailRedactor` and `birthNumberRedactor` (Slovak \"rodné číslo\") ship built in:\n\n```ts\n// app.module.ts\nimport {emailRedactor, birthNumberRedactor} from '@bratislava/log-nest'\n\nLogSanitizationModule.forRoot({redactors: [emailRedactor, birthNumberRedactor]}) // global: applied to every request/response\n```\n\nWriting your own is the same `{name, redact}` shape:\n\n```ts\nimport {LogRedactor} from '@bratislava/log-nest'\n\nexport const ipRedactor: LogRedactor = {\n  name: 'ip',\n  redact: (line) => line.replaceAll(/\\b\\d{1,3}(\\.\\d{1,3}){3}\\b/g, '<ip>'),\n}\n```\n\n```ts\n\n@Controller('users')\nexport class UserController {\n  @Get(':id')\n  @LogRedact('email') // extra, endpoint-specific redactor on top of the global set, by name\n  async getUser(@Param('id') id: string): Promise<User> {\n    return this.userService.findById(id)\n  }\n}\n```\n\nLike `@LogAllowList`, it's additive across the global/endpoint levels, and runs independently of allowlist filtering:\n\n- allowlist decides *which keys* are logged,\n- redaction decides *what's left visible inside them*.\n\n### Decorators\n\n`@HandleErrors(loggerName?)` logs and swallows anything thrown by the method (resolves to `null`). Use it on entry\npoints that run *outside* the request-handling chain (cron jobs, event handlers, ...). There the exception filters never\nsee a thrown error, so it would surface as Nest's default multi-line stack trace instead of a logfmt line. The decorator\ncatches it and logs it through `LineLoggerService`:\n\n```ts\n\n@Injectable()\nexport class PaymentCronService {\n  @HandleErrors('CronJobs')\n  async syncPayments(): Promise<void> {\n    // any throw here is logged and swallowed\n  }\n}\n```\n\n`@CatchDatabaseError()` remaps anything thrown by the method into an `UnprocessableEntityException` with\n`ErrorEnum.DATABASE_ERROR`, keeping the original error as the logged cause. The class must expose the error factory\nservice as\n`errorFactoryService` (enforced by the `HasErrorFactoryService` interface):\n\n```ts\n\n@Injectable()\nexport class FormRepository implements HasErrorFactoryService {\n  constructor(public readonly errorFactoryService: ErrorFactoryService) {\n  }\n\n  @CatchDatabaseError()\n  async findForm(id: string): Promise<Form> {\n    return this.prisma.form.findUniqueOrThrow({where: {id}})\n  }\n}\n```\n\n## Exports\n\n| Export                                                         | Kind              | Purpose                                                                                             |\n|----------------------------------------------------------------|-------------------|-----------------------------------------------------------------------------------------------------|\n| `NestLoggingModule`                                            | module            | `forRoot({ alertReporting })`; provides + globally exports the error factory service                |\n| `ErrorFactoryService<T>`, `LogNestErrorEnumRegistry`           | injectable / type | exception factory, generic over the enum union; registry for the no-generic default                 |\n| `LineLoggerService`                                         | class             | logfmt `LoggerService`                                                                              |\n| `ErrorFilter`, `HttpExceptionFilter`, `UnknownExceptionFilter` | filters           | global exception handling                                                                           |\n| `AppLoggerMiddleware`                                          | middleware        | request/response logging + log/response split                                                       |\n| `LogSanitizationModule`                                        | module            | `forRoot({ redactors, allowShape, onDisallowed })`; provides + globally exports both services below |\n| `LogRedactionService`, `LogRedactor`                           | class / type      | content-based redaction, by name                                                                    |\n| `emailRedactor`, `birthNumberRedactor`                         | redactor          | built-in redactors, ready to pass to `LogSanitizationModule.forRoot({redactors})`                   |\n| `LogAllowListService`, `LogAllowShape`                         | class / type      | structural key filtering for logged data                                                            |\n| `ErrorEnum`, `ErrorResponseEnum`                               | enums             | shared base error codes + messages                                                                  |\n| `toLogfmt`, `errorToLogfmt`, `escapeForLogfmt`                 | functions         | logfmt helpers                                                                                      |\n| `HandleErrors`, `CatchDatabaseError`, `HasErrorFactoryService` | decorators / type | error-handling decorators                                                                           |\n| `LogRedact`, `LogAllowList`                                    | decorators        | per-route redaction / allowlist filtering, additive over the global config                          |\n\n## Developing and running tests\n\nRequires node 24 (see `engines` in `package.json`; [volta](https://volta.sh/) picks it up automatically). Then:\n\n```sh\nnpm ci             # install dependencies\nnpm run build      # type-check, then build dist/ with tsdown\nnpm run typecheck  # tsc --noEmit\nnpm run lint       # eslint (lint:fix to autofix, format for prettier)\nnpm test           # vitest (test:watch for watch mode)\n```\n\nSource imports have no file extensions, tsdown adds `.js` in `dist/`. Don't use top-level await in `src`, `require(esm)` can't load modules that use it.\n\n## License\n\n[EUPL-1.2](./LICENSE.md)\n","readmeFilename":"README.md"}