{"_id":"mongoose-query-find","_rev":"27-203309d43ae3ae8dcef1d24db120d5bc","name":"mongoose-query-find","dist-tags":{"latest":"4.0.2"},"versions":{"1.1.0":{"name":"mongoose-query-find","version":"1.1.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.1.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"af13c8e986cef04ce4b225e4b9be845fa4bf6a9b","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.1.0.tgz","fileCount":6,"integrity":"sha512-EVAsviKZTBTTFnLfMW2E+2GhuIte5Ux4M8+nYOtld8fAX3/BC/TU5RrzkIo4Rwbv+oloS1ZRUOL0UKmiXuda9A==","signatures":[{"sig":"MEQCIGpWdPvTNu3mh/ThsZ1+e4lNR0FJEOp1AHBQaubOUogVAiBcnneOUiAmkqY66R9+EnOhsbE7zv0t80l7INVhcU8bEA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15080},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"67ce4c20a1323dd89dc571c37ad670c8c2d61b71","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.1.0_1775029339650_0.6711182932209254","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"mongoose-query-find","version":"1.2.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.2.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"64d836e4ec032bb653e07700a7b8dd9851361876","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.2.0.tgz","fileCount":6,"integrity":"sha512-pAywpirMqmdngJgUy1UAtCnIq5khqrJGk1jXsMGe+mN8V2I9L8TMbZD1ntLwnKamW8rZW6jT0C+Xra+KnXv3Rw==","signatures":[{"sig":"MEQCIGl2HP8ojb+2ZbgmMxlmbX6bToE0i3sz1wFCnPsy9dNZAiA5A2la0V4p55XbLpAwGzxjO46JnusKVSQLTK79xohWTg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15135},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"67ce4c20a1323dd89dc571c37ad670c8c2d61b71","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.2.0_1775319408539_0.7844984042764445","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"mongoose-query-find","version":"1.3.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.3.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"bf4d148e52ccbbe3fc3307795d6c27a1e91c3bcf","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.3.0.tgz","fileCount":6,"integrity":"sha512-WRdAFBzH1IYzcBoMafBfM518jVgT3E8an+Ni2jb3yS2slZ7Bbm0LRdlVNZwzL4jrPbAw+c8D+5vsM86yiFvIIw==","signatures":[{"sig":"MEYCIQC8tlS8ZDCssJHUlmKkJD/BKEFDNcmc7p2mACjxYqPfFwIhALCjXxfYW0FTJYYI4NolkgQJdPpKEmTm8lZ6NBk5RTwF","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15169},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"a16257ad2a5b72f6b2a210e0b1509a6bdbe2cd3d","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.3.0_1775320359883_0.9493111605817297","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"mongoose-query-find","version":"1.4.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.4.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"cc1713bf4e55738cd43efe1041c1a526b91d765c","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.4.0.tgz","fileCount":6,"integrity":"sha512-5VV4qBW7ZkTyT4LZuN2wsHup5zlgtoO7vTZfarYdKkpgG+U5XPn9NzKxx/nxQJz6KZUYoKdlnR6u4xPbdt19Ug==","signatures":[{"sig":"MEUCIQCvcx4mCNp6zaLCLKjcQbP1uZHDPGFWgsDTJcf+P+dSSgIgCchW/q9Zsf8HWQh08k9BPcqT9RcQPhBkF3Rmr5bJgag=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15548},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"a16257ad2a5b72f6b2a210e0b1509a6bdbe2cd3d","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.4.0_1776624116889_0.5414319497482485","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"mongoose-query-find","version":"1.5.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.5.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"15a9f323f295ff781564cdf2ca5dd01bf17ed054","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.5.0.tgz","fileCount":6,"integrity":"sha512-KEwVtpioUrEFQiINBTBT5jPBevXrvVMWQm6ZddnRDFcnmn4OEXcwWwGQvg/IVvmd3ihYs6Kwj7IsLAM6oig3Ig==","signatures":[{"sig":"MEYCIQCu1NcPO0BppBh89XRgXf7FO7Gt2fCMVqn07VaR8pjIDgIhANyTDbhlDdJe+NPjvM6uYJw+nRn8Nme0npMnmcxw6VI4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22287},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"f65b8b3c82a87beafe2bac263a42a133d4dfcf26","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.5.0_1776655430873_0.3227459226630889","host":"s3://npm-registry-packages-npm-production"}},"1.6.0":{"name":"mongoose-query-find","version":"1.6.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.6.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"c2a8f9a82a9cc5764077b9a3103640268602f742","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.6.0.tgz","fileCount":6,"integrity":"sha512-GTreRYx3sL7gLZTlLEOPjt3hX6788KHMuHZ+GaSVo5dut7IWTWdnUbGonht/O7oGpVezQxmSeeqBNsCgv0L51w==","signatures":[{"sig":"MEYCIQDphEKS3pBF1Nb1okAAtajKA2JC8vW3SUPZakrtxWvwXgIhAPr95crmSNBU0Pdp5jWfB/wNtYeoy5aLnsRIg4F2gQ05","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24420},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"bf8dfbca023e1a0db49c19c9780539c1a0712de4","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.6.0_1776656156431_0.9429886382739361","host":"s3://npm-registry-packages-npm-production"}},"1.7.0":{"name":"mongoose-query-find","version":"1.7.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.7.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"8902ee472225ce7a6d17afb43681c15729120d5f","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.7.0.tgz","fileCount":6,"integrity":"sha512-G6khEYvGlHmkeXc1B25oMuDQJ2BwOvE16i+89bngqXoZHsVMuwt0mpqEpmnv7mWlZWYTHK33fyGmJ4IKLhsqHA==","signatures":[{"sig":"MEUCIQC+FlJR2gyBZw/u8G8LkAxPn5BxiIknamjXMslMfffdtwIgYehTd8e2AXwJpZWo0ZHs8q7zMnZV8v9BrslOnu4FHw0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24411},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"b4b0d12ae1f2f4bf2aed0cc5c5df26f123d86db8","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.7.0_1776656821312_0.5038499164726018","host":"s3://npm-registry-packages-npm-production"}},"1.8.0":{"name":"mongoose-query-find","version":"1.8.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.8.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"b14e6b389c1a87020059e4622e3bfbaf1e384bf1","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.8.0.tgz","fileCount":6,"integrity":"sha512-6YtGZL3Rdoa7j/6U4KZBCIBVtLtl/anhK/kczZB3UVTfJOwstbmgR2jopx7XqR7AxJ9Hx9hMtMtn+ouQM6WT1A==","signatures":[{"sig":"MEYCIQDz5fxi3MVENLJvYV+dpSnJrKen/mtrVS2FXZw1yjdOWwIhAMgSUQI5jRSp9hlSal2a4nUp1ntYNOPZNcgXtnrWFV+9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27593},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"b4b0d12ae1f2f4bf2aed0cc5c5df26f123d86db8","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.8.0_1777286337367_0.19282665449342562","host":"s3://npm-registry-packages-npm-production"}},"1.9.0":{"name":"mongoose-query-find","version":"1.9.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@1.9.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"f51698bd239376fb68374a3990811f9bed1dedbf","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-1.9.0.tgz","fileCount":6,"integrity":"sha512-mXSBIN8S86PdLtEpcRdcdiAMDR0dQ/dyioDoBLfnldnvBE/wYFnKxwkldsUZ0K8XEuvutkrdTvZxgJSdmOuHmg==","signatures":[{"sig":"MEQCIH830E6OzGT5eiLIBHIuTDPhv4gpey1Qq/R9r8TlETXdAiB7l2q+CE9sRCgJ79fmhv0C0VBum6moK/wHsKNjVEihIA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32726},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"76e1c88237e794af9d79cf2d40ce4ae5ad40826f","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_1.9.0_1777478272620_0.12012216827926325","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"mongoose-query-find","version":"2.1.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@2.1.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"ec94b7f5724abfbd710952f28d9f5bc60217c5c9","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-2.1.0.tgz","fileCount":6,"integrity":"sha512-G0dLBtW6eTs+eANnGPpFzoXSKGBdu/kK+P5ehUuZ/sd3mb3q0IZHC794VwZ0tZSDO9ZMouYfqEBz5IXjPJOqMg==","signatures":[{"sig":"MEUCIHEL4NTZe+LS+tf/+NfvFGb+st8cl+MP1dbsLhmmwnTpAiEApD3loDivabBTS7SwnOUW6QqAN1bqGLAhxnL8PkXQ164=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33550},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"e0f0c079715e27fb8fae6ab9024b95a82650db11","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_2.1.0_1777487471200_0.838374766663543","host":"s3://npm-registry-packages-npm-production"}},"2.1.1":{"name":"mongoose-query-find","version":"2.1.1","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@2.1.1","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"ed526e4b848d444de03ac5755ff5380e27fb6262","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-2.1.1.tgz","fileCount":6,"integrity":"sha512-8VTFuoeJXje2LttWTJ5TW3g7B77dJ1mqSLDj3Z3mEDpTVU0sDaaPAuqAOk9947RAQO/uZaWZDPESBg80C08epA==","signatures":[{"sig":"MEYCIQC7fYxj5/Z/dpsMBEdOjBCyA5kd26rXYqcHx7Ott9VBRgIhAKdkVTbHXTiDnQ7z1LNP9fAAGJ5DjWQ8h9dy/+VRtCn5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":34780},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"f7c8ae9654490d876f27b9afa28320d8305e0826","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_2.1.1_1777660509992_0.3575032764328834","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"mongoose-query-find","version":"3.0.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@3.0.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"2d259423f7799106d018e3077e813a7782e567b0","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.0.tgz","fileCount":6,"integrity":"sha512-GUqoBMsFnxBran1lMNI0SGydkRcTxy2TQTTnCJCn8LED+6And6WQjSW1MNAUyUzHW/tKzHRe7A/+4UEB/o3AZw==","signatures":[{"sig":"MEUCIHN7hMR/wo/fKUShphajVB+0EZPx1Pv4mzn36ue3KmPDAiEA+/0skucC0O9IqKvibpmnmum26xgJ/jPRI1cVdHCM+Dw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57096},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"39939060083cf9a42425676fbcea1b8655246caf","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.0_1781242833104_0.5513596298002357","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"name":"mongoose-query-find","version":"3.0.1","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@3.0.1","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"7afc3c60895bc8a4897df8f50b466b6149e171c8","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.1.tgz","fileCount":6,"integrity":"sha512-siBBMVdU/zic5j9v43PXuRiQPvD3Yt/hoo4HZxvEwGN59DwGsb23t2NZdY/moe+Z7yY7SRSZnOb/bZ0aqwqFNw==","signatures":[{"sig":"MEYCIQCmdfWehpKO2Bw20efe0W7dOhZ9YG8wXaImJyC2GrsgCgIhAOvR8I/RliMB8F46ye9NQwBDmKWgO/PWaaplQFevdck4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60043},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"d5a1ae768798dff6d1813ffb0db23c0418991a6c","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.1_1781255710329_0.3940726727838981","host":"s3://npm-registry-packages-npm-production"}},"3.0.2":{"name":"mongoose-query-find","version":"3.0.2","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@3.0.2","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"996bef972ca282203f10e56053843e8253b6cfb0","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.2.tgz","fileCount":6,"integrity":"sha512-4gsQIppmnUUXicxciLHlhtrcM8q+WogrACTiGxamMksEO59Pinsj2g7Hq1y2iL0MYb+UbZTyBrAoiBjT3IFhXw==","signatures":[{"sig":"MEYCIQCbm3qhVznqJPSuM4X0IucaUNRZ5fPdBOGPY9lTWJptvwIhAMP/eluZNcPnFQ2PKq6v7Lzzv9PkSJEFueH8bcATK6WQ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62528},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"30171286fa1c4b93895abd976b8665de8a727ce7","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.2_1781257287720_0.31003879119094013","host":"s3://npm-registry-packages-npm-production"}},"3.0.3":{"name":"mongoose-query-find","version":"3.0.3","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@3.0.3","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"2d047339a41e3dced128a73bac62778fff2da646","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.3.tgz","fileCount":6,"integrity":"sha512-U/SI1u5HJ8/RipgxcsTvpnfeEEyxUT4wB0m9gs7PJYs02yWw5KpRYYnMOCTDJsViEctRrU/YKwkvzsy1rF6Mzg==","signatures":[{"sig":"MEYCIQDd+X14tZJIIEAinDYv4gtXCwd4GfSOfBqtdKXQZjO0rwIhAOKvMIM10qv7/xkWTEJwAvOvXMG0YAzrIld6162hxAsL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":63910},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"1b412e638dde3483e91003b1d6d39b58847be7c9","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.3_1781258123122_0.11036737016112208","host":"s3://npm-registry-packages-npm-production"}},"3.0.4":{"name":"mongoose-query-find","version":"3.0.4","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@3.0.4","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"ff12644d65c1cf8859ce66edd90dddfb08f9873c","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.4.tgz","fileCount":6,"integrity":"sha512-Xg0iGU4PYTQ8KfXHssH0hNBbejPWdkp62e3RlhnKnEH4yEYvP+9MR/s580Uwlo7b6Wl6yYIrPjdy2DSz9ZoQ6Q==","signatures":[{"sig":"MEUCIQD87RsbVJEJKW/J9jnMQZxmOLcAUhVBLm7LDmjBX+WmtwIgPQN1Zq4vvG2WdVW3kIbZlQOnviHvXNQmNr7iKK6IL0Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71366},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"2a75af27dd53f71c60fb03f1eaa0ab635d8ebb37","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.4_1781261808455_0.28271304959427335","host":"s3://npm-registry-packages-npm-production"}},"3.0.5":{"name":"mongoose-query-find","version":"3.0.5","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"ISC","_id":"mongoose-query-find@3.0.5","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"654f119719569cf3609bd197e91f912d274504f5","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.5.tgz","fileCount":6,"integrity":"sha512-pm9NzxiHK7V1Uiw0SNioHUfsMPD+BmF3hV4diSHCa7UYxhsOnLHkkeIHDTVUS6dQujOQxX/7RF3iq4SlOqEbZQ==","signatures":[{"sig":"MEYCIQCGCHvWvV6/z5b2fldYu+lLieLcbBspQhGwJwlsNZvBaQIhAKux2g2FCK4D+pHMQnOX3ZjVkbbn3tLvn3B1Xmu4Jy9X","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75054},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"1808c93cca77282b96ca0be26dc10cbb7fe124e9","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.8.2","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.5_1781506169738_0.364595758710045","host":"s3://npm-registry-packages-npm-production"}},"3.0.6":{"name":"mongoose-query-find","version":"3.0.6","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@3.0.6","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"a13f9a299e6fd0656cda6562cc8936963e294e08","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.6.tgz","fileCount":6,"integrity":"sha512-uhpFD6VL9G8Kh3ksCdV7N4Qx++qKgtgQ97FU5UXHEZNVy+lSbLwZ7x+xa6KBa9wrOxfNebqEnmq9de4XhkCQdQ==","signatures":[{"sig":"MEUCIH/asD2I8x686gPEn9CnRI0/h5cSVfcYTMCALKEBKcdLAiEArPGc6YCsF8IfSe/XcW1r0e69kox5+U2H8jwmg1xteZQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75627},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"7b2b02d47bb0ea912c844d4fd862f1655929bb4d","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.6_1788457368212_0.32454670045486433","host":"s3://npm-registry-packages-npm-production"}},"3.0.7":{"name":"mongoose-query-find","version":"3.0.7","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@3.0.7","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"b45bcd8395c26538345b56d7861532740b03269a","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.7.tgz","fileCount":14,"integrity":"sha512-9TrxRgLpU1/Dd+e8Qzs12NZQWLOOubLIXkeWUL0GxGxCb4fOGjwFfSUjibUC1XZVqL1Gtn2SkCam2xl6T4g1jg==","signatures":[{"sig":"MEUCIEUDAbZyAg+LMI6Z05oLrlkSdI18O3/5RYav1bWE4Zl4AiEA6CIoKXRsYGqqrtcHMQk/B1DM85VtoTgu3fZLG68pt5w=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":166536},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"c271b00fa376d0c2c78264a64e6b406ad47140f8","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.7_1789158854375_0.3745816299214235","host":"s3://npm-registry-packages-npm-production"}},"3.0.8":{"name":"mongoose-query-find","version":"3.0.8","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@3.0.8","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"1e9b586d07b1840bbe37e00d1f4fa48a52600613","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.8.tgz","fileCount":14,"integrity":"sha512-ppvVrUwWb84VMySeGgAxjGs2mWWtZ5TbQLngdE9dwlQyLDXZ376nbvYrFjj60V0doRlD6d1UStgzqxZ8G/fOqQ==","signatures":[{"sig":"MEQCIALZTDQSuw0vqu0tvum2eJv13h6lQ5zbRgu4C4n4qbzAAiAVVZm0ME1j69+FaBoU0YMhsRMGNJh59gHnqV2EdmaBug==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":168366},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"99ecc510b73169c0637f5be72a814f83d15becb1","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.8_1789159624243_0.11084402906705937","host":"s3://npm-registry-packages-npm-production"}},"3.0.9":{"name":"mongoose-query-find","version":"3.0.9","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@3.0.9","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"d7720ba3481e1996387d0c40912410dcc1b6a983","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.0.9.tgz","fileCount":14,"integrity":"sha512-JImV9dIqa1fxDTa3+INMQ5qge3Lv7qIb8pkYLmtI4XO03CQ8QazU+y5UMzeqP/A95TeARzjaKCH0TKFQltOdog==","signatures":[{"sig":"MEQCIE4B79r7HkaYmlDVtp3LAbIqn8xq0O/Ou+PFOAFudZLfAiBdRTRBWzRQyW70+sxgHZn9Vst/XKIdcvk851rCq9p9OA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":169103},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"826bf8fe4a3a1e7026ca7d980440db7421ec0524","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.0.9_1789463476848_0.07000980490428832","host":"s3://npm-registry-packages-npm-production"}},"3.1.0":{"name":"mongoose-query-find","version":"3.1.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@3.1.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"249a79667a6818e45ea137e0a70ccff89d472a40","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.1.0.tgz","fileCount":14,"integrity":"sha512-yjTYMFiVkXrZJ5PQApuqebui8KGIaBVeHrx11mFJESpCx5kkRzxsblak2ifwVttzFU2BAMRc0N8Sq6knfuq6sQ==","signatures":[{"sig":"MEQCICJoGGTnsIrzdT/RyBQiBbr0plnKEtuyeVslOhXrhgyDAiBk4Ys/Mz1g6ZN8gICf4UoPWZ+7hswlqT/kz3O0YE1BwQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":172722},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"1b9cdfe3be27985855b125028bf2f7966941cc2e","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.1.0_1789464855017_0.7899879490152399","host":"s3://npm-registry-packages-npm-production"}},"3.2.0":{"name":"mongoose-query-find","version":"3.2.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@3.2.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"614aca7a295ee36e9b696b67c89962eff68a4da6","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.2.0.tgz","fileCount":14,"integrity":"sha512-cIORytOLIYwF6T71YEEuH58rwkMTjbpwbEES/X+xuW4pNq9xl+vbt4SQ1JW8sBOXcQaS2KWc1p6zug80W0uYIw==","signatures":[{"sig":"MEUCIQDlw7PMoCdpZkrnndV56ykos/rDmcuwlOrGxg5JYHXHAgIgOEQxLnUSBlZ7Dv5ZlS7SW3ed0xxg9J4nFV1snX2K13Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIDBRBhsSPjU4mgj0PSjjPuIys2fbO48e2ooPsS2WN9m+AiA8CpzM277fS/d7AjWUi4luRp8uxUe21oWAdnPe06R9fQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":224981},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"1b9cdfe3be27985855b125028bf2f7966941cc2e","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.2.0_1790108000714_0.9597110773472648","host":"s3://npm-registry-packages-npm-production"}},"3.3.0":{"name":"mongoose-query-find","version":"3.3.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@3.3.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"bd4c60d274428a30a5319a187b9509985344f157","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-3.3.0.tgz","fileCount":14,"integrity":"sha512-O9Jb5o/uqEoEYfD0SGKsXKo8HF3v4RpTJOhE08hk/na0n2ohfXVgw+2aO8G0Lm+awCYV3DPUgwDG0OeCh7DHyg==","signatures":[{"sig":"MEUCIAYe75BWXNERgTxXS3mHmDhvNAq53Z0dDd5fT6vdwTL1AiEA9PRYmmKaCWiVCG0Y6XzTnOzFlb3j79Pas2ltvANsIrk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCM+GU+f7kd8qFpNlfhCg1wjNXkrLv3aWXxJPkZLwCOLwIgB+PU7OqLTRWsgnKF4yGZkX7+Pf+VyJY4Un7UtVoHW64=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":234666},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"47c2c128142607a8f10449bd399b3b340b9c0efd","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_3.3.0_1790108626075_0.49530063011121017","host":"s3://npm-registry-packages-npm-production"}},"4.0.0":{"name":"mongoose-query-find","version":"4.0.0","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@4.0.0","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"d349a1690609c584482e0e22b89a2b7abcb53226","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-4.0.0.tgz","fileCount":18,"integrity":"sha512-xVWTUlEnkUQtThaA/0TZp6Ps2l3XQgunL3mHXF92jmUffkMALUYnaf6xN/IgGXXOOBrlaII6xoaHaU4AwZrXxg==","signatures":[{"sig":"MEUCICtz1HtkTA0nFAhRWC5Ll6Uj/Izz+jW7vpqk1rXMtDd1AiEAym8ujvlwT0SbM700aZWu38wwknF2dGP5VSxbSIRfnoU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQCQR6udqJL1m+L4c/iw9GardEf6eqffPYxrrqznlCVrqAIhAPDOk8N32E8fs9XR1/4S1kzu6cZVXu+nkm/FRss34hz7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":325173},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"ec4a646456e3ce20b3027893aa2cba777de10806","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_4.0.0_1790136932781_0.4692633626182632","host":"s3://npm-registry-packages-npm-production"}},"4.0.1":{"name":"mongoose-query-find","version":"4.0.1","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","_id":"mongoose-query-find@4.0.1","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"2d2c46e4b6ec39dc69f2c6c0ff297ae813a64bd3","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-4.0.1.tgz","fileCount":18,"integrity":"sha512-k72KasBqNyDlmwTieDVC51j21yzvM9OP2BpMoFk/Q2MNvVs5LnCCr99JLEiyABp8qpOFS/I8+K64op8GT5AWiA==","signatures":[{"sig":"MEYCIQDyF376NZ2mGhf5cElW04DQI1ddJrFpMYyreZIt+sL8YAIhAJaWbmpUtZUePjSxgD9Wrf9rquphJpoxO6ZLALXu1HNz","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIFZ8wwVTByK3TxX4tryOlrOk6etAz9my4twqskKc+LvOAiEA4LK9vsk+KoOsqv6DBOWkTzE93G/oBNlHMxZ7G4Tk3GY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":293125},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"d99b512790d5791278b3eb33a1a4971b7cd618d1","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-query-find_4.0.1_1790137479594_0.5596867250818183","host":"s3://npm-registry-packages-npm-production"}},"4.0.2":{"_id":"mongoose-query-find@4.0.2","bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"dist":{"shasum":"ddee187f7262d089e0e4890cd6e2d1d1e57a44f5","tarball":"https://registry.npmjs.org/mongoose-query-find/-/mongoose-query-find-4.0.2.tgz","fileCount":18,"integrity":"sha512-CwpjNak/EJTO6OwgIRCeHTbG5S4SUgtRJVnP3HmMdYGwh4OUNfAfYxSH38ghbIJloGunjyqD695MlOjoTty5nw==","signatures":[{"sig":"MEUCIDtN7NxlzYONIgRCj/9mPCcONl9Ok+uvfjQJ8sxUUf3+AiEAwtx/GG7c9Baw0sAP/Zbkx7nj7CEe6dqwS+hFZV9U3yQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFwG8/QYaRhAAkx/YJg+gacAeyejAlKdDQ1KeQl1zNfMAiEAsz2GQPRm3qbgQ0aeI5sUTuLxV2jYgiACk5Bpo7a6kXs="}],"unpackedSize":355417},"main":"dist/index.js","name":"mongoose-query-find","types":"dist/index.d.ts","author":{"name":"jsdev.robin@gmail.com"},"engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"ba32e7cb882af8072ca34f76e39b36b7b677bbb1","license":"MIT","private":false,"scripts":{"build":"tsc","prepublishOnly":"npm run build"},"version":"4.0.2","_npmUser":{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"},"homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"_npmVersion":"10.9.8","description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","directories":{},"maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"_nodeVersion":"22.23.2","dependencies":{"date-fns":"^4.4.0","date-fns-tz":"^3.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.6.1","eslint":"^10.1.0","globals":"^17.4.0","@eslint/js":"^10.0.1","typescript":"5.2.2","@types/node":"^25.5.0","typescript-eslint":"^8.58.0"},"peerDependencies":{"mongoose":"^8 || ^9"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mongoose-query-find_4.0.2_1790137929245_0.4525610688408801"}}},"time":{"created":"2026-04-01T07:42:19.553Z","modified":"2026-09-23T04:32:09.607Z","1.1.0":"2026-04-01T07:42:19.784Z","1.2.0":"2026-04-04T16:16:48.698Z","1.3.0":"2026-04-04T16:32:40.030Z","1.4.0":"2026-04-19T18:41:57.026Z","1.5.0":"2026-04-20T03:23:51.009Z","1.6.0":"2026-04-20T03:35:56.577Z","1.7.0":"2026-04-20T03:47:01.446Z","1.8.0":"2026-04-27T10:38:57.527Z","1.9.0":"2026-04-29T15:57:52.775Z","2.1.0":"2026-04-29T18:31:11.383Z","2.1.1":"2026-05-01T18:35:10.137Z","3.0.0":"2026-06-12T05:40:33.223Z","3.0.1":"2026-06-12T09:15:10.537Z","3.0.2":"2026-06-12T09:41:27.903Z","3.0.3":"2026-06-12T09:55:23.349Z","3.0.4":"2026-06-12T10:56:48.607Z","3.0.5":"2026-06-15T06:49:29.873Z","3.0.6":"2026-09-03T17:42:48.336Z","3.0.7":"2026-09-11T20:34:14.510Z","3.0.8":"2026-09-11T20:47:04.383Z","3.0.9":"2026-09-15T09:11:16.987Z","3.1.0":"2026-09-15T09:34:15.205Z","3.2.0":"2026-09-22T20:13:20.819Z","3.3.0":"2026-09-22T20:23:46.174Z","4.0.0":"2026-09-23T04:15:32.868Z","4.0.1":"2026-09-23T04:24:40.197Z","4.0.2":"2026-09-23T04:32:09.357Z"},"bugs":{"url":"https://github.com/jsdev-robin/mongoose-query-find/issues"},"author":{"name":"jsdev.robin@gmail.com"},"license":"MIT","homepage":"https://github.com/jsdev-robin/mongoose-query-find#readme","keywords":["mongoose","typescript","query-builder","pagination","filtering","sorting","field-limiting","regex-search","global-search","api-query","mongodb","document-query","dynamic-query","query-helper","async-query","crud-utilities","aggregation","aggregation-pipeline","lookup","join","timezone","date-filtering"],"repository":{"url":"git+https://github.com/jsdev-robin/mongoose-query-find.git","type":"git"},"description":"TypeScript query and aggregation-pipeline builder for Mongoose. Provides QueryFind for allowlisted filtering, global search, sorting, field selection, population, and pagination on Model.find(), and QueryAggregate for the same safety model on Model.aggreg","maintainers":[{"name":"jsdev-robin","email":"jsdev.robin@gmail.com"}],"readme":"# mongoose-query-find\r\n\r\n[![npm version](https://img.shields.io/npm/v/mongoose-query-find)](https://www.npmjs.com/package/mongoose-query-find)\r\n[![license](https://img.shields.io/npm/l/mongoose-query-find)](https://github.com/jsdev-robin/mongoose-query-find/blob/main/LICENSE)\r\n[![mongoose peer](https://img.shields.io/badge/mongoose-%5E8%20%7C%7C%20%5E9-brightgreen)](https://mongoosejs.com)\r\n\r\nThree fluent, chainable, allowlisted query builders for Mongoose — **`QueryFind`** for simple `Model.find()` queries, **`QueryAggregate`** for anything that needs joins, grouping, timezone-aware date filtering, or advanced pagination, and **`QueryAnalytics`** for dashboards and reporting (time series, cohorts, funnels, sessionization, and more). All three are driven directly from URL query parameters with zero boilerplate.\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n**QueryFind**\r\n\r\n- [Installation](#installation)\r\n- [Quick Start](#quick-start)\r\n- [Constructor](#constructor)\r\n- [Builder Methods](#builder-methods)\r\n  - [.allowFields()](#allowfieldsfields-string)\r\n  - [.filter()](#filter)\r\n  - [.where()](#whereconditions)\r\n  - [.globalSearch()](#globalsearchfields-string)\r\n  - [.sort()](#sort)\r\n  - [.limitFields()](#limitfieldsdefaultfields-string)\r\n  - [.populate()](#populatepath-string--populateoptions-select-string)\r\n- [Terminal Method](#terminal-method)\r\n  - [.paginate()](#paginate)\r\n- [Query Parameter Reference](#query-parameter-reference)\r\n- [Full Example (Express)](#full-example-express)\r\n- [More Examples](#more-examples)\r\n- [TypeScript Types](#typescript-types)\r\n- [Security](#security)\r\n- [Links](#links)\r\n- [License](#license)\r\n\r\n**QueryAggregate**\r\n\r\n- [Overview](#queryaggregate-overview)\r\n- [When to use QueryAggregate vs QueryFind](#when-to-use-queryaggregate-vs-queryfind)\r\n- [Installation](#installation-1)\r\n- [Quick Start](#quick-start-1)\r\n- [Constructor & Options](#constructor--options-1)\r\n- [Timezone Handling](#timezone-handling)\r\n- [Builder Methods](#builder-methods-1)\r\n  - [.allowFields()](#allowfieldsfields-string-1)\r\n  - [.dateFields()](#datefieldsfields-string)\r\n  - [.objectIdFields()](#objectidfieldsfields-string)\r\n  - [.filter()](#filter-1)\r\n  - [.where()](#whereconditions-1)\r\n  - [.range()](#rangefield-string-preset-rangepreset)\r\n  - [Custom Range Methods](#custom-range-methods)\r\n    - [.rangeCustomDay()](#rangecustomdayfield-string-date-string)\r\n    - [.rangeBetween()](#rangebetweenfield-string-custom-customdaterangeinput)\r\n    - [.rangeLastNDays()](#rangelastndaysfield-string-days-number)\r\n    - [.rangeLastNHours()](#rangelastnhoursfield-string-hours-number)\r\n    - [.rangeHours()](#rangehoursfield-string-custom-customhourrangeinput)\r\n    - [.rangeCustom()](#rangecustomfield-string-custom-customdatetimerangeinput)\r\n  - [.excludeSoftDeleted()](#excludesoftdeletedfield-deletedat)\r\n  - [.whereIf()](#whereifcondition-conditions)\r\n  - [.globalSearch()](#globalsearchfields-string-1)\r\n  - [.sort()](#sort-1)\r\n  - [.project()](#projectdefaultprojection-string--recordstring-0--1)\r\n  - [.lookup()](#lookupopts-lookupoptions)\r\n  - [.computeField()](#computefieldname-string-expression-unknown)\r\n  - [.addStage()](#addstagestage-pipelinestage)\r\n- [Terminal & Utility Methods](#terminal--utility-methods)\r\n  - [.paginate()](#paginatecachedtotal-number)\r\n  - [.paginateCursor()](#paginatecursoropts-cursorpaginateoptions)\r\n  - [.count()](#count)\r\n  - [.distinct()](#distinctfield-string)\r\n  - [.stats()](#statsspecs-statspec)\r\n  - [.stream()](#stream)\r\n  - [.explain()](#explainverbosity)\r\n  - [.clone()](#clone)\r\n  - [.debugPipeline()](#debugpipeline)\r\n- [Recipes](#recipes)\r\n- [Pipeline Stage Ordering](#pipeline-stage-ordering)\r\n- [Security](#security-1)\r\n- [TypeScript Types](#typescript-types-1)\r\n- [Links](#links-1)\r\n- [License](#license-1)\r\n\r\n**QueryAnalytics**\r\n\r\n- [Overview](#queryanalytics-overview)\r\n- [When to use QueryAnalytics](#when-to-use-queryanalytics)\r\n- [Installation](#installation-2)\r\n- [Quick Start](#quick-start-2)\r\n- [Constructor & Options](#constructor--options-2)\r\n- [Base Filter Methods](#base-filter-methods)\r\n  - [.match()](#matchconditions)\r\n  - [.dateRange()](#daterangefield-string-range--from-string-to-string-)\r\n  - [.addStage()](#addstagestage-pipelinestage-1)\r\n  - [.clone()](#clone-1)\r\n- [Time Series](#time-series)\r\n  - [.timeSeries()](#timeseriesopts)\r\n  - [.cumulativeSeries()](#cumulativeseriesopts)\r\n- [Comparisons & Breakdowns](#comparisons--breakdowns)\r\n  - [.compare()](#compareopts)\r\n  - [.breakdown()](#breakdownfield-metrics-opts)\r\n  - [.rankedBreakdown()](#rankedbreakdownfield-metrics-opts)\r\n  - [.multiBreakdown()](#multibreakdownfields-metrics-opts)\r\n- [Funnels & Cohorts](#funnels--cohorts)\r\n  - [.funnel()](#funnelsteps)\r\n  - [.cohortRetention()](#cohortretentionopts)\r\n  - [.sessionize()](#sessionizeopts)\r\n- [Statistics](#statistics)\r\n  - [.percentiles()](#percentilesfield-ps)\r\n  - [.stdDevOutliers()](#stddevoutliersfield-opts)\r\n  - [.metrics()](#metricsspecs)\r\n- [Post-Processing Helpers (static)](#post-processing-helpers-static)\r\n  - [QueryAnalytics.movingAverage()](#queryanalyticsmovingaverage)\r\n  - [QueryAnalytics.trend()](#queryanalyticstrend)\r\n  - [QueryAnalytics.forecast()](#queryanalyticsforecast)\r\n- [.heatmap()](#heatmapdatefield-valuefield)\r\n- [Live Updates](#live-updates)\r\n  - [.watch()](#watchonchange-opts)\r\n- [Export & Batch Helpers (static)](#export--batch-helpers-static)\r\n  - [QueryAnalytics.toCSV() / toNDJSON()](#queryanalyticstocsv--tondjson)\r\n  - [QueryAnalytics.dashboard()](#queryanalyticsdashboard)\r\n  - [QueryAnalytics.clearCache()](#queryanalyticsclearcache)\r\n- [Recipes](#recipes-1)\r\n- [Security](#security-2)\r\n- [TypeScript Types](#typescript-types-2)\r\n- [Links](#links-2)\r\n- [License](#license-2)\r\n\r\n---\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install mongoose-query-find\r\n```\r\n\r\n```bash\r\nyarn add mongoose-query-find\r\n```\r\n\r\n```bash\r\npnpm add mongoose-query-find\r\n```\r\n\r\n> **Peer dependency:** requires `mongoose ^8` or `mongoose ^9` installed in your project.\r\n\r\n---\r\n\r\n## Quick Start\r\n\r\n```ts\r\nimport { queryFind } from 'mongoose-query-find';\r\nimport UserModel from './models/user';\r\n\r\nconst result = await queryFind(UserModel.find(), req.query)\r\n  .allowFields(['name', 'email', 'role', 'createdAt'])\r\n  .where({ deletedAt: null })\r\n  .filter()\r\n  .globalSearch(['name', 'email'])\r\n  .sort()\r\n  .limitFields('-password -__v')\r\n  .paginate();\r\n```\r\n\r\n`result` will look like:\r\n\r\n```json\r\n{\r\n  \"data\": [...],\r\n  \"total\": 84,\r\n  \"page\": 2,\r\n  \"totalPages\": 9,\r\n  \"limit\": 10,\r\n  \"hasNextPage\": true,\r\n  \"hasPrevPage\": true\r\n}\r\n```\r\n\r\n---\r\n\r\n## Constructor\r\n\r\n```ts\r\nnew QueryFind(query, queryString, options?);\r\n// or use the factory function (recommended):\r\nqueryFind(query, queryString, options?);\r\n```\r\n\r\n| Parameter     | Type                                | Description                                           |\r\n| ------------- | ----------------------------------- | ----------------------------------------------------- |\r\n| `query`       | `Query<TRawDocType[], TRawDocType>` | A Mongoose query, e.g. `Model.find()`                 |\r\n| `queryString` | `QueryParams`                       | The parsed URL query object, e.g. `req.query`         |\r\n| `options`     | `QueryFindOptions` _(optional)_     | Configuration options (see [Options](#options) below) |\r\n\r\n### Options\r\n\r\n| Option                 | Type                                     | Default     | Description                                                            |\r\n| ---------------------- | ---------------------------------------- | ----------- | ---------------------------------------------------------------------- |\r\n| `maxTimeMS`            | `number`                                 | `5000`      | Max milliseconds MongoDB may spend on each query. Pass `0` to disable. |\r\n| `slowQueryThresholdMS` | `number`                                 | `maxTimeMS` | Threshold above which `onSlowQuery` fires.                             |\r\n| `maxLimit`             | `number`                                 | `100`       | Hard cap on `?limit=`.                                                 |\r\n| `lean`                 | `boolean`                                | `true`      | Whether `.find()` calls `.lean()` for faster plain-object reads.       |\r\n| `onSlowQuery`          | `(info: SlowQueryInfo) => void`          | —           | Callback fired when a query exceeds `slowQueryThresholdMS`.            |\r\n| `onSanitizeDrop`       | `(path: string, value: unknown) => void` | —           | Callback fired whenever sanitization drops a client-supplied value.    |\r\n\r\n---\r\n\r\n## Builder Methods\r\n\r\nAll builder methods return `this` and are fully chainable. The recommended call order is:\r\n\r\n```\r\nallowFields → where → filter → globalSearch → sort → limitFields → populate → paginate\r\n```\r\n\r\n---\r\n\r\n### `.allowFields(fields: string[])`\r\n\r\nDeclares which fields may appear in URL filters, sort parameters, and field projections. Acts as an allowlist — any field not listed is silently stripped from client input before it reaches MongoDB.\r\n\r\n**Call this before `.filter()`, `.sort()`, and `.limitFields()`.**\r\n\r\n```ts\r\n.allowFields(['name', 'email', 'role', 'createdAt'])\r\n```\r\n\r\n---\r\n\r\n### `.filter()`\r\n\r\nParses the URL query string into a Mongoose filter. Automatically:\r\n\r\n- Strips reserved keys (`page`, `limit`, `sort`, `fields`, `q`)\r\n- Converts comparison operator names to MongoDB `$` syntax (`gt` → `$gt`, `lte` → `$lte`, etc.)\r\n- Recursively coerces string booleans to real booleans (`\"true\"` → `true`, `\"false\"` → `false`)\r\n- Coerces date-like strings to `Date` instances for fields named `createdAt`, `updatedAt`, `deletedAt`, `date`, `birthDate`, or `expiresAt`\r\n- Recursively validates and rejects banned operators (`$where`, `$expr`, `$function`, etc.)\r\n\r\n**Supported operators:** `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`\r\n\r\n```\r\nGET /users?age[gte]=18&isActive=true&role=admin\r\n```\r\n\r\n```ts\r\n.filter()\r\n// → { age: { $gte: 18 }, isActive: true, role: 'admin' }\r\n```\r\n\r\n---\r\n\r\n### `.where(conditions)`\r\n\r\nApplies mandatory server-side conditions that the URL **cannot override**. Use this for multi-tenancy, soft-delete exclusion, and any security-critical constraints.\r\n\r\n```ts\r\n.where({ orgId: req.user.orgId, deletedAt: null })\r\n```\r\n\r\n> `.where()` always wins — conditions are merged after `.filter()` and will overwrite any conflicting URL params.\r\n\r\n---\r\n\r\n### `.globalSearch(fields: string[])`\r\n\r\nAdds a case-insensitive `$or` regex search across the specified fields when the `?q=` query parameter is present. If `q` is absent or exceeds 200 characters, this method is a no-op.\r\n\r\nSearch terms are regex-escaped to prevent ReDoS attacks.\r\n\r\n```\r\nGET /users?q=john\r\n```\r\n\r\n```ts\r\n.globalSearch(['name', 'email'])\r\n// → { $or: [{ name: /john/i }, { email: /john/i }] }\r\n```\r\n\r\nIf a `$or` clause already exists in the filter (e.g. from `.filter()`), both are safely merged under `$and`.\r\n\r\n---\r\n\r\n### `.sort()`\r\n\r\nApplies sort order from the `?sort=` query parameter. Prefix a field with `-` for descending order. Multiple fields are comma-separated, capped at 5 fields.\r\n\r\n```\r\nGET /users?sort=-createdAt,name\r\n```\r\n\r\n```ts\r\n.sort()\r\n// → sorts by createdAt DESC, then name ASC\r\n```\r\n\r\nDefaults to `{ createdAt: -1 }` when the `sort` param is absent. Fields not in the allowlist are silently skipped.\r\n\r\n---\r\n\r\n### `.limitFields(defaultFields?: string)`\r\n\r\nControls which fields are returned (projection). Uses the `?fields=` query param when present, otherwise falls back to `defaultFields`.\r\n\r\nFields requested via `?fields=` are filtered against the allowlist — clients cannot project sensitive fields like `password` or `resetToken`.\r\n\r\n**Priority order:**\r\n\r\n1. `?fields=` query param — always wins when present (allowlist-filtered)\r\n2. `defaultFields` argument — used as fallback when no query param\r\n3. No projection when both are absent (all fields returned)\r\n\r\n```\r\nGET /users?fields=name,email,role\r\n```\r\n\r\n```ts\r\n.limitFields('-password -__v')\r\n// With ?fields=name,email,role  → selects only name, email, role\r\n// Without ?fields               → excludes password and __v\r\n```\r\n\r\n---\r\n\r\n### `.populate(path: string | PopulateOptions, select?: string)`\r\n\r\nRegisters a populate directive. Chainable — each call appends to the internal list. All registered populates are applied inside `paginate()`.\r\n\r\nAccepts the same arguments as Mongoose's own `.populate()`:\r\n\r\n```ts\r\n// Plain path string\r\n.populate('author')\r\n\r\n// Path + select string\r\n.populate('author', 'name email')\r\n\r\n// Full PopulateOptions object\r\n.populate({ path: 'comments', select: 'text createdAt', match: { visible: true } })\r\n\r\n// Multiple calls — fully chainable\r\n.populate('author')\r\n.populate({ path: 'comments', select: 'text createdAt' })\r\n```\r\n\r\n---\r\n\r\n## Terminal Method\r\n\r\n### `.paginate()`\r\n\r\nExecutes the query and returns a `Promise<PaginatedResult<T>>`.\r\n\r\n- `countDocuments` and `find` run **in parallel** via `Promise.all` (saves one network round-trip).\r\n- Uses `estimatedDocumentCount` as a fast-path when no filter is applied (O(1) vs O(n)).\r\n- `.lean()` is applied for ~3–5× faster reads on plain object responses.\r\n- `maxTimeMS` is applied to both count and find to prevent runaway collection scans.\r\n- If the requested `page` exceeds `totalPages` (e.g. after a deletion), page `1` is returned automatically.\r\n\r\n```\r\nGET /users?page=2&limit=20\r\n```\r\n\r\n**Returns:**\r\n\r\n```ts\r\n{\r\n  data: T[];\r\n  total: number;        // Total matching documents across all pages\r\n  page: number;         // Current page (auto-corrects to 1 if out of range)\r\n  totalPages: number;\r\n  limit: number;\r\n  hasNextPage: boolean;\r\n  hasPrevPage: boolean;\r\n}\r\n```\r\n\r\n**Defaults:** `page=1`, `limit=10`, max `limit=100`.\r\n\r\n---\r\n\r\n## Query Parameter Reference\r\n\r\n| Parameter   | Example                   | Description                                            |\r\n| ----------- | ------------------------- | ------------------------------------------------------ |\r\n| `page`      | `?page=3`                 | Page number (default: `1`, min: `1`)                   |\r\n| `limit`     | `?limit=25`               | Documents per page (default: `10`, max: `100`)         |\r\n| `sort`      | `?sort=-createdAt,name`   | Sort fields; prefix `-` for descending (max: 5 fields) |\r\n| `fields`    | `?fields=name,email`      | Comma-separated fields to include in the response      |\r\n| `q`         | `?q=john`                 | Global search term (max: 200 chars)                    |\r\n| _(any key)_ | `?role=admin&age[gte]=18` | Field-level filters processed by `.filter()`           |\r\n\r\n---\r\n\r\n## Full Example (Express)\r\n\r\n```ts\r\nimport { Request, Response } from 'express';\r\nimport { queryFind } from 'mongoose-query-find';\r\nimport UserModel from '../models/user';\r\n\r\nexport const getUsers = async (req: Request, res: Response) => {\r\n  const result = await queryFind(UserModel.find(), req.query)\r\n    .allowFields(['name', 'email', 'username', 'role', 'isActive', 'createdAt'])\r\n    .where({ orgId: req.user.orgId, deletedAt: null })\r\n    .filter()\r\n    .globalSearch(['name', 'email', 'username'])\r\n    .sort()\r\n    .limitFields('-password -__v')\r\n    .populate('role', 'name permissions')\r\n    .paginate();\r\n\r\n  res.json({ status: 'success', ...result });\r\n};\r\n```\r\n\r\n**Example requests:**\r\n\r\n```bash\r\n# Page 2, 15 per page, only active admins sorted by name\r\nGET /users?page=2&limit=15&role=admin&isActive=true&sort=name\r\n\r\n# Search \"alice\" across name, email, and username\r\nGET /users?q=alice\r\n\r\n# Users older than 25, return only name and email\r\nGET /users?age[gt]=25&fields=name,email\r\n\r\n# Filter by exact date\r\nGET /users?createdAt=2024-06-01\r\n\r\n# Filter by date range\r\nGET /users?createdAt[gte]=2024-01-01&createdAt[lte]=2024-12-31\r\n\r\n# Combined: search + filter + sort + pagination\r\nGET /users?q=john&role=editor&sort=-createdAt&page=1&limit=5\r\n```\r\n\r\n---\r\n\r\n## More Examples\r\n\r\n**E-commerce product listing — nested price filters + category populate:**\r\n\r\n```ts\r\nexport const getProducts = async (req: Request, res: Response) => {\r\n  const result = await queryFind(ProductModel.find(), req.query)\r\n    .allowFields(['name', 'price', 'category', 'inStock', 'brand', 'createdAt'])\r\n    .where({ deletedAt: null, published: true })\r\n    .filter()\r\n    .globalSearch(['name', 'brand'])\r\n    .sort()\r\n    .limitFields('name price inStock brand')\r\n    .populate('category', 'name slug')\r\n    .paginate();\r\n\r\n  res.json(result);\r\n};\r\n```\r\n\r\n```bash\r\n# Price between $20 and $100, in stock, sorted cheapest first\r\nGET /products?price[gte]=20&price[lte]=100&inStock=true&sort=price\r\n\r\n# Search \"nike\" across name and brand\r\nGET /products?q=nike&sort=-createdAt\r\n```\r\n\r\n**Blog posts — multiple populates + logical operators:**\r\n\r\n```ts\r\nexport const getPosts = async (req: Request, res: Response) => {\r\n  const result = await queryFind(PostModel.find(), req.query)\r\n    .allowFields(['title', 'status', 'authorId', 'tags', 'publishedAt'])\r\n    .where({ deletedAt: null })\r\n    .filter()\r\n    .globalSearch(['title'])\r\n    .sort()\r\n    .limitFields('title status publishedAt')\r\n    .populate('authorId', 'name avatar')\r\n    .populate({\r\n      path: 'comments',\r\n      select: 'text createdAt',\r\n      match: { visible: true },\r\n    })\r\n    .paginate();\r\n\r\n  res.json(result);\r\n};\r\n```\r\n\r\n```bash\r\n# Published OR featured posts, using the safe top-level $or\r\nGET /posts?$or[0][status]=published&$or[1][featured]=true\r\n\r\n# Posts tagged \"typescript\" published this year\r\nGET /posts?tags=typescript&publishedAt[gte]=2026-01-01\r\n```\r\n\r\n**Admin panel — strict field allowlist, sensitive fields never leak:**\r\n\r\n```ts\r\nexport const adminListUsers = async (req: Request, res: Response) => {\r\n  const result = await queryFind(UserModel.find(), req.query, { maxLimit: 250 })\r\n    .allowFields(['name', 'email', 'role', 'isActive', 'lastLoginAt'])\r\n    .where({ orgId: req.user.orgId })\r\n    .filter()\r\n    .sort()\r\n    .limitFields('-password -passwordResetToken -__v') // client's ?fields= can never override this\r\n    .paginate();\r\n\r\n  res.json(result);\r\n};\r\n```\r\n\r\n```bash\r\n# Even if a client tries ?fields=password, it's stripped — not in allowFields()\r\nGET /admin/users?fields=password,email&role=admin\r\n```\r\n\r\n**Cached count pattern — skip re-counting on subsequent pages:**\r\n\r\n```ts\r\nexport const getNotifications = async (req: Request, res: Response) => {\r\n  const builder = queryFind(NotificationModel.find(), req.query)\r\n    .allowFields(['userId', 'read', 'createdAt'])\r\n    .where({ userId: req.user.id })\r\n    .filter()\r\n    .sort();\r\n\r\n  // First request from the client includes no cachedTotal\r\n  const cachedTotal = req.query.cachedTotal\r\n    ? Number(req.query.cachedTotal)\r\n    : undefined;\r\n  const result = await builder.paginate(cachedTotal);\r\n\r\n  res.json(result); // client stores result.total and sends it back as ?cachedTotal= on the next page\r\n};\r\n```\r\n\r\n---\r\n\r\n## TypeScript Types\r\n\r\nAll types are exported:\r\n\r\n```ts\r\nimport {\r\n  queryFind,\r\n  QueryFind,\r\n  QueryParams,\r\n  PaginatedResult,\r\n  QueryFindOptions,\r\n  QueryFindError,\r\n  QueryFindValidationError,\r\n  SlowQueryInfo,\r\n} from 'mongoose-query-find';\r\n```\r\n\r\n```ts\r\ninterface QueryParams {\r\n  page?: string;\r\n  limit?: string;\r\n  sort?: string;\r\n  fields?: string;\r\n  q?: string;\r\n  [key: string]: unknown;\r\n}\r\n\r\ninterface PaginatedResult<T> {\r\n  data: T[];\r\n  total: number;\r\n  page: number;\r\n  totalPages: number;\r\n  limit: number;\r\n  hasNextPage: boolean;\r\n  hasPrevPage: boolean;\r\n}\r\n\r\ninterface QueryFindOptions {\r\n  maxTimeMS?: number; // default: 5000\r\n  slowQueryThresholdMS?: number; // default: maxTimeMS\r\n  maxLimit?: number; // default: 100\r\n  lean?: boolean; // default: true\r\n  onSlowQuery?: (info: SlowQueryInfo) => void;\r\n  onSanitizeDrop?: (path: string, value: unknown) => void;\r\n}\r\n\r\ninterface SlowQueryInfo {\r\n  elapsedMs: number;\r\n  filter: Record<string, unknown>;\r\n  sort: Record<string, 1 | -1>;\r\n  page: number;\r\n  limit: number;\r\n}\r\n```\r\n\r\n---\r\n\r\n## Security\r\n\r\n| Protection                  | Detail                                                                                   |\r\n| --------------------------- | ---------------------------------------------------------------------------------------- |\r\n| **NoSQL injection**         | Allowlist enforced on filters, sort, and projection via `.allowFields()`                 |\r\n| **Banned operators**        | `$where`, `$expr`, `$function`, and others are rejected recursively at any nesting depth |\r\n| **ReDoS**                   | Search terms are regex-escaped before compilation                                        |\r\n| **Oversized search**        | `?q=` capped at 200 characters                                                           |\r\n| **Deep nesting DoS**        | Filter object nesting capped at depth 5                                                  |\r\n| **Page size DoS**           | `limit` hard-capped at 100                                                               |\r\n| **Sort abuse**              | Sort fields hard-capped at 5                                                             |\r\n| **Runaway queries**         | `maxTimeMS` applied to both count and find (default: 5s)                                 |\r\n| **Sensitive field leakage** | `?fields=` projection stripped against allowlist                                         |\r\n\r\n---\r\n\r\n## Links\r\n\r\n- [npm](https://www.npmjs.com/package/mongoose-query-find)\r\n- [GitHub](https://github.com/jsdev-robin/mongoose-query-find)\r\n- [Issues](https://github.com/jsdev-robin/mongoose-query-find/issues)\r\n\r\n---\r\n\r\n## License\r\n\r\nISC © [jsdev.robin@gmail.com](mailto:jsdev.robin@gmail.com)\r\n\r\n<br>\r\n<br>\r\n\r\n---\r\n\r\n---\r\n\r\n# QueryAggregate\r\n\r\nA fluent, allowlisted **aggregation-pipeline** builder for Mongoose (`^8 || ^9`), built on `Model.aggregate()`. It mirrors the same safety model as `QueryFind` — allowlisted filter fields, sanitized/rejected operators, capped pagination — but adds `$lookup` joins, `$group`, timezone-aware date filtering, custom date/hour ranges, keyset pagination, and one-round-trip dashboard stats.\r\n\r\n## QueryAggregate Overview\r\n\r\n`QueryFind` is built on `Model.find()` and can't express joins or grouping. `QueryAggregate` solves that while keeping the same guardrails, plus a lot more:\r\n\r\n- Allowlisted filter/sort/projection fields\r\n- Recursive sanitization of client input (drops class instances, functions, over-deep nesting)\r\n- Banned-operator rejection (`$where`, `$expr`, `$function`, `$accumulator`, `$map`, `$reduce`, `$filter`)\r\n- Capped pagination (`limit`, `page`)\r\n- Timezone-aware date coercion, `$lookup` joins, arbitrary developer-authored pipeline stages via `.addStage()`\r\n- Single-round-trip pagination via `$facet`\r\n- **New:** six custom date-range methods (arbitrary day, arbitrary span, rolling N-days/N-hours, hour-of-day windows, absolute datetime windows)\r\n- **New:** `count()`, `distinct()`, and `stats()` for cheap metrics without fetching a data page\r\n- **New:** `stream()` for memory-safe large exports, and `paginateCursor()` for keyset pagination on huge collections\r\n- **New:** `explain()`, `debugPipeline()`, and `clone()` for debugging and query reuse\r\n\r\n## When to use QueryAggregate vs QueryFind\r\n\r\n| Need                                                           | Use              |\r\n| -------------------------------------------------------------- | ---------------- |\r\n| Simple filter/sort/paginate on one collection                  | `QueryFind`      |\r\n| Joining another collection (`$lookup`)                         | `QueryAggregate` |\r\n| Grouping / rollups / computed fields (`$group`, `$addFields`)  | `QueryAggregate` |\r\n| Client filters need to respect the caller's local timezone     | `QueryAggregate` |\r\n| A custom day / date span / rolling window / hour-of-day filter | `QueryAggregate` |\r\n| Dashboard totals (sum/avg/count) without fetching a page       | `QueryAggregate` |\r\n| Very large collections where `$skip` pagination gets slow      | `QueryAggregate` |\r\n| Streaming a large export without buffering it in memory        | `QueryAggregate` |\r\n| You need a custom developer-defined pipeline stage             | `QueryAggregate` |\r\n\r\n## Installation\r\n\r\nShips from the same package as `QueryFind`:\r\n\r\n```bash\r\nnpm install mongoose-query-find\r\n```\r\n\r\n## Quick Start\r\n\r\n```ts\r\nimport { QueryAggregate } from 'mongoose-query-find';\r\nimport CategoryModel from './models/category';\r\n\r\nconst timezone = (req.headers['x-timezone'] as string) ?? 'Asia/Dhaka';\r\n\r\nconst result = await new QueryAggregate(CategoryModel, req.query, { timezone })\r\n  .allowFields(['name', 'status', 'createdAt'])\r\n  .dateFields(['createdAt'])\r\n  .where({ branchId: req.self.linkedTo, status: { $ne: 'ARCHIVED' } })\r\n  .filter()\r\n  .globalSearch(['name'])\r\n  .sort()\r\n  .project('name status createdAt updatedAt')\r\n  .paginate();\r\n```\r\n\r\n`result` shape is identical to `QueryFind`'s `PaginatedResult<T>`:\r\n\r\n```json\r\n{\r\n  \"data\": [...],\r\n  \"total\": 84,\r\n  \"page\": 2,\r\n  \"totalPages\": 9,\r\n  \"limit\": 10,\r\n  \"hasNextPage\": true,\r\n  \"hasPrevPage\": true\r\n}\r\n```\r\n\r\n## Constructor & Options\r\n\r\n```ts\r\nnew QueryAggregate<T>(model, queryString, options?);\r\n```\r\n\r\n| Parameter     | Type                    | Description                                     |\r\n| ------------- | ----------------------- | ----------------------------------------------- |\r\n| `model`       | `Model<T>`              | A Mongoose model                                |\r\n| `queryString` | `QueryParams`           | Parsed URL query object, e.g. `req.query`       |\r\n| `options`     | `QueryAggregateOptions` | Configuration options (see below), all optional |\r\n\r\n### `QueryAggregateOptions`\r\n\r\n| Option                 | Type                                     | Default        | Description                                                                                                                                                   |\r\n| ---------------------- | ---------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| `timezone`             | `string`                                 | `'Asia/Dhaka'` | IANA timezone for interpreting client-supplied local dates. Invalid values fall back to the default (validated once at construction, never throws mid-query). |\r\n| `maxTimeMS`            | `number`                                 | `5000`         | Max time MongoDB may spend per query. `0` disables.                                                                                                           |\r\n| `slowQueryThresholdMS` | `number`                                 | `maxTimeMS`    | Threshold above which `onSlowQuery` fires.                                                                                                                    |\r\n| `maxLimit`             | `number`                                 | `100`          | Hard cap on `?limit=`.                                                                                                                                        |\r\n| `allowDiskUse`         | `boolean`                                | `false`        | Forwarded to `.allowDiskUse()` — enable for large sort/group pipelines.                                                                                       |\r\n| `weekStartsOn`         | `0–6`                                    | `6` (Saturday) | Used by `.range('thisWeek')`.                                                                                                                                 |\r\n| `onSlowQuery`          | `(info: SlowQueryInfo) => void`          | —              | Callback fired when a query exceeds `slowQueryThresholdMS`.                                                                                                   |\r\n| `onSanitizeDrop`       | `(path: string, value: unknown) => void` | —              | Callback fired whenever sanitization drops a value (bad nesting, non-plain value).                                                                            |\r\n\r\n## Timezone Handling\r\n\r\nMongoDB always stores/compares dates in UTC. Clients think and filter in local time — `?createdAt=2026-09-01` means \"Sept 1st in the caller's timezone,\" not UTC midnight.\r\n\r\n- `fromZonedTime(localString, tz)` → exact UTC instant for a wall-clock time in `tz`. Used to turn a client-supplied local date into the correct `$gte`/`$lt` UTC range in `$match`.\r\n- `toZonedTime(utcDate, tz)` → wall-clock `Date` for `tz`. Used to compute \"what day is it right now, in the caller's timezone\" for `.range()` presets and the rolling-N-days custom range.\r\n\r\nThe timezone is a **per-request** value, typically sourced from a header:\r\n\r\n```ts\r\nconst timezone = (req.headers['x-timezone'] as string) ?? 'Asia/Dhaka';\r\n```\r\n\r\nAn invalid/unknown IANA timezone string (bad header, typo) never throws — it's validated once at construction (`resolveTimezone`) and silently falls back to the default.\r\n\r\nPlain `YYYY-MM-DD` filter values are expanded into a `$gte`/`$lt` range spanning that local day; values with an explicit offset (`Z`, `+06:00`) are treated as absolute instants and passed through as-is. Every custom range method below applies the same timezone-aware logic.\r\n\r\n## Builder Methods\r\n\r\nAll builder methods return `this` and are fully chainable. Recommended call order:\r\n\r\n```\r\nallowFields → dateFields/objectIdFields → where → range/rangeXxx → filter → globalSearch → sort → project → lookup/addStage/computeField → paginate/paginateCursor/count/distinct/stats/stream\r\n```\r\n\r\n---\r\n\r\n### `.allowFields(fields: string[])`\r\n\r\nSame role as in `QueryFind`: declares which fields the client may filter, sort, and project. Case-insensitive. Call before `.filter()`, `.sort()`, `.project()`.\r\n\r\n```ts\r\n.allowFields(['name', 'status', 'createdAt'])\r\n```\r\n\r\n---\r\n\r\n### `.dateFields(fields: string[])`\r\n\r\nExplicit list of fields treated as dates for timezone-aware coercion. When omitted, falls back to a name heuristic matching `createdAt`, `updatedAt`, `deletedAt`, `archivedAt`, `expiresAt`, `date`, `birthDate` (only when that word is the _last_ path segment, e.g. `user.updatedAt` matches, `updatedAtSomething` doesn't).\r\n\r\n```ts\r\n.dateFields(['createdAt', 'updatedAt'])\r\n```\r\n\r\n---\r\n\r\n### `.objectIdFields(fields: string[])`\r\n\r\nExplicit list of fields treated as ObjectId references, so a client-supplied 24-char hex string (`?categoryId=...`) is coerced to `Types.ObjectId` instead of being compared as a plain string (which would never match, since it's a different BSON type). When omitted, falls back to a name heuristic matching `_id` or any field ending in `Id` (e.g. `categoryId`, `basicInfo.brandId`).\r\n\r\n```ts\r\n.objectIdFields(['branchId', 'categoryId'])\r\n```\r\n\r\n---\r\n\r\n### `.filter()`\r\n\r\nParses `req.query` into the aggregation `$match` stage's client-derived portion. Runs, in order:\r\n\r\n1. Strips reserved keys (`page`, `sort`, `limit`, `fields`, `q`)\r\n2. `sanitize()` — recursively drops non-plain values (class instances, functions), capped at nesting depth 5\r\n3. `coerceAll()` — timezone-aware date coercion + ObjectId coercion + boolean coercion + numeric coercion + operator-name coercion (`gt` → `$gt`, etc.)\r\n4. `enforceAllowlist()` — strips any non-operator key not in `.allowFields()`\r\n5. `rejectDisallowedOperators()` — throws `QueryAggregateValidationError` on `$where`, `$expr`, `$function`, `$accumulator`, `$map`, `$reduce`, `$filter`, or any unexpected top-level `$` operator\r\n\r\n```\r\nGET /categories?createdAt=2026-09-01&status=ACTIVE\r\n```\r\n\r\n```ts\r\n.filter()\r\n// → { createdAt: { $gte: <UTC midnight in tz>, $lt: <next UTC midnight in tz> }, status: 'ACTIVE' }\r\n```\r\n\r\n---\r\n\r\n### `.where(conditions)`\r\n\r\nMandatory server-side conditions the client **cannot override** — kept in a separate internal bucket (`_serverConditions`) and merged last via `$and`, so URL params can never win against it.\r\n\r\n```ts\r\n.where({ branchId: req.self.linkedTo, status: { $ne: 'ARCHIVED' } })\r\n```\r\n\r\n---\r\n\r\n### `.range(field: string, preset: RangePreset)`\r\n\r\nAdds a hard, server-defined UTC date range on `field`, computed in this instance's timezone. Not client-overridable (goes into `_serverConditions`, same as `.where()`).\r\n\r\n**Presets:** `'today'`, `'yesterday'`, `'last7Days'`, `'last30Days'`, `'thisWeek'`, `'thisMonth'`\r\n\r\n```ts\r\n.range('createdAt', 'last7Days')\r\n```\r\n\r\n`.range('thisWeek')` respects the `weekStartsOn` constructor option (defaults to Saturday).\r\n\r\n---\r\n\r\n## Custom Range Methods\r\n\r\nBeyond the fixed `RangePreset` values above, six additional methods cover any custom day, span, rolling window, or hour-based filter — without touching `.range()`'s own logic. All of them behave exactly like `.range()`: they write a hard, non-client-overridable condition into `_serverConditions`, in this instance's timezone, and throw `QueryAggregateValidationError` on bad input.\r\n\r\n> ⚠️ Only call **one** range method per field. If you call two on the same field (e.g. `.range()` then `.rangeCustom()` on `createdAt`), the later call simply overwrites the earlier one.\r\n\r\n### `.rangeCustomDay(field: string, date: string)`\r\n\r\nA single arbitrary local calendar date (`YYYY-MM-DD`) — for \"show me everything from this specific day\" rather than a relative preset.\r\n\r\n```ts\r\n.rangeCustomDay('createdAt', '2026-03-14')\r\n```\r\n\r\n### `.rangeBetween(field: string, custom: CustomDateRangeInput)`\r\n\r\nAn arbitrary inclusive local-date span — the classic \"from / to\" date-picker filter.\r\n\r\n```ts\r\n.rangeBetween('createdAt', { from: '2026-01-01', to: '2026-01-31' })\r\n```\r\n\r\n### `.rangeLastNDays(field: string, days: number)`\r\n\r\nA rolling window of the last N whole calendar days, inclusive of today — a parametrized version of the built-in `last7Days`/`last30Days` presets.\r\n\r\n```ts\r\n.rangeLastNDays('createdAt', 14) // last 14 calendar days\r\n```\r\n\r\n### `.rangeLastNHours(field: string, hours: number)`\r\n\r\nA rolling window of the last N hours, ending at the exact moment the query runs. Unlike the day-based methods, this uses raw UTC instants with no calendar-day snapping — ideal for \"recent activity\" widgets.\r\n\r\n```ts\r\n.rangeLastNHours('createdAt', 6) // activity in the last 6 hours\r\n```\r\n\r\n### `.rangeHours(field: string, custom: CustomHourRangeInput)`\r\n\r\nAn explicit hour-of-day window on a specific local date — e.g. filtering orders placed during business hours.\r\n\r\n```ts\r\n.rangeHours('createdAt', { date: '2026-03-14', fromHour: 9, toHour: 17 })\r\n// toHour is exclusive; pass 24 for \"through end of day\"\r\n```\r\n\r\n### `.rangeCustom(field: string, custom: CustomDateTimeRangeInput)`\r\n\r\nThe most flexible option — an explicit absolute datetime window. Each of `from`/`to` accepts:\r\n\r\n- `\"YYYY-MM-DD\"` → local midnight in this instance's timezone\r\n- `\"YYYY-MM-DDTHH:mm:ss[.sss]\"` → local wall-clock time in this instance's timezone\r\n- an offset-bearing ISO string (`...Z`, `...+06:00`) → treated as an absolute instant\r\n\r\n```ts\r\n.rangeCustom('createdAt', {\r\n  from: '2026-03-14T09:30:00', // local wall-clock\r\n  to: '2026-03-14T18:45:00',\r\n})\r\n\r\n// or mixed with an explicit UTC instant:\r\n.rangeCustom('createdAt', { from: '2026-03-14T00:00:00Z', to: '2026-03-15T00:00:00Z' })\r\n```\r\n\r\n---\r\n\r\n### `.excludeSoftDeleted(field = 'deletedAt')`\r\n\r\nConvenience server-side condition excluding soft-deleted documents. Matches both `null` and a genuinely missing field, per MongoDB's equality semantics. Not client-overridable.\r\n\r\n```ts\r\n.excludeSoftDeleted() // defaults to 'deletedAt'\r\n.excludeSoftDeleted('removedAt') // or a custom field name\r\n```\r\n\r\n---\r\n\r\n### `.whereIf(condition, conditions)`\r\n\r\nApplies `.where(conditions)` only when `condition` is truthy — avoids scattering `if` statements around a chained builder call.\r\n\r\n```ts\r\n.whereIf(req.query.vip === 'true', { tier: 'vip' })\r\n.whereIf(req.self.role !== 'admin', { branchId: req.self.linkedTo })\r\n```\r\n\r\n---\r\n\r\n### `.globalSearch(fields: string[])`\r\n\r\nIdentical behavior to `QueryFind`: case-insensitive `$or` regex search across the given fields when `?q=` is present (max 200 chars). Regex-escaped to prevent ReDoS. Fields are filtered against the allowlist first.\r\n\r\n```ts\r\n.globalSearch(['name'])\r\n```\r\n\r\n---\r\n\r\n### `.sort()`\r\n\r\nParses `?sort=` (e.g. `-createdAt,name`), capped at 5 fields, dropping any field not in the allowlist. Falls back to `{ createdAt: -1 }` when absent.\r\n\r\n```ts\r\n.sort()\r\n```\r\n\r\n> `.paginateCursor()` (below) seeks on this same sort configuration, so call `.sort()` before it if you need a field other than the default `createdAt`.\r\n\r\n---\r\n\r\n### `.project(defaultProjection?: string | Record<string, 0 | 1>)`\r\n\r\nControls the `$project` stage. Accepts a space-separated string (`\"name status -_id\"`) or a raw `{ field: 0 | 1 }` object.\r\n\r\n- `?fields=` (when present) is parsed and filtered through the allowlist.\r\n- If **every** requested field is blocked, falls back to `defaultProjection` rather than silently returning everything.\r\n- `_id` and `__v` are always allowed through even if not in `allowFields()`.\r\n\r\n```ts\r\n.project('name status createdAt updatedAt')\r\n```\r\n\r\n> ⚠️ If a preceding `.addStage()` (e.g. `$group`) reshapes documents, `.project()`'s allowlist still only knows about the fields passed to `.allowFields()` — new computed fields may be silently dropped. Use `.addStage({ $project: {...} })` instead when you need full control post-reshape.\r\n\r\n---\r\n\r\n### `.lookup(opts: LookupOptions)`\r\n\r\nAdds a `$lookup` (join) stage, optionally followed by `$unwind`.\r\n\r\n| Field          | Type               | Description                                                                                                                        |\r\n| -------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |\r\n| `from`         | `string`           | Collection name (not the model name) to join against.                                                                              |\r\n| `as`           | `string`           | Output array field name.                                                                                                           |\r\n| `localField`   | `string?`          | Local join field.                                                                                                                  |\r\n| `foreignField` | `string?`          | Foreign join field.                                                                                                                |\r\n| `pipeline`     | `PipelineStage[]?` | Sub-pipeline form of `$lookup`, for complex/correlated joins. Cannot contain `$out`/`$merge`.                                      |\r\n| `single`       | `boolean?`         | When `true`, `$unwind`s the joined array (`preserveNullAndEmptyArrays: true`) so `as` becomes a single object instead of an array. |\r\n\r\n```ts\r\n.lookup({\r\n  from: 'branches',\r\n  as: 'branch',\r\n  localField: 'branchId',\r\n  foreignField: '_id',\r\n  single: true,\r\n})\r\n```\r\n\r\n---\r\n\r\n### `.computeField(name: string, expression: unknown)`\r\n\r\nConvenience wrapper over `.addStage()` for a single computed `$addFields` entry.\r\n\r\n```ts\r\n.computeField('margin', { $subtract: ['$price', '$cost'] })\r\n// equivalent to: .addStage({ $addFields: { margin: { $subtract: ['$price', '$cost'] } } })\r\n```\r\n\r\n---\r\n\r\n### `.addStage(stage: PipelineStage)`\r\n\r\nEscape hatch for any other developer-authored stage: `$addFields`, `$group`, a post-join `$match`, a custom `$sort`, etc.\r\n\r\n- Blocks **only** `$out` / `$merge` (destructive stages).\r\n- **Does not** run the client-input sanitizer/allowlist/operator-rejection — that only applies to `.filter()`/`.where()`, which parse untrusted `req.query`/`req.body` values.\r\n- **Never pass raw client input directly into `.addStage()`.** It's for stages _you_ author server-side. If you need a client-supplied value inside a custom stage, validate/whitelist it yourself first.\r\n\r\n```ts\r\n.addStage({\r\n  $group: {\r\n    _id: '$status',\r\n    count: { $sum: 1 },\r\n    totalAmount: { $sum: '$amount' },\r\n  },\r\n})\r\n```\r\n\r\n## Terminal & Utility Methods\r\n\r\n### `.paginate(cachedTotal?: number)`\r\n\r\nExecutes the pipeline and returns `Promise<PaginatedResult<T>>`.\r\n\r\n- Uses `$facet` to fetch the data page and total count in a **single round trip**.\r\n- Pass a prior page-1 `total` as `cachedTotal` on later pages to **skip the count stage entirely**.\r\n- `maxTimeMS` / `allowDiskUse` from constructor options are applied to the aggregation.\r\n- If the requested `page` exceeds `totalPages`, page `1` is returned automatically (with the real `total`/`totalPages` still reported).\r\n- `onSlowQuery` (if configured) fires with `{ elapsedMs, pipeline, sort, page, limit }` when the query exceeds `slowQueryThresholdMS`.\r\n\r\n> **Note:** `$facet` buffers both branches in memory per query. For very large collections with expensive pre-facet stages (heavy `$lookup`/`$group` before pagination), pass `cachedTotal` on subsequent pages, use `.paginateCursor()` instead, or run `.count()`/data as two separate calls.\r\n\r\n```ts\r\nconst page1 = await builder.paginate();\r\nconst page2 = await builder.paginate(page1.total); // skips re-counting\r\n```\r\n\r\n---\r\n\r\n### `.paginateCursor(opts?: CursorPaginateOptions)`\r\n\r\nKeyset (\"seek\") pagination — scales far better than `.paginate()`'s `$skip` on deep pages of large collections, at the cost of not supporting \"jump to page N\". Seeks on whatever field `.sort()` resolved to (falls back to `createdAt` if `.sort()` wasn't called), using `_id` as a stable tiebreaker.\r\n\r\n```ts\r\ninterface CursorPaginateOptions {\r\n  cursor?: string; // opaque cursor from a previous page's nextCursor\r\n  limit?: number; // overrides ?limit=, still capped by maxLimit\r\n}\r\n\r\ninterface CursorPaginatedResult<T> {\r\n  data: T[];\r\n  limit: number;\r\n  nextCursor: string | null; // pass this back in as `cursor` for the next page\r\n  hasNextPage: boolean;\r\n}\r\n```\r\n\r\n```ts\r\nconst p1 = await builder.sort().paginateCursor({ limit: 20 });\r\nconst p2 = await builder.paginateCursor({ cursor: p1.nextCursor!, limit: 20 });\r\n// hasNextPage: false / nextCursor: null means you've reached the end\r\n```\r\n\r\nGreat fit for infinite-scroll feeds and large-export pagination; not a drop-in replacement where the UI needs page numbers.\r\n\r\n---\r\n\r\n### `.count()`\r\n\r\nReturns just the matching document count — cheaper than `.paginate()` when you don't need the data page. Ignores `.project()`/`.sort()`.\r\n\r\n```ts\r\nconst total = await builder.count();\r\n```\r\n\r\n---\r\n\r\n### `.distinct(field: string)`\r\n\r\nReturns the distinct values of `field` under the current `.filter()`/`.where()`/range conditions.\r\n\r\n```ts\r\nconst statuses = await builder.distinct('status');\r\n// e.g. ['PENDING', 'PAID', 'CANCELLED']\r\n```\r\n\r\n---\r\n\r\n### `.stats(specs: StatSpec[])`\r\n\r\nComputes one or more aggregate metrics — `sum`, `avg`, `min`, `max`, `count` — in a **single round trip**. Ideal for dashboard totals. Ignores `.project()`/`.sort()`/pagination.\r\n\r\n```ts\r\ninterface StatSpec {\r\n  name: string;\r\n  op: 'sum' | 'avg' | 'min' | 'max' | 'count';\r\n  field?: string; // required for every op except 'count'\r\n}\r\n```\r\n\r\n```ts\r\nconst totals = await builder.stats([\r\n  { name: 'revenue', op: 'sum', field: 'total' },\r\n  { name: 'avgOrder', op: 'avg', field: 'total' },\r\n  { name: 'orders', op: 'count' },\r\n]);\r\n// { revenue: 48213, avgOrder: 62.1, orders: 776 }\r\n```\r\n\r\n---\r\n\r\n### `.stream()`\r\n\r\nReturns a Mongoose `AggregationCursor` — async-iterable, no buffering of the full result set in memory. Use for large exports (CSV generation, bulk processing).\r\n\r\n```ts\r\nfor await (const doc of builder.stream()) {\r\n  csvWriter.write(doc);\r\n}\r\n```\r\n\r\nIgnores `page`/`limit`; add your own `$limit` via `.addStage()` if you want a hard cap.\r\n\r\n---\r\n\r\n### `.explain(verbosity?)`\r\n\r\nRuns the built pipeline through Mongoose's `.explain()` — for debugging slow queries and verifying index usage. `verbosity` is `'queryPlanner'` (default), `'executionStats'`, or `'allPlansExecution'`.\r\n\r\n```ts\r\nconst plan = await builder.explain('executionStats');\r\n```\r\n\r\n> Development/ops tool only — never call this in a production request path.\r\n\r\n---\r\n\r\n### `.clone()`\r\n\r\nDeep-copies the current builder state (filters, sort, projection, stages) into a new, independent `QueryAggregate` instance. Handy for branching one shared base query into e.g. both `.paginate()` and `.stats()` without rebuilding the filters twice.\r\n\r\n```ts\r\nconst base = builder.filter().sort().project('name total createdAt');\r\n\r\nconst [page, totals] = await Promise.all([\r\n  base.clone().paginate(),\r\n  base.clone().stats([{ name: 'revenue', op: 'sum', field: 'total' }]),\r\n]);\r\n```\r\n\r\n---\r\n\r\n### `.debugPipeline()`\r\n\r\nReturns the fully-built pipeline (`PipelineStage[]`) without executing it — for logging, snapshot tests, or sanity-checking what will actually run.\r\n\r\n```ts\r\nconsole.log(builder.debugPipeline());\r\n```\r\n\r\n## Recipes\r\n\r\n**Join + post-join filter + group + paginate:**\r\n\r\n```ts\r\nconst result = await new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['status', 'createdAt'])\r\n  .dateFields(['createdAt'])\r\n  .filter()\r\n  .lookup({\r\n    from: 'customers',\r\n    as: 'customer',\r\n    localField: 'customerId',\r\n    foreignField: '_id',\r\n    single: true,\r\n  })\r\n  .addStage({ $match: { 'customer.vip': true } }) // server-defined, not client-controlled\r\n  .addStage({\r\n    $group: {\r\n      _id: '$customer._id',\r\n      customerName: { $first: '$customer.name' },\r\n      orderCount: { $sum: 1 },\r\n    },\r\n  })\r\n  .sort()\r\n  .paginate();\r\n```\r\n\r\n**Relative date range preset (server-enforced, not client-overridable):**\r\n\r\n```ts\r\nconst result = await new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['status'])\r\n  .where({ branchId: req.self.linkedTo })\r\n  .range('createdAt', 'last30Days')\r\n  .filter()\r\n  .sort()\r\n  .paginate();\r\n```\r\n\r\n**Custom hour-of-day window (business hours report):**\r\n\r\n```ts\r\nconst result = await new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['status'])\r\n  .where({ branchId: req.self.linkedTo })\r\n  .rangeHours('createdAt', {\r\n    date: req.query.day as string,\r\n    fromHour: 9,\r\n    toHour: 21,\r\n  })\r\n  .filter()\r\n  .sort()\r\n  .paginate();\r\n```\r\n\r\n**Dashboard: page + totals in parallel, off one shared base:**\r\n\r\n```ts\r\nconst base = new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['status', 'branchId', 'createdAt', 'total'])\r\n  .dateFields(['createdAt'])\r\n  .where({ branchId: req.self.linkedTo })\r\n  .excludeSoftDeleted()\r\n  .filter()\r\n  .sort()\r\n  .project('status createdAt total');\r\n\r\nconst [page, totals] = await Promise.all([\r\n  base.clone().paginate(),\r\n  base.clone().stats([\r\n    { name: 'revenue', op: 'sum', field: 'total' },\r\n    { name: 'orders', op: 'count' },\r\n  ]),\r\n]);\r\n```\r\n\r\n**Infinite scroll with keyset pagination:**\r\n\r\n```ts\r\nconst builder = new QueryAggregate(PostModel, req.query, { timezone })\r\n  .allowFields(['authorId', 'createdAt'])\r\n  .where({ published: true })\r\n  .filter()\r\n  .sort();\r\n\r\nconst page = await builder.paginateCursor({\r\n  cursor: req.query.cursor as string | undefined,\r\n  limit: 20,\r\n});\r\n```\r\n\r\n**Streaming a large CSV export:**\r\n\r\n```ts\r\nconst builder = new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['status', 'createdAt'])\r\n  .where({ branchId: req.self.linkedTo })\r\n  .range('createdAt', 'thisMonth')\r\n  .filter()\r\n  .project('status createdAt total');\r\n\r\nres.setHeader('Content-Type', 'text/csv');\r\nfor await (const row of builder.stream()) {\r\n  res.write(`${row.status},${row.createdAt.toISOString()},${row.total}\\n`);\r\n}\r\nres.end();\r\n```\r\n\r\n**ObjectId filter fields (ref lookups by hex ID from the URL):**\r\n\r\n```\r\nGET /orders?branchId=64f1a2b3c4d5e6f7a8b9c0d1&categoryId=64f1a2b3c4d5e6f7a8b9c0d2\r\n```\r\n\r\n```ts\r\nconst result = await new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['branchId', 'categoryId', 'status'])\r\n  .objectIdFields(['branchId', 'categoryId']) // coerces hex strings -> Types.ObjectId\r\n  .filter()\r\n  .sort()\r\n  .paginate();\r\n```\r\n\r\n**Role-based server conditions with `.whereIf()` + `.excludeSoftDeleted()`:**\r\n\r\n```ts\r\nconst result = await new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['status', 'branchId', 'createdAt'])\r\n  .excludeSoftDeleted() // deletedAt: null, always applied\r\n  .whereIf(req.self.role !== 'admin', { branchId: req.self.linkedTo }) // branch staff see only their branch\r\n  .whereIf(req.query.vip === 'true', { tier: 'vip' })\r\n  .filter()\r\n  .sort()\r\n  .paginate();\r\n```\r\n\r\n**Populating a filter UI — distinct values + count, no data fetch:**\r\n\r\n```ts\r\nconst builder = new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['status', 'branchId'])\r\n  .where({ branchId: req.self.linkedTo })\r\n  .filter();\r\n\r\nconst [statuses, total] = await Promise.all([\r\n  builder.clone().distinct('status'), // populate a <select> of possible statuses\r\n  builder.clone().count(), // \"X results\" label\r\n]);\r\n```\r\n\r\n**Debug route — inspect the built pipeline / query plan without exposing it to normal users:**\r\n\r\n```ts\r\nrouter.get('/orders/_debug', requireAdmin, async (req, res) => {\r\n  const builder = new QueryAggregate(OrderModel, req.query, { timezone })\r\n    .allowFields(['status', 'createdAt'])\r\n    .where({ branchId: req.self.linkedTo })\r\n    .range('createdAt', 'last7Days')\r\n    .filter()\r\n    .sort();\r\n\r\n  res.json({\r\n    pipeline: builder.debugPipeline(),\r\n    explain: await builder.explain('executionStats'),\r\n  });\r\n});\r\n```\r\n\r\n**Cursor pagination on a non-default sort field:**\r\n\r\n```ts\r\nconst result = await new QueryAggregate(ProductModel, req.query, { timezone })\r\n  .allowFields(['price', 'name'])\r\n  .where({ inStock: true })\r\n  .filter()\r\n  .sort() // resolves ?sort=price into { price: 1 } — paginateCursor() will seek on \"price\"\r\n  .paginateCursor({\r\n    cursor: req.query.cursor as string | undefined,\r\n    limit: 24,\r\n  });\r\n```\r\n\r\n**Correlated sub-pipeline `$lookup` (needs `let`):**\r\n\r\nThe built-in `LookupOptions` type doesn't expose `let` for correlated sub-pipelines. For that case, drop to `.addStage()` directly:\r\n\r\n```ts\r\n.addStage({\r\n  $lookup: {\r\n    from: 'orderItems',\r\n    let: { orderId: '$_id' },\r\n    pipeline: [\r\n      { $match: { $expr: { $eq: ['$orderId', '$$orderId'] } } },\r\n      { $project: { name: 1, qty: 1 } },\r\n    ],\r\n    as: 'items',\r\n  },\r\n})\r\n```\r\n\r\n**Sales analytics dashboard — join, group, and rolling window in one pipeline:**\r\n\r\n```ts\r\nconst timezone = (req.headers['x-timezone'] as string) ?? 'Asia/Dhaka';\r\n\r\nconst result = await new QueryAggregate(OrderModel, req.query, { timezone })\r\n  .allowFields(['branchId', 'status'])\r\n  .where({ status: 'PAID' })\r\n  .rangeLastNDays('createdAt', 30) // rolling 30-day window, client can't override\r\n  .filter()\r\n  .lookup({\r\n    from: 'branches',\r\n    as: 'branch',\r\n    localField: 'branchId',\r\n    foreignField: '_id',\r\n    single: true,\r\n  })\r\n  .addStage({\r\n    $group: {\r\n      _id: '$branchId',\r\n      branchName: { $first: '$branch.name' },\r\n      revenue: { $sum: '$total' },\r\n      orders: { $sum: 1 },\r\n      avgOrderValue: { $avg: '$total' },\r\n    },\r\n  })\r\n  .addStage({ $sort: { revenue: -1 } })\r\n  .paginate();\r\n```\r\n\r\n**Notification feed — lookup + unwind a single related document per row:**\r\n\r\n```ts\r\nconst result = await new QueryAggregate(NotificationModel, req.query, {\r\n  timezone,\r\n})\r\n  .allowFields(['read', 'type', 'createdAt'])\r\n  .dateFields(['createdAt'])\r\n  .where({ userId: req.user.id })\r\n  .filter()\r\n  .lookup({\r\n    from: 'users',\r\n    as: 'actor',\r\n    localField: 'actorId',\r\n    foreignField: '_id',\r\n    single: true, // unwinds so `actor` is a single object, not an array\r\n  })\r\n  .sort()\r\n  .project('type read createdAt actor.name actor.avatar')\r\n  .paginate();\r\n```\r\n\r\n**Audit log search — global search + a bounded absolute datetime window:**\r\n\r\n```ts\r\nconst result = await new QueryAggregate(AuditLogModel, req.query, { timezone })\r\n  .allowFields(['action', 'actorId', 'createdAt'])\r\n  .dateFields(['createdAt'])\r\n  .where({ orgId: req.user.orgId })\r\n  .rangeCustom('createdAt', {\r\n    from: (req.query.from as string) ?? '2026-01-01T00:00:00',\r\n    to: (req.query.to as string) ?? new Date().toISOString(),\r\n  })\r\n  .filter()\r\n  .globalSearch(['action'])\r\n  .sort()\r\n  .project('action actorId createdAt')\r\n  .paginate();\r\n```\r\n\r\n**Multi-branch inventory report — objectIdFields + computeField + stats side by side:**\r\n\r\n```ts\r\nconst base = new QueryAggregate(ProductModel, req.query, { timezone })\r\n  .allowFields(['branchId', 'category', 'stock', 'price'])\r\n  .objectIdFields(['branchId'])\r\n  .where({ deletedAt: null })\r\n  .filter()\r\n  .computeField('stockValue', { $multiply: ['$stock', '$price'] })\r\n  .sort()\r\n  .project('branchId category stock price stockValue');\r\n\r\nconst [page, totals] = await Promise.all([\r\n  base.clone().paginate(),\r\n  base.clone().stats([\r\n    { name: 'totalStockValue', op: 'sum', field: 'stockValue' },\r\n    { name: 'lowStockAvg', op: 'avg', field: 'stock' },\r\n    { name: 'skuCount', op: 'count' },\r\n  ]),\r\n]);\r\n\r\nres.json({ ...page, totals });\r\n```\r\n\r\n## Pipeline Stage Ordering\r\n\r\nThe final pipeline is always assembled in this fixed order:\r\n\r\n```\r\n$match          (from .where() + .filter() + .range()/rangeXxx() + .globalSearch(), merged via $and)\r\n  ↓\r\n[.lookup() / .addStage() / .computeField() calls, in the order you called them]\r\n  ↓\r\n$project        (from .project(), if set)\r\n  ↓\r\n$sort           (from .sort())\r\n  ↓\r\n[terminal stage: $skip/$limit, $facet, keyset $match + $limit, or $count/$group — added internally by the method you call]\r\n```\r\n\r\nImplications:\r\n\r\n- Anything added via `.addStage()`/`.computeField()` runs **after** the base filter match and **before** projection/sort.\r\n- If you `$group`/reshape documents, do it before `.project()` — or skip `.project()` and use `.addStage({ $project: {...} })` for full control over the new shape.\r\n- If you add your own `$sort` via `.addStage()` _and_ also call `.sort()`, both stages are included (the builder's `.sort()` stage is always appended last in the base pipeline) — harmless but redundant. Prefer one or the other.\r\n- `.count()`, `.distinct()`, and `.stats()` intentionally skip `$project`/`$sort` — they only need `$match` + your `.lookup()`/`.addStage()` stages before their own terminal `$count`/`$group`.\r\n- `.paginateCursor()` builds its own `$match`/`$sort`/`$limit` tail (the keyset condition), separate from `.paginate()`'s `$facet` tail.\r\n\r\n## Security\r\n\r\n| Protection                        | Detail                                                                                                                                                                                                                                         |\r\n| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| **NoSQL injection**               | Allowlist enforced on `.filter()`/`.sort()`/`.project()` via `.allowFields()`                                                                                                                                                                  |\r\n| **Banned operators**              | `$where`, `$expr`, `$function`, `$accumulator`, `$map`, `$reduce`, `$filter` rejected recursively, any depth, in client input                                                                                                                  |\r\n| **Destructive stages**            | `$out` / `$merge` rejected in `.addStage()` / `.lookup()` sub-pipelines                                                                                                                                                                        |\r\n| **ReDoS**                         | `.globalSearch()` terms are regex-escaped before compilation                                                                                                                                                                                   |\r\n| **Oversized search**              | `?q=` capped at 200 characters                                                                                                                                                                                                                 |\r\n| **Deep nesting DoS**              | Client filter object nesting capped at depth 5 (`sanitize()`)                                                                                                                                                                                  |\r\n| **Page size DoS**                 | `limit` hard-capped at `maxLimit` (default 100), including in `.paginateCursor()`                                                                                                                                                              |\r\n| **Sort abuse**                    | Sort fields hard-capped at 5                                                                                                                                                                                                                   |\r\n| **Runaway queries**               | `maxTimeMS` applied to the aggregation; `onSlowQuery` for observability                                                                                                                                                                        |\r\n| **Bad timezone input**            | Invalid IANA zone validated once at construction, falls back to default, never throws mid-query                                                                                                                                                |\r\n| **Bad custom-range input**        | `rangeCustomDay`/`rangeBetween`/`rangeLastNDays`/`rangeLastNHours`/`rangeHours`/`rangeCustom` all throw `QueryAggregateValidationError` on malformed dates, non-positive counts, or invalid hour bounds — never silently produce a wrong range |\r\n| **Server condition override**     | `.where()` / `.range()` / every custom range method are merged last via `$and` — URL params can never override them                                                                                                                            |\r\n| **Tampered cursors**              | `.paginateCursor()` cursors are opaque base64url-encoded JSON; a malformed cursor throws `QueryAggregateValidationError` rather than silently misbehaving                                                                                      |\r\n| **Untrusted `.addStage()` input** | Not sanitized/allowlisted by design — documented as a trusted, developer-authored-only escape hatch                                                                                                                                            |\r\n\r\n> **Important:** `.addStage()` and the `pipeline` option in `.lookup()` are for **server-defined stages only**. They skip the sanitizer/allowlist/operator-rejection that protects `.filter()`/`.where()`. Never interpolate raw `req.query`/`req.body` values into an `.addStage()` call without validating them yourself first.\r\n\r\n## TypeScript Types\r\n\r\n```ts\r\nimport {\r\n  QueryAggregate,\r\n  QueryAggregateOptions,\r\n  QueryAggregateError,\r\n  QueryAggregateValidationError,\r\n  LookupOptions,\r\n  RangePreset,\r\n  SlowQueryInfo,\r\n  QueryParams,\r\n  PaginatedResult,\r\n  CustomDayInput,\r\n  CustomDateRangeInput,\r\n  CustomHourRangeInput,\r\n  CustomDateTimeRangeInput,\r\n  CursorPaginateOptions,\r\n  CursorPaginatedResult,\r\n  StatSpec,\r\n} from 'mongoose-query-find';\r\n```\r\n\r\n```ts\r\ninterface QueryAggregateOptions {\r\n  timezone?: string;\r\n  maxTimeMS?: number;\r\n  slowQueryThresholdMS?: number;\r\n  maxLimit?: number;\r\n  allowDiskUse?: boolean;\r\n  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\r\n  onSlowQuery?: (info: SlowQueryInfo) => void;\r\n  onSanitizeDrop?: (path: string, value: unknown) => void;\r\n}\r\n\r\ninterface LookupOptions {\r\n  from: string;\r\n  as: string;\r\n  localField?: string;\r\n  foreignField?: string;\r\n  pipeline?: PipelineStage[];\r\n  single?: boolean;\r\n}\r\n\r\ntype RangePreset =\r\n  | 'today'\r\n  | 'yesterday'\r\n  | 'last7Days'\r\n  | 'last30Days'\r\n  | 'thisWeek'\r\n  | 'thisMonth';\r\n\r\ninterface SlowQueryInfo {\r\n  elapsedMs: number;\r\n  pipeline: PipelineStage[];\r\n  sort: Record<string, 1 | -1>;\r\n  page: number;\r\n  limit: number;\r\n}\r\n\r\n// ── Custom range types ──────────────────────────────────────────────────────\r\n\r\ntype CustomDayInput = string; // \"YYYY-MM-DD\"\r\n\r\ninterface CustomDateRangeInput {\r\n  from: string; // \"YYYY-MM-DD\"\r\n  to: string; // \"YYYY-MM-DD\"\r\n}\r\n\r\ninterface CustomHourRangeInput {\r\n  date: string; // \"YYYY-MM-DD\"\r\n  fromHour: number; // 0–24\r\n  toHour: number; // 0–24, exclusive, must be > fromHour\r\n}\r\n\r\ninterface CustomDateTimeRangeInput {\r\n  from: string; // \"YYYY-MM-DD\" | \"YYYY-MM-DDTHH:mm:ss[.sss]\" | offset-bearing ISO string\r\n  to: string;\r\n}\r\n\r\n// ── Advanced-feature types ──────────────────────────────────────────────────\r\n\r\ninterface CursorPaginateOptions {\r\n  cursor?: string;\r\n  limit?: number;\r\n}\r\n\r\ninterface CursorPaginatedResult<T> {\r\n  data: T[];\r\n  limit: number;\r\n  nextCursor: string | null;\r\n  hasNextPage: boolean;\r\n}\r\n\r\ninterface StatSpec {\r\n  name: string;\r\n  op: 'sum' | 'avg' | 'min' | 'max' | 'count';\r\n  field?: string;\r\n}\r\n```\r\n\r\n---\r\n\r\n## Links\r\n\r\n- [npm](https://www.npmjs.com/package/mongoose-query-find)\r\n- [GitHub](https://github.com/jsdev-robin/mongoose-query-find)\r\n- [Issues](https://github.com/jsdev-robin/mongoose-query-find/issues)\r\n\r\n---\r\n\r\n## License\r\n\r\nISC © [jsdev.robin@gmail.com](mailto:jsdev.robin@gmail.com)\r\n\r\n<br>\r\n<br>\r\n\r\n---\r\n\r\n---\r\n\r\n# QueryAnalytics\r\n\r\nA purpose-built analytics/reporting layer for Mongoose (`^8 || ^9`), sitting alongside `QueryFind` and `QueryAggregate`. Where `QueryAggregate` is a general-purpose, client-input-safe pipeline builder, `QueryAnalytics` is a **server-authored dashboard toolkit**: time series, cohort retention, funnels, sessionization, percentiles, and more, each backed by a native MongoDB 7.0+ operator instead of a hand-rolled pipeline.\r\n\r\n## QueryAnalytics Overview\r\n\r\n`QueryAnalytics` wraps `Model.aggregate()` with dashboard-shaped methods that return ready-to-chart data instead of raw pipeline rows:\r\n\r\n- `timeSeries()` / `cumulativeSeries()` — bucketed metrics over time, gap-filled, with a native running total\r\n- `compare()` — period-over-period totals and % change for preset windows (today, this week, last 30 days, ...)\r\n- `breakdown()` / `rankedBreakdown()` / `multiBreakdown()` — top-N by one or two dimensions, with an optional native dense rank\r\n- `funnel()` — step-by-step conversion counts\r\n- `cohortRetention()` — a full cohort-by-cohort retention matrix\r\n- `sessionize()` — gap-based event sessionization\r\n- `percentiles()` / `stdDevOutliers()` — native `$percentile` / `$stdDevPop` statistics\r\n- `metrics()` — several independent totals in a single round trip\r\n- `heatmap()` — weekday × hour-of-day activity grid\r\n- `movingAverage()` / `trend()` / `forecast()` — pure-JS post-processing on a series you already fetched\r\n- `watch()` — a live change-stream feed for dashboards that update in real time\r\n- `toCSV()` / `toNDJSON()` / `dashboard()` — export and batch helpers\r\n\r\nIt targets **MongoDB 7.0+** and uses `$setWindowFields`, `$denseRank`, `$shift`, `$stdDevPop`, and `$percentile` directly — there's no fallback path for older servers, so calling `cumulativeSeries()`, `rankedBreakdown()`, `sessionize()`, `percentiles()`, or `stdDevOutliers()` against a pre-7.0 deployment will fail at the server, not in this library.\r\n\r\n## When to use QueryAnalytics\r\n\r\n| Need                                                             | Use              |\r\n| ---------------------------------------------------------------- | ---------------- |\r\n| Filtered/sorted/paginated list from client query params          | `QueryFind`      |\r\n| Joins, grouping, or a client-driven filtered data page           | `QueryAggregate` |\r\n| A chart-ready time series with gaps filled in                    | `QueryAnalytics` |\r\n| Week-over-week / month-over-month KPI comparisons                | `QueryAnalytics` |\r\n| Top-N breakdowns, ranked leaderboards, or a two-dimension pivot  | `QueryAnalytics` |\r\n| Signup → activation → purchase funnel counts                     | `QueryAnalytics` |\r\n| \"% of signups still active N weeks later\" retention tables       | `QueryAnalytics` |\r\n| Turning a raw event stream into user sessions                    | `QueryAnalytics` |\r\n| p50/p90/p99 latency or order-value percentiles                   | `QueryAnalytics` |\r\n| Flagging statistical outliers for an anomalies widget            | `QueryAnalytics` |\r\n| A whole dashboard's worth of metrics in one call from your route | `QueryAnalytics` |\r\n| Pushing live updates to a dashboard instead of polling           | `QueryAnalytics` |\r\n\r\n`QueryAnalytics` is **not** a client-input parser. Unlike `QueryFind`/`QueryAggregate`, it has no `.filter()`, no `.allowFields()`, and no operator-rejection pass over untrusted input — see [Security](#security-2) below.\r\n\r\n## Installation\r\n\r\nShips from the same package as `QueryFind` and `QueryAggregate`:\r\n\r\n```bash\r\nnpm install mongoose-query-find\r\n```\r\n\r\n## Quick Start\r\n\r\n```ts\r\nimport { QueryAnalytics } from 'mongoose-query-f","readmeFilename":"README.md"}