{"_id":"@aginix/adonis-vulcan","_rev":"7-1cd9252cd783162162198050c01c6ad1","name":"@aginix/adonis-vulcan","dist-tags":{"beta":"0.0.1","latest":"0.1.5"},"versions":{"0.0.1":{"name":"@aginix/adonis-vulcan","version":"0.0.1","keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"author":{"name":"n3n"},"license":"UNLICENSED","_id":"@aginix/adonis-vulcan@0.0.1","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"c8":{"exclude":["tests/**"],"reporter":["text","html"]},"dist":{"shasum":"f23bfe90a9a35f735ce833c025e77fb058dda372","tarball":"https://registry.npmjs.org/@aginix/adonis-vulcan/-/adonis-vulcan-0.0.1.tgz","fileCount":43,"integrity":"sha512-OasP9409u++PKjLDeR9Fows05Sh1mKF7P8/Y2mujF1VbiKOTUAof2hBwQK7aJ5UaVcH7hisuC8RMxXv85ZKnLA==","signatures":[{"sig":"MEQCIAKJAsRUmU5FFQwiK9YKdmL2ODbyL34v0Fs2A2uKDy/lAiB4laDE6+IBRMM1Jjd+V+JSntiAkElzqiqg3xCqB87/WQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":117604},"main":"build/index.js","type":"module","types":"./build/index.d.ts","tsdown":{"dts":false,"clean":true,"entry":["./index.ts","./configure.ts","./src/types.ts","./src/resource_types.ts","./src/base_model_filter.ts","./src/filterable_mixin.ts","./src/condition_applier.ts","./src/filter_parser.ts","./src/lucid_filter_builder.ts","./src/lucid_embedded_builder.ts","./src/postgrest_model_filter.ts","./src/resource_serializer.ts","./src/base_resource_controller.ts","./providers/filter_provider.ts","./commands/make_filter.ts","./commands/make_resource_controller.ts"],"format":"esm","minify":"dce-only","outDir":"./build","target":"esnext","external":["@poppinss/utils","@poppinss/utils/string","@poppinss/utils/string_builder","@adonisjs/core","@adonisjs/core/ace","@adonisjs/core/http","@adonisjs/core/transformers","@adonisjs/core/types","@adonisjs/core/types/helpers","@adonisjs/core/commands/configure","@adonisjs/lucid","@adonisjs/lucid/orm","@adonisjs/lucid/types/model"],"treeshake":false,"sourcemaps":false,"fixedExtension":false},"engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./filter_parser":"./build/src/filter_parser.js","./resource_types":"./build/src/resource_types.js","./filter_provider":"./build/providers/filter_provider.js","./filterable_mixin":"./build/src/filterable_mixin.js","./base_model_filter":"./build/src/base_model_filter.js","./resource_serializer":"./build/src/resource_serializer.js","./lucid_filter_builder":"./build/src/lucid_filter_builder.js","./lucid_embedded_builder":"./build/src/lucid_embedded_builder.js","./postgrest_model_filter":"./build/src/postgrest_model_filter.js","./base_resource_controller":"./build/src/base_resource_controller.js"},"gitHead":"cc5c288961dd1e3a5c946a8236c3e3f6c795208e","scripts":{"lint":"eslint .","test":"c8 npm run quick:test","build":"npm run compile","format":"prettier --write .","compile":"tsdown && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"release-it","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint","quick:test":"node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/*.stub\" build","index:commands":"adonis-kit index build/commands","prepublishOnly":"npm run build"},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"prettier":"@adonisjs/prettier-config","release-it":{"git":{"push":true,"tagName":"v${version}","commitMessage":"chore(release): ${version}","tagAnnotation":"v${version}","requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":true,"skipChecks":true},"github":{"release":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"_npmVersion":"11.8.0","description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","directories":{},"_nodeVersion":"24.13.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^11.0.0","eslint":"^10.0.3","tsdown":"^0.21.0","prettier":"^3.8.1","copyfiles":"^2.4.1","cross-env":"^10.1.0","release-it":"^19.2.4","typescript":"^5.9.3","@types/node":"^25.3.5","@japa/assert":"^4.2.0","@japa/runner":"^5.3.0","@adonisjs/core":"^7.0.1","@adonisjs/lucid":"^22.0.0","@poppinss/ts-exec":"^1.4.4","@adonisjs/tsconfig":"^2.0.0","@adonisjs/assembler":"^8.0.0","@adonisjs/eslint-config":"^3.0.0","@adonisjs/prettier-config":"^1.4.5","@release-it/conventional-changelog":"^10.0.5"},"peerDependencies":{"@adonisjs/core":"^7.0.0","@adonisjs/lucid":"^22.0.0","@adonisjs/assembler":"^8.0.0"},"peerDependenciesMeta":{"@adonisjs/assembler":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-vulcan_0.0.1_1778436099467_0.4892940238527168","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@aginix/adonis-vulcan","version":"0.1.0","keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"author":{"name":"n3n"},"license":"UNLICENSED","_id":"@aginix/adonis-vulcan@0.1.0","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"c8":{"exclude":["tests/**"],"reporter":["text","html"]},"dist":{"shasum":"ceecce388db0854e6d5d28495264e195a766d557","tarball":"https://registry.npmjs.org/@aginix/adonis-vulcan/-/adonis-vulcan-0.1.0.tgz","fileCount":42,"integrity":"sha512-iLX3HApI9S6qHtFgWbZgkCvSvtv61F/yg2FaLD6NmtY2hXEJatyBo7jsHbnX/hr56ai+Jdvwik5jx2kHB9QFXQ==","signatures":[{"sig":"MEYCIQDGHO/utT3FG+rzfh0lgfpEau1j3MNORmUiHUje1ql1vgIhAL/FwA+eksRF/J8YvAhg+6bIgeCuO3TvhqZzDH9T6eUR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121864},"main":"build/index.js","type":"module","types":"./build/index.d.ts","tsdown":{"dts":false,"clean":true,"entry":["./index.ts","./configure.ts","./src/types.ts","./src/resource_types.ts","./src/base_model_filter.ts","./src/filterable_mixin.ts","./src/condition_applier.ts","./src/filter_parser.ts","./src/lucid_filter_builder.ts","./src/lucid_embedded_builder.ts","./src/postgrest_model_filter.ts","./src/resource_serializer.ts","./src/base_resource_controller.ts","./providers/filter_provider.ts","./commands/make_filter.ts","./commands/make_resource_controller.ts"],"format":"esm","minify":"dce-only","outDir":"./build","target":"esnext","external":["@poppinss/hooks","@poppinss/utils","@poppinss/utils/string","@poppinss/utils/string_builder","@adonisjs/core","@adonisjs/core/ace","@adonisjs/core/http","@adonisjs/core/transformers","@adonisjs/core/types","@adonisjs/core/types/helpers","@adonisjs/core/commands/configure","@adonisjs/lucid","@adonisjs/lucid/orm","@adonisjs/lucid/types/model"],"treeshake":false,"sourcemaps":false,"fixedExtension":false},"engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./filter_parser":"./build/src/filter_parser.js","./resource_types":"./build/src/resource_types.js","./filter_provider":"./build/providers/filter_provider.js","./filterable_mixin":"./build/src/filterable_mixin.js","./base_model_filter":"./build/src/base_model_filter.js","./resource_serializer":"./build/src/resource_serializer.js","./lucid_filter_builder":"./build/src/lucid_filter_builder.js","./lucid_embedded_builder":"./build/src/lucid_embedded_builder.js","./postgrest_model_filter":"./build/src/postgrest_model_filter.js","./base_resource_controller":"./build/src/base_resource_controller.js"},"gitHead":"1d753ffb825e83061eca96136a33866c2638f7a4","scripts":{"lint":"eslint .","test":"c8 npm run quick:test","build":"npm run compile","format":"prettier --write .","compile":"tsdown && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"release-it","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint","quick:test":"node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/*.stub\" build","index:commands":"adonis-kit index build/commands","prepublishOnly":"npm run build"},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"prettier":"@adonisjs/prettier-config","release-it":{"git":{"push":true,"tagName":"adonis-vulcan-v${version}","commitMessage":"chore(adonis-vulcan): release ${version}","tagAnnotation":"adonis-vulcan-v${version}","requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":true,"skipChecks":true},"github":{"release":true,"releaseName":"@aginix/adonis-vulcan ${version}"},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"_npmVersion":"11.8.0","description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","directories":{},"_nodeVersion":"24.13.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^11.0.0","eslint":"^10.0.3","tsdown":"^0.21.0","prettier":"^3.8.1","copyfiles":"^2.4.1","cross-env":"^10.1.0","release-it":"^19.2.4","typescript":"^5.9.3","@types/node":"^25.3.5","@japa/assert":"^4.2.0","@japa/runner":"^5.3.0","@adonisjs/core":"^7.0.1","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@poppinss/ts-exec":"^1.4.4","@adonisjs/tsconfig":"^2.0.0","@adonisjs/assembler":"^8.0.0","@adonisjs/eslint-config":"^3.0.0","@adonisjs/prettier-config":"^1.4.5","@release-it/conventional-changelog":"^10.0.5"},"peerDependencies":{"@adonisjs/core":"^7.0.0","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@adonisjs/assembler":"^8.0.0"},"peerDependenciesMeta":{"@adonisjs/assembler":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-vulcan_0.1.0_1778479436795_0.3070315999759259","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aginix/adonis-vulcan","version":"0.1.1","keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"author":{"name":"n3n"},"license":"UNLICENSED","_id":"@aginix/adonis-vulcan@0.1.1","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"c8":{"exclude":["tests/**"],"reporter":["text","html"]},"dist":{"shasum":"18a9ef5b6ce2790c57cbe32b8be0aaddb01f3373","tarball":"https://registry.npmjs.org/@aginix/adonis-vulcan/-/adonis-vulcan-0.1.1.tgz","fileCount":42,"integrity":"sha512-Z1fbs+KYiZE8dPqGzFmLANkhkHWTPqjf1CzkRI8X2MYR5RESP7/OnLRFcSEYPayDzE0voaEU/J0tBArvNGURuQ==","signatures":[{"sig":"MEQCIA9cTHYFt5PTNx/tT9t+Ent2NPQKKJUamlyJVjE2teKqAiAKH0KOfMWuovPWKZBcDztyqEhQsaYuOr3MNQWjZdzoKw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":124922},"main":"build/index.js","type":"module","types":"./build/index.d.ts","tsdown":{"dts":false,"clean":true,"entry":["./index.ts","./configure.ts","./src/types.ts","./src/resource_types.ts","./src/base_model_filter.ts","./src/filterable_mixin.ts","./src/condition_applier.ts","./src/filter_parser.ts","./src/lucid_filter_builder.ts","./src/lucid_embedded_builder.ts","./src/postgrest_model_filter.ts","./src/resource_serializer.ts","./src/base_resource_controller.ts","./providers/filter_provider.ts","./commands/make_filter.ts","./commands/make_resource_controller.ts"],"format":"esm","minify":"dce-only","outDir":"./build","target":"esnext","external":["@poppinss/hooks","@poppinss/utils","@poppinss/utils/string","@poppinss/utils/string_builder","@adonisjs/core","@adonisjs/core/ace","@adonisjs/core/http","@adonisjs/core/transformers","@adonisjs/core/types","@adonisjs/core/types/helpers","@adonisjs/core/commands/configure","@adonisjs/lucid","@adonisjs/lucid/orm","@adonisjs/lucid/types/model"],"treeshake":false,"sourcemaps":false,"fixedExtension":false},"engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./filter_parser":"./build/src/filter_parser.js","./resource_types":"./build/src/resource_types.js","./filter_provider":"./build/providers/filter_provider.js","./filterable_mixin":"./build/src/filterable_mixin.js","./base_model_filter":"./build/src/base_model_filter.js","./resource_serializer":"./build/src/resource_serializer.js","./lucid_filter_builder":"./build/src/lucid_filter_builder.js","./lucid_embedded_builder":"./build/src/lucid_embedded_builder.js","./postgrest_model_filter":"./build/src/postgrest_model_filter.js","./base_resource_controller":"./build/src/base_resource_controller.js"},"gitHead":"5979ec1e29a8f5f350e770d31ff209a4d53182b4","scripts":{"lint":"eslint .","test":"c8 npm run quick:test","build":"npm run compile","format":"prettier --write .","compile":"tsdown && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"release-it","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint","quick:test":"node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/*.stub\" build","index:commands":"adonis-kit index build/commands","prepublishOnly":"npm run build"},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"prettier":"@adonisjs/prettier-config","release-it":{"git":{"push":true,"tagName":"adonis-vulcan-v${version}","commitMessage":"chore(adonis-vulcan): release ${version}","tagAnnotation":"adonis-vulcan-v${version}","requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":true,"skipChecks":true},"github":{"release":true,"releaseName":"@aginix/adonis-vulcan ${version}"},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"_npmVersion":"11.8.0","description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","directories":{},"_nodeVersion":"24.13.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^11.0.0","eslint":"^10.0.3","tsdown":"^0.21.0","prettier":"^3.8.1","copyfiles":"^2.4.1","cross-env":"^10.1.0","release-it":"^19.2.4","typescript":"^5.9.3","@types/node":"^25.3.5","@japa/assert":"^4.2.0","@japa/runner":"^5.3.0","@adonisjs/core":"^7.0.1","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@poppinss/ts-exec":"^1.4.4","@adonisjs/tsconfig":"^2.0.0","@adonisjs/assembler":"^8.0.0","@adonisjs/eslint-config":"^3.0.0","@adonisjs/prettier-config":"^1.4.5","@release-it/conventional-changelog":"^10.0.5"},"peerDependencies":{"@adonisjs/core":"^7.0.0","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@adonisjs/assembler":"^8.0.0"},"peerDependenciesMeta":{"@adonisjs/assembler":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-vulcan_0.1.1_1778512368278_0.4978852020274598","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aginix/adonis-vulcan","version":"0.1.2","keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"author":{"name":"n3n"},"license":"UNLICENSED","_id":"@aginix/adonis-vulcan@0.1.2","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"c8":{"exclude":["tests/**"],"reporter":["text","html"]},"dist":{"shasum":"4d24cb40911a912790f1899d1eddef9adedc3935","tarball":"https://registry.npmjs.org/@aginix/adonis-vulcan/-/adonis-vulcan-0.1.2.tgz","fileCount":42,"integrity":"sha512-5V7wel+2UkwPgkLa27AmA2kPTqNAu2SjxNyI6nOuqbSK2JcNpIvhNjAqUI+43MuvDGzx6KWFCRjnrAoqDujz0g==","signatures":[{"sig":"MEYCIQDWgPicyHEyQC0OE4jeL1laytE5Mi9lHkaTOpu/Wa+IgQIhAMC13Mvv7KmCnYGrxVllxMoIw8JzwyIosLjeFKAnqvgi","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":125876},"main":"build/index.js","type":"module","types":"./build/index.d.ts","tsdown":{"dts":false,"clean":true,"entry":["./index.ts","./configure.ts","./src/types.ts","./src/resource_types.ts","./src/base_model_filter.ts","./src/filterable_mixin.ts","./src/condition_applier.ts","./src/filter_parser.ts","./src/lucid_filter_builder.ts","./src/lucid_embedded_builder.ts","./src/postgrest_model_filter.ts","./src/resource_serializer.ts","./src/base_resource_controller.ts","./providers/filter_provider.ts","./commands/make_filter.ts","./commands/make_resource_controller.ts"],"format":"esm","minify":"dce-only","outDir":"./build","target":"esnext","external":["@poppinss/hooks","@poppinss/utils","@poppinss/utils/string","@poppinss/utils/string_builder","@adonisjs/core","@adonisjs/core/ace","@adonisjs/core/http","@adonisjs/core/transformers","@adonisjs/core/types","@adonisjs/core/types/helpers","@adonisjs/core/commands/configure","@adonisjs/lucid","@adonisjs/lucid/orm","@adonisjs/lucid/types/model"],"treeshake":false,"sourcemaps":false,"fixedExtension":false},"engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./filter_parser":"./build/src/filter_parser.js","./resource_types":"./build/src/resource_types.js","./filter_provider":"./build/providers/filter_provider.js","./filterable_mixin":"./build/src/filterable_mixin.js","./base_model_filter":"./build/src/base_model_filter.js","./resource_serializer":"./build/src/resource_serializer.js","./lucid_filter_builder":"./build/src/lucid_filter_builder.js","./lucid_embedded_builder":"./build/src/lucid_embedded_builder.js","./postgrest_model_filter":"./build/src/postgrest_model_filter.js","./base_resource_controller":"./build/src/base_resource_controller.js"},"gitHead":"2fb36cfb1a4606e03c27905856c158fd28f5d140","scripts":{"lint":"eslint .","test":"c8 npm run quick:test","build":"npm run compile","format":"prettier --write .","compile":"tsdown && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"release-it","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint","quick:test":"node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/*.stub\" build","index:commands":"adonis-kit index build/commands","prepublishOnly":"npm run build"},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"prettier":"@adonisjs/prettier-config","release-it":{"git":{"push":true,"tagName":"adonis-vulcan-v${version}","commitMessage":"chore(adonis-vulcan): release ${version}","tagAnnotation":"adonis-vulcan-v${version}","requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":true,"skipChecks":true},"github":{"release":true,"releaseName":"@aginix/adonis-vulcan ${version}"},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"_npmVersion":"11.8.0","description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","directories":{},"_nodeVersion":"24.13.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^11.0.0","eslint":"^10.0.3","tsdown":"^0.21.0","prettier":"^3.8.1","copyfiles":"^2.4.1","cross-env":"^10.1.0","release-it":"^19.2.4","typescript":"^5.9.3","@types/node":"^25.3.5","@japa/assert":"^4.2.0","@japa/runner":"^5.3.0","@adonisjs/core":"^7.0.1","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@poppinss/ts-exec":"^1.4.4","@adonisjs/tsconfig":"^2.0.0","@adonisjs/assembler":"^8.0.0","@adonisjs/eslint-config":"^3.0.0","@adonisjs/prettier-config":"^1.4.5","@release-it/conventional-changelog":"^10.0.5"},"peerDependencies":{"@adonisjs/core":"^7.0.0","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@adonisjs/assembler":"^8.0.0"},"peerDependenciesMeta":{"@adonisjs/assembler":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-vulcan_0.1.2_1778580879477_0.02876031589395267","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@aginix/adonis-vulcan","version":"0.1.3","keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"author":{"name":"n3n"},"license":"UNLICENSED","_id":"@aginix/adonis-vulcan@0.1.3","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"c8":{"exclude":["tests/**"],"reporter":["text","html"]},"dist":{"shasum":"bdf32bd2cc76aefe34d30c16f77b689f45b953c1","tarball":"https://registry.npmjs.org/@aginix/adonis-vulcan/-/adonis-vulcan-0.1.3.tgz","fileCount":42,"integrity":"sha512-bmXXsYp4KZsZBIRKAnhOTL4gommVID0x4b9G9namG5DLrC7NaHn1qKODUrKdIbh4sxR3urNduwF1Gxqyig6cpw==","signatures":[{"sig":"MEQCIGZD/C/1wSIGnprEagDgHSUJqWWZuwOhgDdoSIbObHH7AiBZ740tO1pN4aLC/yEfUlx8j2dlVxIVblSEPya6y6/C7w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":130642},"main":"build/index.js","type":"module","types":"./build/index.d.ts","tsdown":{"dts":false,"clean":true,"entry":["./index.ts","./configure.ts","./src/types.ts","./src/resource_types.ts","./src/base_model_filter.ts","./src/filterable_mixin.ts","./src/condition_applier.ts","./src/filter_parser.ts","./src/lucid_filter_builder.ts","./src/lucid_embedded_builder.ts","./src/postgrest_model_filter.ts","./src/resource_serializer.ts","./src/base_resource_controller.ts","./providers/filter_provider.ts","./commands/make_filter.ts","./commands/make_resource_controller.ts"],"format":"esm","minify":"dce-only","outDir":"./build","target":"esnext","external":["@poppinss/hooks","@poppinss/utils","@poppinss/utils/string","@poppinss/utils/string_builder","@adonisjs/core","@adonisjs/core/ace","@adonisjs/core/http","@adonisjs/core/transformers","@adonisjs/core/types","@adonisjs/core/types/helpers","@adonisjs/core/commands/configure","@adonisjs/lucid","@adonisjs/lucid/orm","@adonisjs/lucid/types/model"],"treeshake":false,"sourcemaps":false,"fixedExtension":false},"engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./filter_parser":"./build/src/filter_parser.js","./resource_types":"./build/src/resource_types.js","./filter_provider":"./build/providers/filter_provider.js","./filterable_mixin":"./build/src/filterable_mixin.js","./base_model_filter":"./build/src/base_model_filter.js","./resource_serializer":"./build/src/resource_serializer.js","./lucid_filter_builder":"./build/src/lucid_filter_builder.js","./lucid_embedded_builder":"./build/src/lucid_embedded_builder.js","./postgrest_model_filter":"./build/src/postgrest_model_filter.js","./base_resource_controller":"./build/src/base_resource_controller.js"},"gitHead":"5423f0862033145161bfed47e10d69bd1c6927e2","scripts":{"lint":"eslint .","test":"c8 npm run quick:test","build":"npm run compile","format":"prettier --write .","compile":"tsdown && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"release-it","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint","quick:test":"node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/*.stub\" build","index:commands":"adonis-kit index build/commands","prepublishOnly":"npm run build"},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"prettier":"@adonisjs/prettier-config","release-it":{"git":{"push":true,"tagName":"adonis-vulcan-v${version}","commitMessage":"chore(adonis-vulcan): release ${version}","tagAnnotation":"adonis-vulcan-v${version}","requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":true,"skipChecks":true},"github":{"release":true,"releaseName":"@aginix/adonis-vulcan ${version}"},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"_npmVersion":"11.8.0","description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","directories":{},"_nodeVersion":"24.13.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^11.0.0","eslint":"^10.0.3","tsdown":"^0.21.0","prettier":"^3.8.1","copyfiles":"^2.4.1","cross-env":"^10.1.0","release-it":"^19.2.4","typescript":"^5.9.3","@types/node":"^25.3.5","@japa/assert":"^4.2.0","@japa/runner":"^5.3.0","@adonisjs/core":"^7.0.1","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@poppinss/ts-exec":"^1.4.4","@adonisjs/tsconfig":"^2.0.0","@adonisjs/assembler":"^8.0.0","@adonisjs/eslint-config":"^3.0.0","@adonisjs/prettier-config":"^1.4.5","@release-it/conventional-changelog":"^10.0.5"},"peerDependencies":{"@adonisjs/core":"^7.0.0","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@adonisjs/assembler":"^8.0.0"},"peerDependenciesMeta":{"@adonisjs/assembler":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-vulcan_0.1.3_1779964977701_0.11966815271741571","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@aginix/adonis-vulcan","version":"0.1.4","keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"author":{"name":"n3n"},"license":"UNLICENSED","_id":"@aginix/adonis-vulcan@0.1.4","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"c8":{"exclude":["tests/**"],"reporter":["text","html"]},"dist":{"shasum":"00441157f019eb2774dd1402473f056dcac76f1e","tarball":"https://registry.npmjs.org/@aginix/adonis-vulcan/-/adonis-vulcan-0.1.4.tgz","fileCount":42,"integrity":"sha512-SusET/D6TtuqGIzgMBE1Buh1jyel6bgmkzhYO/z+WTxZpDQqd+xLm8667w/XdiD544Gpsy59inH4rHLaO8FDFg==","signatures":[{"sig":"MEQCIHHnDal+o6OdCegW2fobdCK8fPwypqHk7dyL1J/hYLiYAiBBh/0+QCgymVCTbtNQHLJymJvGearckNS88BAWZxSsuQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":130647},"main":"build/index.js","type":"module","types":"./build/index.d.ts","tsdown":{"dts":false,"clean":true,"entry":["./index.ts","./configure.ts","./src/types.ts","./src/resource_types.ts","./src/base_model_filter.ts","./src/filterable_mixin.ts","./src/condition_applier.ts","./src/filter_parser.ts","./src/lucid_filter_builder.ts","./src/lucid_embedded_builder.ts","./src/postgrest_model_filter.ts","./src/resource_serializer.ts","./src/base_resource_controller.ts","./providers/filter_provider.ts","./commands/make_filter.ts","./commands/make_resource_controller.ts"],"format":"esm","minify":"dce-only","outDir":"./build","target":"esnext","external":["@poppinss/hooks","@poppinss/utils","@poppinss/utils/string","@poppinss/utils/string_builder","@adonisjs/core","@adonisjs/core/ace","@adonisjs/core/http","@adonisjs/core/transformers","@adonisjs/core/types","@adonisjs/core/types/helpers","@adonisjs/core/commands/configure","@adonisjs/lucid","@adonisjs/lucid/orm","@adonisjs/lucid/types/model"],"treeshake":false,"sourcemaps":false,"fixedExtension":false},"engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./filter_parser":"./build/src/filter_parser.js","./resource_types":"./build/src/resource_types.js","./filter_provider":"./build/providers/filter_provider.js","./filterable_mixin":"./build/src/filterable_mixin.js","./base_model_filter":"./build/src/base_model_filter.js","./resource_serializer":"./build/src/resource_serializer.js","./lucid_filter_builder":"./build/src/lucid_filter_builder.js","./lucid_embedded_builder":"./build/src/lucid_embedded_builder.js","./postgrest_model_filter":"./build/src/postgrest_model_filter.js","./base_resource_controller":"./build/src/base_resource_controller.js"},"gitHead":"4b02aafd49466f58db20e99e0c2478782765ca76","scripts":{"lint":"eslint .","test":"c8 npm run quick:test","build":"npm run compile","format":"prettier --write .","compile":"tsdown && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"release-it","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint","quick:test":"node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/*.stub\" build","index:commands":"adonis-kit index build/commands","prepublishOnly":"npm run build"},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"prettier":"@adonisjs/prettier-config","release-it":{"git":{"push":true,"tagName":"adonis-vulcan-v${version}","commitMessage":"chore(adonis-vulcan): release ${version}","tagAnnotation":"adonis-vulcan-v${version}","requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":true,"skipChecks":true},"github":{"release":true,"releaseName":"@aginix/adonis-vulcan ${version}"},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"_npmVersion":"11.8.0","description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","directories":{},"_nodeVersion":"24.13.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^11.0.0","eslint":"^10.0.3","tsdown":"^0.21.0","prettier":"^3.8.1","copyfiles":"^2.4.1","cross-env":"^10.1.0","release-it":"^19.2.4","typescript":"^5.9.3","@types/node":"^25.3.5","@japa/assert":"^4.2.0","@japa/runner":"^5.3.0","@adonisjs/core":"^7.0.1","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@poppinss/ts-exec":"^1.4.4","@adonisjs/tsconfig":"^2.0.0","@adonisjs/assembler":"^8.0.0","@adonisjs/eslint-config":"^3.0.0","@adonisjs/prettier-config":"^1.4.5","@release-it/conventional-changelog":"^10.0.5"},"peerDependencies":{"@adonisjs/core":"^7.0.0","@adonisjs/lucid":"^22.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1","@adonisjs/assembler":"^8.0.0"},"peerDependenciesMeta":{"@adonisjs/assembler":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-vulcan_0.1.4_1783614965531_0.514191415814901","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@aginix/adonis-vulcan","description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","version":"0.1.5","engines":{"node":">=24.0.0"},"type":"module","main":"build/index.js","exports":{".":"./build/index.js","./types":"./build/src/types.js","./resource_types":"./build/src/resource_types.js","./filter_parser":"./build/src/filter_parser.js","./lucid_filter_builder":"./build/src/lucid_filter_builder.js","./lucid_embedded_builder":"./build/src/lucid_embedded_builder.js","./postgrest_model_filter":"./build/src/postgrest_model_filter.js","./base_model_filter":"./build/src/base_model_filter.js","./resource_serializer":"./build/src/resource_serializer.js","./base_resource_controller":"./build/src/base_resource_controller.js","./filterable_mixin":"./build/src/filterable_mixin.js","./filter_provider":"./build/providers/filter_provider.js","./commands":"./build/commands/main.js"},"scripts":{"copy:templates":"copyfiles --up=1 \"stubs/**/*.stub\" build","index:commands":"adonis-kit index build/commands","typecheck":"tsc --noEmit","lint":"eslint .","format":"prettier --write .","quick:test":"node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts","pretest":"npm run lint","test":"c8 npm run quick:test","precompile":"npm run lint","compile":"tsdown && tsc --emitDeclarationOnly --declaration","postcompile":"npm run copy:templates && npm run index:commands","build":"npm run compile","release":"release-it","version":"npm run build","prepublishOnly":"npm run build"},"keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"author":{"name":"n3n"},"license":"UNLICENSED","devDependencies":{"@adonisjs/assembler":"^8.0.0","@adonisjs/core":"^7.0.1","@adonisjs/eslint-config":"^3.0.0","@adonisjs/lucid":"^22.0.0","@adonisjs/prettier-config":"^1.4.5","@adonisjs/tsconfig":"^2.0.0","@japa/assert":"^4.2.0","@japa/runner":"^5.3.0","@poppinss/hooks":"^7.3.0","@poppinss/ts-exec":"^1.4.4","@poppinss/utils":"^7.0.1","@release-it/conventional-changelog":"^10.0.5","@types/node":"^25.3.5","c8":"^11.0.0","copyfiles":"^2.4.1","cross-env":"^10.1.0","eslint":"^10.0.3","prettier":"^3.8.1","release-it":"^19.2.4","tempura":"^0.4.1","tsdown":"^0.21.0","typescript":"^5.9.3"},"peerDependencies":{"@adonisjs/core":"^7.0.0","@adonisjs/lucid":"^22.0.0","@adonisjs/assembler":"^8.0.0","@poppinss/hooks":"^7.3.0","@poppinss/utils":"^7.0.1"},"peerDependenciesMeta":{"@adonisjs/assembler":{"optional":true}},"publishConfig":{"access":"public"},"tsdown":{"entry":["./index.ts","./configure.ts","./src/types.ts","./src/resource_types.ts","./src/base_model_filter.ts","./src/filterable_mixin.ts","./src/condition_applier.ts","./src/filter_parser.ts","./src/lucid_filter_builder.ts","./src/lucid_embedded_builder.ts","./src/postgrest_model_filter.ts","./src/resource_serializer.ts","./src/base_resource_controller.ts","./providers/filter_provider.ts","./commands/make_filter.ts","./commands/make_resource_controller.ts"],"outDir":"./build","clean":true,"format":"esm","minify":"dce-only","fixedExtension":false,"dts":false,"treeshake":false,"sourcemaps":false,"target":"esnext","external":["@poppinss/hooks","@poppinss/utils","@poppinss/utils/string","@poppinss/utils/string_builder","@adonisjs/core","@adonisjs/core/ace","@adonisjs/core/http","@adonisjs/core/transformers","@adonisjs/core/types","@adonisjs/core/types/helpers","@adonisjs/core/commands/configure","@adonisjs/lucid","@adonisjs/lucid/orm","@adonisjs/lucid/types/model"]},"release-it":{"git":{"requireCleanWorkingDir":true,"requireUpstream":true,"commitMessage":"chore(adonis-vulcan): release ${version}","tagAnnotation":"adonis-vulcan-v${version}","push":true,"tagName":"adonis-vulcan-v${version}"},"github":{"release":true,"releaseName":"@aginix/adonis-vulcan ${version}"},"npm":{"publish":true,"skipChecks":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"c8":{"reporter":["text","html"],"exclude":["tests/**"]},"prettier":"@adonisjs/prettier-config","gitHead":"788bf98cbbf248dc1a25ced93bc10a6cc20573cd","types":"./build/index.d.ts","_id":"@aginix/adonis-vulcan@0.1.5","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-HUwWi2fY6B3Tx9796/M7sWWJWIFQTjxBlJKgedxVpkbH+DqpGNykssm4lzpjn5ITnonxPDJxfZFWbgPQ2S/T1A==","shasum":"b0a6261d29aca8d9fadb435ba5247eb0154eafe0","tarball":"https://registry.npmjs.org/@aginix/adonis-vulcan/-/adonis-vulcan-0.1.5.tgz","fileCount":42,"unpackedSize":130668,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDEWpceQUlEY/XB+b7KIwc9rpMf5roq9iZ3M6vHMEMH1wIhAJpn8kH8ABjDSpYPfvYGu7xIKawDlsY92K7M9hV7XSR8"}]},"_npmUser":{"name":"n3n","email":"nonpawit.tee@gmail.com"},"directories":{},"maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/adonis-vulcan_0.1.5_1788618538254_0.7141333810190655"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-10T18:01:39.341Z","modified":"2026-09-05T14:28:58.559Z","0.0.1":"2026-05-10T18:01:39.631Z","0.1.0":"2026-05-11T06:03:56.958Z","0.1.1":"2026-05-11T15:12:48.454Z","0.1.2":"2026-05-12T10:14:39.621Z","0.1.3":"2026-05-28T10:42:57.879Z","0.1.4":"2026-07-09T16:36:05.662Z","0.1.5":"2026-09-05T14:28:58.391Z"},"author":{"name":"n3n"},"license":"UNLICENSED","keywords":["adonisjs","adonis","lucid","filter","postgrest","rest","api","query","resource"],"description":"PostgREST-style filtering and resource scaffolding for AdonisJS Lucid ORM","maintainers":[{"name":"n3n","email":"nonpawit.tee@gmail.com"}],"readme":"# @aginix/adonis-vulcan\n\n> PostgREST-style filtering and Rapid CRUD controllers for AdonisJS Lucid ORM.\n\nTwo layers, each independently usable:\n\n1. **Filter layer** — a query parser that brings the [PostgREST horizontal filtering grammar](https://docs.postgrest.org/en/v14/references/api/tables_views.html#horizontal-filtering) to AdonisJS: comparison operators, logical groups (`and`/`or`), pattern matching, regex, array/range/full-text operators, and embedded resource filters via dot notation.\n2. **Rapid CRUD layer** — `BaseResourceController` so a controller `extends BaseResourceController` ships index/show/store/update/destroy with PostgREST filtering, page-based pagination, optional VineJS validators, and integrates with AdonisJS's official [`BaseTransformer`](https://docs.adonisjs.com/guides/digging-deeper/transformers) — all behind a stable `{ data }` / `{ data, metadata }` response contract, with override-friendly primitives for Bouncer-style authorization.\n\n## Installation\n\nUse `node ace add` to install and configure the package in one step:\n\n```sh\nnode ace add @aginix/adonis-vulcan\n```\n\nThis will:\n\n- Register the package's provider in your `adonisrc.ts` so `Model.filter(...)` becomes available on every Lucid query builder.\n- Register the package's commands so `node ace make:filter` and `node ace make:resource-controller` work.\n\nIf you prefer to install manually:\n\n```sh\nnpm install @aginix/adonis-vulcan\nnode ace configure @aginix/adonis-vulcan\n```\n\nThe package has no runtime dependency on `adonis-lucid-filter` — `BaseModelFilter`, `Filterable`, and the `filter()` query macro are vendored directly to avoid the broken `getDirname` import in `adonis-lucid-filter@5.2.0` (which fails under `@poppinss/utils` v6+).\n\n---\n\n## Quick start (filter layer)\n\n### 1. Scaffold a filter\n\n```sh\nnode ace make:filter Course\n```\n\nThat writes `app/models/filters/course_filter.ts`:\n\n```typescript\nimport { PostgRESTModelFilter } from '@aginix/adonis-vulcan'\nimport type { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'\nimport Course from '#models/course'\n\nexport default class CourseFilter extends PostgRESTModelFilter {\n  declare $query: ModelQueryBuilderContract<typeof Course>\n\n  // Add custom filter methods here. Each public method becomes a query\n  // parameter — defining `q(value)` enables `?q=search` in URLs.\n}\n```\n\n### 2. Wire the filter to a model\n\nAdd the `Filterable` mixin and a static `$filter` getter to your model:\n\n```typescript\nimport { compose } from '@adonisjs/core/helpers'\nimport { BaseModel, column } from '@adonisjs/lucid/orm'\nimport { Filterable } from '@aginix/adonis-vulcan'\nimport CourseFilter from '#models/filters/course_filter'\n\nexport default class Course extends compose(BaseModel, Filterable) {\n  static $filter = () => CourseFilter\n\n  @column({ isPrimary: true })\n  declare id: string\n}\n```\n\n### 3. Use it in a controller (manual)\n\n```typescript\nimport Course from '#models/course'\nimport type { HttpContext } from '@adonisjs/core/http'\n\nexport default class CoursesController {\n  async index({ request }: HttpContext) {\n    return Course.filter(request.qs()).exec()\n  }\n}\n```\n\n### 4. Query with PostgREST-style URL parameters\n\n```\nGET /courses?status=eq.active&faculty.nameTh=like.*วิศว*\n```\n\n---\n\n## Rapid CRUD with `BaseResourceController`\n\nFor a typical resource endpoint you can skip the manual controller. Subclass `BaseResourceController`, declare a model, and you get index/show/store/update/destroy with PostgREST filtering, pagination, and the standard response envelope.\n\n### Minimal controller\n\n```sh\nnode ace make:resource-controller Course\n```\n\n```typescript\nimport Course from '#models/course'\nimport { BaseResourceController } from '@aginix/adonis-vulcan'\n\nexport default class CoursesController extends BaseResourceController<typeof Course> {\n  protected model = Course\n}\n```\n\nWire it to a resource route:\n\n```typescript\nrouter.resource('courses', '#controllers/courses_controller').apiOnly()\n```\n\nYou now have:\n\n| Route | Response | Status |\n|-------|----------|--------|\n| `GET /courses` | `{ data: Course[], metadata: {...} }` — PostgREST filters + `?page=&perPage=` | 200 |\n| `GET /courses/:id` | `{ data: Course }` | 200 |\n| `POST /courses` | `{ data: Course }` | 201 |\n| `PUT /courses/:id` | `{ data: Course }` | 200 |\n| `DELETE /courses/:id` | _(empty)_ | 204 |\n\n`metadata` comes straight from Lucid's paginator `.getMeta()`.\n\n### Full controller with all optional slots\n\n```typescript\nimport Course from '#models/course'\nimport CourseFilter from '#models/filters/course_filter'\nimport CourseTransformer from '#transformers/course_transformer'\nimport { createCourseValidator, updateCourseValidator } from '#validators/course'\nimport { BaseResourceController } from '@aginix/adonis-vulcan'\nimport type { HttpContext } from '@adonisjs/core/http'\nimport type { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'\n\nexport default class CoursesController extends BaseResourceController<typeof Course> {\n  protected model = Course\n  protected filter = CourseFilter              // optional — falls back to Model.filter()\n  protected transformer = CourseTransformer    // pass the CLASS, not new ...\n  protected validators = {\n    store: createCourseValidator,\n    update: updateCourseValidator,\n  }\n  protected pagination = { defaultPerPage: 20, maxPerPage: 100 }\n\n  // Apply on every list request — tenant scope, default ordering, .preload(), etc.\n  protected async modifyQuery(\n    ctx: HttpContext,\n    query: ModelQueryBuilderContract<typeof Course>\n  ) {\n    query.where('tenantId', ctx.auth.user!.tenantId).preload('faculty')\n  }\n\n  // Or override an action and compose primitives — `super.<action>()` also works\n  async index(ctx: HttpContext) {\n    return this.respondPaginated(ctx, (q) => q.where('published', true))\n  }\n}\n```\n\n### Transformers\n\nUse AdonisJS's official [`BaseTransformer`](https://docs.adonisjs.com/guides/digging-deeper/transformers) — this package does NOT ship its own transformer class:\n\n```sh\nnode ace make:transformer Course\n```\n\n```typescript\n// app/transformers/course_transformer.ts\nimport { BaseTransformer } from '@adonisjs/core/transformers'\nimport type Course from '#models/course'\n\nexport default class CourseTransformer extends BaseTransformer<Course> {\n  toObject() {\n    return this.pick(this.resource, ['id', 'nameTh', 'facultyId'])\n  }\n\n  // Variants — call via .useVariant('forDetailedView') in overrides\n  async forDetailedView() {\n    return {\n      ...this.toObject(),\n      description: this.resource.description,\n    }\n  }\n}\n```\n\nPass the **class** (not an instance) on the controller's `transformer` slot. The controller wires:\n\n- `index` → `Transformer.paginate(rows, paginator.getMeta())` → serializer → `{ data, metadata }`\n- `show` / `store` / `update` → `Transformer.transform(record)` → serializer → `{ data }`\n\nWhen `transformer` is omitted, the controller hand-shapes the same envelope using `model.serialize()` so the response shape is identical either way.\n\n### Serializer (`{ data, metadata }` envelope)\n\nThe default [`ResourceSerializer`](src/resource_serializer.ts) extends `BaseSerializer` from `@adonisjs/core/transformers` with `wrap: 'data'` and identity metadata, matching the response shape documented for AdonisJS transformers:\n\n- `Item` → `{ data: {...} }`\n- `Collection` → `{ data: [...] }`\n- `Paginator` → `{ data: [...], metadata: {...} }`\n\n**Resolution order** in `pickSerializer(ctx)` — first match wins:\n\n1. `this.serializer` — per-controller override (subclass sets it)\n2. `ctx.serialize` — app-wide, when augmented onto `HttpContext` via `HttpContext.instanceProperty('serialize', ...)` ([Adonis pattern](https://docs.adonisjs.com/guides/digging-deeper/transformers))\n3. `sharedResourceSerializer` — package default\n\nSo if your app already wires `ctx.serialize` (a custom `BaseSerializer` subclass on every request), `BaseResourceController` picks it up automatically — no per-controller config:\n\n```typescript\n// start/serialize.ts (your app, wired once)\nHttpContext.instanceProperty('serialize', /* serialize fn from ApiSerializer */)\n\n// any controller — uses ctx.serialize transparently\nexport default class CoursesController extends BaseResourceController<typeof Course> {\n  protected model = Course\n  protected transformer = CourseTransformer\n}\n```\n\nTo override per-controller (different wrap key, different metadata shape, etc.):\n\n```typescript\nimport { BaseSerializer } from '@adonisjs/core/transformers'\n\nclass MinimalSerializer extends BaseSerializer<{\n  Wrap: 'result'\n  PaginationMetaData: { page: number; total: number }\n}> {\n  wrap = 'result' as const\n  definePaginationMetaData(meta: any) {\n    return { page: meta.currentPage, total: meta.total }\n  }\n}\n\nexport default class CoursesController extends BaseResourceController<typeof Course> {\n  protected model = Course\n  protected serializer = new MinimalSerializer()\n}\n```\n\n### Validators\n\nVineJS validators on the `validators` slot are run via `ctx.request.validateUsing(...)`:\n\n```typescript\nprotected validators = {\n  store: createCourseValidator,\n  update: updateCourseValidator,\n}\n```\n\nOmit either action to accept the raw `ctx.request.body()` for that action.\n\n### Hooks\n\nOverride any of these in a subclass — no need to touch the action method:\n\n```\nmodifyQuery(ctx, query)              // every list — global query scope\nbeforeIndex(ctx, query)              // before pagination (mutate in place)\nafterIndex(ctx, response)  → response?\nbeforeStore(ctx, payload)  → payload?\nafterStore(ctx, record)    → record?\nbeforeUpdate(ctx, record, payload) → { payload }?\nafterUpdate(ctx, record)   → record?\nbeforeDestroy(ctx, record)\nafterDestroy(ctx, record)\n```\n\n### Authorization (Bouncer)\n\nTwo override patterns are supported. The base controller has no built-in Bouncer integration — choose the one that matches your authorization needs:\n\n**Action-level (no record needed)** — call `super.<action>(ctx)`:\n\n```typescript\nasync store(ctx: HttpContext) {\n  await ctx.bouncer.with(CoursePolicy).authorize('create')\n  return super.store(ctx)\n}\n```\n\n**Record-bound** — compose `findOrFail` + `respondItem`:\n\n```typescript\nasync show(ctx: HttpContext) {\n  const course = await this.findOrFail(ctx)\n  await ctx.bouncer.with(CoursePolicy).authorize('view', course)\n  return this.respondItem(course, ctx)\n}\n```\n\n`findOrFail`, `respondItem`, `respondPaginated`, `buildListQuery`, `validateInput`, `resolvePagination`, `pickSerializer` are all `protected` — compose them freely.\n\n### Preloading relationships\n\n| Action | Where to call `.preload()` |\n|--------|---------------------------|\n| `index` (list) | `modifyQuery(ctx, query)` hook, or pass an `extra` mutator to `respondPaginated(ctx, q => q.preload('...'))` in an overridden action |\n| `show` / `update` / `destroy` | Override `findOrFail` — the default uses `Model.findByOrFail()` which doesn't support preload |\n\n```typescript\n// Index preload\nprotected async modifyQuery(_ctx: HttpContext, query: any) {\n  query.preload('faculty').preload('department')\n}\n\n// Single-resource preload\nprotected async findOrFail(ctx: HttpContext) {\n  return this.model.query()\n    .preload('faculty')\n    .preload('department')\n    .where(this.findBy, ctx.request.param(this.paramKey))\n    .firstOrFail()\n}\n```\n\n### Customizing pagination\n\n```typescript\nprotected pagination = {\n  defaultPerPage: 50,         // default 20\n  maxPerPage: 200,            // default 100 — clamps the requested page size\n  pageInput: 'p',             // default 'page'\n  perPageInput: 'limit',      // default 'perPage'\n}\n```\n\nEmpty/garbage values fall back to `defaultPerPage`. `perPage > maxPerPage` is silently clamped.\n\n### Lookups by something other than `id`\n\n```typescript\nprotected findBy = 'slug'    // Lucid column to match\nprotected paramKey = 'slug'  // route param name\n```\n\n---\n\n## Operators\n\n### Comparison\n\n| Operator | Description           | Example               |\n| -------- | --------------------- | --------------------- |\n| `eq`     | Equal                 | `?status=eq.active`   |\n| `neq`    | Not equal             | `?status=neq.deleted` |\n| `gt`     | Greater than          | `?price=gt.100`       |\n| `gte`    | Greater than or equal | `?price=gte.100`      |\n| `lt`     | Less than             | `?price=lt.500`       |\n| `lte`    | Less than or equal    | `?price=lte.500`      |\n\n### Lists, null/boolean, patterns\n\n```\n?id=in.(1,2,3,4,5)\n?id=in.()               # empty set — returns 0 rows\n?deletedAt=is.null\n?isActive=is.true\n?status=is.unknown\n?name=like.*test*       # contains \"test\" (case-sensitive)\n?name=ilike.*TEST*      # contains \"TEST\" (case-insensitive)\n?name=like(any).{*foo*,*bar*}  # OR across patterns\n?name=like(all).{*urgent*,*important*}  # AND across patterns\n```\n\nThe `(any)` / `(all)` modifiers also apply to `ilike`, `match`, and `imatch`.\n\n**Empty and malformed `in` values:** `?id=in.()` parses as an empty list and emits `WHERE 1 = 0`, returning no rows (matches PostgREST semantics). A malformed `in` — missing the list entirely (`?id=in.`, `?id=in`) or missing the parentheses (`?id=in.1,2`) — throws `FilterValidationError` and returns HTTP 400, even in lenient mode. This prevents the previous footgun where a malformed `in` would be silently dropped and the request would fall through with no constraint.\n\n### Regex (PostgreSQL)\n\n```\n?code=match.^CPE[0-9]+$    # case-sensitive\n?code=imatch.^cpe[0-9]+$   # case-insensitive\n```\n\n### Array (PostgreSQL)\n\n| Operator | SQL  | Description  |\n| -------- | ---- | ------------ |\n| `cs`     | `@>` | Contains     |\n| `cd`     | `<@` | Contained in |\n| `ov`     | `&&` | Overlaps     |\n\n```\n?tags=cs.{admin,user}\n?roles=cd.{admin,user,guest}\n?categories=ov.{tech,science}\n```\n\n### Range (PostgreSQL)\n\n| Operator | SQL                | Description              |\n| -------- | ------------------ | ------------------------ |\n| `sl`     | `<<`               | Strictly left of         |\n| `sr`     | `>>`               | Strictly right of        |\n| `nxr`    | `&<`               | Does not extend right of |\n| `nxl`    | `&>`               | Does not extend left of  |\n| `adj`    | <code>-&#124;-</code> | Adjacent to              |\n\nBounds use PostgreSQL range syntax: `[a,b]` inclusive, `(a,b)` exclusive.\n\n```\n?period=sl.[2024-01-01,2024-12-31]   # period is entirely before this range\n?period=adj.[2024-01-01,2024-06-30]  # period is directly adjacent\n```\n\n### Full-text search (PostgreSQL)\n\n| Operator | Variant                         |\n| -------- | ------------------------------- |\n| `fts`    | `to_tsquery`                    |\n| `plfts`  | `plainto_tsquery` (plain query) |\n| `phfts`  | `phraseto_tsquery` (phrase)     |\n| `wfts`   | `websearch_to_tsquery`          |\n\n```\n?description=fts.search+terms\n?description=fts.thai.ค้นหา      # with language config\n?description=plfts.simple+phrase\n```\n\n### Negation\n\nAdd `not.` before any operator:\n\n```\n?status=not.eq.deleted\n?id=not.in.(1,2,3)\n?deletedAt=not.is.null\n?name=not.like.*test*\n```\n\n---\n\n## Logical operators\n\n### `and` / `or` groups\n\n```\n?or=(status.eq.active,status.eq.pending)\n?and=(price.gte.100,price.lte.500)\n?not.or=(status.eq.deleted,status.eq.archived)\n```\n\n### Nested groups\n\n```\n?and=(or(status.eq.active,status.eq.pending),or(type.eq.course,type.eq.program))\n?or=(status.eq.draft,and(status.eq.approved,publishedAt.not.is.null))\n```\n\nFlat filters are implicitly `AND`-ed with logical groups:\n\n```\n?facultyId=eq.1&or=(status.eq.active,status.eq.pending)\n```\n\n> **Inside a logical group the syntax is `<column>.<operator>.<value>`** (dot-separated, no `=`). Use `?or=(name.like.*test*,...)`, NOT `?or=(name=like.*test*,...)`.\n\n---\n\n## Embedded filters\n\nFilter on related resources using dot notation. Works automatically — no configuration required.\n\n```\n?faculty.nameTh=like.*วิศว*\n?faculty.department.code=eq.CPE\n?course.instructor.faculty.id=eq.1\n?faculty.order=nameTh.asc\n?faculty.order=score.desc.nullslast\n?faculty.limit=10\n?courseSpecifications.exists=true\n```\n\n### Preloading and relationship mapping\n\nBy default embedded filters only apply `whereHas`. To preload the related data or remap names:\n\n```typescript\nexport default class CourseFilter extends PostgRESTModelFilter {\n  declare $query: ModelQueryBuilderContract<typeof Course>\n\n  protected $embeddedOptions = {\n    // true | false | string[]\n    preloadEmbedded: ['faculty', 'department'],\n    relationshipMap: {\n      dept: 'department',\n      specs: 'courseSpecifications',\n    },\n  }\n}\n```\n\n---\n\n## Custom filter methods\n\nAny public method on the filter class is treated as a custom filter. The query parameter name matches the method name; the value is forwarded as the first argument.\n\n> **Custom methods take precedence over PostgREST parsing.** If a query key matches a public method name, the method is invoked and the key is consumed before the parser runs — so defining `q(value)` shadows any incoming `?q=eq.foo` filter.\n\n> **Operator prefix is auto-stripped.** A frontend like [`@aginix/vulcan-data-provider`](../vulcan-data-provider) with `defaultListOp: 'eq'` will emit `?year=eq.2025` — the custom method receives `'2025'` as the first argument (already stripped), with the parsed operator handed in as an optional second argument: `methodName(value, operator?: CustomFilterMethodOperator)`. Single strings expose the operator; array values (multiple `field=` params) are stripped element-wise and the second argument is left `undefined`.\n\n```typescript\nimport {\n  type CustomFilterMethodOperator,\n  PostgRESTModelFilter,\n} from '@aginix/adonis-vulcan'\n\nexport default class CourseFilter extends PostgRESTModelFilter {\n  declare $query: ModelQueryBuilderContract<typeof Course>\n\n  // ?q=search+term\n  q(value: string): void {\n    this.$query\n      .orWhereILike('nameTh', `%${value}%`)\n      .orWhereILike('nameEn', `%${value}%`)\n      .orWhereILike('code', `%${value}%`)\n  }\n\n  // ?hasApprover=true\n  hasApprover(value: string): void {\n    this.$query.whereRaw(\n      value === 'true' ? 'cardinality(\"approverEmails\") > 0' : 'cardinality(\"approverEmails\") = 0'\n    )\n  }\n\n  // ?year=2024 OR ?year=eq.2024 — both deliver value='2024' here\n  year(value: string, op?: CustomFilterMethodOperator): void {\n    const year = Number(value)\n    if (!Number.isFinite(year)) return\n    const operator = op?.negate ? '!=' : '='\n    this.$query.whereRaw(`EXTRACT(YEAR FROM \"createdAt\") ${operator} ?`, [year])\n  }\n}\n```\n\n---\n\n## Notes & gotchas\n\n- **Top-level `order` is applied by `BaseResourceController`.** `?order=name.asc`, `?order=name.asc,createdAt.desc`, and `?order=name.asc.nullslast` work out of the box on any controller subclassing `BaseResourceController`. Unknown columns are dropped through `$columnsDefinitions` (override `resolveOrderColumn` to widen, or set `orderInput = null` to disable when a `PostgRESTModelFilter` subclass owns the `order` param via a custom `order(value)` method). The filter-layer (`PostgRESTModelFilter` / `Model.filter()`) does not apply ordering on its own — call from a controller, or apply manually via `Model.query().orderBy(...)`.\n- **Top-level `select`, `limit`, `offset`, `on_conflict`, `columns`, `not` are reserved** — recognized as non-filters and skipped, but not yet implemented at the top level. Use the embedded form (`<rel>.order=...`, `<rel>.limit=...`) for related-resource ordering, and the controller's `pagination` slot (or `?page=&perPage=`) for top-level pagination.\n- **Unknown columns are silently dropped.** `LucidFilterBuilder` only applies conditions for columns declared in the model's `$columnsDefinitions` — typos and missing `@column()` decorations result in no-op filters with no error.\n- **Parsing is lenient by default.** Invalid operator strings are skipped without erroring. Pass `{ strict: true }` to `FilterParser` (or `parseFilters(params, true)`) when you want a `ParseError` instead.\n- **Malformed `in` is the one exception to lenient parsing.** A missing list (`?id=in.`, `?id=in`) or missing parentheses (`?id=in.1,2`) throws `FilterValidationError` (status 400) regardless of strict mode, because silently dropping it would let the request return every row. Empty lists (`?id=in.()`) are valid and return 0 rows.\n- **Hooks that take a query are void-only.** `modifyQuery` / `beforeIndex` mutate in place — they don't return the query. `ModelQueryBuilderContract` is thenable in Lucid (`await query` returns the rows), so awaiting `Promise<Query | void>` would silently unwrap a returned query into `InstanceType<Model>[]`. To swap the query entirely, override `buildListQuery`.\n\n---\n\n## Programmatic API\n\n```typescript\nimport { FilterParser, parseFilters } from '@aginix/adonis-vulcan'\n\nconst parser = new FilterParser()\n\n// Flat conditions\nparser.parse({ status: 'eq.active', price: 'gte.100' })\n\n// + logical groups\nparser.parseWithLogical({\n  status: 'eq.active',\n  or: '(type.eq.a,type.eq.b)',\n})\n\n// + embedded filters\nparser.parseWithEmbedded({\n  'status': 'eq.active',\n  'faculty.name': 'like.*test*',\n  'faculty.order': 'name.asc',\n})\n\n// Strict mode — throws ParseError on invalid filters\nnew FilterParser({ strict: true }).parse({ status: 'bogus.value' })\n\n// Convenience helper\nparseFilters({ status: 'eq.active' })\n```\n\nThe package also exports `LucidFilterBuilder`, `LucidEmbeddedBuilder`, `BaseResourceController`, `ResourceSerializer`, and the underlying types from `@aginix/adonis-vulcan/types`.\n\n---\n\n## Worked example\n\n```\nGET /courses\n  ?status=eq.active\n  &or=(type.eq.required,type.eq.elective)\n  &faculty.nameTh=like.*วิศวกรรม*\n  &faculty.department.code=eq.CPE\n  &courseSpecifications.exists=true\n  &credits=gte.3\n  &not.deletedAt=is.null\n  &page=2&perPage=20\n```\n\nConceptually equivalent to:\n\n```sql\nSELECT * FROM courses\nWHERE status = 'active'\n  AND (type = 'required' OR type = 'elective')\n  AND EXISTS (\n    SELECT 1 FROM faculties\n    WHERE faculties.id = courses.faculty_id\n      AND faculties.name_th LIKE '%วิศวกรรม%'\n      AND EXISTS (\n        SELECT 1 FROM departments\n        WHERE departments.faculty_id = faculties.id\n          AND departments.code = 'CPE'\n      )\n  )\n  AND EXISTS (\n    SELECT 1 FROM course_specifications\n    WHERE course_specifications.course_id = courses.id\n  )\n  AND credits >= 3\n  AND deleted_at IS NOT NULL\nLIMIT 20 OFFSET 20;\n```\n\nWrapped in a `{ data, metadata }` envelope by the rapid CRUD controller.\n\n---\n\n## Development\n\n```sh\nyarn install\nyarn quick:test    # run unit tests without lint\nyarn test          # lint + tests + coverage\nyarn typecheck\nyarn build         # compiles to ./build\n```\n\n---\n\n## License\n\nProprietary — Copyright (c) Aginix. All rights reserved. See [LICENSE.md](./LICENSE.md).\n","readmeFilename":"README.md"}