{"_id":"@kamalyb/mongoose-explore","_rev":"28-f1be023ad5e5863f4fa10e4d38694d63","name":"@kamalyb/mongoose-explore","dist-tags":{"latest":"4.1.0"},"versions":{"1.0.0":{"name":"@kamalyb/mongoose-explore","version":"1.0.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@1.0.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"0563711b8a89e8c74d41b5f0102b5dd37a5e563f","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-1.0.0.tgz","fileCount":42,"integrity":"sha512-DQ+7eiZFiLAbnOCJvg1ZU/pYMR0Fvq9Vox9SeAcF8/24oW/e2kbtMgNR3rQkLQDl1gR+hsGvQ0GsKzvjo82klQ==","signatures":[{"sig":"MEQCIGb4lDh/H9P/7cnzZHEbUh58nnWOc0bL0p5XbbBxec92AiAgl3UJJTbGS0eFolp3sPYcIJhBcOfOHD611rIEagnmEA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":118010},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"39ea8fa858730375ae5b966a0f2f7459e9c1fc8e","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface \r generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.1","lodash.set":"^4.3.2","ts-pattern":"^5.5.0","lodash.pickby":"^4.6.0","lodash.isempty":"^4.4.0","lodash.mapvalues":"^4.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e","devDependencies":{"tsup":"^8.3.0","chance":"^1.1.12","eslint":"^9.13.0","consola":"^3.2.3","express":"^4.21.1","mongoose":"^8.7.2","prettier":"^3.3.3","@eslint/js":"^9.13.0","@types/pug":"^2.0.10","typescript":"^5.6.3","@types/node":"^22.7.7","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","@types/lodash.set":"^4.3.9","typescript-eslint":"^8.11.0","@prettier/plugin-pug":"^3.2.0","@types/lodash.pickby":"^4.6.9","express-async-errors":"^3.1.1","@types/lodash.isempty":"^4.4.9","eslint-config-prettier":"^9.1.0","eslint-plugin-import-x":"^4.3.1","eslint-plugin-prettier":"^5.2.1","@types/lodash.mapvalues":"^4.6.9","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_1.0.0_1732426491081_0.21250932329288696","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@kamalyb/mongoose-explore","version":"1.0.1","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@1.0.1","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"cb4e4f856e7c832788e45910ca24832bb672ba54","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-1.0.1.tgz","fileCount":42,"integrity":"sha512-20I/63giRLJ+Lvlf6vQND0eLDTaD4Syp+fBNBCjIZH4klswzmU9rceDFMBzRsDR6rlUF8MwxlzjEtwbnaiUzmw==","signatures":[{"sig":"MEQCIF4n9nvO29XW4+6zF0HyzW+W3PsLjwYkr524uIX4ndd3AiBf1Y07rA+46m+Oi+5AaJxiGOqPzhx9RSbV6MFe4UZDxw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":118035},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"0ce28988bdc9ac37fb8b07e26eb5cb1e6042fec8","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface \r generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.1","lodash.set":"^4.3.2","ts-pattern":"^5.5.0","lodash.pickby":"^4.6.0","lodash.isempty":"^4.4.0","lodash.mapvalues":"^4.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e","devDependencies":{"tsup":"^8.3.0","chance":"^1.1.12","eslint":"^9.13.0","consola":"^3.2.3","express":"^4.21.1","mongoose":"^8.7.2","prettier":"^3.3.3","@eslint/js":"^9.13.0","@types/pug":"^2.0.10","typescript":"^5.6.3","@types/node":"^22.7.7","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","@types/lodash.set":"^4.3.9","typescript-eslint":"^8.11.0","@prettier/plugin-pug":"^3.2.0","@types/lodash.pickby":"^4.6.9","express-async-errors":"^3.1.1","@types/lodash.isempty":"^4.4.9","eslint-config-prettier":"^9.1.0","eslint-plugin-import-x":"^4.3.1","eslint-plugin-prettier":"^5.2.1","@types/lodash.mapvalues":"^4.6.9","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_1.0.1_1732509565122_0.7608732693660101","host":"s3://npm-registry-packages"}},"1.0.2":{"name":"@kamalyb/mongoose-explore","version":"1.0.2","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@1.0.2","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"9cf73b970b7fa46ecab10aacf6a21ed9b9c344f5","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-1.0.2.tgz","fileCount":43,"integrity":"sha512-seDahwcTxXTLaW36mLrA1u2F3BAaWcllV6SmcgP+JpsDiQ8h4I0pSuzdCKnrdBOxdVnwb+kFoWW+LCu1LpeZ7g==","signatures":[{"sig":"MEYCIQD1CiJT0O+AZR5tTKJRuT7zJEQ3VTIYT7lqyPt/OxpHeAIhANWleeQSsiddKraRSk6qUy6T15bW4TVN9EQr2gslpd4V","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":114695},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"f6a2c3c4fdd758a2bce9d55c23eb27ff23063515","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.1","lodash.set":"^4.3.2","ts-pattern":"^5.5.0","lodash.pickby":"^4.6.0","lodash.isempty":"^4.4.0","lodash.mapvalues":"^4.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e","devDependencies":{"tsup":"^8.3.0","chance":"^1.1.12","eslint":"^9.13.0","consola":"^3.2.3","express":"^4.21.1","mongoose":"^8.7.2","prettier":"^3.3.3","@eslint/js":"^9.13.0","@types/pug":"^2.0.10","typescript":"^5.6.3","@types/node":"^22.7.7","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","@types/lodash.set":"^4.3.9","typescript-eslint":"^8.11.0","@prettier/plugin-pug":"^3.2.0","@types/lodash.pickby":"^4.6.9","express-async-errors":"^3.1.1","@types/lodash.isempty":"^4.4.9","eslint-config-prettier":"^9.1.0","eslint-plugin-import-x":"^4.3.1","eslint-plugin-prettier":"^5.2.1","@types/lodash.mapvalues":"^4.6.9","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_1.0.2_1732587698832_0.6782278335662661","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"@kamalyb/mongoose-explore","version":"1.1.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@1.1.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"85d756f3c4464f5aaaaaf750d08f6a1b4a38c7e7","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-1.1.0.tgz","fileCount":44,"integrity":"sha512-j1aXaaoLQlBt8qt3zsXg440cHtiQNgojRwxHny371fIDfE2G8QzcSWseyoLplGnZxwZ8wGiyZ/QrfH7wqWsorA==","signatures":[{"sig":"MEUCIQCfDQCzBjwqes5KHFVgC5X87xS/sMX3YAjWG5arPj3NuQIgR6m6GgowUBu2Brk0VmUQji74f3oU+XygDjYOZXXp0LM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":120240},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"5c8e4b0e9d3b7f6f7bc18874de0a3b6283c877ca","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.5","chance":"^1.1.12","eslint":"^9.17.0","consola":"^3.3.3","express":"^4.21.2","mongoose":"^8.9.3","prettier":"^3.4.2","@eslint/js":"^9.17.0","@types/pug":"^2.0.10","typescript":"^5.7.2","@types/node":"^22.10.5","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.19.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^9.1.0","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.1","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_1.1.0_1736125542274_0.6812312673153889","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@kamalyb/mongoose-explore","version":"1.2.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@1.2.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"7019fe9d0ae08a9c63601cb895274b49bcecd247","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-1.2.0.tgz","fileCount":44,"integrity":"sha512-AlGY+mzB1ALppj8qmcf5S0ufRQ8Y6DoOYFVfOh15hVwwZSH+PPsqfKe7qRSCVoG2YIMZnCbLUDKLrEJ6A2bWMg==","signatures":[{"sig":"MEYCIQDnhX6SGxy1bf0sCN1UdinQc61koRjVBEkBKNDzdIvRIgIhAN4H8l8hkdr0oWZzcbyG3AZ/23qCijgZqyqF3jKGeQUU","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":121545},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"2395320627e8ff5e15e23e1c370c6f5ab1fbfd3c","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.5","chance":"^1.1.12","eslint":"^9.17.0","consola":"^3.3.3","express":"^4.21.2","mongoose":"^8.9.3","prettier":"^3.4.2","@eslint/js":"^9.17.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.10.6","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.19.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^9.1.0","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.1","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_1.2.0_1736977211790_0.372501799432128","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@kamalyb/mongoose-explore","version":"1.3.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@1.3.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"85672d341a5db19531c99786b19d9f630d1c54f3","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-1.3.0.tgz","fileCount":44,"integrity":"sha512-noaEqVO1DGqIkutao3EtV3n7ql2+8SmMRNj5+B6wfXNirczCJo4UA/ooxdxPVl4P8bXaMsKxXaO8PiyDx4ZUEQ==","signatures":[{"sig":"MEQCIAsaD613nFBgGXFHM/K5z0LSryG2QB2i6lYACjwqsBq+AiArkO1JUba21iPHyqDATgj2+P9cfci64tvtV7aVoKvFIQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":121745},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"f795f8a09f02dc630876fefb24dce62dc0bcef1e","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.5","chance":"^1.1.12","eslint":"^9.17.0","consola":"^3.3.3","express":"^4.21.2","mongoose":"^8.9.3","prettier":"^3.4.2","@eslint/js":"^9.17.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.10.6","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.19.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^9.1.0","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.1","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_1.3.0_1737057220501_0.7746697705627297","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@kamalyb/mongoose-explore","version":"2.0.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.0.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"a75181c72926a5168b351cf9c51cad3c59147959","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.0.0.tgz","fileCount":44,"integrity":"sha512-6vTM8Ux2h0NTAZQJ8V4TTuxwCL717PuncBk8eAXzzk3TKZRAwYuwHaYQaVkn+Asdd8w6VQsQmsEfC2mEWf+jOA==","signatures":[{"sig":"MEUCIHgB1y/GXJvsPyViNRcRBGL2Jmva72lmVI3FcBrV60mkAiEAmosdgzt0ZUtBB/VHyjfEGjo0b6RSjMjshdiojbA5kTA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":121453},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"d155bb0af86d8aee264fbbe0cac355843aeef8c2","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.5","chance":"^1.1.12","eslint":"^9.18.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.9.5","prettier":"^3.4.2","@eslint/js":"^9.18.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.10.7","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.20.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.2","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.0.0_1737167714480_0.32014061733772947","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@kamalyb/mongoose-explore","version":"2.0.1","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.0.1","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"4cf0475d2acdc3f9938cec23d8a33c49fe065e53","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.0.1.tgz","fileCount":44,"integrity":"sha512-VNW8zofN4tfVd1ZHBGoMPR1hgtI3HLNgp8F1QfBJeKr7ZFdls+D1gh98zX1ZXMldGGX+EIZf7zGWsBa4u+vZmQ==","signatures":[{"sig":"MEYCIQDpw3k+MWuSBOhDkCcDHhdr2jIsfkYivCKQSRj439BgagIhAId47DzXqS35jM916dlZHuYIK9B58I4hUAdzvOdWeHqg","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":121551},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"53a556169bf07a54b1a3171371e5698cf9e4d036","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface generated using [pug](https://pugjs.org/api/getting-started.html) to _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.5","chance":"^1.1.12","eslint":"^9.18.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.9.5","prettier":"^3.4.2","@eslint/js":"^9.18.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.10.7","@types/chance":"^1.1.6","@types/express":"^5.0.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.20.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.2","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.0.1_1737241616386_0.0830515458700698","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@kamalyb/mongoose-explore","version":"2.1.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.1.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"89373af8dbe6c698f060b559df1e74dc2f8f1177","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.1.0.tgz","fileCount":44,"integrity":"sha512-aAqjvR2l4BMqs8ldXfsMgfDgNKM7UPh4RPOdw048R8jrSF1AHcoN3jp+3h2+DF2MUufky1FLZ1s4ghJa6yLAPA==","signatures":[{"sig":"MEUCIEYRSzlcTAdnf7T3hWpg4k7kUxcu3hDMe5KiVMs+UsABAiEA/npcU3bX1+caIMPiKnzfUpKSMABnnSmCxz2tAeBP9qQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":138959},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"be2982fc2045ac52afdb55f3dc9049ca786ee979","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface \r generated using [pug](https://pugjs.org/api/getting-started.html) to \r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.5","madge":"^8.0.0","eslint":"^9.18.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.9.5","prettier":"^3.4.2","@eslint/js":"^9.18.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.10.7","@types/express":"^5.0.0","@faker-js/faker":"^9.4.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.20.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.2","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.1.0_1737676993992_0.8528288134509214","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@kamalyb/mongoose-explore","version":"2.2.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.2.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"bc1f3429045b367645180882ae7853a0c42599df","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.2.0.tgz","fileCount":44,"integrity":"sha512-9GTezoRRUzPbOgTHPrbxAbMPosmtAZNh4HIjLhi4HzxWHyD1aevUmJJU+EZOYw6v14LmArP/GpAkKFPBK4MZNw==","signatures":[{"sig":"MEQCIF5NROpZtQYmRdEz4a/li9km457Q2Ix/fA+17DuatCvQAiA1EBmnn1OSGA3wqnPuANKo4TiA6boNZXX5nYkoIkbY7g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":145241},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"2ead4b0ec4c3cec9403dd0a91f24834735782a7b","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.6","madge":"^8.0.0","eslint":"^9.19.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.9.5","prettier":"^3.4.2","@eslint/js":"^9.19.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.10.10","@types/express":"^5.0.0","@faker-js/faker":"^9.4.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.21.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.2.0_1738096786921_0.04122580563154732","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"name":"@kamalyb/mongoose-explore","version":"2.3.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.3.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"1551a90d43f5af1e159fda498ab61ee635f33ff5","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.3.0.tgz","fileCount":44,"integrity":"sha512-27TdfgcUQXqn4XqBuymWqcCCQUOUw4uXEJE7XWeXy2DqZMWDsk1xJ60vd2OXQjNWDFySa/orgEqLaEV0MK2bJw==","signatures":[{"sig":"MEQCIB6gJyh08ZzDMawQrx6shFJuibeMpg0r/Wb/XDCHdw0oAiA/uXCiyNoU4uA9LE/aPUP6v9jGYv7PJWS+6YuOcEMdvQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":154380},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"528553f6db879453ce316aeba2ebb0fc1b69bb38","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.31.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.6","madge":"^8.0.0","eslint":"^9.19.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.9.5","prettier":"^3.4.2","@eslint/js":"^9.19.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.10.10","@types/express":"^5.0.0","@faker-js/faker":"^9.4.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.21.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.3.0_1738640207375_0.27116620207756603","host":"s3://npm-registry-packages-npm-production"}},"2.4.0":{"name":"@kamalyb/mongoose-explore","version":"2.4.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.4.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"7aa53494c25c7975d48ef0aa8b0bcb2effe22fef","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.4.0.tgz","fileCount":44,"integrity":"sha512-F7F8kiAM/x80vxuPdyoV/y1Cn53tijoNVGfcGsLT2tEZbwrSnTEYZTrEixesvo6hA0EbKFNGE1gIXcB5mSDLlg==","signatures":[{"sig":"MEYCIQDb3W/v7OLoyQu0z7kcagz5rr+uMEcaOkqiFtYZK9VU4wIhAPkUFoqJ2sQ50LKE2thde09+WMXmpaC7DNs+oY2LkFLO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":155567},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"125b424ad69c869cb027504947cb7b6176bfae89","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.32.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.6","madge":"^8.0.0","eslint":"^9.19.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.10.0","prettier":"^3.4.2","@eslint/js":"^9.19.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.13.1","@types/express":"^5.0.0","@faker-js/faker":"^9.4.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.21.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.4.0_1738814338613_0.29745720919362495","host":"s3://npm-registry-packages-npm-production"}},"2.4.1":{"name":"@kamalyb/mongoose-explore","version":"2.4.1","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.4.1","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"a66e2165a93b54a83eaf70e0f81d0ad9b7fd306a","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.4.1.tgz","fileCount":44,"integrity":"sha512-QYDDcjGaqV1b/RTXKGZRslYKz9ukoYVL4gImfLUML2Wqx6jXtq5xHAcgthl/y82qYrry2TjgJRVYuVrMwmEm6Q==","signatures":[{"sig":"MEUCIEoVjnzua8Kbu4eDzqalG4diUE38wb5toCbFtz4vN7DPAiEAyd0Sxj+cBJVEu7m1vcZQSuxcGzjM1JH1gC8x738HM8I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":156392},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"189d685308df63abbc5e81f633e96d4cc5e66431","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.32.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.3.6","madge":"^8.0.0","eslint":"^9.19.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.10.0","prettier":"^3.4.2","type-fest":"^4.35.0","@eslint/js":"^9.19.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.13.1","@types/express":"^5.0.0","@faker-js/faker":"^9.4.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.21.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.4.1_1740803077914_0.20886733805481006","host":"s3://npm-registry-packages-npm-production"}},"2.5.0":{"name":"@kamalyb/mongoose-explore","version":"2.5.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.5.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"e0e8b207328cafe6c5bb7deb62bda55fd6628a32","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.5.0.tgz","fileCount":47,"integrity":"sha512-dj6dlywzcPyT7mR9zuK2+9g3UiCPLGiumj763ZijMCMOP8VjR4DXzOv0rKhjZdnC3EDUSoEOQch3drl7DTtnTA==","signatures":[{"sig":"MEQCIB7PWGZyEvIsROUlLUoBJYGzhF0a+mysc8GH1KDBetP5AiAszoH4t5gadtwpH9haGH5WtWespHM+heNgsIOKeICCFA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":164515},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"d54caa1619cea5359d87060e3ea3d050de2f22f6","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.32.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.4.0","madge":"^8.0.0","eslint":"^9.19.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.10.0","prettier":"^3.4.2","type-fest":"^4.35.0","@eslint/js":"^9.19.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.13.1","@types/express":"^5.0.0","@faker-js/faker":"^9.4.0","typescript-eslint":"^8.21.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.5.0_1741135756599_0.26729884911125934","host":"s3://npm-registry-packages-npm-production"}},"2.5.1":{"name":"@kamalyb/mongoose-explore","version":"2.5.1","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.5.1","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"1f8015f28a90131f2a6d7492acefbea69daaab60","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.5.1.tgz","fileCount":47,"integrity":"sha512-4xU7hYH62BsLvcgk8qh3Dk3IF9HtMwfqwTi4FdgsDHRq29trehaJ5WTtFq2dCK0GNP3EAKh158lI9z8Pf5gk7w==","signatures":[{"sig":"MEYCIQCvdraBh2SG2tK5erE8Zz8HOnIV9WbyirqW94aWhK/eDgIhAMU01E+L6u+o2K0j09mIBJ++3eBQHJeWZzehIDEeAbpt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":165478},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"0da26b51001c47f4a7285a91f33009bc876d4186","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.32.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.4.0","madge":"^8.0.0","eslint":"^9.19.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.10.0","prettier":"^3.4.2","type-fest":"^4.35.0","@eslint/js":"^9.19.0","@types/pug":"^2.0.10","typescript":"^5.7.3","@types/node":"^22.13.1","@types/express":"^5.0.0","@faker-js/faker":"^9.4.0","typescript-eslint":"^8.21.0","@prettier/plugin-pug":"^3.2.0","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.0.1","eslint-plugin-import-x":"^4.6.1","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.5.1_1741536450768_0.44182035503319095","host":"s3://npm-registry-packages-npm-production"}},"2.5.2":{"name":"@kamalyb/mongoose-explore","version":"2.5.2","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.5.2","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"133b99bbd1bad31ca0e17486d37f1419c28c8fcc","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.5.2.tgz","fileCount":47,"integrity":"sha512-gYNqP3I0jSBOQ2f7bAbGC0TZDIVFklCa8sTPullUeOwBs4uR+GVvGz5nDbolKAA2Avogi2KUs+viWAumdvP3Lg==","signatures":[{"sig":"MEUCIQCLj+9KRFybuaoMg0xgwff/R05xKwPH5q+ckgbOM9v5aQIgDlS6Ct+UtCEM/9KFaZWd/w7pFPb5guud8eIEBo8+2q4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":166318},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"5c1f8b9b7b5cb1b98f38438c2a3e0926a14804f1","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.33.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.4.0","madge":"^8.0.0","eslint":"^9.22.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.12.1","prettier":"^3.5.3","type-fest":"^4.37.0","@eslint/js":"^9.22.0","@types/pug":"^2.0.10","typescript":"^5.8.2","@types/node":"^22.13.10","@types/express":"^5.0.0","@faker-js/faker":"^9.6.0","typescript-eslint":"^8.26.1","@prettier/plugin-pug":"^3.2.1","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.1.1","eslint-plugin-import-x":"^4.8.0","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.5.2_1742183350545_0.22099874871513325","host":"s3://npm-registry-packages-npm-production"}},"2.5.3":{"name":"@kamalyb/mongoose-explore","version":"2.5.3","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.5.3","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"ca6ac2e795eba7523b8bdf3d752f03a67ebd6b8a","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.5.3.tgz","fileCount":27,"integrity":"sha512-f3vCrVZH90uCIutJ3n8l2KLd1oK6Apw8CQ9MPU3Itj/Lg1/bG16iEeerXrTce/+4yMm+AsCT1pH9ZoBI9cls+A==","signatures":[{"sig":"MEUCIQD6MhbyXpvWv0tr8OohJEHTij8S1MZmFz2k4Y9NiIl1VgIgH4fEC2sJJmZzarlTBacjBEGeXCZM3IczksGDf1aarLs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":383726},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"bda39812d43ca2563cbb06e1983db19aa928b8fb","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"an **opinionated** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.33.0","ts-pattern":"^5.6.2","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.4.0","madge":"^8.0.0","eslint":"^9.22.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.12.1","prettier":"^3.5.3","type-fest":"^4.37.0","@eslint/js":"^9.22.0","@types/pug":"^2.0.10","typescript":"^5.8.2","@types/node":"^22.13.10","@types/express":"^5.0.0","@faker-js/faker":"^9.6.0","typescript-eslint":"^8.26.1","@prettier/plugin-pug":"^3.2.1","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.1.1","eslint-plugin-import-x":"^4.8.0","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.5.3_1743132826561_0.08960115631959265","host":"s3://npm-registry-packages-npm-production"}},"2.5.4":{"name":"@kamalyb/mongoose-explore","version":"2.5.4","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.5.4","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"964556bd6cdc869f974a21d91a8a22b37c793ecf","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.5.4.tgz","fileCount":31,"integrity":"sha512-LXN05wmd7M/FY0ytM5dEFfXHvbwygErVrVN+uAU72atwcNf4E5QGmr7ZqtAaiWdNVXd61/AGkVda2M9EaHcPbQ==","signatures":[{"sig":"MEYCIQDQhaBfQZaSKNczl9Tp3UIalFCrDorrDOTyGNgtoaKNKQIhAMmLYuzlb5ST/cf+k9lfdt3xu7fBrcUOSxBtxNRwgTOZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1905274},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"409f901ae0bb9eaf7ffa037ee55d7d0254e30fef","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"a somewhat **_opinionated_** and highly customizable ~~admin interface~~ interface\r generated using [pug](https://pugjs.org/api/getting-started.html) to\r _explore_ your mongoose models. strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","celebrate":"^15.0.3","es-toolkit":"^1.33.0","ts-pattern":"^5.6.2","compression":"^1.8.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.4.0","madge":"^8.0.0","eslint":"^9.22.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.12.1","prettier":"^3.5.3","type-fest":"^4.37.0","@eslint/js":"^9.22.0","@types/pug":"^2.0.10","typescript":"^5.8.2","@types/node":"^22.13.10","@types/express":"^5.0.0","@faker-js/faker":"^9.6.0","typescript-eslint":"^8.26.1","@types/compression":"^1.7.5","@prettier/plugin-pug":"^3.2.1","express-async-errors":"^3.1.1","eslint-config-prettier":"^10.1.1","eslint-plugin-import-x":"^4.8.0","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.5.4_1743220902658_0.9384567890291566","host":"s3://npm-registry-packages-npm-production"}},"2.6.0":{"name":"@kamalyb/mongoose-explore","version":"2.6.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@2.6.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"a0eba51412e48d7aa9ffea99ad89c1b808ca3219","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-2.6.0.tgz","fileCount":31,"integrity":"sha512-BX2ywqfZX/ZceNPgp94d4AB6v0sAxEajTdz5DHuFMhRNFrqCi7qtvFWeSMSxI1tNfNqVOHIEg7zVVmAiVo7/DA==","signatures":[{"sig":"MEUCIQCQTXmId/GfVq2fini07ZrIlBLIOWTzBxrITyySelUT8wIgXh2V3WR0ldcbX8v9IbCdR1mAk4p6Jl5kaePIWD9pkNA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1906527},"type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":"./dist/index.js","gitHead":"acce98d05a5a73cb31c596ab773ec3333d2b6403","scripts":{"dev":"tsup-node","lint":"eslint src bin --fix","build":"tsc --noEmit && tsup-node --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"yarn run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"a somewhat **_opinionated_** and feature-rich **_admin panel_** for\r mongoose. built with [pug](https://pugjs.org/api/getting-started.html) and\r strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.10.0","dependencies":{"joi":"^17.9.2","pug":"^3.0.3","flat":"^6.0.1","mongodb":"^6.16.0","celebrate":"^15.0.3","es-toolkit":"^1.33.0","ts-pattern":"^5.7.0","compression":"^1.8.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.5.3","devDependencies":{"tsup":"^8.4.0","madge":"^8.0.0","eslint":"^9.22.0","consola":"^3.4.0","express":"^4.21.2","mongoose":"^8.13.2","prettier":"^3.5.3","type-fest":"^4.40.0","@eslint/js":"^9.22.0","@types/pug":"^2.0.10","typescript":"^5.8.3","@types/node":"^22.14.1","@types/express":"^5.0.0","@faker-js/faker":"^9.6.0","typescript-eslint":"^8.26.1","@types/compression":"^1.7.5","@prettier/plugin-pug":"^3.2.1","express-async-errors":"^3.1.1","@kamalyb/mongoose-seed":"^1.3.1","eslint-config-prettier":"^10.1.1","eslint-plugin-import-x":"^4.8.0","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_2.6.0_1746034139229_0.11409434828157217","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@kamalyb/mongoose-explore","version":"3.0.0","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@3.0.0","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"c0a0037ab3073bd91778260e85752b79a6f953f6","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-3.0.0.tgz","fileCount":31,"integrity":"sha512-bXWbjMX7O2NxU3Sd170YN70tznPd36hDmrH1WLnYNSVL4zw475k85fbQW6DhIHN5JcEgHTN9Q/GIMNHczkxNXA==","signatures":[{"sig":"MEUCIQDPbAf6q/2qGFkdgwc6bC0WWCMxT/0jIf4AoEF6hx5rkwIgNi89AbmmKxUkZrwXCckJlAJJbDKKwqDz2pEwslyImSw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1935395},"type":"module","types":"./dist/index.d.mts","engines":{"node":">=22"},"exports":"./dist/index.mjs","gitHead":"f936f7ee179f1132b4a50c17e69bc59ff4c40ec5","scripts":{"dev":"rimraf build && tsdown","lint":"eslint src bin --fix","build":"rimraf dist && tsc --noEmit && tsdown --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"pnpm run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"a somewhat **_opinionated_** and feature-rich **_admin panel_** for\r mongoose. built with [pug](https://pugjs.org/api/getting-started.html) and\r strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.20.0","dependencies":{"joi":"^18.0.2","pug":"^3.0.3","flat":"^6.0.1","mongodb":"^7.0.0","es-toolkit":"^1.43.0","ts-pattern":"^5.9.0","compression":"^1.8.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"madge":"^8.0.0","eslint":"^9.39.2","rimraf":"^6.1.2","tsdown":"^0.18.1","consola":"^3.4.0","express":">=5","mongoose":">=9","prettier":"^3.7.4","type-fest":"^5.3.1","@eslint/js":"^9.39.2","@types/pug":"^2.0.10","typescript":"^5.8.3","@types/node":"^25.0.3","@types/express":"^5.0.6","@faker-js/faker":"^10.1.0","typescript-eslint":"^8.50.0","@types/compression":"^1.7.5","@prettier/plugin-pug":"^3.2.1","@kamalyb/mongoose-seed":"^1.3.1","eslint-config-prettier":"^10.1.1","eslint-plugin-import-x":"^4.8.0","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"peerDependencies":{"express":">=5","mongoose":">=9"},"peerDependenciesMeta":{"express":{"optional":false},"mongoose":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_3.0.0_1766273277916_0.07490736264292908","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"name":"@kamalyb/mongoose-explore","version":"3.0.1","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@3.0.1","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"297f6d9ac73fd1b8f91c8e065c39479322765c8b","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-3.0.1.tgz","fileCount":31,"integrity":"sha512-HuR2e4gQOPUh6JMcJOS52387vD7O1zHuZM2diNZd8H2gc77voP9br2voreZY8XEbeC2TdZJ1MbhN0nfUN8Y0XQ==","signatures":[{"sig":"MEUCIQDu6txOHzQlVj8PIwZLR6iCL3ecaJmXcox4bsZ/FCXAZwIgf1UuvFxxGU588ncAW1NYcjyFEcLBcc9+kGpfZEZM0Lw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1935451},"type":"module","types":"./dist/index.d.mts","engines":{"node":">=22"},"exports":"./dist/index.mjs","gitHead":"580aef51899965c9f7224b8776b79bbe55c75ae6","scripts":{"dev":"rimraf build && tsdown","lint":"eslint src bin --fix","build":"rimraf dist && tsc --noEmit && tsdown --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"pnpm run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"a somewhat **_opinionated_** and feature-rich **_admin panel_** for\r mongoose. built with [pug](https://pugjs.org/api/getting-started.html) and\r strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.20.0","dependencies":{"joi":"^18.0.2","pug":"^3.0.3","flat":"^6.0.1","mongodb":"^7.0.0","es-toolkit":"^1.43.0","ts-pattern":"^5.9.0","compression":"^1.8.0","html-minifier-terser":"^7.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"madge":"^8.0.0","eslint":"^9.39.2","rimraf":"^6.1.2","tsdown":"^0.18.1","consola":"^3.4.0","express":">=5","mongoose":">=9","prettier":"^3.7.4","type-fest":"^5.3.1","@eslint/js":"^9.39.2","@types/pug":"^2.0.10","typescript":"^5.8.3","@types/node":"^25.0.3","@types/express":"^5.0.6","@faker-js/faker":"^10.1.0","typescript-eslint":"^8.50.0","@types/compression":"^1.7.5","@prettier/plugin-pug":"^3.2.1","@kamalyb/mongoose-seed":"^1.3.1","eslint-config-prettier":"^10.1.1","eslint-plugin-import-x":"^4.8.0","eslint-plugin-prettier":"^5.2.3","@types/html-minifier-terser":"^7.0.2"},"peerDependencies":{"express":">=5","mongoose":">=9"},"peerDependenciesMeta":{"express":{"optional":false},"mongoose":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_3.0.1_1766273529657_0.46295245279782415","host":"s3://npm-registry-packages-npm-production"}},"3.0.2":{"name":"@kamalyb/mongoose-explore","version":"3.0.2","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Yusuf"},"license":"MIT","_id":"@kamalyb/mongoose-explore@3.0.2","maintainers":[{"name":"kamalyusuf","email":"kbomoi@yahoo.com"}],"dist":{"shasum":"343a0e6aa07dc4a34e53bba08756f30b281cec8a","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-3.0.2.tgz","fileCount":32,"integrity":"sha512-ZPfeuQ50+6AYii1WDyqGWLHjbANyeEOTFxudcHbDTjRgaeW0yWf+i3USTJ8/8WigyIP6NLQemT2SossaSRlFJw==","signatures":[{"sig":"MEUCIQCrRygI0KZd0XIgTK0kIQJUNv0C+K7ctxBbBX8XBk+o6wIgZSRqDu4eUPk2XPuptU7gmwwILu1xD5Kxa434da4Rgmg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1940782},"type":"module","types":"./dist/index.d.mts","engines":{"node":">=22"},"exports":"./dist/index.mjs","gitHead":"4548688af74c9267791768646154315d0d637c0b","scripts":{"dev":"rimraf build && tsdown --config dev.config.ts","lint":"eslint src bin --fix","build":"rimraf dist && tsc --noEmit && tsdown --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","typecheck":"tsc --noEmit","format:pug":"prettier --write **/*.pug","prepublishOnly":"pnpm run build"},"_npmUser":{"name":"kamalyusuf","email":"kbomoi@yahoo.com"},"_npmVersion":"10.9.0","description":"a somewhat **_opinionated_** and feature-rich **_admin panel_** for\r mongoose. built with [pug](https://pugjs.org/api/getting-started.html) and\r strongly inspired by [adminjs](https://adminjs.co)","directories":{},"_nodeVersion":"22.20.0","dependencies":{"joi":"^18.0.2","pug":"^3.0.3","flat":"^6.0.1","mongodb":"^7.1.0","es-toolkit":"^1.44.0","ts-pattern":"^5.9.0","compression":"^1.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"madge":"^8.0.0","eslint":"^9.39.2","rimraf":"^6.1.2","tsdown":"^0.18.1","consola":"^3.4.0","express":">=5","mongoose":"^9.1.6","prettier":"^3.8.1","type-fest":"^5.4.4","@eslint/js":"^9.39.2","@types/pug":"^2.0.10","typescript":"^5.8.3","@types/node":"^25.2.2","@types/express":"^5.0.6","@faker-js/faker":"^10.3.0","typescript-eslint":"^8.54.0","@types/compression":"^1.7.5","@prettier/plugin-pug":"^3.2.1","@kamalyb/mongoose-seed":"^2.0.2","eslint-config-prettier":"^10.1.1","eslint-plugin-import-x":"^4.8.0","eslint-plugin-prettier":"^5.5.5"},"peerDependencies":{"express":">=5","mongoose":">=9"},"peerDependenciesMeta":{"express":{"optional":false},"mongoose":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_3.0.2_1770582424602_0.9835551593985088","host":"s3://npm-registry-packages-npm-production"}},"4.0.2":{"name":"@kamalyb/mongoose-explore","version":"4.0.2","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"author":{"name":"Kamal Bomoi"},"license":"MIT","_id":"@kamalyb/mongoose-explore@4.0.2","maintainers":[{"name":"kamalyusuf","email":"kyb.dev@pm.me"}],"dist":{"shasum":"65d8d56bce9cf61a3ff91a533edf39821a8109cb","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-4.0.2.tgz","fileCount":39,"integrity":"sha512-pCNqz9ZsB9YazaBrdfClbs0h2bZXKUl+1dpW8w22zwL38/dHAIdqICETM5BInxejwERo7ApcMTK2C3fMK7GazA==","signatures":[{"sig":"MEYCIQDvjAyc8UGm8aBhoGOxZxURpkWltEVAdToRyxy7NeqCcgIhALTULsNgQ7Dax2LRcW/GwqPhAw+xsj0MvyShaMB8Uwl1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQCnv6Rz4OVvzxplfJXsl+BrBo/S6pBWpEEpXovv2Ozq1gIhAKSvrmAfaeDsDWHtawGd6gMXFmmso3wQ77OgayBDd/tL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1898235},"type":"module","types":"./dist/index.d.mts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"ebf9e5279b06a4672dc8de6932a322c4dfb5abf5","scripts":{"dev":"rimraf build && tsdown --config dev.config.ts","lint":"biome check --write","test":"vitest run","build":"rimraf dist && tsc -p build.config.json && tsdown --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","format":"biome format --write","typecheck":"tsc -p build.config.json","format:pug":"prettier --write **/*.pug","prepublishOnly":"pnpm lint && pnpm typecheck && pnpm test && pnpm build"},"_npmUser":{"name":"kamalyusuf","email":"kyb.dev@pm.me"},"_npmVersion":"10.9.0","description":"A feature-rich admin panel for MongoDB, built on Express and Mongoose.","directories":{},"_nodeVersion":"22.20.0","dependencies":{"pug":"^3.0.4","flat":"^6.0.1","express":">=5","mongodb":"^7.6.0","valibot":"^1.4.2","es-toolkit":"^1.52.0","ts-pattern":"^5.9.0","compression":"^1.8.0","@standard-schema/spec":"^1.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.22.0+sha512.1ff870c4c6133dfd88fb2afc46dd13d47f09c9794b438c6fdb47ca98caf3bc16381ee0be93a091b8e3824cf01f889f46d7d9e20910fb0be1ab0fb5baa80dd621","devDependencies":{"madge":"^8.0.0","rimraf":"^6.1.3","tsdown":"^0.22.14","vitest":"^4.1.11","consola":"^3.4.0","mongoose":"^9.9.4","prettier":"^3.9.6","supertest":"^7.2.2","type-fest":"^5.9.0","@types/pug":"^2.0.10","typescript":"^7.0.2","@types/node":"^26.4.0","@biomejs/biome":"^2.5.11","@types/express":"^5.0.6","cookie-session":"^2.1.1","@faker-js/faker":"^10.6.0","@types/supertest":"^7.2.1","@types/compression":"^1.7.5","@prettier/plugin-pug":"^3.4.2","@types/cookie-session":"^2.0.49","mongodb-memory-server":"^11.2.0","@kamalyb/mongoose-seed":"^2.1.0"},"peerDependencies":{"mongoose":"^8.0.0 || ^9.0.0"},"peerDependenciesMeta":{"express":{"optional":false},"mongoose":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/mongoose-explore_4.0.2_1788404365103_0.467152695343964","host":"s3://npm-registry-packages-npm-production"}},"4.1.0":{"_id":"@kamalyb/mongoose-explore@4.1.0","dist":{"shasum":"57f868f59dbeb4f60c0139176a1a4528091fce3a","tarball":"https://registry.npmjs.org/@kamalyb/mongoose-explore/-/mongoose-explore-4.1.0.tgz","fileCount":39,"integrity":"sha512-RqBf8tAm1+OF8Mo4TIpjWmxpr/vhPl3pTHuuFRTm0VegGIExZzKKs9sk1juuJJjs+GjOgj4j0fQAQnth1Ot9Zg==","signatures":[{"sig":"MEYCIQC7HcGA2U648jQU3IutO9G/UIcGlxNDztKcyhwqzlINMAIhAPMGg+bjiy7MrBKvkU/frQLzXYhBnjIimchumsA2RnLs","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID6MtfiVbmItK2EnEAMptAnWru2z7TaNc9yq/j60f1viAiBem6HKxnZCC+CCiby/AQaeKv8aI9X+w941BVW9/ZspVA=="}],"unpackedSize":1905636},"name":"@kamalyb/mongoose-explore","type":"module","types":"./dist/index.d.mts","author":{"name":"Kamal Bomoi"},"engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"090002360c0b21beffde8ecf40833bf3ee077e6c","license":"MIT","scripts":{"dev":"rimraf build && tsdown --config dev.config.ts","lint":"biome check --write","test":"vitest run","build":"rimraf dist && tsc -p build.config.json && tsdown --config build.config.ts","madge":"madge src/index.ts --ts-config tsconfig.json -c --warning","format":"biome format --write","typecheck":"tsc -p build.config.json","format:pug":"prettier --write **/*.pug","prepublishOnly":"pnpm lint && pnpm typecheck && pnpm test && pnpm build"},"version":"4.1.0","_npmUser":{"name":"kamalyusuf","email":"kyb.dev@pm.me"},"keywords":["express","mongoose","admin","admin-panel","admin-interface"],"_npmVersion":"10.9.0","description":"A feature-rich admin panel for MongoDB, built on Express and Mongoose.","directories":{},"maintainers":[{"name":"kamalyusuf","email":"kyb.dev@pm.me"}],"_nodeVersion":"22.20.0","dependencies":{"pug":"^3.0.4","flat":"^6.0.1","express":">=5","mongodb":"^7.6.0","valibot":"^1.4.2","es-toolkit":"^1.52.0","ts-pattern":"^5.9.0","compression":"^1.8.0","@standard-schema/spec":"^1.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.22.0+sha512.1ff870c4c6133dfd88fb2afc46dd13d47f09c9794b438c6fdb47ca98caf3bc16381ee0be93a091b8e3824cf01f889f46d7d9e20910fb0be1ab0fb5baa80dd621","devDependencies":{"madge":"^8.0.0","rimraf":"^6.1.3","tsdown":"^0.22.14","vitest":"^4.1.11","consola":"^3.4.0","mongoose":"^9.9.4","prettier":"^3.9.6","supertest":"^7.2.2","type-fest":"^5.9.0","@types/pug":"^2.0.10","typescript":"^7.0.2","@types/node":"^26.4.0","@biomejs/biome":"^2.5.11","@types/express":"^5.0.6","cookie-session":"^2.1.1","@faker-js/faker":"^10.6.0","@types/supertest":"^7.2.1","@types/compression":"^1.7.5","@prettier/plugin-pug":"^3.4.2","@types/cookie-session":"^2.0.49","mongodb-memory-server":"^11.2.0","@kamalyb/mongoose-seed":"^2.1.0"},"peerDependencies":{"mongoose":"^8.0.0 || ^9.0.0"},"peerDependenciesMeta":{"express":{"optional":false},"mongoose":{"optional":false}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mongoose-explore_4.1.0_1789405780768_0.07158321761327913"}}},"time":{"created":"2024-11-24T05:34:50.949Z","modified":"2026-09-14T17:09:41.099Z","1.0.0":"2024-11-24T05:34:51.278Z","1.0.1":"2024-11-25T04:39:25.302Z","1.0.2":"2024-11-26T02:21:39.024Z","1.1.0":"2025-01-06T01:05:42.457Z","1.2.0":"2025-01-15T21:40:11.970Z","1.3.0":"2025-01-16T19:53:40.697Z","2.0.0":"2025-01-18T02:35:14.663Z","2.0.1":"2025-01-18T23:06:56.592Z","2.1.0":"2025-01-24T00:03:14.278Z","2.2.0":"2025-01-28T20:39:47.107Z","2.3.0":"2025-02-04T03:36:47.593Z","2.4.0":"2025-02-06T03:58:58.814Z","2.4.1":"2025-03-01T04:24:38.090Z","2.5.0":"2025-03-05T00:49:16.884Z","2.5.1":"2025-03-09T16:07:31.006Z","2.5.2":"2025-03-17T03:49:10.760Z","2.5.3":"2025-03-28T03:33:46.740Z","2.5.4":"2025-03-29T04:01:42.934Z","2.6.0":"2025-04-30T17:28:59.455Z","3.0.0":"2025-12-20T23:27:58.103Z","3.0.1":"2025-12-20T23:32:09.860Z","3.0.2":"2026-02-08T20:27:04.876Z","4.0.0":"2026-09-01T21:22:17.037Z","4.0.1":"2026-09-01T21:23:39.819Z","4.0.2":"2026-09-03T02:59:25.225Z","4.1.0":"2026-09-14T17:09:40.910Z"},"author":{"name":"Kamal Bomoi"},"license":"MIT","keywords":["express","mongoose","admin","admin-panel","admin-interface"],"description":"A feature-rich admin panel for MongoDB, built on Express and Mongoose.","maintainers":[{"name":"kamalyusuf","email":"kyb.dev@pm.me"}],"readme":"# mongoose explore\r\n\r\na somewhat **_opinionated_**, highly customizable and feature-rich **_admin\r\npanel_** for mongoose. built with [pug](https://pugjs.org/api/getting-started.html)\r\nand strongly inspired by [adminjs](https://adminjs.co)\r\n\r\n## features\r\n\r\n- browse, create, edit and delete documents across all your mongoose models\r\n- filter and sort any collection from the interface, with an operator menu per\r\n  field (exact match, ranges, comparison, presence, regex)\r\n- a configurable dashboard with stat, chart and tabular widgets and full\r\n  layout composition via `widgets.render(key)`\r\n- custom pages with forms, validation and access control\r\n- a query runner for ad-hoc `find`/`findOne`/`aggregate`/`count` queries, with\r\n  per-role operation and preset gating\r\n- role-based access control at both the explorer and resource level, down to\r\n  individual fields, operations, actions and presets\r\n- an intent system: proof-of-intent codes gating destructive or sensitive\r\n  operations, with minting policies, requests/assignments, verification, and a\r\n  pluggable store\r\n- custom actions (document and bulk) that can collect typed inputs through a\r\n  modal, validated through any standard-schema validator\r\n- theming: dark and light themes, color tokens, fonts, and full css control\r\n- an event system for audit logging and side effects\r\n- deep customization: virtual fields, field renderers, cascade deletes, and\r\n  per-model configuration\r\n\r\n## table of contents\r\n\r\n1. [installation](#installation)\r\n2. [prerequisites](#prerequisites)\r\n3. [quick start](#quick-start)\r\n4. [configuration reference](#configuration-reference)\r\n5. [identity & access](#identity--access)\r\n6. [resources](#resources)\r\n7. [query runner](#query-runner)\r\n8. [custom pages](#custom-pages)\r\n9. [dashboard & widgets](#dashboard--widgets)\r\n10. [theming](#theming)\r\n11. [intent system](#intent-system)\r\n12. [events](#events)\r\n13. [features and behavior](#features-and-behavior)\r\n14. [limitations](#limitations)\r\n\r\n## installation\r\n\r\n```bash\r\nnpm install @kamalyb/mongoose-explore\r\n\r\n# or\r\n\r\nyarn add @kamalyb/mongoose-explore\r\n\r\n# or\r\n\r\npnpm add @kamalyb/mongoose-explore\r\n```\r\n\r\n## prerequisites\r\n\r\n1. requires node >= 22, mongoose >= 9 and express >= 5\r\n2. do not disable mongoose's schema types casting as `Model.castObject()` is\r\n   used to accurately convert url-encoded data to align with the corresponding\r\n   model's schema when creating and editing documents\r\n3. if your application utilizes `helmet` middleware, allow the `unsafe-inline`\r\n   and `self` script source directive in your content security policy\r\n\r\n```typescript\r\napp.use(\r\n  helmet({\r\n    contentSecurityPolicy: {\r\n      directives: {\r\n        \"script-src\": [\"'unsafe-inline'\", \"'self'\"],\r\n        \"script-src-attr\": [\"'unsafe-inline'\"]\r\n      }\r\n    }\r\n  })\r\n);\r\n```\r\n\r\n## quick start\r\n\r\n```typescript\r\nimport express from \"express\";\r\nimport mongoose from \"mongoose\";\r\nimport { MongooseExplorer } from \"@kamalyb/mongoose-explore\";\r\nimport \"./models.js\";\r\n\r\nconst app = express();\r\n\r\nconst explorer = new MongooseExplorer({ mongoose });\r\n\r\napp.use(explorer.rootpath, explorer.router());\r\n\r\nawait mongoose.connect();\r\n\r\napp.listen(port);\r\n```\r\n\r\n> voila! you now have an admin panel on _/admin/explorer_ to explore and\r\n> manipulate your mongoose models\r\n\r\n## configuration reference\r\n\r\nthe `MongooseExplorer` constructor accepts a configuration object. every\r\noption below is verified against `MongooseExplorerOptions` — anything not\r\nlisted here does not exist.\r\n\r\n| option               | type                                     | default           | what it does                                                                                                      |\r\n| -------------------- | ---------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------- |\r\n| `mongoose`           | `typeof mongoose`                        | — (required)      | the mongoose instance used for all database operations                                                            |\r\n| `rootpath`           | `` `/${string}` ``                       | `/admin/explorer` | base url path where the panel is mounted                                                                          |\r\n| `title`              | `string`                                 | —                 | browser title of the panel                                                                                        |\r\n| `css`                | `string \\| string[]`                     | —                 | global custom styles; each entry is an absolute file path or inline css                                           |\r\n| `favicon`            | `string`                                 | bundled default   | filesystem path to a custom favicon                                                                               |\r\n| `static_max_age`     | `string \\| number`                       | `\"1d\"`            | cache duration for bundled styles/scripts; set to `0` during development                                          |\r\n| `datetime_formatter` | `(date: Date) => string`                 | —                 | how datetimes render everywhere in the panel                                                                      |\r\n| `fallback_value`     | `string`                                 | —                 | what to show when a field is empty                                                                                |\r\n| `timestamps`         | `{ created?: string; updated?: string }` | —                 | names of your timestamp fields if they aren't the mongoose defaults                                               |\r\n| `version_key`        | `string`                                 | `__v`             | the version key of your schemas, if enabled                                                                       |\r\n| `explorables`        | `string[]`                               | all models        | which models appear in the sidebar; an inclusion list (`[\"A\", \"B\"]`) or an exclusion list (`[\"-A\"]`) — never both |\r\n| `show_indexes`       | `boolean`                                | `false`           | show model indexes in the interface                                                                               |\r\n| `indexes_ttl`        | `number`                                 | —                 | how long (seconds) collected index info is cached                                                                 |\r\n| `query_runner`       | `boolean \\| string[]`                    | `false`           | enable the query runner; pass an array of model names to restrict it                                              |\r\n| `titles`             | `Titles`                                 | —                 | customize page titles                                                                                             |\r\n| `theming`            | `ThemingOptions`                         | dark              | theme base, colors and font — see [theming](#theming)                                                             |\r\n| `widgets`            | `Widget[]`                               | —                 | dashboard widgets — see [dashboard & widgets](#dashboard--widgets)                                                |\r\n| `dashboard`          | `Dashboard`                              | widget grid       | dashboard customization and composition                                                                           |\r\n| `resources`          | `ResourcesOptions`                       | `{}`              | per-model configuration — see [resources](#resources)                                                             |\r\n| `pages`              | `CustomPage[]`                           | —                 | custom pages — see [custom pages](#custom-pages)                                                                  |\r\n| `access_control`     | `ExplorerAccessControlPolicy`            | —                 | role-based access control — see [identity & access](#identity--access)                                            |\r\n| `user_resolver`      | `(req) => user`                          | —                 | resolves the authenticated admin from the request                                                                 |\r\n| `oversight`          | `(req) => boolean`                       | —                 | marks a request as coming from an overseer                                                                        |\r\n| `admins_resolver`    | `() => ExplorerAdminRef[]`               | —                 | lists all admins, used for code assignments and binding                                                           |\r\n| `admins_page`        | `AdminsPageOptions`                      | —                 | rebrands or replaces the built-in admins page markup — see [identity & access](#identity--access)                 |\r\n| `intent`             | `IntentOptions`                          | —                 | configures the intent system — see [intent system](#intent-system)                                                |\r\n\r\ndetails on the options that need more than a table row:\r\n\r\n### `rootpath`\r\n\r\n```typescript\r\nconst explorer = new MongooseExplorer({\r\n  mongoose,\r\n  rootpath: \"/custom-admin\" // access via http://your-app.com/custom-admin\r\n});\r\n```\r\n\r\n### `css`\r\n\r\neach entry may be an absolute path to a css file or an inline css string:\r\n\r\n```typescript\r\nconst explorer = new MongooseExplorer({\r\n  mongoose,\r\n  css: [\r\n    \"/users/john/project/global.css\",\r\n    `\r\n      .custom-class {\r\n        color: red;\r\n      }\r\n    `\r\n  ]\r\n});\r\n```\r\n\r\n### `datetime_formatter` and `fallback_value`\r\n\r\n`datetime_formatter` controls how every datetime renders in the panel;\r\n`fallback_value` is what an empty field displays:\r\n\r\n```typescript\r\nconst explorer = new MongooseExplorer({\r\n  mongoose,\r\n  datetime_formatter: date => date.toLocaleDateString(),\r\n  fallback_value: \"—\"\r\n});\r\n```\r\n\r\nif your schemas use custom timestamp field names:\r\n\r\n```typescript\r\nconst explorer = new MongooseExplorer({\r\n  mongoose,\r\n  timestamps: {\r\n    created: \"created_at\",\r\n    updated: \"updated_at\"\r\n  }\r\n});\r\n```\r\n\r\n### `titles`\r\n\r\n```typescript\r\ninterface Titles {\r\n  dashboard?: string;\r\n  list?: (resource: string) => string;\r\n  view?(resource: string, doc: RawDoc): string;\r\n  edit?(resource: string, doc: RawDoc): string;\r\n  create?: (resource: string) => string;\r\n  query_runner?: (resource: string) => string;\r\n  error?: (error: Error) => string;\r\n}\r\n```\r\n\r\n### `user_resolver`\r\n\r\nthis is the panel's integration point with your authentication. it receives\r\nthe express request and returns the identity of the admin currently using the\r\npanel. how you authenticate (sessions, jwt, …) is entirely your app's\r\nbusiness. the panel only consumes what this resolver returns.\r\n\r\n```typescript\r\nuser_resolver: async req => ({\r\n  username: req.user.email, // display name\r\n  id: req.user._id.toString(), // required — see below\r\n  avatar: req.user.avatar_url, // optional\r\n  logout: {\r\n    path: \"/logout\", // your app's logout endpoint\r\n    method: \"post\" // \"post\" | \"delete\"\r\n  }\r\n});\r\n```\r\n\r\n> **breaking in v4:** the returned identity must now include a stable `id`.\r\n> every intent record (code ownership, bindings, requests, assignments,\r\n> decisions) keys on this id — never on the username or email — so the audit\r\n> trail survives later identity changes. without `user_resolver` the panel\r\n> runs without identity: no user menu, no intent system.\r\n\r\n## identity & access\r\n\r\nthe panel never authenticates anyone. your host app decides who is allowed\r\ninto the panel and how; the panel only asks \"who is this request from?\" and\r\n\"what can they do?\" through a handful of integration points:\r\n\r\n- `user_resolver` — who the admin is (identity shape, above)\r\n- `access_control` — what their role may see and do\r\n- `oversight` — whether they are an overseer (intent minting authority)\r\n- `admins_resolver` — the full list of admins (for code assignments)\r\n\r\n### `access_control` (explorer level)\r\n\r\n```typescript\r\ninterface ExplorerAccessControlPolicy {\r\n  role_resolver: (req: Request) => string;\r\n  rules?: Record<string, ExplorerAccessControlRule>;\r\n  augment?: (\r\n    role: string,\r\n    req: Request,\r\n    rule: ExplorerAccessControlRule\r\n  ) =>\r\n    | MaybePromise<string>\r\n    | MaybePromise<{ rule: ExplorerAccessControlRule; replace?: boolean }>;\r\n}\r\n\r\ninterface ExplorerAccessControlRule {\r\n  pages?: string[] | Record<string, PageAccessControl>;\r\n  resources?: string[];\r\n  widgets?: string[];\r\n}\r\n\r\ninterface PageAccessControl {\r\n  view: boolean;\r\n  action: boolean;\r\n}\r\n```\r\n\r\n- `role_resolver` tells the panel which role the current request belongs to.\r\n  roles are just strings — \"super-admin\", \"editor\", anything you like.\r\n- `rules` maps roles to what they may see. every role resolved by\r\n  `role_resolver` must have a matching rule — the panel throws if it\r\n  doesn't find one.\r\n- `resources` lists the model names the role may access (sidebar + pages).\r\n- `widgets` lists widget `key`s (falling back to `title`) the role may see.\r\n- `pages` either lists page paths (view-only) or, per page, an object with\r\n  `view` and `action` flags — so a role can see a page's form but never\r\n  submit it.\r\n- `augment` lets you adjust a role's rule per-request. return a role string\r\n  to borrow that role's rule instead, or `{ rule, replace }` — when\r\n  `replace` is true the given rule replaces the role's rule outright,\r\n  otherwise it is merged on top. return `\"*\"` (the wildcard string) to\r\n  grant **full access** — all restrictions are cleared for that request.\r\n\r\n```typescript\r\naccess_control: {\r\n  role_resolver: req => req.user.role,\r\n  rules: {\r\n    editor: {\r\n      resources: [\"Post\", \"Comment\"],\r\n      widgets: [\"total-users\"],\r\n      pages: {\r\n        robot: { view: true, action: false }\r\n      }\r\n    }\r\n  },\r\n  augment: (role, req, rule) =>\r\n    req.user.tenants_only ? { rule: { ...rule, resources: rule.resources?.filter(r => TENANT_MODELS.includes(r)) } } : rule\r\n}\r\n```\r\n\r\n### `access_control` on a resource (resource level)\r\n\r\neach model can carry its own finer-grained policy — operations, fields,\r\nquery-runner capabilities and even individual actions. see\r\n[resource access control](#access_control-resource-level).\r\n\r\n### `oversight` and `admins_resolver`\r\n\r\nthese two power the intent system's trust model:\r\n\r\n- `oversight(req)` returns `true` for overseers — the admins who approve\r\n  code requests and mint codes when minting is restricted.\r\n- `admins_resolver()` returns every admin as `{ id, username, ... }`.\r\n  you may attach arbitrary extra properties to each object — they are\r\n  carried through but not used by the panel.\r\n  when a code is bound to another admin, the panel uses this list to resolve\r\n  who should receive the assignment.\r\n\r\n> **breaking in v4:** `admins_resolver` must return objects with `id` and\r\n> `username`. the old string shorthand is gone.\r\n\r\n### `admins_page`\r\n\r\nthe admins directory page (`/admins`) ships a default table of\r\n`admins_resolver` refs. `admins_page` replaces its markup.\r\n\r\n```typescript\r\ninterface AdminsPageOptions {\r\n  pug?: string;\r\n  render?: (ctx: AdminsPageRenderContext) => MaybePromise<string>;\r\n  label?: string;\r\n  title?: string;\r\n  head?: string | string[];\r\n  js?: string | string[];\r\n  css?: string | string[];\r\n  locals?: (req: Request) => MaybePromise<AnyObject>;\r\n}\r\n\r\ninterface AdminsPageRenderContext {\r\n  req: Request;\r\n  theme: Theme;\r\n  admins: ExplorerAdminRef[];\r\n  intent_enabled: boolean;\r\n  admins_url: string;\r\n  intents_url: string;\r\n  new_code_url: (admin: ExplorerAdminRef) => string;\r\n  link: (path: string) => string;\r\n}\r\n```\r\n\r\n- `pug` is an absolute path to a standalone pug template rendered with\r\n  `admins`, `theme`, `intent_enabled`, `admins_url`, `intents_url`,\r\n  `new_code_url(admin)`, `link(...)` and whatever `locals(req)` returns\r\n  merged in. `render` is the full-control alternative — return an html\r\n  string (build it with `link(...)`, `admins_url` and `new_code_url`,\r\n  never hardcode panel paths). the two are mutually exclusive — omit both\r\n  to keep the built-in table and only rebrand it; `admins_page` cannot be\r\n  set without `admins_resolver`.\r\n- `head`, `js` and `css` accept inline content or absolute file paths,\r\n  mixed in arrays.\r\n- `label` renames the sidebar entry and the page header; `title` sets the\r\n  browser title. these work without a custom template — passing only\r\n  `label` (or `title`) keeps the built-in table and rebrands it, as do\r\n  `head`, `js` and `css`; `locals` only applies to `pug` templates.\r\n- `intents_url` links to the intent screen (requests, approvals); when\r\n  `intent_enabled` is true, `new_code_url(admin)` builds the same\r\n  pre-filled minting link the default table uses — an anchor to the\r\n  intents screen with the admin preselected as the bind target. render\r\n  one per admin to reproduce the default \"New code\" column in a custom\r\n  layout.\r\n\r\n```typescript\r\nadmins_resolver: async () => Admin.find().lean(),\r\nadmins_page: {\r\n  pug: asset(\"admins.pug\"),\r\n  label: \"Directory\"\r\n}\r\n```\r\n\r\n## resources\r\n\r\nthe `resources` option is a map of model name → per-model configuration:\r\n\r\n```typescript\r\ninterface ResourceOptions {\r\n  explorable?: boolean;\r\n  creatable?: boolean;\r\n  deletable?: boolean | ((doc: Anything) => boolean);\r\n  editable?: boolean | ((doc: Anything) => boolean);\r\n  sortable?: boolean;\r\n  limit?: number;\r\n  properties?: Record<string, PropertyOptions>;\r\n  viewables?: string[];\r\n  filterables?: string[];\r\n  creatables?: string[];\r\n  editables?: string[];\r\n  sortables?: string[];\r\n  ref_newtab?: boolean;\r\n  virtuals?: Record<string, (doc: Anything) => string>;\r\n  show_indexes?: boolean;\r\n  query_runner?: {\r\n    enabled?: boolean;\r\n    presets?: { name: string; resolver: () => Promise<Anything> }[];\r\n  };\r\n  actions?: Actions;\r\n  cascade_delete?:\r\n    | {\r\n        model: Model<Anything>;\r\n        relation: { local_field: string; foreign_field: string };\r\n      }[]\r\n    | ((doc: Document, session?: ClientSession) => Promise<void>);\r\n  access_control?: ResourceAccessControlPolicy;\r\n  intent?: true | { operations?: GuardedOperationSelection[]; scope?: Scope };\r\n}\r\n```\r\n\r\n### visibility flags\r\n\r\n- `explorable`, `creatable`, `deletable`, `editable`, `sortable` toggle the\r\n  corresponding features. `deletable`/`editable` may also be functions\r\n  receiving the document — returning `false` hides the button for that\r\n  document only.\r\n- `viewables`, `creatables`, `editables`, `filterables`, `sortables` restrict\r\n  which fields appear in each context. anything not listed is dropped there.\r\n- `limit` sets the page size of the documents list (default: `15`,\r\n  changeable from the interface afterwards).\r\n- `ref_newtab` opens referenced documents in a new tab.\r\n\r\n### `properties`\r\n\r\nper-field rendering and behavior:\r\n\r\n```typescript\r\ninterface PropertyOptions {\r\n  label?: string;\r\n  editable?: boolean;\r\n  sortable?: boolean;\r\n  viewable?: boolean;\r\n  filterable?: boolean;\r\n  required?: boolean | ((value?: Anything) => boolean);\r\n  creatable?: boolean;\r\n  textarea?: boolean;\r\n  as_image?: boolean;\r\n  renderers?: Renderers;\r\n}\r\n\r\ninterface Renderers {\r\n  list?: (value: Anything) => string;\r\n  view?: (value: Anything) => string;\r\n  create?: (enum_values: Anything[] | null) => string;\r\n  edit?: (params: {\r\n    value: Anything;\r\n    enum_values: Anything[] | null;\r\n  }) => string;\r\n  filter?: (enum_values: Anything[] | null) => string;\r\n}\r\n```\r\n\r\n`as_image` renders string urls as `<img>`. `renderers` let you take full\r\ncontrol of how a field displays (list/view) or what its editor input looks\r\nlike (create/edit/filter).\r\n\r\n### `virtuals`\r\n\r\ncomputed columns on the list page, keyed by a display name:\r\n\r\n```typescript\r\nvirtuals: {\r\n  \"full name\": doc => `${doc.first_name} ${doc.last_name}`\r\n}\r\n```\r\n\r\n### `cascade_delete`\r\n\r\nwhen a document is deleted, related documents can go with it. either declare\r\nthe relations declaratively:\r\n\r\n```typescript\r\ncascade_delete: [\r\n  {\r\n    model: Comment,\r\n    relation: { local_field: \"_id\", foreign_field: \"post_id\" }\r\n  }\r\n];\r\n```\r\n\r\nor take full control with a function (receives an optional transaction\r\nsession — use it if you need consistency):\r\n\r\n```typescript\r\ncascade_delete: async (doc, session) => {\r\n  await Comment.deleteMany({ post_id: doc._id }, { session });\r\n};\r\n```\r\n\r\n> **the function handles related documents only.** the library deletes the\r\n> parent document itself, after your function runs. do **not** delete the\r\n> parent inside your function — it would be deleted twice (the second\r\n> delete a silent no-op), and it works against the transaction and ordering\r\n> the library sets up for you.\r\n\r\n> **good to know:**\r\n>\r\n> - overriding `actions.delete` replaces the built-in delete entirely —\r\n>   including the cascade. handle cascading yourself there if you need it\r\n> - on a replica set, your function and the parent delete run inside one\r\n>   transaction; on a standalone mongodb there is no transaction, so a\r\n>   mid-way failure can leave partial state\r\n\r\n### `actions`\r\n\r\nlifecycle overrides and custom actions:\r\n\r\n```typescript\r\ninterface Actions {\r\n  create?(ctx: { req: Request; body: AnyObject }): Promise<Document>;\r\n  delete?(ctx: { req: Request; doc: Document }): Promise<Document>;\r\n  update?(ctx: {\r\n    req: Request;\r\n    doc: Document;\r\n    body: AnyObject;\r\n    timestamps: boolean;\r\n  }): Promise<Document>;\r\n  custom?: CustomActions;\r\n}\r\n```\r\n\r\nthe `create`/`delete`/`update` overrides replace the panel's default\r\nbehavior for those operations. when you override `create`, the panel skips\r\nits own create form handling for whatever you decide.\r\n\r\n> **breaking in v4:** all of these receive a single destructured context\r\n> object instead of positional arguments.\r\n\r\n#### custom actions\r\n\r\ncustom actions hang off documents or selections of documents:\r\n\r\n```typescript\r\ninterface DocumentAction {\r\n  operation: string;\r\n  fields?: ActionField[];\r\n  schema?: StandardSchemaV1 | (() => StandardSchemaV1);\r\n  execute: (ctx: DocumentActionContext) => MaybePromise<void>;\r\n  applicable?(doc: RawDoc): boolean;\r\n  guard?: boolean | string;\r\n  intent?: boolean | { scope?: Scope };\r\n  element?: {\r\n    variant?: Variant;\r\n    color?: Color;\r\n    size?: Size;\r\n    label?: string;\r\n    style?: string;\r\n  };\r\n}\r\n\r\ninterface DocumentActionContext {\r\n  req: Request;\r\n  doc: Document;\r\n  data: AnyObject;\r\n}\r\n```\r\n\r\n`BulkAction` is identical except `execute` receives `BulkActionContext`\r\n(`{ req, ids, data }`) and there is no `applicable`.\r\n\r\npressing the action's button:\r\n\r\n- with no `fields` and no `schema`, the action runs immediately\r\n- with `schema` only, the panel prompts for a raw input; the schema receives\r\n  **raw strings**\r\n- with `fields` declared, the panel opens a modal with the configured\r\n  inputs; the submitted values are converted to the declared types before\r\n  your schema/`execute` sees them, and an optional `schema` validates the\r\n  **typed** values\r\n- with `intent` set and `fields` declared, one combined modal collects the\r\n  intent code and the inputs in a single submit\r\n\r\n`fields` shape:\r\n\r\n```typescript\r\ninterface ActionField {\r\n  name: string;\r\n  label?: string;\r\n  type?: \"string\" | \"number\" | \"boolean\" | \"datetime\" | \"select\" | \"textarea\";\r\n  required?: boolean;\r\n  default?: string | number | boolean;\r\n  placeholder?: string;\r\n  options?: (string | number | { label: string; value: string | number })[];\r\n  textarea?: boolean;\r\n  rows?: number;\r\n  hint?: string;\r\n}\r\n```\r\n\r\n`applicable` hides a document action on documents where it doesn't make\r\nsense. `element` styles the button. `guard` adds a browser confirmation\r\nbefore the action runs: `guard: true` shows a generic \"Are you sure?\",\r\nwhile a string shows that message as the confirmation prompt — without it\r\nthe action runs immediately. it's a confirmation, not an authorization\r\ngate; use `access_control` for that.\r\n\r\nfor full type inference in `execute`, use the `define_document_action` /\r\n`define_bulk_action` helpers — the `data` type is inferred from the schema:\r\n\r\n```typescript\r\nimport { define_document_action } from \"@kamalyb/mongoose-explore\";\r\nimport * as v from \"valibot\";\r\n\r\nactions: {\r\n  custom: {\r\n    document: [\r\n      define_document_action({\r\n        operation: \"send-reset-email\",\r\n        element: { color: \"primary\", label: \"Send reset email\" },\r\n        fields: [\r\n          {\r\n            name: \"template\",\r\n            label: \"Template\",\r\n            type: \"select\",\r\n            required: true,\r\n            options: [\"generic\", \"strict\"]\r\n          },\r\n          { name: \"reason\", label: \"Reason\", type: \"textarea\", required: true }\r\n        ],\r\n        schema: v.strictObject({\r\n          template: v.picklist([\"generic\", \"strict\"]),\r\n          reason: v.pipe(v.string(), v.nonEmpty())\r\n        }),\r\n        execute: async ({ doc, data }) => {\r\n          // data is fully typed: { template: \"generic\" | \"strict\"; reason: string }\r\n          await send_reset_email(doc, data);\r\n        }\r\n      })\r\n    ];\r\n  }\r\n}\r\n```\r\n\r\n**`fields` vs `schema` — two different jobs.** `fields` serves two purposes:\r\nit generates the modal UI _and_ drives type coercion. when `fields` are\r\ndeclared, values are converted to the declared types\r\n(`\"5\"` → `5`, `\"on\"` → `true`, …) before the schema sees them.\r\n\r\n```typescript\r\n// schema-only: no fields → schema receives raw strings\r\n{\r\n  operation: \"send-reset-email\",\r\n  schema: v.strictObject({ reason: v.string() }),\r\n  execute: ({ doc, data }) => {\r\n    // data.reason is the raw string from the prompt\r\n  }\r\n}\r\n\r\n// with fields: coercion happens before validation\r\n{\r\n  operation: \"send-reset-email\",\r\n  fields: [{ name: \"reason\", label: \"Reason\", type: \"textarea\", required: true }],\r\n  schema: v.strictObject({ reason: v.string() }),\r\n  execute: ({ doc, data }) => {\r\n    // data.reason is a typed, coerced value\r\n  }\r\n}\r\n```\r\n\r\na schema-only action (no `fields`) receives raw strings — the schema must\r\nhandle type conversion itself. when `fields` are declared, values are\r\nconverted to the declared types before the schema and `execute` see them.\r\n\r\nevery custom action run emits a `document_action` / `bulk_action` event that\r\nincludes the submitted `data` — see [events](#events).\r\n\r\n### `access_control` (resource level)\r\n\r\n```typescript\r\ninterface ResourceAccessControlPolicy {\r\n  rules: Record<string, ResourceAccessControlRule>;\r\n  augment?: (\r\n    role: string,\r\n    req: Request,\r\n    rule\r\n  ) => MaybePromise<string> | MaybePromise<{ rule; replace?: boolean }>;\r\n}\r\n\r\ninterface ResourceAccessControlRule {\r\n  operations?: ResourceOperation[] | [\"*\"] | `-${ResourceOperation}`[];\r\n  fields?: {\r\n    viewables?: string[];\r\n    creatables?: string[];\r\n    editables?: string[];\r\n    filterables?: string[];\r\n    sortables?: string[];\r\n  };\r\n  query_runner?: {\r\n    operations?: QueryRunnerOperation[] | [\"*\"] | `-${QueryRunnerOperation}`[];\r\n    presets?: string[];\r\n  };\r\n  actions?: {\r\n    bulk?: string[];\r\n    document?: string[];\r\n  };\r\n}\r\n\r\ntype ResourceOperation =\r\n  \"create\" | \"read\" | \"update\" | \"delete\" | \"query_runner\";\r\ntype QueryRunnerOperation =\r\n  | \"find\"\r\n  | \"findOne\"\r\n  | \"aggregate\"\r\n  | \"countDocuments\"\r\n  | \"estimatedDocumentCount\";\r\n```\r\n\r\n- `operations` lists what the role may do. use `[\"*\"]` for everything, or\r\n  prefix with `-` to allow everything **except** the listed operations\r\n  (exclusions cannot be mixed with inclusions). the query runner respects\r\n  this too — `aggregate` only runs when the role's `query_runner.operations`\r\n  allow it.\r\n- `fields` narrows the visible/editable/filterable/sortable fields per role.\r\n- `query_runner.presets` restricts which named presets the role can run.\r\n- `actions` gates individual custom actions by their `operation` name.\r\n- `augment` works exactly like the explorer-level one.\r\n\r\n### `intent` (per resource)\r\n\r\npass `intent: true` to guard all of the resource's operations behind an\r\nintent code, or scope it down:\r\n\r\n```typescript\r\nintent: {\r\n  operations: [\"-create\"], // guard everything except create\r\n  scope: \"users\"\r\n}\r\n```\r\n\r\nsee the [intent system](#intent-system) for the full picture.\r\n\r\n## query runner\r\n\r\nenable ad-hoc queries from the interface with `query_runner: true` (or pass\r\nan array of model names — `\"User\"`, `\"Post\"` — to offer the runner on those\r\nmodels only). it exposes `find`, `findOne`, `aggregate`,\r\n`countDocuments` and `estimatedDocumentCount`, with two labeled editors —\r\n**Query** for the query (or the aggregate pipeline) and **Options** for query\r\noptions. the JSON editor syntax-colors are theme-aware.\r\n\r\n> known quirk: running a `count` operation against an empty result can\r\n> surface a validation error from the runner's request schema. the\r\n> operation itself succeeds; the response payload is what trips validation.\r\n\r\nper-role capabilities are controlled by the resource-level\r\n`access_control` rules (`query_runner.operations`, `query_runner.presets`).\r\npresets are named, server-defined queries you offer as one-click buttons:\r\n\r\n```typescript\r\nresources: {\r\n  User: {\r\n    query_runner: {\r\n      enabled: true,\r\n      presets: [\r\n        {\r\n          name: \"admins\",\r\n          resolver: () => User.find({ role: { $ne: \"default\" } }).lean()\r\n        }\r\n      ]\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nevery query run emits a `query_runner` event with the query and options —\r\nuse it for auditing.\r\n\r\n## custom pages\r\n\r\ncustom pages mount inside the panel's layout with the sidebar intact:\r\n\r\n```typescript\r\ninterface CustomPage {\r\n  path: string;\r\n  pug: string;\r\n  label?: string;\r\n  head?: string | string[];\r\n  js?: string | string[];\r\n  css?: string | string[];\r\n  locals?: (req: Request) => MaybePromise<AnyObject>;\r\n  action?: (ctx: PageActionContext) => MaybePromise<void>;\r\n  schema?: StandardSchemaV1 | (() => StandardSchemaV1);\r\n  intent?: true | { scope?: Scope };\r\n  title?: string;\r\n}\r\n\r\ninterface PageActionContext {\r\n  req: Request;\r\n  body: AnyObject;\r\n}\r\n```\r\n\r\n- `path` is the page's URL slug (e.g. `\"robot\"`) — do not prefix it with `/`.\r\n- `pug` is an absolute path to your pug template; it renders with `locals`\r\n  merged in. `head` entries are inline html (external stylesheets, scripts);\r\n  `js` and `css` accept absolute paths or inline content, mixed in arrays.\r\n- `schema` is any [standard schema](https://standardschema.dev/) — valibot,\r\n  zod v3.23+, arktype v2+, vinejs v4+, … (joi was removed in v4). the\r\n  function form simply returns a schema; it no longer receives a joi root.\r\n  page form submissions arrive as **raw strings**, so the schema is\r\n  responsible for type conversion (e.g. `v.pipe(v.string(), v.toNumber())`\r\n  or `z.coerce.number()`). this is by design — pages use custom pug\r\n  templates, not generated modal ui, so there is no field-type metadata\r\n  to drive automatic coercion.\r\n- `action` handles the page's form submit and receives the (validated) body.\r\n  the page's failure UX is the panel's: validation errors are shown inline\r\n  and nothing reaches your `action` until the schema passes.\r\n\r\n```typescript\r\nimport { define_page_action } from \"@kamalyb/mongoose-explore\";\r\nimport * as v from \"valibot\";\r\n\r\npages: [\r\n  define_page_action({\r\n    path: \"robot\",\r\n    pug: asset(\"robot.pug\"),\r\n    label: \"Robot\",\r\n    intent: true,\r\n    action({ body }) {\r\n      // body is fully typed from the schema\r\n    },\r\n    schema: v.strictObject({\r\n      name: v.optional(v.pipe(v.string(), v.nonEmpty())),\r\n      age: v.optional(v.pipe(v.string(), v.toNumber(), v.minValue(10)))\r\n    })\r\n  })\r\n];\r\n```\r\n\r\n**type coercion examples.** since page form values arrive as raw strings,\r\nyour schema must convert them. here's how common types look in valibot\r\nand zod:\r\n\r\n```typescript\r\n// valibot — pipe strings through coercion transforms\r\nimport * as v from \"valibot\";\r\n\r\nschema: v.strictObject({\r\n  name: v.pipe(v.string(), v.nonEmpty()),\r\n  age: v.pipe(v.string(), v.toNumber(), v.minValue(0)),\r\n  active: v.pipe(v.string(), v.toBoolean()),\r\n  role: v.picklist([\"admin\", \"editor\", \"viewer\"]),\r\n  // for datetime inputs, parse the string into a Date\r\n  start_date: v.pipe(v.string(), v.transform(s => new Date(s)))\r\n})\r\n```\r\n\r\n```typescript\r\n// zod — coerce helpers handle the string→type conversion\r\nimport { z } from \"zod\";\r\n\r\nschema: z.strictObject({\r\n  name: z.string().min(1),\r\n  age: z.coerce.number().min(0),\r\n  active: z.coerce.boolean(),\r\n  role: z.enum([\"admin\", \"editor\", \"viewer\"]),\r\n  start_date: z.coerce.date()\r\n})\r\n```\r\n\r\n> the two-sided intent contract: `intent: true` on a page means the page's\r\n> action requires a code, and the page's template must declare where the\r\n> code goes. `action` only fires after the code verifies (or, with a\r\n> `scope`, after it verifies against that scope). access control separately\r\n> decides whether a role can view (`PageAccessControl.view`) and submit\r\n> (`PageAccessControl.action`) the page — a role with view-only access sees\r\n> the form but can never trigger the action.\r\n\r\n**wiring a page up with intent.** the panel hands your pug template an\r\n`intent_scope()` helper. set `data-intent` on your form to the page's scope\r\nand set a label — the panel attaches the code input to that form and takes\r\ncare of submission and verification:\r\n\r\n```pug\r\n//- robot.pug\r\nform(\r\n  method=\"post\",\r\n  data-intent=intent_scope(),\r\n  data-intent-label=\"Robot action\"\r\n)\r\n  input.input.size-sm(name=\"name\", type=\"text\", placeholder=\"name\")\r\n  button.button.color-primary.variant-filled.size-sm(type=\"submit\") submit\r\n```\r\n\r\n```typescript\r\npages: [\r\n  define_page_action({\r\n    path: \"robot\",\r\n    pug: asset(\"robot.pug\"),\r\n    label: \"Robot\",\r\n    intent: true, // the action now requires a code\r\n    action({ body }) {\r\n      // body arrives clean — the intent code field is handled by the panel\r\n    },\r\n    schema: v.strictObject({ name: v.string() })\r\n  })\r\n];\r\n```\r\n\r\nnotes:\r\n\r\n- `intent_scope()` returns the page's default scope (`pages.<path>`);\r\n  pass a string (`intent_scope(\"payments\")`) to use your own.\r\n- if intent is disabled or the visitor already has nothing to verify, the\r\n  attribute is harmless — the form behaves like a normal form.\r\n\r\n**wiring a page up with access control.** the panel passes your pug template\r\na `can_execute_action` boolean — `true` when the role may submit the form,\r\n`false` when view-only. wrap your form in a conditional to hide it from roles\r\nthat cannot act:\r\n\r\n```pug\r\n//- robot.pug\r\nif can_execute_action\r\n  form(\r\n    method=\"post\",\r\n    data-intent=intent_scope(),\r\n    data-intent-label=\"Robot action\"\r\n  )\r\n    input.input.size-sm(name=\"name\", type=\"text\", placeholder=\"name\")\r\n    button.button.color-primary.variant-filled.size-sm(type=\"submit\") submit\r\nelse\r\n  p you do not have permission to perform this action\r\n```\r\n\r\nthe template also receives a `link(path)` helper for generating links to\r\nother custom pages inside the panel.\r\n\r\n## dashboard & widgets\r\n\r\n### widgets\r\n\r\n```typescript\r\nconst explorer = new MongooseExplorer({\r\n  mongoose,\r\n  widgets: [\r\n    {\r\n      type: \"stat\",\r\n      key: \"total-users\",\r\n      title: \"Total users\",\r\n      resolver: async () => User.countDocuments()\r\n    },\r\n    {\r\n      type: \"bar chart\",\r\n      title: \"Monthly signups\",\r\n      resolver: async () => ({\r\n        labels: [\"Jan\", \"Feb\", \"Mar\"],\r\n        datasets: [{ label: \"New users\", data: [12, 34, 21] }]\r\n      })\r\n    },\r\n    {\r\n      type: \"tabular\",\r\n      title: \"Recent orders\",\r\n      header: true,\r\n      resolver: async () =>\r\n        Order.find().sort({ created_at: -1 }).limit(10).lean()\r\n    },\r\n    {\r\n      type: \"custom\",\r\n      title: \"Custom content\",\r\n      render: theme => `<div style=\"text-align:center\">…</div>`,\r\n      element: { style: \"background: white; border-radius: 8px;\" }\r\n    }\r\n  ]\r\n});\r\n```\r\n\r\nwidget types: `stat`, `bar chart`, `pie chart`, `doughnut chart`, `line\r\nchart`, `radar chart`, `tabular`, `custom`. by default they render in a\r\nresponsive grid — circular charts are square and centered, bar/line charts\r\nfill their card, and tabular widgets span the full width with a scrollable\r\nbody.\r\n\r\n**`key` serves two purposes:** it is the lookup key for dashboard composition\r\n_and_ the identifier used by access control. it must be unique across\r\nwidgets. if omitted, `title` falls back as the key (supported for\r\ncompatibility, not recommended).\r\n\r\nper-widget `element.style` overrides the type-level style (`dashboard.styles.<type>`)\r\nfor that card only.\r\n\r\nstat widgets accept an optional `render` callback —\r\n`(value: string | number, theme: Theme) => string` — to customize how the\r\nvalue displays (e.g. adding a suffix or coloring):\r\n\r\n### composition (custom layouts)\r\n\r\n> **breaking in v4:** `dashboard.render` now receives a single context\r\n> object instead of `(req, theme)`.\r\n\r\n```typescript\r\ndashboard: {\r\n  render: ({ req, theme, widgets }) => `\r\n    ${widgets.has(\"total-users\") ? `\r\n    <section>\r\n      <h3>Users</h3>\r\n      ${widgets.render(\"total-users\")}\r\n    </section>\r\n    ` : \"\"}\r\n  `,\r\n  head: `<link rel=\"stylesheet\" href=\"https://cdn.example.com/custom-styles.css\">`,\r\n  js: `document.querySelector(\".stat-card\")?.addEventListener(\"click\", () => {})`,\r\n  css: `.stat-card { border-radius: var(--radius-md); }`,\r\n  styles: {\r\n    wrapper: `padding: 2rem;`,\r\n    widgets: `display: grid; gap: 1rem;`,\r\n    stat: `background: white;`,\r\n    piechart: ``,\r\n    barchart: ``,\r\n    tabular: ``,\r\n    doughnutchart: ``,\r\n    linechart: ``,\r\n    radarchart: ``\r\n  }\r\n}\r\n```\r\n\r\nthe widget context:\r\n\r\n```typescript\r\ninterface WidgetContext {\r\n  /** render the full widget card, keyed by its `key ?? title` */\r\n  render: (key: string) => string;\r\n  /** metadata + pre-rendered html for every widget, for arbitrary layout */\r\n  all: { key: string; title: string; type: string; html: string }[];\r\n  /** access-control aware check — always use it in custom dashboards */\r\n  has: (key: string) => boolean;\r\n}\r\n```\r\n\r\n- when a custom `render` is provided, the default widget grid is suppressed —\r\n  you decide where widgets go. both `dashboard.render` and `custom` widget\r\n  `render` may be async.\r\n- `widgets.has(key)` checks access control before rendering. **always use it\r\n  in custom dashboards** — without it, a user who lacks access to a widget\r\n  sees an empty card with a title but no content. with `has()`, the card is\r\n  omitted entirely and the layout stays clean.\r\n- `dashboard.js` is rendered for real in v4 (dashboard-specific javascript\r\n  previously never loaded).\r\n\r\n### layout (declarative composition)\r\n\r\nif writing html for your dashboard feels like overkill, `dashboard.layout`\r\ndescribes the same result as rows of widget references. it's the middle ground\r\nbetween the default flat grid and a hand-written `render`.\r\n\r\n```typescript\r\ndashboard: {\r\n  layout: {\r\n    columns: 12,            // optional, defaults to 12 for every row\r\n    gap: \"1.5rem\",          // optional vertical spacing between rows (defaults to var(--spacing-lg))\r\n    rows: [\r\n      // a full-width hero widget\r\n      { widgets: [{ key: \"summary\" }] },\r\n      // two half-width stats\r\n      { widgets: [\r\n        { key: \"revenue\",  span: 6 },\r\n        { key: \"signups\",  span: 6 }\r\n      ] },\r\n      // an 8-wide chart next to a 4-wide aside\r\n      { widgets: [\r\n        { key: \"orders-chart\", span: 8 },\r\n        { key: \"latest-orders\", span: 4 }\r\n      ] }\r\n    ]\r\n  },\r\n  styles: {\r\n    wrapper: `padding: 1rem;`   // theme the rows/cells via styles.wrapper\r\n  }\r\n}\r\n```\r\n\r\nrules:\r\n\r\n- each row is a CSS grid of `columns` tracks (per-row `columns` overrides the\r\n  layout default). `widgets` reference widgets by their `key ?? title`.\r\n- `gap` sets the vertical spacing between rows (any css length, e.g. a\r\n  `--spacing-*` token); it defaults to `var(--spacing-lg)`. the spacing\r\n  _within_ a row — between cells — stays at `var(--spacing-lg)` and isn't\r\n  configurable yet.\r\n- rows with no `span`s share the row **equally**. when any `span` is declared,\r\n  every widget must declare one and their sum must equal the row's column\r\n  count — the constructor throws otherwise (so a row mis-match is caught at\r\n  startup, not on the dashboard page).\r\n- widgets render through the exact same pipeline as the default grid, so\r\n  per-type styles, custom `render`, charts, and `element.style` all behave the\r\n  same. each widget is wrapped in a `.widget-row` / `.widget-cell` you can\r\n  target via `dashboard.styles.wrapper` (or global css).\r\n- access control still governs **whether** a widget renders. a referenced\r\n  widget the user can't see simply drops out and the row re-flows.\r\n- `layout` and `render` are mutually exclusive — set one or the other.\r\n- the whole thing collapses to a single column on narrow screens by default.\r\n\r\n## theming\r\n\r\n```typescript\r\ninterface ThemingOptions {\r\n  theme?: \"dark\" | \"light\"; // default \"dark\"\r\n  colors?: Colors;\r\n  font?: { family: string; url?: string };\r\n}\r\n```\r\n\r\n- pick a base with `theme`. every color token can be overridden through\r\n  `colors`; unknown extra tokens go in `colors.custom` (any css value is\r\n  accepted — hex, rgb, `color-mix`, …).\r\n- `font.url` is now optional: a system font stack alone no longer triggers\r\n  an external stylesheet load.\r\n- the light theme is **derived** from the dark palette (shared accents,\r\n  background/text pinned to white/black, neutrals computed from those\r\n  anchors), so the two themes stay in sync as you override.\r\n\r\ntokens include: `primary`, `secondary`, `danger`, `warning`, `success`,\r\n`border`, `text`, `shade`, `dark`, `gray`, `background`, `white`,\r\n`button_text`, `table_header_bg`, `table_header_text`, `row_hover_bg`,\r\n`badge_bg`, `badge_text`, `input_bg`, `input_text`, `dropdown_bg`,\r\n`dropdown_text`, `disabled_bg`, `disabled_text`.\r\n\r\nprefer tweaking tokens over writing raw css — but `css` (global) and the\r\nbundled css classes are still fully available for everything else. the\r\npublic class names, tokens, element ids and customization hooks are\r\nunchanged in v4, even though the UI itself was redesigned.\r\n\r\n#### css hooks reference\r\n\r\nevery color token above becomes two things in the panel:\r\n\r\n- a css variable: `--color-<name>` (e.g. `var(--color-primary)`)\r\n- a text class: `.color-<name>` (e.g. `class=\"color-danger\"`), and\r\n  `.button.color-<name>` restyles a button's background\r\n\r\n`colors.custom` entries get the same treatment, so `custom: { brand: \"#f00\" }`\r\ngives you `var(--color-brand)` and `.color-brand` for free.\r\n\r\nthe stable building blocks, everywhere in the panel and in your own pages,\r\nwidgets and templates:\r\n\r\n- components: `.button`, `.input`, `.select`, `.card`, `.badge`\r\n- button variants: `.variant-filled`, `.variant-outline`, `.variant-light`,\r\n  `.variant-subtle`\r\n- sizes: `.size-xs` … `.size-xl` (plus `.size-compact-*` twins)\r\n- layout/utility: `.page-header`, `.page-meta`, `.stack`, `.split`,\r\n  `.actions`\r\n- other tokens: `--radius-*`, `--shadow-*`, `--font-size`,\r\n  `--font-weight-strong`, `--spacing-xs` … `--spacing-xl`\r\n\r\nanything not listed here is an implementation detail and may change —\r\nstick to these hooks (and the tokens) to stay upgrade-safe.\r\n\r\n```typescript\r\ntheming: {\r\n  theme: \"light\",\r\n  colors: { primary: \"#7c3aed\" },\r\n  font: { family: \"Inter\", url: \"https://fonts.googleapis.com/css2?family=Inter&display=swap\" }\r\n}\r\n```\r\n\r\naction buttons accept `variant` (`filled` | `outline` | `light` | `subtle`)\r\nand `size` (`xs` … `xl`, with `compact-*` twins) through each action's\r\n`element`. `element.color` accepts named tokens (`primary`, `secondary`,\r\n`danger`, ...) or any CSS color string — see [resources](#custom-actions).\r\n\r\n## intent system\r\n\r\nthe intent system is a proof-of-intent gate for panel operations. before a\r\nguarded operation runs, the admin must supply a valid intent code — a\r\nshort-lived, use-limited token minted through the panel's dedicated intent\r\nscreen. think \"confirm this destructive action with a code an overseer gave\r\nyou\", enforced by the machine instead of a click.\r\n\r\n### concept\r\n\r\n- every code carries a **scope** (a string you define, e.g. `\"users\"`) and\r\n  guards the operations it covers.\r\n- codes have a **ttl** and a **max-uses**. using a code on a covered\r\n  operation burns a **use**; using it on an operation it doesn't cover burns\r\n  an **attempt** instead. attempts drain until the code is burned — so\r\n  guessing codes against your own operations is self-defeating.\r\n- identical codes can never be active twice: minting a duplicate of an\r\n  active code is rejected. (renewing a constant-generated code after use is\r\n  supported.)\r\n- records bind to the admin's stable `id` (required in `user_resolver`),\r\n  never their email/username — emails change; the audit trail doesn't. rows\r\n  display the name captured when the record was written, so later identity\r\n  changes never rewrite history; the sidebar shows the live identity.\r\n\r\n### configuration\r\n\r\n```typescript\r\ninterface IntentOptions {\r\n  enabled: boolean;\r\n  ttl: number; // how long a code lives, in milliseconds\r\n  max_uses: number; // successful uses per code\r\n  attempts: number; // wrong-scope guesses before burned\r\n  mint_policy?: \"open\" | \"overseers\" | ((req) => MaybePromise<MintPolicy>);\r\n  store?: IntentStore;\r\n  request_store?: IntentRequestStore;\r\n  code?: IntentCodeConfig | (() => MaybePromise<string>);\r\n  user_required?: boolean;\r\n  ui?: IntentUIOptions;\r\n  secret?: string | (() => string);\r\n}\r\n\r\ninterface IntentCodeConfig {\r\n  format?: \"hex\" | \"base32\" | \"base64url\" | \"uuid\";\r\n  length?: number;\r\n  uppercase?: boolean;\r\n}\r\n```\r\n\r\n> when `enabled` is true, `ttl`, `max_uses` and `attempts` are **required** —\r\n> the package never imposes policy defaults for you. leaving any of them out\r\n> throws at startup.\r\n\r\n- **`mint_policy`** — `\"open\"` (default) lets any admin mint codes directly.\r\n  `\"overseers\"` restricts minting to overseers; other admins submit requests\r\n  that an overseer approves before they can mint the approved scope. it may\r\n  also be a per-request resolver so your app can decide dynamically (roles,\r\n  feature flags, …).\r\n- **`user_required`** — codes must be bound to a specific admin.\r\n- **`ui`** — customizes the code prompt's title/description, statically or\r\n  per-operation (`(operation, model) => string`).\r\n- **`code`** — controls code generation: pick a `format`/`length`, or supply\r\n  your own generator function (must return at least 8 characters). a\r\n  constant generator is allowed — renewal of a used constant code works;\r\n  simultaneous duplicates are rejected.\r\n- **`secret`** — set it (e.g. an environment variable) to protect stored\r\n  codes with your own key: a leaked codes collection cannot be brute-forced\r\n  back to the plaintext codes.\r\n  ⚠ changing or removing `intent.secret` leaves existing codes listed in the\r\n  panel but permanently unusable — they fail verification like unknown\r\n  codes. rotate only when invalidating every outstanding code is acceptable.\r\n\r\n### minting\r\n\r\nmint from the panel's intent screen: pick the scope, ttl, max-uses and —\r\nwhen `user_required` is on — the admin to bind the code to (resolved from\r\n`admins_resolver`). blank ttl/max-uses inputs fall back to your configured\r\ndefaults (shown as placeholders).\r\n\r\n**bound codes** reach their person as an assignment they claim with one\r\nclick; the plaintext code is only ever shown to them.\r\n\r\n### restricted minting: requests & assignments\r\n\r\nwhen `mint_policy` is `\"overseers\"`:\r\n\r\n- non-overseer admins submit a **request** (scope, ttl, max-uses, note —\r\n  capped at 500 characters). no code exists without an overseer's approval.\r\n- overseers see the request queue and approve or deny it. approval creates a\r\n  slip from which the requester mints the approved scope. requesters can\r\n  cancel their own pending requests from the intent screen.\r\n- overseers can also mint directly and assign codes to other admins —\r\n  assignments appear in the recipient's panel to claim.\r\n\r\nrequest records capture `requested_by` (stable id) plus the display name at\r\nrequest time, `origin` (`\"request\"` | `\"assignment\"`), and who decided.\r\n\r\n### the code lifecycle in the panel\r\n\r\n- wrong/unknown codes show an error and change nothing — real codes stay\r\n  intact.\r\n- a wrong code on an operation a code doesn't cover burns an **attempt**;\r\n  attempts are visible per-code and drain until the code is burned.\r\n- fully-spent or burned codes disappear from the panel immediately (any\r\n  filter) but are **never hard-deleted by the system** — they are retained\r\n  invisibly in storage for audit. only explicit Delete removes a code\r\n  permanently.\r\n- the panel lists codes with scope, owner, binding, uses/attempts left and\r\n  expiry; codes can be revoked.\r\n\r\n### guarding operations\r\n\r\nturn the gate on per resource or per custom page:\r\n\r\n```typescript\r\nresources: {\r\n  User: {\r\n    intent: {\r\n      operations: [\"-create\"], // guard everything except create\r\n      scope: \"users\" // …and verify against this scope\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nguarded operations: `create`, `update`, `delete`,\r\n`bulk_action`, `document_action`, `query_runner`, `page_action`. prefix an\r\noperation with `-` to guard everything **except** it (exclusions cannot be\r\nmixed with inclusions).\r\n\r\ncustom actions and pages carry `intent: true | { scope?: Scope }` directly.\r\nan intent-guarded action **with `fields`** shows one combined modal (code +\r\ninputs, single submit).\r\n\r\n### what a code covers: scopes\r\n\r\nevery guarded operation maps to a scope string, and a code is valid for the\r\noperations its scopes cover:\r\n\r\n| operation       | scope                             |\r\n| --------------- | --------------------------------- |\r\n| create          | `<model>.create`                  |\r\n| update          | `<model>.update`                  |\r\n| delete          | `<model>.delete`                  |\r\n| bulk action     | `<model>.actions.bulk.<name>`     |\r\n| document action | `<model>.actions.document.<name>` |\r\n| query runner    | `<model>.query_runner`            |\r\n| custom page     | `pages.<path>`                    |\r\n\r\nwhen you set `scope` on a resource, action or page, you override the default\r\nwith your own string — so you can group operations under one umbrella (e.g.\r\n`scope: \"payments\"` on a resource makes all its guarded operations ask for a\r\ncode covering `\"payments\"`).\r\n\r\nmatching is prefix-based and forgiving:\r\n\r\n- a code covering `\"User\"` also covers `\"User.delete\"`, `\"User.update\"`, …\r\n- a code covering `\"User.delete\"` does **not** cover `\"User.update\"` or\r\n  plain `\"User\"`\r\n- `*` matches everything at its position — `\"User.*\"` covers every User\r\n  operation, `\"*\"` covers everything\r\n- a code can carry multiple scopes, including exclusions: `[\"*\", \"-Log\"]`\r\n  covers everything except Log operations\r\n\r\nin practice: mint with the narrowest scope that matches what you're guarding,\r\nor a shared scope string when several operations should share one code.\r\n\r\nscopes must match `^[a-zA-Z0-9_\\-*]+(\\.[a-zA-Z0-9_\\-*]+)*$` —\r\ndot-separated segments of alphanumeric characters, underscores, hyphens,\r\nor wildcards.\r\n\r\n### stores\r\n\r\ncodes and requests persist through two abstract stores:\r\n\r\n```typescript\r\nexport abstract class IntentStore {\r\n  create(code: StoredIntentCode): MaybePromise<void>;\r\n  find_by_hash(hash: string): MaybePromise<StoredIntentCode | undefined>;\r\n  find_by_id(id: string): MaybePromise<StoredIntentCode | undefined>;\r\n  update(code: StoredIntentCode): MaybePromise<void>;\r\n  delete(id: string): MaybePromise<void>;\r\n  list(\r\n    query?: IntentCodeQuery\r\n  ): MaybePromise<PaginatedResult<StoredIntentCode>>;\r\n}\r\n\r\nexport abstract class IntentRequestStore {\r\n  create(request: IntentRequestRecord): MaybePromise<void>;\r\n  find(id: string): MaybePromise<IntentRequestRecord | undefined>;\r\n  list(query?: SlipQuery): MaybePromise<PaginatedResult<IntentRequestRecord>>;\r\n  update(request: IntentRequestRecord): MaybePromise<void>;\r\n  delete(id: string): MaybePromise<void>;\r\n}\r\n```\r\n\r\n- shipped defaults: `InMemoryIntentStore` and the in-memory request store —\r\n  both reset on restart.\r\n- for durability use the mongoose-backed `MongooseIntentStore` /\r\n  `MongooseIntentRequestStore` (exported from the package root), or subclass\r\n  the abstract stores against any backend. both stores accept optional\r\n  `{ mongoose?, collection? }` — pass a different mongoose instance or\r\n  override the default collection name.\r\n\r\n```typescript\r\nimport { MongooseIntentStore, MongooseIntentRequestStore } from \"@kamalyb/mongoose-explore\";\r\n\r\nintent: {\r\n  enabled: true,\r\n  ttl: 15 * 60 * 1000,\r\n  max_uses: 1,\r\n  attempts: 5,\r\n  mint_policy: \"overseers\",\r\n  store: new MongooseIntentStore(),\r\n  request_store: new MongooseIntentRequestStore(),\r\n  user_required: true\r\n}\r\n```\r\n\r\n### verification pipeline\r\n\r\nwhen a guarded operation is attempted:\r\n\r\n1. the panel checks the submitted code against the codes it knows about.\r\n2. failure reasons — surfaced as `intent_rejected` events — are: `missing`,\r\n   `invalid_code`, `expired`, `scope_mismatch`, `attempts_exhausted`,\r\n   `revoked`, `user_mismatch`.\r\n3. a wrong code burns an attempt; a correct code on a covered operation\r\n   burns a use and emits `intent_verified`.\r\n\r\nthe observable contract: a failed verification never mutates your data, and\r\na valid code stays usable for its remaining uses.\r\n\r\n## events\r\n\r\nevery operation flowing through the panel emits an event. subscribe with\r\n`explorer.on(event, handler)` for audit logging and side effects.\r\n\r\n### available events\r\n\r\n| event                    | payload highlights                                                  |\r\n| ------------------------ | ------------------------------------------------------------------- |\r\n| `create`                 | `{ operation, model, doc, note }`                                   |\r\n| `update`                 | `{ operation, model, doc, changes: { previous, current }, note }`   |\r\n| `delete`                 | `{ operation, model, doc }`                                         |\r\n| `query_runner`           | `{ operation, model, sub_operation, query, options }`               |\r\n| `bulk_action`            | `{ operation, model, sub_operation, document_ids, data? }`          |\r\n| `document_action`        | `{ operation, model, sub_operation, doc, data? }`                   |\r\n| `page_action`            | `{ operation, page, body }`                                         |\r\n| `intent_issued`          | `{ operation, model?, scope, expires_at, max_uses, owner?, user? }` |\r\n| `intent_requested`       | `{ id, requested_by?, scope }`                                      |\r\n| `intent_request_decided` | `{ id, approved, decided_by? }`                                     |\r\n| `intent_revoked`         | `{ id }`                                                            |\r\n| `intent_verified`        | `{ operation, model?, scope, code_id }`                             |\r\n| `intent_rejected`        | `{ operation, model?, scope, code_id?, reason }`                    |\r\n\r\n> note: `intent_requested`, `intent_request_decided`, `intent_revoked`,\r\n> `intent_verified` and `intent_rejected` payloads carry their own\r\n> `timestamp`; the CRUD/operation events carry `timestamp` + `model`. the\r\n> document/bulk action events include the submitted `data`.\r\n\r\nall handlers receive `(req, event)`.\r\n\r\n```typescript\r\nexplorer.on(\"delete\", async (req, event) => {\r\n  await AuditLog.create({\r\n    actor: req.user?.id,\r\n    operation: event.operation,\r\n    model: event.model,\r\n    doc: event.doc,\r\n    at: event.timestamp\r\n  });\r\n});\r\n```\r\n\r\n## features and behavior\r\n\r\n### 1. filtering with operators\r\n\r\nthe filter drawer exposes an operator menu per field, offering only what\r\napplies to the field's type:\r\n\r\n- exact match, not equal, and is-set on everything\r\n- inclusive `from`/`to` ranges, greater/less than on numeric and datetime\r\n  fields\r\n- regex on string fields\r\n- uuid, buffer and bigint fields filter by exact match only; `double` and\r\n  `decimal128` fields participate in numeric range filtering\r\n\r\noperators map to `_from`, `_to`, `_gt`, `_lt`, `_ne`, `_exists`, `_regex`\r\nquery-string suffixes.\r\n\r\n### 2. query identification: `is_explorer_operation`\r\n\r\nexported helper to detect whether an incoming query is one the explorer\r\nissued (useful for skipping your own audit hooks):\r\n\r\n```typescript\r\nimport { is_explorer_operation } from \"@kamalyb/mongoose-explore\";\r\n```\r\n\r\n### 3. inclusion and exclusion\r\n\r\nthe sidebar is controlled two ways:\r\n\r\n- when `explorables` is omitted, every model shows unless its resource sets\r\n  `explorable: false`\r\n- when `explorables` is set, the list wins entirely — give it the model\r\n  names to show (`[\"Post\", \"Comment\"]`) or, prefixed with `-`, the ones to\r\n  hide (`[\"-Log\"]`). you cannot mix both forms in one list.\r\n\r\nan empty `explorables` array shows nothing.\r\n\r\n## limitations\r\n\r\n- the panel does not authenticate — that is your app's job. the library\r\n  documents its integration points (`user_resolver`, `oversight`,\r\n  `admins_resolver`, access-control policies, stores) and leaves the\r\n  implementations to you.\r\n- mongoose >= 9 and express >= 5 are required.\r\n- the in-memory intent stores reset on every restart; use the mongoose\r\n  stores for persistence.\r\n- rotating or removing `intent.secret` permanently invalidates every\r\n  outstanding code.\r\n- custom page templates must be pug; there is no other template hook.\r\n","readmeFilename":"README.md"}