{"_id":"@concepta/nestjs-repository","_rev":"14-ed44dc1d5c2eac35f74cff35ce1b049f","name":"@concepta/nestjs-repository","dist-tags":{"latest":"8.0.0-alpha.12","alpha":"8.0.0-alpha.11"},"versions":{"8.0.0-alpha.0":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.0","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.0","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"492eae865c79a44e02a97e25b736f32e298a5fd4","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.0.tgz","fileCount":2,"integrity":"sha512-4I9/0dWn/VVka1T+MY5TzPpjP9D/cMp94jHTyAvY0+ZwNjgJ+p6wYfJukzxUa2Zge5wMrCyBi+o5xu5/NDF0bQ==","signatures":[{"sig":"MEUCIDf+NK3hOuWNlahtjQHYAN2jwNZK7QafUQXeZp3ycE2CAiEApf48DpOC7Kijh7o2Pe3jzVLpZAymjSqzlyAQqOg9IaI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":30794},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"8621ea0a2b7a14f52a2324b578dee4b4318ad7a5","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-hook":"^7.0.0-alpha.10","@concepta/nestjs-common":"^7.0.0-alpha.10"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@nestjs/testing":"^11.1.9"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.0_1772655572275_0.9973735808681039","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.1":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.1","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.1","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"c3d91d231364361fd6e796436d9c80a578514bba","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.1.tgz","fileCount":74,"integrity":"sha512-a3Sbp6JQftnWPfYFeb5gepoKewo7koryyf9wxFF2kxsUReEaVfTqjRwnYW0oUIU9Whe7jT0yNBZyQH1zMg6pqg==","signatures":[{"sig":"MEUCIFSSQSb9OJVv9vqAQXfwinSsYoWp/HRP143Rgh4z1FfSAiEA0VHH6kcn6s6LzhPbQznZ2Vw+9hMXrBRPiBoEwkBt5Hc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":167612},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"e5df07eee73df53a7ea9ec12e43a078b07d7f7b1","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-hook":"8.0.0-alpha.1","@concepta/nestjs-common":"8.0.0-alpha.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@nestjs/testing":"^11.1.9"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.1_1772715665861_0.021971455367042925","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.2":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.2","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.2","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"16dbaf43642295db05c874a117ad09096ced89df","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.2.tgz","fileCount":122,"integrity":"sha512-g55j7oJWtVMsLQeQ+POKrJmVs8VZTtAumfFuNnZ8OYUClBepmyOqrqgPasTkz1idrcwbtBocnOmTxCg61GdRAg==","signatures":[{"sig":"MEYCIQDl7SB9RE+vnozhLEGr2YXXowaGD/SgjlDcgqwDv5ZNjgIhAMG0yh282b2eUhsQv+upRYxa6x7UpSeclFYxZUCNsL+5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":199881},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"5df7cda706188f42a0f42295a6c77300c297a9f7","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-hook":"8.0.0-alpha.2","@concepta/nestjs-common":"8.0.0-alpha.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@nestjs/testing":"^11.1.9"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.2_1773954951577_0.9108993525076468","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.3":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.3","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.3","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"85acb344fc02c060c067b3b9adb8c30bad819e92","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.3.tgz","fileCount":130,"integrity":"sha512-ovR/ZGainoBdZdMa/FFb3Odx+LyFMJEzUoxqjoOhcYyovNkX2U8UBsjutowC27SBlKcZdChhYTjpw+QboxrFmg==","signatures":[{"sig":"MEQCIHgJxtYxRkthaW+bjhhE9MUmcY6thh1Y0BJyrAnko3iPAiAvgbOxV8TRmeSujPu9kyh5uRpZtV09jhpFZzFGPYcWdQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206958},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"580151a37425d0c17c8f113ab55866dc5b0f6855","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-hook":"8.0.0-alpha.3","@concepta/nestjs-common":"8.0.0-alpha.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@nestjs/testing":"^11.1.9"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.3_1775221757377_0.16750972098659056","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.4":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.4","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.4","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"1067dd048d0d35edb47a2db3f3765b5404a83403","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.4.tgz","fileCount":130,"integrity":"sha512-35VyDN/1JgDaEB/gAQ23KEZUg1KkqxLFp6dp0DcthFuP12sMFm3nT4ySwkFxhXkgaUJLCcJ/V18XKHo5h7IGjA==","signatures":[{"sig":"MEUCIQDITs1pe6WBlu8555EI/33gydH9TFIf73xkcGzIZUNHVwIgGUN/Aw/YRawzLKOnMvokXFvw5laFCrGA2lsnJTITZpc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206958},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"448b8886aa73db1001b026f5b2ea582dc4d990ce","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-hook":"8.0.0-alpha.4","@concepta/nestjs-common":"8.0.0-alpha.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@nestjs/testing":"^11.1.9"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.4_1775482258517_0.26862111677553857","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.5":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.5","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.5","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"482e32345fead4daaa8a0ceab88f48bc8a639067","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.5.tgz","fileCount":130,"integrity":"sha512-DYHp8dxx9ClySEScFnFXRayRUYnAdqv5nw6Y74Ttc+OvWjoqe4x3h2QONQw2Pj9NEQrEVs61tMqA1llRyoZtaw==","signatures":[{"sig":"MEYCIQDSpyUL0yDjiAmgBj/muvSslU42komAeVg5t+W9J2s/TwIhAJOm21sogde94SP1yharOLI7KExhi4qrXcUAZjQxkwUv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206958},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"48fcef1f8f6123ab7e4a3c5102d730b753b97d7f","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-hook":"8.0.0-alpha.5","@concepta/nestjs-common":"8.0.0-alpha.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@nestjs/testing":"^11.1.9"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.5_1777058519381_0.05710326141352895","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.6":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.6","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.6","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"26a860fdbfcb23ebf5334a1d6e66d5d5b4a9d981","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.6.tgz","fileCount":130,"integrity":"sha512-9hxjDcDRbZWmCQBSJ24nJzvJ1DI1FNlE3SLKbYgY97ttDivSsBGuUmjdPcUa038dxiU9CM/XE/hlwx97H+MfWw==","signatures":[{"sig":"MEUCIDiSY5gSPz8dA+YdchosM1RzyhAOIsPC4PcrjSqKbU+YAiEA9S3kpHa9BSp9Y6azRqSVUjqgrDpIaKJ51gEQHCn/vlA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":207155},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"02173c2a821a2bfa82e0534fd878939b6e690a39","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-hook":"8.0.0-alpha.6","@concepta/nestjs-common":"8.0.0-alpha.6"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@nestjs/testing":"^11.1.9","jest-mock-extended":"^4.0.0"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.6_1777495822607_0.8174316487734772","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.7":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.7","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.7","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"d2e3436cf14a95ed02d2e7e0583801dd379e5185","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.7.tgz","fileCount":120,"integrity":"sha512-WuNxp9IJbqw3EyobYu/kqRdSituFe0bBh6pTlDrNdGhHa4tSpP+2slJ9RqEgQNBWDjTv/S28c/29aOgYQVrETg==","signatures":[{"sig":"MEQCIBZbbbhvVAyKNwKyvbqYiuGClR3vGxF+na+/QkptV7NYAiBN2B9LEoPoc16M/glrWoS1GrW1GSDuWFk8DHyFi8UWtQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":199319},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"e5040610813ded61b169a9d6e32add5007924ff2","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"^11.1.9","@nestjs/common":"^11.1.9","@tsyche/membrane":"^0.7.0","@concepta/nestjs-core":"^8.0.0-alpha.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@nestjs/testing":"^11.1.9","jest-mock-extended":"^4.0.0"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.7_1781985071603_0.9604656979358281","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.8":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.8","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.8","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"}],"dist":{"shasum":"72f12605bb5ba43f36849cced86937a1c8022f8b","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.8.tgz","fileCount":120,"integrity":"sha512-2u62cO1flWJeTehP6Hna4zUkrHHeoATGhTPaBrVTdEc3+wxMtH7AfV55K0YPtw96jNOYxXdJetrwIkJP/Zrx/A==","signatures":[{"sig":"MEUCIAfrWZAvrJPHhWrb7yAeYlXhH8WUrs72dNycIKU68AxkAiEA6g1uNVRS0IXjsRqbLuU0VnZ16iNLdQN4eJ55YDGKB8k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":171995},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"3b752ac1855f1385461c378562da549ca3c974fb","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"10.8.2","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"20.19.5","dependencies":{"@nestjs/core":"12.0.0-alpha.5","@nestjs/common":"12.0.0-alpha.5","@tsyche/membrane":"^0.7.0","@concepta/nestjs-core":"^8.0.0-alpha.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"vitest":"^4.1.9","@nestjs/testing":"12.0.0-alpha.5","vitest-mock-extended":"^4.0.0"},"peerDependencies":{"rxjs":"^7.8.1","class-validator":"*","class-transformer":"*"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.8_1784411141487_0.7607780158774193","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.9":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.9","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.9","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},{"name":"tnramalho","email":"thiago.nramalho@gmail.com"}],"dist":{"shasum":"daae7e087adec005f2883df2ca0cd6baf03dc31a","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.9.tgz","fileCount":120,"integrity":"sha512-6ngA/kSAIzTLH92vF2fk9qCIUlUBatSVKqBLgd8JKSIAM4YPtTsxzm4likQ6RlZrV9G1zqspsjSaMkz9eAazfw==","signatures":[{"sig":"MEYCIQCuoYZ3tu3DjxubH1kElSi2kIRkgCHf/yMuhv4+/hoNbAIhAINEOd+8iZYq59ljJKij8FlLGUTfNXb14ODooi6HU76S","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":174770},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"d374a8ecbb2364c229a46c283bedb7f381ec0dbe","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"11.16.0","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"24.18.0","dependencies":{"@nestjs/core":"12.0.0-alpha.6","@nestjs/common":"12.0.0-alpha.6","@tsyche/membrane":"^0.7.0","@concepta/nestjs-core":"^8.0.0-alpha.9"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"vitest":"^4.1.9","@nestjs/testing":"12.0.0-alpha.6","vitest-mock-extended":"^4.0.0"},"peerDependencies":{"rxjs":"^7.8.1"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.9_1787607363324_0.8415700624389715","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.10":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.10","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.10","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},{"name":"tnramalho","email":"thiago.nramalho@gmail.com"}],"dist":{"shasum":"b6eac9f9d4be3883148022b8e3e782136599f09c","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.10.tgz","fileCount":126,"integrity":"sha512-pVuFuxfGviSz1byzdy9NbL0nt5fWnBZhVIZ8TCBO8kccvcGzliqeTtZlfCFKYWSzf9hZwWn+YMD15QhnQ5W/zQ==","signatures":[{"sig":"MEUCIAQGTEJJpZwsm7LkECVJvPfhKKqvnvJzF09RrYs9yJyqAiEAsfTd6scLP2baNIjHB2Jvo3fnACjO4JpC8pXvidBEmgI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":188543},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"gitHead":"3d5b377e5b94786d547083850abcc98338b314bc","_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"_npmVersion":"11.16.0","description":"Rockets NestJS Repository","directories":{},"_nodeVersion":"24.18.0","dependencies":{"@nestjs/core":"^12.0.1","@nestjs/common":"^12.0.1","@tsyche/membrane":"^0.7.0","@concepta/nestjs-core":"^8.0.0-alpha.10"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"vitest":"^4.1.9","@nestjs/testing":"^12.0.1","vitest-mock-extended":"^4.0.0"},"peerDependencies":{"rxjs":"^7.8.1"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.10_1788105350506_0.8801737475473437","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.11":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.11","license":"BSD-3-Clause","_id":"@concepta/nestjs-repository@8.0.0-alpha.11","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},{"name":"tnramalho","email":"thiago.nramalho@gmail.com"}],"dist":{"shasum":"7ecd60ca0c6c93a4d25e2fc0d5decc77bb4b8d0c","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.11.tgz","fileCount":129,"integrity":"sha512-e7yH2KfCDj3cgCdtqZ0q6D6X5OBKgzOk39Mne1EXdV6K/RaSQOYzUfSsKR4bsJCyZYphDSeK+GqxovPm6bUsmw==","signatures":[{"sig":"MEQCIDAGKHGT525sELQTSmh46c4UelcbpAX8NBu0WM19NfmhAiAU5mLT/AHCxIxQ4dKwJVt6dOwjc0PLe3MtIBWq02XSkQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":194908},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"description":"Rockets NestJS Repository","directories":{},"dependencies":{"@tsyche/membrane":"^0.8.1","@concepta/nestjs-core":"^8.0.0-alpha.11"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.9","@nestjs/core":"^12.0.1","@nestjs/common":"^12.0.1","@nestjs/testing":"^12.0.1","vitest-mock-extended":"^4.0.0"},"peerDependencies":{"rxjs":"^7.8.1","vitest":">=4.0.0","@nestjs/core":"^12.0.1","@nestjs/common":"^12.0.1","vitest-mock-extended":"^4.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true},"vitest-mock-extended":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nestjs-repository_8.0.0-alpha.11_1789070427598_0.8376298036517928","host":"s3://npm-registry-packages-npm-production"}},"8.0.0-alpha.12":{"name":"@concepta/nestjs-repository","version":"8.0.0-alpha.12","description":"Rockets NestJS Repository","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"}},"license":"BSD-3-Clause","publishConfig":{"access":"public"},"dependencies":{"@concepta/nestjs-core":"^8.0.0-alpha.12","@tsyche/membrane":"^0.8.1"},"devDependencies":{"@nestjs/common":"^12.0.1","@nestjs/core":"^12.0.1","@nestjs/testing":"^12.0.1","vitest":"^4.1.9","vitest-mock-extended":"^4.0.0"},"peerDependencies":{"@nestjs/common":"^12.0.1","@nestjs/core":"^12.0.1","rxjs":"^7.8.1","vitest":">=4.0.0","vitest-mock-extended":"^4.0.0"},"peerDependenciesMeta":{"vitest":{"optional":true},"vitest-mock-extended":{"optional":true}},"_id":"@concepta/nestjs-repository@8.0.0-alpha.12","dist":{"integrity":"sha512-XuPThk2Cxdwh8lKXGA7Tgt950w8p89m74nMheWN8MYU8TH4GtoVoIgZNsWOeGjmXOhmSxMgHHrOXIqYA5abZRw==","shasum":"9c3a3e0590b658d9982cfd77c803140f459952c1","tarball":"https://registry.npmjs.org/@concepta/nestjs-repository/-/nestjs-repository-8.0.0-alpha.12.tgz","fileCount":133,"unpackedSize":214905,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIG/klmbQlvEF4qp8upuEqlyimuNO9zk4KzmzNTCPLlDwAiEAnmQcW3HmVAs8Yy3K6pyU8UKWpq6JxgXuLDZvsWvva5o="}]},"_npmUser":{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},"directories":{},"maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},{"name":"tnramalho","email":"thiago.nramalho@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nestjs-repository_8.0.0-alpha.12_1789312474779_0.11166770169153661"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-04T20:19:32.164Z","modified":"2026-09-13T15:14:35.095Z","8.0.0-alpha.0":"2026-03-04T20:19:32.435Z","8.0.0-alpha.1":"2026-03-05T13:01:06.058Z","8.0.0-alpha.2":"2026-03-19T21:15:51.730Z","8.0.0-alpha.3":"2026-04-03T13:09:17.526Z","8.0.0-alpha.4":"2026-04-06T13:30:58.694Z","8.0.0-alpha.5":"2026-04-24T19:21:59.537Z","8.0.0-alpha.6":"2026-04-29T20:50:22.765Z","8.0.0-alpha.7":"2026-06-20T19:51:11.802Z","8.0.0-alpha.8":"2026-07-18T21:45:41.646Z","8.0.0-alpha.9":"2026-08-24T21:36:03.659Z","8.0.0-alpha.10":"2026-08-30T15:55:50.659Z","8.0.0-alpha.11":"2026-09-10T20:00:27.763Z","8.0.0-alpha.12":"2026-09-13T15:14:34.912Z"},"license":"BSD-3-Clause","description":"Rockets NestJS Repository","maintainers":[{"name":"leoafarias","email":"leoafarias@gmail.com"},{"name":"mrmaz","email":"marshall.sorenson@gmail.com"},{"name":"tnramalho","email":"thiago.nramalho@gmail.com"}],"readme":"# @concepta/nestjs-repository\n\nRepository abstraction module for NestJS. Provides a driver-agnostic\n`RepositoryAdapter` base class, transaction management with automatic\nnesting, and a two-level repository hook system.\n\n## Project\n\n[![NPM Latest](https://img.shields.io/npm/v/@concepta/nestjs-repository)](https://www.npmjs.com/package/@concepta/nestjs-repository)\n[![NPM Downloads](https://img.shields.io/npm/dw/@concepta/nestjs-repository)](https://www.npmjs.com/package/@concepta/nestjs-repository)\n[![GH Last Commit](https://img.shields.io/github/last-commit/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets)\n[![GH Contrib](https://img.shields.io/github/contributors/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets/graphs/contributors)\n[![NestJS Dep](https://img.shields.io/github/package-json/dependency-version/conceptadev/nestjs-modules/peer/@nestjs/common/feature/version-8?label=NestJS&logo=nestjs&filename=packages%2Fnestjs-repository%2Fpackage.json)](https://www.npmjs.com/package/@nestjs/common)\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Module Registration](#module-registration)\n- [Architecture Overview](#architecture-overview)\n- [Repository Adapter](#repository-adapter)\n- [Relations and Joins](#relations-and-joins)\n- [Where Clause Builder](#where-clause-builder)\n- [Order Clause Builder](#order-clause-builder)\n- [Transaction Management](#transaction-management)\n- [Transactional Decorator](#transactional-decorator)\n- [Repository Hooks](#repository-hooks)\n- [Repository Registry](#repository-registry)\n- [Federation](#federation)\n- [Injecting Repositories](#injecting-repositories)\n- [Exceptions](#exceptions)\n- [Entry Points](#entry-points)\n\n## Installation\n\n```sh\nyarn add @concepta/nestjs-repository @nestjs/common @nestjs/core rxjs\n```\n\n### Requirements\n\nESM-only — no CJS build is published. Requires Node `>= 22.12` and\nNestJS 12.\n\n### Dependencies\n\n| Package | Notes |\n| --- | --- |\n| `@concepta/nestjs-core` | Core interfaces, hook system, and utilities |\n| `@tsyche/membrane` | Hook pipeline (`Permeator`/`Membrane`) — ^0.8.1 |\n\n### Peer Dependencies\n\n| Package | Required | Notes |\n| --- | --- | --- |\n| `@nestjs/common` | Yes | NestJS core — install explicitly, no longer bundled |\n| `@nestjs/core` | Yes | Reflector for metadata — install explicitly |\n| `rxjs` | Yes | Used by `TransactionalRunner` and interceptor |\n\n## Module Registration\n\n### forRoot\n\n`forRoot()` registers the module **globally** and sets up the transaction\ninfrastructure (factory registry, scope, runner, interceptor).\n\n```ts\nimport { RepositoryModule } from '@concepta/nestjs-repository';\n\n@Module({\n  imports: [\n    RepositoryModule.forRoot({\n      defaultTimeout: 30000, // transaction timeout in ms (default)\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n### forRootAsync\n\n```ts\n@Module({\n  imports: [\n    RepositoryModule.forRootAsync({\n      useFactory: async (configService: ConfigService) => ({\n        defaultTimeout: configService.get('TX_TIMEOUT', 30000),\n      }),\n      inject: [ConfigService],\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n### forFeature\n\n`forFeature()` registers repository providers for specific entities. It\ndelegates to the driver module's own `forFeature()` method and automatically\nregisters entities in the repository registry and transaction factories.\n\n```ts\nimport { RepositoryModule } from '@concepta/nestjs-repository';\nimport { TypeOrmRepositoryModule } from '@concepta/nestjs-repository-typeorm';\n\n@Module({\n  imports: [\n    RepositoryModule.forFeature({\n      module: TypeOrmRepositoryModule,\n      entities: [\n        { key: 'orders', entity: Order },\n        { key: 'customers', entity: Customer },\n      ],\n    }),\n  ],\n})\nexport class OrderModule {}\n```\n\nEach entity registration creates a dynamic repository provider that can be\ninjected by key using `@InjectDynamicRepository()`.\n\n### Settings\n\n```ts\ninterface RepositoryModuleOptionsInterface {\n  defaultTimeout?: number; // Transaction timeout in milliseconds (default: 30000)\n}\n```\n\n## Architecture Overview\n\n```text\nApplication Code\n  |\nRepositoryModule (forRoot / forFeature)\n  |\n  +-- RepositoryAdapter (abstract, driver-agnostic)\n  |     Concrete implementations: TypeOrmRepository, etc.\n  |\n  +-- Transaction Layer\n  |     TransactionScope -> TransactionManager -> TransactionFactory\n  |\n  +-- Hook System\n  |     @RepoHook + @BeforeCreate / @AfterFind / etc.\n  |\n  +-- Registry\n        RepositoryRegistryService (duplicate key detection at bootstrap)\n```\n\n- **RepositoryAdapter** -- abstract base class implementing\n  `RepositoryInterface` with query, create, update, delete, and lifecycle\n  operations\n- **Transaction Layer** -- `TransactionScope` orchestrates transaction\n  lifecycle with automatic nesting; `TransactionManager` manages active\n  transactions for one scope, shared by every participant via a refcount;\n  factories are registered per driver/datasource\n- **Hook System** -- two-level decorators (high-level semantic + fine-grained)\n  for cross-cutting concerns like auditing, tenant filtering, and validation\n- **Registry** -- validates at application bootstrap that no duplicate\n  repository keys exist across features\n\n## Repository Adapter\n\n`RepositoryAdapter` is the abstract base class that all driver-specific\nrepository implementations extend. It implements `RepositoryInterface` with\na template-method design: the public operations (`find`, `create`, `update`,\netc.) are concrete wrappers that run the hook pipeline, each delegating to a\nprotected abstract `do*` method that the driver implements.\n\n### Abstract Members\n\nConcrete implementations must provide these protected `do*` methods, plus\nthe abstract `transform`/`merge` utilities and the `metadata` property:\n\n| Category | Method | Signature |\n| --- | --- | --- |\n| Query | `doFind` | `(options?) => Promise<Entity[]>` |\n| Query | `doFindOne` | `(options) => Promise<Entity \\| null>` |\n| Query | `doCount` | `(options?) => Promise<number>` |\n| Query | `doFindAndCount` | `(options?) => Promise<[Entity[], number]>` |\n| Create | `doCreate` | `(entity, options?) => Promise<Entity>` |\n| Create | `doCreateMany` | `(entities, options?) => Promise<Entity[]>` |\n| Update | `doUpdate` | `(entity, data, options?) => Promise<Entity>` |\n| Update | `doUpsert` | `(entity, options?) => Promise<Entity>` |\n| Update | `doReplace` | `(entity, data, options?) => Promise<Entity>` |\n| Delete | `doDelete` | `(entity, options?) => Promise<Entity>` |\n| Delete | `doDeleteMany` | `(entities, options?) => Promise<Entity[]>` |\n| Delete | `doSoftDelete` | `(entity, options?) => Promise<Entity>` |\n| Lifecycle | `doRestore` | `(entity, options?) => Promise<Entity>` |\n| Utility | `transform` | `(entityLike) => Entity` |\n| Utility | `merge` | `(mergeIntoEntity, ...entityLikes) => Entity` |\n| Utility | `metadata` | `RepositoryMetadataInterface<Entity>` (abstract property) |\n\n### Concrete Members\n\nThe public `find`, `findOne`, `count`, `findAndCount`, `create`,\n`createMany`, `update`, `upsert`, `replace`, `delete`, `deleteMany`,\n`softDelete`, and `restore` methods are concrete — each runs the\n[hook pipeline](#hook-pipeline) around the matching `do*` method.\n\n| Member | Visibility | Description |\n| --- | --- | --- |\n| `prepare(dto)` | public | Returns `dto` unchanged if it is already an entity instance, otherwise `Object.assign(new entityType(), dto)` |\n| `getPrimaryColumns()` | protected | Get primary key column names from metadata (subclass-author API) |\n| `getVersionColumn()` | protected | Get the optimistic-locking version column name from metadata, if any — backs both the in-request lock and `expectedVersion` (subclass-author API) |\n| `getDeleteDateColumn()` | protected | Get the soft-remove date column name from metadata, if any (subclass-author API) |\n| `toDnf(clause)` | protected | Convert `WhereClause` AST to Disjunctive Normal Form (subclass-author API) |\n| `runHooks(methodKey, payload, ctx)` | protected | Execute repository hooks for a lifecycle event (subclass-author API) |\n| `resolveJoinClauses(join?)` | protected | Resolve structural join properties from relation metadata (subclass-author API) |\n\n### Implementing a Repository\n\n```ts\nimport { RepositoryAdapter } from '@concepta/nestjs-repository';\n\nclass MyDriverRepository<Entity> extends RepositoryAdapter<Entity> {\n  readonly metadata = { /* ... */ };\n\n  protected async doFind(options?) {\n    return this.repo.find(options);\n  }\n\n  protected async doCreate(entity, options?) {\n    return this.repo.save(entity);\n  }\n\n  // ... implement the remaining do* methods, transform, and merge\n}\n```\n\nEach entry in `metadata.columns` must supply `name`, `isPrimary`,\n`isRemoveDate`, and `isVersion`. Set `isVersion: true` on the\noptimistic-locking version column, if the driver has one — `getVersionColumn()`\nreads it. Adapters that leave it `false` everywhere simply get no\noptimistic-locking support.\n\n### Soft-Deleted Immutability\n\nA soft-deleted row is immutable: `update`, `replace`, and `upsert` all reject\nit with `SoftDeletedImmutableException` (409 Conflict), for every driver —\nthe guard lives in `RepositoryAdapter` itself, above the `do*` methods, so no\ndriver implementation can forget it or diverge from it. `restore()` is the\nsanctioned way back.\n\n```ts\nimport { SoftDeletedImmutableException } from '@concepta/nestjs-repository';\n\ntry {\n  await repository.update(entity, { name: 'New Name' });\n} catch (err) {\n  if (err instanceof SoftDeletedImmutableException) {\n    // entity is soft-deleted — restore it first, or use { force: true }\n  }\n}\n```\n\nPass `{ force: true }` to bypass the guard for server-side carve-outs (e.g.\npre-purge PII masking, admin data-integrity corrections). It is not exposed\nover HTTP by `nestjs-crud` — only server-side callers can opt in.\n\nSoft-deleting an already-soft-deleted row is a no-op rather than an error:\n`softDelete()` returns the entity unchanged instead of re-stamping its delete\ndate, since a retried HTTP DELETE must not fail.\n\n### Expected Version\n\nThe in-request optimistic lock (see\n[nestjs-repository-typeorm's Optimistic Locking](../nestjs-repository-typeorm/README.md#optimistic-locking))\nguards a read-then-write inside one request, but two requests that each\nre-read before writing both pass it, since each compares against its own\nfresh value. `expectedVersion` closes that gap: pass the version the caller\nlast read, and `update`, `replace`, `delete`, `softDelete`, and `restore`\nall reject a mismatch with `OptimisticLockException` (409 Conflict) —\nenforced once in `RepositoryAdapter`, for every driver.\n\n```ts\nimport { OptimisticLockException } from '@concepta/nestjs-repository';\n\ntry {\n  await repository.update(entity, { name: 'New Name' }, { expectedVersion: 3 });\n} catch (err) {\n  if (err instanceof OptimisticLockException) {\n    // someone else changed this row since the caller last read it\n  }\n}\n```\n\nPassing `expectedVersion` against an entity with no version column throws a\n`RuntimeException` with `fault: 'usage'` — a caller asking for a guarantee\nthe entity cannot provide is a wiring mistake, not a client error.\n`upsert()` and `deleteMany()` don't accept it: neither has a single\ncaller-held row whose version the client could have read.\n\nDrivers never decide any of this — `RepositoryAdapter` resolves a\n`RepositoryVersionGuardInterface` descriptor (`{ column, value }`) and\npasses it down via `options.versionGuard`; a driver's only job is to\nexecute an atomic compare-and-swap on `column === value` before writing.\nPassing `versionGuard` directly is a no-op — the adapter always overwrites\nit with its own resolution.\n\n## Relations and Joins\n\nRepository find options accept a `join` array of `JoinClause` entries to load\nrelated entities alongside the root query.\n\n### JoinClause\n\nEach `JoinClause` describes how to join a related entity:\n\n```ts\ninterface JoinClause {\n  relation: string;           // relation name (must match entity metadata)\n  joinType?: 'LEFT' | 'INNER';  // default: 'LEFT'\n}\n```\n\nStructural properties (`on`, `through`, `cardinality`) are resolved\nautomatically from entity relation metadata by the adapter (via the\nprotected `resolveJoinClauses()`).\n\n### Join Helper\n\nThe `Join` helper builds `JoinClause` arrays:\n\n```ts\nimport { Join } from '@concepta/nestjs-repository';\n\n// Load a single relation (LEFT join by default)\nconst [users, total] = await userRepo.findAndCount({\n  ...Join.join(Join.left('company')),\n});\n// users[0].company → Company | null\n\n// Multiple relations with different join types\nconst [users, total] = await userRepo.findAndCount({\n  ...Join.join(\n    Join.left('posts'),\n    Join.inner('company'),\n  ),\n});\n\n// Many-to-many (junction configured in relation metadata)\nconst [users, total] = await userRepo.findAndCount({\n  ...Join.join(Join.left('roles')),\n});\n```\n\n### Join Methods\n\n| Method | Description |\n| --- | --- |\n| `left(relation)` | LEFT JOIN (default — includes rows with no match) |\n| `inner(relation)` | INNER JOIN (excludes rows with no match) |\n| `join(...clauses)` | Wrap join clauses into `{ join: clauses }` for passing to `find()` |\n\n### Filtering by Relations\n\nUse `Where.rel()` to filter by fields on a related entity. The relation\nmust be included in the join:\n\n```ts\nconst w = Where.for<UserEntity>();\n\nconst [users, total] = await userRepo.findAndCount({\n  ...Join.join(Join.left('posts')),\n  ...w.where(\n    w.and(\n      w.eq('status', 'active'),\n      w.rel('posts', Where.eq<PostEntity>('published', true)),\n    ),\n  ),\n});\n```\n\n### Sorting by Relations\n\nUse `OrderBy.rel()` to sort by fields on a related entity:\n\n```ts\nconst o = OrderBy.for<UserEntity>();\n\nconst [users, total] = await userRepo.findAndCount({\n  ...Join.join(Join.left('posts')),\n  ...o.order(\n    o.rel('posts', OrderBy.desc<PostEntity>('createdAt')),\n    o.asc('name'),\n  ),\n});\n```\n\n### Relation Metadata\n\nRelation metadata is populated automatically by the ORM driver (e.g.,\n`TypeOrmRepository` reads TypeORM's `RelationMetadata`). You can also\nconfigure per-relation behavior in `forFeature()`:\n\n```ts\nRepositoryModule.forFeature({\n  module: TypeOrmRepositoryModule,\n  entities: [{\n    key: 'users',\n    entity: UserEntity,\n    relations: {\n      posts: { federated: true },        // use separate queries\n      company: { onDelete: 'delegate' }, // defer to DB cascade settings\n    },\n  }],\n});\n```\n\nRelations marked `federated: true` use separate queries instead of SQL\nJOINs. See [Federation](#federation) for details.\n\n## Where Clause Builder\n\nThe `Where` helper builds ORM-agnostic\n`WhereClause` AST objects that `RepositoryAdapter` implementations translate\ninto driver-specific queries.\n\n### How Translation Works\n\n1. The `Where` helper builds a `WhereClause` AST (tree of conditions and\n   compound operators)\n2. `RepositoryAdapter.toDnf()` flattens the AST into Disjunctive Normal Form\n   (an OR of ANDs)\n3. The concrete driver (e.g., `TypeOrmRepository`) translates each AND-branch\n   into a driver-specific query object\n4. Same-field conditions within a branch are merged (e.g., `gt` + `lt` on the\n   same field become a combined range)\n\n### Static API\n\nPass the entity type as a generic parameter on each call:\n\n```ts\nimport { Where } from '@concepta/nestjs-repository';\n\n// Simple equality\nconst activeOrders = await orderRepo.find(\n  Where.where(Where.eq<OrderEntity>('status', 'active')),\n);\n\n// Compound conditions\nconst result = await orderRepo.find(\n  Where.where(\n    Where.and(\n      Where.eq<OrderEntity>('status', 'active'),\n      Where.gt<OrderEntity>('total', 100),\n      Where.contains<OrderEntity>('notes', 'urgent'),\n    ),\n  ),\n);\n\n// OR conditions\nconst result = await orderRepo.find(\n  Where.where(\n    Where.or(\n      Where.eq<OrderEntity>('status', 'shipped'),\n      Where.eq<OrderEntity>('status', 'delivered'),\n    ),\n  ),\n);\n```\n\n### Typed Builder API\n\nBind the entity type once with `Where.for<Entity>()`. All subsequent calls\ntype-check field names against the entity:\n\n```ts\nimport { Where } from '@concepta/nestjs-repository';\n\nconst w = Where.for<OrderEntity>();\n\n// Simple query\nconst orders = await orderRepo.find(\n  w.where(w.eq('status', 'active')),\n);\n\n// Nested AND/OR\nconst orders = await orderRepo.find(\n  w.where(\n    w.and(\n      w.eq('status', 'active'),\n      w.or(\n        w.gte('total', 1000),\n        w.contains('notes', 'priority'),\n      ),\n    ),\n  ),\n);\n\n// Null checks and range\nconst orders = await orderRepo.find(\n  w.where(\n    w.and(\n      w.notNull('assigneeId'),\n      w.between('total', 100, 500),\n    ),\n  ),\n);\n\n// Set membership\nconst orders = await orderRepo.find(\n  w.where(\n    w.in('status', ['pending', 'processing', 'shipped']),\n  ),\n);\n\n// Pattern matching\nconst orders = await orderRepo.find(\n  w.where(\n    w.and(\n      w.starts('sku', 'ELEC-'),\n      w.notContains('notes', 'cancelled'),\n    ),\n  ),\n);\n```\n\n### Relation Conditions\n\nUse `rel()` to tag a condition with a relation name. The condition is applied\nas a filter on the related entity (see [Filtering by Relations](#filtering-by-relations)):\n\n```ts\nconst w = Where.for<OrderEntity>();\n\n// Filter orders by customer tier\nconst orders = await orderRepo.findAndCount({\n  ...Join.join(Join.left('customer')),\n  ...w.where(\n    w.and(\n      w.eq('status', 'active'),\n      w.rel('customer', Where.eq<CustomerEntity>('tier', 'gold')),\n    ),\n  ),\n});\n```\n\n### Condition Operators\n\n| Method | Description |\n| --- | --- |\n| `eq(field, value)` | Equal |\n| `ne(field, value)` | Not equal |\n| `gt(field, value)` | Greater than |\n| `gte(field, value)` | Greater than or equal |\n| `lt(field, value)` | Less than |\n| `lte(field, value)` | Less than or equal |\n| `contains(field, value)` | Contains substring |\n| `notContains(field, value)` | Does not contain substring |\n| `starts(field, value)` | Starts with |\n| `notStarts(field, value)` | Does not start with |\n| `ends(field, value)` | Ends with |\n| `notEnds(field, value)` | Does not end with |\n| `in(field, values)` | In array |\n| `notIn(field, values)` | Not in array |\n| `isNull(field)` | Is null |\n| `notNull(field)` | Is not null |\n| `between(field, from, to)` | Between range (inclusive) |\n\n### Compound Operators\n\n| Method | Description |\n| --- | --- |\n| `and(...conditions)` | All conditions must match |\n| `or(...conditions)` | Any condition must match |\n\n### Utility Methods\n\n| Method | Description |\n| --- | --- |\n| `where(clause)` | Wrap a `WhereClause` into `{ where: clause }` for passing to `find()` |\n| `rel(relation, condition)` | Tag a condition with a relation name |\n| `for<Entity>()` | Create a typed builder with field name checking |\n\n## Order Clause Builder\n\nThe `OrderBy` helper builds ORM-agnostic\n`OrderClause` arrays that `RepositoryAdapter` implementations translate\ninto driver-specific sort options.\n\n### Static OrderBy API\n\nPass the entity type as a generic parameter on each call:\n\n```ts\nimport { OrderBy } from '@concepta/nestjs-repository';\n\n// Single sort\nconst users = await userRepo.find(\n  OrderBy.order(OrderBy.asc<UserEntity>('name')),\n);\n\n// Multiple sorts (priority follows array order)\nconst users = await userRepo.find(\n  OrderBy.order(\n    OrderBy.desc<UserEntity>('createdAt'),\n    OrderBy.asc<UserEntity>('name'),\n  ),\n);\n```\n\n### Typed OrderBy Builder API\n\nBind the entity type once with `OrderBy.for<Entity>()`. All subsequent calls\ntype-check field names against the entity:\n\n```ts\nimport { OrderBy } from '@concepta/nestjs-repository';\n\nconst o = OrderBy.for<UserEntity>();\n\nconst users = await userRepo.find(\n  o.order(o.desc('createdAt'), o.asc('name')),\n);\n```\n\n### Relation Sorting\n\nUse `rel()` to sort by a field on a related entity (see\n[Sorting by Relations](#sorting-by-relations)):\n\n```ts\n// Sort users by post title, then by creation date\nconst users = await userRepo.findAndCount({\n  ...Join.join(Join.left('posts')),\n  ...OrderBy.order(\n    OrderBy.rel('posts', OrderBy.asc<PostEntity>('title')),\n    OrderBy.desc<UserEntity>('createdAt'),\n  ),\n});\n```\n\n### Sort Methods\n\n| Method | Description |\n| --- | --- |\n| `asc(field)` | Ascending sort |\n| `desc(field)` | Descending sort |\n\n### OrderBy Utility Methods\n\n| Method | Description |\n| --- | --- |\n| `order(...keys)` | Wrap sort keys into `{ order: keys }` for passing to `find()` |\n| `rel(relation, key)` | Tag a sort key with a relation name |\n| `relDot(dotField, key)` | Extract relation from `\"relation.field\"` dot notation |\n| `for<Entity>()` | Create a typed builder with field name checking |\n\n### Combining Where + OrderBy\n\nSpread both helpers into find options:\n\n```ts\nconst w = Where.for<OrderEntity>();\nconst o = OrderBy.for<OrderEntity>();\n\nconst orders = await orderRepo.find({\n  ...w.where(w.eq('status', 'active')),\n  ...o.order(o.desc('createdAt')),\n});\n```\n\n### Passing Context\n\nAll repository methods accept an optional `ctx` property in their options.\nThe `ctx` is a `PlainLiteralObject` that carries the entity key,\ntransaction state, and hook configuration. When `ctx` has an active `trx`\n(TransactionManager), the repository automatically uses the transactional\nconnection — no manual wiring required.\n\nSpread `Where.where()` into options alongside `ctx`:\n\n```ts\nconst w = Where.for<OrderEntity>();\n\n// Query within a transaction\nconst orders = await orderRepo.find({\n  ...w.where(w.eq('status', 'active')),\n  ctx,\n});\n\n// Create within a transaction\nconst order = await orderRepo.create(dto, { ctx });\n\n// Nested service calls share the same transaction via ctx\nawait this.txScope.run(ctx, async (txCtx) => {\n  const orders = await orderRepo.find({\n    ...w.where(w.gt('total', 100)),\n    ctx: txCtx,\n  });\n  await auditRepo.create(\n    { action: 'query', count: orders.length },\n    { ctx: txCtx },\n  );\n});\n```\n\nThe `ctx` is propagated through nested `TransactionScope.run()` calls. Inner\ncalls join the outer transaction automatically. Pass the `txCtx` handed to\nthe operation — not the outer `ctx` — to repository calls inside it. See\n[Transaction Management](#transaction-management) for details.\n\n## Transaction Management\n\nThe transaction layer provides automatic transaction lifecycle management\nwith automatic nesting support.\n\n### TransactionScope\n\n`TransactionScope` is the primary API for running operations within\ntransactions. It is provided globally by `RepositoryModule.forRoot()`.\n\n```ts\nimport { TransactionScope } from '@concepta/nestjs-repository';\n\n@Injectable()\nexport class OrderService {\n  constructor(private readonly txScope: TransactionScope) {}\n\n  async createOrder(ctx: PlainLiteralObject, dto: DeepPartial<OrderEntity>) {\n    return this.txScope.run(ctx, async (txCtx) => {\n      // Pass txCtx, not ctx, to repository calls inside the operation\n      const order = await orderRepo.create(dto, { ctx: txCtx });\n      const item = await inventoryRepo.findOne({\n        where: { id: order.itemId },\n        ctx: txCtx,\n      });\n      await inventoryRepo.update(item, { reserved: true }, { ctx: txCtx });\n\n      // Register post-commit callback\n      txCtx.trx.onCommit(() => {\n        // Send confirmation email after successful commit\n      });\n\n      return order;\n    });\n  }\n}\n```\n\n### Domain Events with mergeObjectContext\n\nWhen using DDD aggregates that extend `AggregateRoot` from `@nestjs/cqrs`,\nuse `EventPublisher.mergeObjectContext()` to wire up event publishing, then\nregister `commit()` and `uncommit()` as post-commit/rollback callbacks.\nThis ensures domain events are only published after the transaction succeeds.\n\n```ts\nimport { CommandHandler, EventPublisher, ICommandHandler } from '@nestjs/cqrs';\nimport { createEventContext } from '@concepta/nestjs-core';\nimport { TransactionScope } from '@concepta/nestjs-repository';\n\n@CommandHandler(CreateOrderCommand)\nexport class CreateOrderHandler implements ICommandHandler<CreateOrderCommand> {\n  constructor(\n    private readonly txScope: TransactionScope,\n    private readonly eventPublisher: EventPublisher,\n    private readonly repositoryResolver: OrderRepositoryResolver,\n  ) {}\n\n  async execute(command: CreateOrderCommand): Promise<Order> {\n    const { ctx, namespace, dto } = command;\n\n    const orderRepo = this.repositoryResolver.resolve(namespace);\n\n    const eventContext = createEventContext(ctx, { namespace }, {});\n\n    return this.txScope.run(ctx, async (txCtx) => {\n      const order = this.eventPublisher.mergeObjectContext(\n        Order.create(eventContext, dto),\n      );\n\n      await orderRepo.save(txCtx, order);\n\n      txCtx.trx.onCommit(() => order.commit());     // publish domain events\n      txCtx.trx.onRollback(() => order.uncommit());  // discard domain events\n\n      return order;\n    });\n  }\n}\n```\n\n```ts\n// Read-only transaction (always rolls back). onRollback callbacks run\n// (the scope did roll back); onCommit callbacks never run.\nawait this.txScope.runReadOnly(ctx, async (txCtx) => {\n  return orderRepo.find({ ctx: txCtx });\n});\n\n// Custom timeout\nawait this.txScope.run(ctx, operation, {\n  timeout: 5000,\n});\n```\n\n### Nesting\n\nNested and concurrent `run()` calls on the same context join a single\ntransaction, which commits or rolls back when the last participant exits —\nnot necessarily the first one to have entered (or immediately if any\nparticipant times out). After that, the context carries no transaction\nstate — a later `run()` on it starts a fresh, independent transaction.\n\n```ts\n// Outermost — creates transaction\nawait this.txScope.run(ctx, async (txCtx) => {\n  await serviceA.doWork(ctx); // joins existing transaction\n  await serviceB.doWork(ctx); // joins existing transaction\n});\n// Transaction commits here (or rolls back on error) — ctx is now released\n\n// A later, unrelated run() on the same ctx starts an independent scope\nawait this.txScope.run(ctx, async (txCtx) => {\n  await serviceC.doWork(ctx);\n});\n```\n\nInside the operation, prefer the `txCtx` handed to you over the outer `ctx`\nfor repository calls: a stale `txCtx` fails loudly with\n`TransactionClosedException` once its scope has settled, while a stale\n`ctx` silently falls back to running non-transactionally. Don't hold either\none past the point `run()` resolves, and don't spawn unawaited work inside\nthe operation — anything still running once the transaction settles writes\noutside it either way.\n\nBecause every participant shares one scope, a sibling's failure can doom\nwork that otherwise succeeded:\n\n- If a participant's own operation succeeds, but a nested or concurrent\n  sibling sharing the same scope fails, that participant's `run()` call\n  rejects with `TransactionScopeFailedException` (carrying the sibling's\n  error as `context.originalError`) rather than resolving as if nothing\n  happened — even if the caller caught the sibling's error itself.\n- `readOnly` is decided once, by whichever `run()` call creates the scope —\n  every later participant just joins it. A nested or concurrent `run()`\n  (or `runReadOnly()`) call whose explicit `readOnly` option conflicts with\n  the scope it's joining throws `TransactionReadOnlyConflictException`\n  instead of silently discarding one side's intent. `timeout`, by contrast,\n  is honored per participant, not scope-wide.\n\n### TransactionManager\n\n`txCtx.trx` is a `TransactionManager` — the handle for registering\npost-commit/rollback work and, for non-repository work, reading the\ncancellation signal.\n\n| Method | Description |\n| --- | --- |\n| `onCommit(fn)` | Register post-commit callback; throws `TransactionClosedException` once the scope has closed |\n| `onRollback(fn)` | Register post-rollback callback; a `readOnly` scope always rolls back, so these run whether or not its operation succeeded; throws `TransactionClosedException` once the scope has closed |\n| `signal` | `AbortSignal` that aborts once the scope is doomed (an operation threw, or the final commit failed), carrying that failure as `signal.reason`. Stays unaborted for a scope that settles cleanly — it signals \"doomed\", not \"settled\" |\n\n`getOrStart(key)` also exists but is used by repository/driver internals —\napplication code doesn't call it directly.\n\n`onCommit`/`onRollback` callbacks flush one at a time, in registration\norder, once the scope settles, so a later callback can rely on an earlier\none having fully finished first. A callback that throws is logged, not\nrethrown — it doesn't fail `run()` and doesn't stop the callbacks after it\nfrom running, so a callback that must not fail silently should handle its\nown errors.\n\n#### Cancellation and timeouts\n\n`trx.signal` aborts as soon as the scope is doomed, so operations doing\nnon-repository work (an HTTP call, a queue publish) can opt in to stopping\nearly instead of running to completion against a transaction that's already\nrolling back:\n\n```ts\nawait txScope.run(ctx, async (txCtx) => {\n  const res = await fetch(url, { signal: txCtx.trx.signal });\n  ...\n});\n```\n\nThis is cooperative — nothing here forcibly stops an operation that ignores\nthe signal. A timed-out `run()` rejects with `TransactionTimeoutException`;\nan operation that outlives the timeout keeps running as an abandoned orphan,\nand its eventual failure is logged rather than surfaced to the caller, who\nhas long since moved on. A timeout settles the scope immediately rather\nthan waiting for every participant to exit, so a sibling still running —\nnested or concurrent, even one well within its own timeout — gets\n`TransactionClosedException` from its `txCtx`, and its `run()` rejects with\n`TransactionScopeFailedException` even if its own operation goes on to\nsucceed.\n\nThe timeout covers the operation, not settlement — a `commit()` or\n`rollback()` call that hangs (a dead connection, a lock wait) is not\nbounded by it and can leave `run()` unresolved indefinitely. There is no\nsafe way to abandon an in-flight commit without knowing whether it landed.\n\n#### Multiple datasources\n\nA single `run()` scope can span transactions on more than one datasource.\nCommits are sequential, not two-phase: if one fails after another has\nalready committed, `run()` rejects with `TransactionHeuristicCommitException`\ninstead of an ordinary commit error, since that earlier commit can't be\nundone. `onRollback` callbacks run in that case — never `onCommit` — even\nfor the datasource(s) that durably committed, so domain events registered\nvia `onCommit(() => agg.commit())` are treated as not-yet-safe-to-publish.\n\n### TransactionFactory\n\nEach driver/datasource provides a `TransactionFactoryInterface`:\n\n```ts\ninterface TransactionFactoryInterface {\n  create(): TransactionInterface;\n}\n```\n\nFactories are registered automatically when using `RepositoryModule.forFeature()`\nwith a driver module that returns `transactionFactories` in its\n`DynamicRepositoryModule`.\n\n## Transactional Decorator\n\nThe `@Transactional()` decorator wraps controller routes in transactions\ndeclaratively. It can be applied at the class level (all routes) or method\nlevel (individual routes).\n\n```ts\nimport { Transactional } from '@concepta/nestjs-repository';\n\n@Controller('orders')\n@Transactional()\nexport class OrderController {\n  @Post()\n  async create(@Body() dto: DeepPartial<OrderEntity>) {\n    // Runs in a transaction\n  }\n\n  // Disable transaction for this route\n  @Get()\n  @Transactional(false)\n  async list() {\n    // No transaction\n  }\n\n  // Read-only transaction\n  @Get(':id')\n  @Transactional({ readOnly: true })\n  async read(@Param('id') id: string) {\n    // Read-only transaction (always rolls back)\n  }\n}\n```\n\n### Options\n\n```ts\ninterface TransactionalOptions {\n  readOnly?: boolean;\n  timeout?: number; // milliseconds (default: 30000)\n}\n```\n\n- **`readOnly`** -- always roll back, for read-only operations (default: `false`)\n- **`timeout`** -- transaction timeout in milliseconds\n\nMethod-level `@Transactional()` overrides class-level settings.\nPass `false` to disable transactions for a specific method.\n\n### TransactionalRunner\n\n`TransactionalRunner` is used internally by `TransactionInterceptor` to\ncheck for `@Transactional()` metadata and wrap operations. It can also be\nused directly in custom interceptors:\n\n```ts\nimport { TransactionalRunner } from '@concepta/nestjs-repository';\n\n@Injectable()\nexport class CustomInterceptor implements NestInterceptor {\n  constructor(private readonly txRunner: TransactionalRunner) {}\n\n  intercept(context: ExecutionContext, next: CallHandler) {\n    return this.txRunner.run(context, () => next.handle());\n  }\n}\n```\n\n### Detecting `@Transactional()`\n\n`isTransactional()` and `getTransactionalOptions()` read the metadata\n`@Transactional()` sets, without depending on the underlying metadata key —\nuseful for building tooling (route audits, OpenAPI generation, custom\ninterceptors) that needs to know whether a route is transactional:\n\n```ts\nimport { getTransactionalOptions, isTransactional } from '@concepta/nestjs-repository';\n\n// Pass targets in override order (e.g. handler before class), same as\n// Nest's own Reflector.getAllAndOverride — the first target that carries\n// the metadata wins.\nisTransactional(context.getHandler(), context.getClass()); // boolean\n\n// Resolve the options themselves (undefined if none, false if explicitly\n// disabled with `@Transactional(false)`)\ngetTransactionalOptions(context.getHandler(), context.getClass());\n```\n\n## Repository Hooks\n\nThe hook system provides cross-cutting concerns for repository operations.\nHooks are resolved at runtime via `@concepta/nestjs-core` and can be scoped\nto specific entities using specifications. This section covers the\nrepository-specific decorators and merge semantics; for the underlying\nmechanism (how a class becomes a hook, how it's attached to a controller,\nand how values get into `ctx`) see `@concepta/nestjs-core`'s\n[Hook Feature](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-core#hook-feature)\nand [Context System](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-core#context-system)\nsections.\n\n### Defining a Hook\n\n```ts\nimport { Injectable } from '@nestjs/common';\nimport {\n  type AppContextInterface,\n  type DeepPartial,\n  OverlayRef,\n} from '@concepta/nestjs-core';\nimport {\n  RepoHook,\n  BeforeFind,\n  BeforeCreate,\n  type RepositoryFindOptions,\n  Where,\n} from '@concepta/nestjs-repository';\n\n// Typed token for the tenant id — attached to the request context by a\n// ContextOverlayInterceptor registered as an APP_INTERCEPTOR (see nestjs-core's\n// Context System docs for the interceptor that populates this).\nexport const TenantCtx = new OverlayRef<'withTenant', { tenantId: string }>(\n  'withTenant',\n);\n\n@RepoHook()\n@Injectable()\nexport class TenantScopeHook {\n  // Scoped reads: hook output replaces the query options wholesale, so a\n  // caller's own `where` clause is ANDed with the tenant filter, never\n  // dropped — the two combine rather than one overriding the other.\n  @BeforeFind()\n  async addTenantFilter(\n    options: RepositoryFindOptions<OrderEntity>,\n    ctx?: AppContextInterface,\n  ): Promise<RepositoryFindOptions<OrderEntity>> {\n    const tenant = ctx?.supports(TenantCtx) ? ctx.with(TenantCtx) : undefined;\n    if (!tenant) return options;\n\n    const condition = Where.eq('tenantId', tenant.tenantId);\n    return {\n      ...options,\n      where: options.where ? Where.and(options.where, condition) : condition,\n    };\n  }\n\n  // Authoritative write: { replace: true } makes this hook's output win over\n  // whatever the caller supplied — a caller-provided `tenantId` in the\n  // request body cannot override the stamp. Without { replace: true }, the\n  // default merge semantics would let a caller's own tenantId win instead —\n  // see \"Hook Pipeline\" below. This is the isolation-relevant half; the\n  // read-side filter alone is not enough to keep tenants apart.\n  @BeforeCreate({ replace: true })\n  async stampTenant(\n    data: DeepPartial<OrderEntity>,\n    ctx?: AppContextInterface,\n  ): Promise<DeepPartial<OrderEntity>> {\n    const tenant = ctx?.supports(TenantCtx) ? ctx.with(TenantCtx) : undefined;\n    if (!tenant) return data;\n\n    return { ...data, tenantId: tenant.tenantId };\n  }\n}\n```\n\n### Wiring Hooks\n\nDefining a hook class is not enough on its own — three things all have to be\ntrue for it to run, and skipping any one of them means the hook silently\nnever fires (no error, no warning beyond `ROCKETS_HOOKS_NOT_WIRED` — see\nbelow — if *no* repository anywhere has hooks wired):\n\n1. **`CoreModule.forRoot()`** (or `.register()`) is imported. It provides\n   `HookResolverService` and the interceptor that reads `@UseHooks()`\n   metadata into the request context.\n2. **`@UseHooks(TheHook)`** is applied to the controller or method — for a\n   plain controller, directly; for a CRUD-generated one, via\n   `extraDecorators: [UseHooks(TheHook)]` on the `crud.controller` options.\n3. **The hook class is listed in `providers`** so Nest's DI can resolve it.\n\n```ts\nimport { Module } from '@nestjs/common';\nimport { CoreModule, UseHooks } from '@concepta/nestjs-core';\n\n@Module({\n  imports: [\n    CoreModule.forRoot(), // 1. required for any hook to run\n    // ...RepositoryModule, TypeOrmModule, etc.\n  ],\n  controllers: [], // if wiring @UseHooks directly on a controller (2)\n  providers: [TenantScopeHook], // 3. required so DI can resolve it\n})\nexport class OrderModule {}\n\n@UseHooks(TenantScopeHook) // 2. required — the actual attachment point\n@Controller('orders')\nexport class OrderController {}\n```\n\nOutside HTTP (a queue worker, a CLI script, a test), there's no controller\nfor `@UseHooks()` to attach to, so build the hook list directly and set it\non the ambient `ctx` yourself:\n\n```ts\nimport { AppContextHost, HooksCtx } from '@concepta/nestjs-core';\nimport { RepoHook } from '@concepta/nestjs-repository';\n\nconst ctx = new AppContextHost();\nctx.defineOverlay(HooksCtx, {\n  hooks: [{ hook: TenantScopeHook, type: RepoHook.KEY }],\n});\n\nawait orderRepo.create(dto, { ctx });\n```\n\n`TenantScopeHook` still needs to be resolvable from the module container\n(`HookResolverService` resolves it via `moduleRef.get(hook, { strict: false\n})`, so any module works, not specifically `CoreModule`'s own) — this only\nreplaces the `@UseHooks()`/controller attachment step, not step 1 above.\n\n### Scoped Hooks\n\nUse specifications to restrict a hook to specific entities:\n\n```ts\nimport { RepoHook, RepoSpec, AfterCreate } from '@concepta/nestjs-repository';\n\n@RepoHook(RepoSpec.isEntity('User'))\nexport class UserOnlyHook {\n  @AfterCreate()\n  notifyUserCreated(result, ctx) {\n    // Only runs for User entity operations\n    return result;\n  }\n}\n```\n\n`RepoSpec.isEntity(name)` builds an `EntitySpecification` (also exported\nfor direct use) that matches when the repository's entity key equals `name`.\n\n### Hook Decorators\n\nHooks are organized into two levels: high-level semantic decorators that\nmatch broad categories, and fine-grained decorators for specific operations.\n\n#### High-Level Semantic\n\n| Decorator | Matches |\n| --- | --- |\n| `@BeforeRead` / `@AfterRead` | find, findOne, count, findAndCount |\n| `@BeforeWrite` / `@AfterWrite` | create, createMany, update, upsert, replace |\n| `@BeforeTransition` / `@AfterTransition` | softDelete, restore |\n| `@BeforeDestroy` / `@AfterDestroy` | delete, deleteMany (hard delete) |\n\n#### Fine-Grained\n\n| Category | Decorators |\n| --- | --- |\n| Query | `@BeforeFind` `@AfterFind` `@BeforeFindOne` `@AfterFindOne` `@BeforeCount` `@AfterCount` `@BeforeFindAndCount` `@AfterFindAndCount` |\n| Create | `@BeforeCreate` `@AfterCreate` `@BeforeCreateMany` `@AfterCreateMany` |\n| Update | `@BeforeUpdate` `@AfterUpdate` `@BeforeUpsert` `@AfterUpsert` `@BeforeReplace` `@AfterReplace` |\n| Delete | `@BeforeDelete` `@AfterDelete` `@BeforeDeleteMany` `@AfterDeleteMany` |\n| Lifecycle | `@BeforeSoftDelete` `@AfterSoftDelete` `@BeforeRestore` `@AfterRestore` |\n\nHook methods receive the operation payload and an optional context, and must\nreturn the (possibly modified) payload.\n\nThe five write decorators — `@BeforeWrite`, `@BeforeCreate`, `@BeforeUpdate`,\n`@BeforeUpsert`, `@BeforeReplace` — additionally accept\n`{ replace: true }` in place of (or alongside) a specification, e.g.\n`@BeforeCreate({ replace: true })`. See \"Hook Pipeline\" below for what it\nchanges and when to use it; every other decorator's signature is unaffected.\n\n### Hook Pipeline\n\nHook execution is orchestrated by `RepoPermeatorFactory`, built on\n`@tsyche/membrane` (`Permeator`/`Membrane`). Each public repository\noperation runs before-hooks on its input, calls the driver's `do*` method,\nthen runs after-hooks on the result, with one of two merge semantics for\nsingle-entity payloads and options objects (`Membrane.object` /\n`Membrane.objectReplace`), and a separate strategy for array payloads\n(`Membrane.collection`):\n\n- **`Membrane.objectReplace`** -- read operations (`find`, `findOne`,\n  `count`, `findAndCount`): the last hook's return value replaces the\n  payload/result wholesale, so hooks may freely transform options and\n  results.\n- **`Membrane.object`** -- single-entity write operations (`create`,\n  `update`, `upsert`, `replace`) and delete/lifecycle operations (`delete`,\n  `deleteMany`, `softDelete`, `restore`): hook output is merged onto the\n  original, which wins on conflict — **the caller's own payload survives\n  hook mutations by default.** This is right for enrichment (a hook filling\n  in a default the caller is free to override) but wrong for authorization\n  (a hook enforcing a value the caller must not be able to override, e.g. a\n  tenant id) — a caller who explicitly supplies that same field defeats the\n  hook silently, no error either way.\n- **`{ replace: true }`** on any of the five write decorators inverts this\n  for that hook only: it runs through `Membrane.objectReplace` instead, in a\n  second pass after every plain (merge) hook for the same key, so its output\n  wins regardless of what the caller supplied. Use this whenever a hook is\n  enforcing something, not suggesting it — see the `@BeforeCreate({ replace:\n  true })` example above. `RepoHookStrategy` (exported alongside the\n  decorators) is the pair of predicates this routing is built on, in case\n  you need to reason about it directly.\n- **`Membrane.collection`** -- `createMany` (`overwrite`: hooks may freely\n  transform the array) and `deleteMany` (`preserve`: the original array\n  wins on conflict) keep the strategy argument, since it governs array\n  merging rather than object replacement. `createMany` hooks already behave\n  like `{ replace: true }` unconditionally — there's no merge-vs-replace\n  choice to make for it.\n\nAny error thrown inside the pipeline (a hook or the driver call) is wrapped\nin `RepositoryQueryException`. `RuntimeException` subclasses — `OptimisticLockException`,\n`FederationException`, the transaction exceptions, and any already-wrapped\n`RepositoryQueryException` — pass through unchanged, so callers can catch\nthem by type and their `httpStatus` survives to the transport layer.\n\nTwo `OverlayRef` tokens are exported for reading repository state from an\n`AppContextHost` (via `ctx.with(ref)` or `@Ctx(ref)`):\n\n| Export | Description |\n| --- | --- |\n| `RepoCtx` | Overlay carrying the entity key in scope: `{ entity: string }` |\n| `TrxCtx` | Overlay carrying the active `TransactionManager`: `{ trx }` |\n\n## Repository Registry\n\n`RepositoryRegistryService` validates at application bootstrap that no\nduplicate repository keys exist across `forFeature()` calls. If duplicates\nare found, it throws `RepositoryDuplicateKeyException` with details about\nwhich keys conflict.\n\n```ts\n// These two registrations would conflict at bootstrap:\nRepositoryModule.forFeature({\n  module: TypeOrmRepositoryModule,\n  entities: [{ key: 'users', entity: UserEntity }],\n});\n\nRepositoryModule.forFeature({\n  module: TypeOrmRepositoryModule,\n  entities: [{ key: 'users', entity: AdminEntity }], // duplicate key!\n});\n// Throws: Duplicate repository keys: \"users\" (registered for UserEntity, attempted for AdminEntity)\n```\n\n## Federation\n\nWhen a relation is marked `federated: true` (see\n[Relation Metadata](#relation-metadata)), the `FederationOrchestrator`\nintercepts `findAndCount` calls and executes **separate queries** for the\nroot entity and each relation instead of using SQL JOINs. Results are\nhydrated together transparently.\n\nThis is useful when:\n\n- JOINs produce expensive Cartesian products\n- Relations live in different datasources\n- Precise pagination control is needed (JOINs inflate row counts)\n\n### How It Works\n\nThe caller uses the same `join`, `Where.rel()`, and `OrderBy.rel()` APIs\ndescribed in [Relations and Joins](#relations-and-joins). The orchestrator\nanalyzes the query and picks a strategy:\n\n| Strategy | When | Flow |\n| --- | --- | --- |\n| **ROOT_FIRST** | No relation filters or sorts | Query root → fetch relations in parallel → hydrate |\n| **RELATION_FIRST** | Has relation filters or sorts | Query relations → discover root IDs → fetch constrained roots → hydrate |\n\nROOT_FIRST is the common case: one root query plus one query per relation,\nall relations fetched in parallel.\n\nRELATION_FIRST handles queries that filter or sort by relation fields. It\niteratively queries the driving relation to discover matching root entity\nIDs, then fetches only those roots.\n\n### distinctFilter\n\nFor many-cardinality federated relations that use sorts or filters, provide\na `distinctFilter` to ensure one relation entity per root. Without it,\nsorting is non-deterministic and a filtered total counts matching relation\nrows rather than distinct roots (e.g. two matching posts for one user would\nreport a total of 2 instead of 1). Missing it throws `FederationException`:\n\n```ts\nrelations: {\n  posts: {\n    federated: true,\n    distinctFilter: Where.eq('isPrimary', true),\n  },\n},\n```\n\n### Constants\n\n| Constant | Default | Description |\n| --- | --- | --- |\n| `FEDERATION_DEFAULT_LIMIT` | 10 | Default page size when none specified |\n| `FEDERATION_MAX_ITERATIONS` | 10 | Max iterations for relation-first constraint discovery |\n| `FEDERATION_MAX_BUFFER_SIZE` | 1000 | Max offset before aborting iterative discovery |\n\n### Limitations\n\n- OR conditions across federated relations are not supported (throws\n  `FederationException`)\n- Filtering or sorting by the owning side of a federated relation (the\n  side holding the foreign key, e.g. the many side of a `@ManyToOne`) is\n  not supported — only the non-owning side can drive relation-first\n  discovery (throws `FederationException`)\n- Only `findAndCount` is federated; `find`, `findOne`, and `count` use\n  standard ORM queries\n\n## Injecting Repositories\n\nUse `@InjectDynamicRepository()` to inject\nrepositories registered via `forFeature()`:\n\n```ts\nimport { InjectDynamicRepository } from '@concepta/nestjs-repository';\n\n@Injectable()\nexport class OrderService {\n  constructor(\n    @InjectDynamicRepository('orders')\n    private readonly orderRepo: RepositoryInterface<Order>,\n  ) {}\n\n  async findAll() {\n    return this.orderRepo.find();\n  }\n}\n```\n\nThe injection token is derived from the `key` provided in\n`RepositoryProviderOptions` via `getDynamicRepositoryToken(key)`, which is\nalso exported for manual provider wiring.\n\n## Exceptions\n\n| Exception | Description |\n| --- | --- |\n| `RepositoryQueryException` | Wraps any opaque error thrown by a repository operation or its hook pipeline. `RuntimeException` subclasses (e.g. `OptimisticLockException`) pass through unwrapped |\n| `OptimisticLockException` | An `update`/`replace` targeted a stale version — the row was modified by another request since it was read |\n| `RepositoryDuplicateKeyException` | Duplicate repository keys detected at bootstrap |\n| `TransactionTimeoutException` | Transaction exceeded timeout duration |\n| `TransactionClosedException` | A settled scope was used again — `getOrStart`, `enter`, `onCommit`, or `onRollback` after close |\n| `TransactionHeuristicCommitException` | A multi-datasource commit failed after at least one datasource had already committed |\n| `TransactionReadOnlyConflictException` | A joining `run()`/`runReadOnly()` call's `readOnly` option conflicts with the scope it's joining |\n| `TransactionScopeFailedException` | A participant's own operation succeeded, but its shared scope had already failed via a sibling |\n| `FederationException` | Unsupported federated query (e.g., OR across federated relations) |\n\n## Entry Points\n\n| Import Path | Contents |\n| --- | --- |\n| `@concepta/nestjs-repository` | Module, adapter, repository interfaces, Where/OrderBy/Join builders, transaction management, hooks, federation, decorators, exceptions |\n| `@concepta/nestjs-repository/testing` | `createMockTransaction`, `createMockRepository`, `MockTransactionHandle` |\n","readmeFilename":""}