@adonis-agora/filter 0.0.0-beta-20261110131309

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +417 -0
  2. package/LICENSE +21 -0
  3. package/README.md +75 -0
  4. package/dist/commands/main.d.ts +19 -0
  5. package/dist/commands/main.d.ts.map +1 -0
  6. package/dist/commands/main.js +19 -0
  7. package/dist/commands/main.js.map +1 -0
  8. package/dist/commands/make_filter.d.ts +18 -0
  9. package/dist/commands/make_filter.d.ts.map +1 -0
  10. package/dist/commands/make_filter.js +32 -0
  11. package/dist/commands/make_filter.js.map +1 -0
  12. package/dist/commands/make_filter_client.d.ts +35 -0
  13. package/dist/commands/make_filter_client.d.ts.map +1 -0
  14. package/dist/commands/make_filter_client.js +85 -0
  15. package/dist/commands/make_filter_client.js.map +1 -0
  16. package/dist/configure.d.ts +15 -0
  17. package/dist/configure.d.ts.map +1 -0
  18. package/dist/configure.js +20 -0
  19. package/dist/configure.js.map +1 -0
  20. package/dist/providers/filter_provider.d.ts +28 -0
  21. package/dist/providers/filter_provider.d.ts.map +1 -0
  22. package/dist/providers/filter_provider.js +33 -0
  23. package/dist/providers/filter_provider.js.map +1 -0
  24. package/dist/src/aggregate.d.ts +70 -0
  25. package/dist/src/aggregate.d.ts.map +1 -0
  26. package/dist/src/aggregate.js +195 -0
  27. package/dist/src/aggregate.js.map +1 -0
  28. package/dist/src/aggregate_path.d.ts +25 -0
  29. package/dist/src/aggregate_path.d.ts.map +1 -0
  30. package/dist/src/aggregate_path.js +35 -0
  31. package/dist/src/aggregate_path.js.map +1 -0
  32. package/dist/src/apply_from_request.d.ts +85 -0
  33. package/dist/src/apply_from_request.d.ts.map +1 -0
  34. package/dist/src/apply_from_request.js +164 -0
  35. package/dist/src/apply_from_request.js.map +1 -0
  36. package/dist/src/base_filter.d.ts +49 -0
  37. package/dist/src/base_filter.d.ts.map +1 -0
  38. package/dist/src/base_filter.js +33 -0
  39. package/dist/src/base_filter.js.map +1 -0
  40. package/dist/src/base_model_filter.d.ts +90 -0
  41. package/dist/src/base_model_filter.d.ts.map +1 -0
  42. package/dist/src/base_model_filter.js +85 -0
  43. package/dist/src/base_model_filter.js.map +1 -0
  44. package/dist/src/coerce_value.d.ts +26 -0
  45. package/dist/src/coerce_value.d.ts.map +1 -0
  46. package/dist/src/coerce_value.js +82 -0
  47. package/dist/src/coerce_value.js.map +1 -0
  48. package/dist/src/cursor.d.ts +92 -0
  49. package/dist/src/cursor.d.ts.map +1 -0
  50. package/dist/src/cursor.js +107 -0
  51. package/dist/src/cursor.js.map +1 -0
  52. package/dist/src/custom_filter.d.ts +36 -0
  53. package/dist/src/custom_filter.d.ts.map +1 -0
  54. package/dist/src/custom_filter.js +127 -0
  55. package/dist/src/custom_filter.js.map +1 -0
  56. package/dist/src/decorator_metadata.d.ts +41 -0
  57. package/dist/src/decorator_metadata.d.ts.map +1 -0
  58. package/dist/src/decorator_metadata.js +83 -0
  59. package/dist/src/decorator_metadata.js.map +1 -0
  60. package/dist/src/decorators.d.ts +73 -0
  61. package/dist/src/decorators.d.ts.map +1 -0
  62. package/dist/src/decorators.js +118 -0
  63. package/dist/src/decorators.js.map +1 -0
  64. package/dist/src/escape-like.d.ts +10 -0
  65. package/dist/src/escape-like.d.ts.map +1 -0
  66. package/dist/src/escape-like.js +15 -0
  67. package/dist/src/escape-like.js.map +1 -0
  68. package/dist/src/field_aliases.d.ts +30 -0
  69. package/dist/src/field_aliases.d.ts.map +1 -0
  70. package/dist/src/field_aliases.js +65 -0
  71. package/dist/src/field_aliases.js.map +1 -0
  72. package/dist/src/filter_class.d.ts +46 -0
  73. package/dist/src/filter_class.d.ts.map +1 -0
  74. package/dist/src/filter_class.js +244 -0
  75. package/dist/src/filter_class.js.map +1 -0
  76. package/dist/src/filter_spec.d.ts +231 -0
  77. package/dist/src/filter_spec.d.ts.map +1 -0
  78. package/dist/src/filter_spec.js +182 -0
  79. package/dist/src/filter_spec.js.map +1 -0
  80. package/dist/src/filterable_mixin.d.ts +49 -0
  81. package/dist/src/filterable_mixin.d.ts.map +1 -0
  82. package/dist/src/filterable_mixin.js +47 -0
  83. package/dist/src/filterable_mixin.js.map +1 -0
  84. package/dist/src/generate_client.d.ts +107 -0
  85. package/dist/src/generate_client.d.ts.map +1 -0
  86. package/dist/src/generate_client.js +164 -0
  87. package/dist/src/generate_client.js.map +1 -0
  88. package/dist/src/group_by_count.d.ts +67 -0
  89. package/dist/src/group_by_count.d.ts.map +1 -0
  90. package/dist/src/group_by_count.js +71 -0
  91. package/dist/src/group_by_count.js.map +1 -0
  92. package/dist/src/in_memory.d.ts +279 -0
  93. package/dist/src/in_memory.d.ts.map +1 -0
  94. package/dist/src/in_memory.js +810 -0
  95. package/dist/src/in_memory.js.map +1 -0
  96. package/dist/src/index.d.ts +51 -0
  97. package/dist/src/index.d.ts.map +1 -0
  98. package/dist/src/index.js +33 -0
  99. package/dist/src/index.js.map +1 -0
  100. package/dist/src/lucid_adapter.d.ts +311 -0
  101. package/dist/src/lucid_adapter.d.ts.map +1 -0
  102. package/dist/src/lucid_adapter.js +469 -0
  103. package/dist/src/lucid_adapter.js.map +1 -0
  104. package/dist/src/lucid_macros.d.ts +87 -0
  105. package/dist/src/lucid_macros.d.ts.map +1 -0
  106. package/dist/src/lucid_macros.js +67 -0
  107. package/dist/src/lucid_macros.js.map +1 -0
  108. package/dist/src/normalizer.d.ts +21 -0
  109. package/dist/src/normalizer.d.ts.map +1 -0
  110. package/dist/src/normalizer.js +61 -0
  111. package/dist/src/normalizer.js.map +1 -0
  112. package/dist/src/operators.d.ts +37 -0
  113. package/dist/src/operators.d.ts.map +1 -0
  114. package/dist/src/operators.js +43 -0
  115. package/dist/src/operators.js.map +1 -0
  116. package/dist/src/paginate.d.ts +12 -0
  117. package/dist/src/paginate.d.ts.map +1 -0
  118. package/dist/src/paginate.js +88 -0
  119. package/dist/src/paginate.js.map +1 -0
  120. package/dist/src/pagination_stash.d.ts +4 -0
  121. package/dist/src/pagination_stash.d.ts.map +1 -0
  122. package/dist/src/pagination_stash.js +19 -0
  123. package/dist/src/pagination_stash.js.map +1 -0
  124. package/dist/src/parse_request.d.ts +50 -0
  125. package/dist/src/parse_request.d.ts.map +1 -0
  126. package/dist/src/parse_request.js +220 -0
  127. package/dist/src/parse_request.js.map +1 -0
  128. package/dist/src/runner.d.ts +65 -0
  129. package/dist/src/runner.d.ts.map +1 -0
  130. package/dist/src/runner.js +371 -0
  131. package/dist/src/runner.js.map +1 -0
  132. package/dist/src/source_resolver.d.ts +18 -0
  133. package/dist/src/source_resolver.d.ts.map +1 -0
  134. package/dist/src/source_resolver.js +64 -0
  135. package/dist/src/source_resolver.js.map +1 -0
  136. package/dist/src/spatie_parser.d.ts +39 -0
  137. package/dist/src/spatie_parser.d.ts.map +1 -0
  138. package/dist/src/spatie_parser.js +134 -0
  139. package/dist/src/spatie_parser.js.map +1 -0
  140. package/dist/src/testing.d.ts +73 -0
  141. package/dist/src/testing.d.ts.map +1 -0
  142. package/dist/src/testing.js +136 -0
  143. package/dist/src/testing.js.map +1 -0
  144. package/dist/src/types.d.ts +226 -0
  145. package/dist/src/types.d.ts.map +1 -0
  146. package/dist/src/types.js +2 -0
  147. package/dist/src/types.js.map +1 -0
  148. package/dist/src/validate-column-filter.d.ts +30 -0
  149. package/dist/src/validate-column-filter.d.ts.map +1 -0
  150. package/dist/src/validate-column-filter.js +148 -0
  151. package/dist/src/validate-column-filter.js.map +1 -0
  152. package/dist/src/value-shape.d.ts +18 -0
  153. package/dist/src/value-shape.d.ts.map +1 -0
  154. package/dist/src/value-shape.js +36 -0
  155. package/dist/src/value-shape.js.map +1 -0
  156. package/dist/stubs/main.d.ts +3 -0
  157. package/dist/stubs/main.d.ts.map +1 -0
  158. package/dist/stubs/main.js +4 -0
  159. package/dist/stubs/main.js.map +1 -0
  160. package/dist/stubs/make/filter/main.stub +45 -0
  161. package/package.json +102 -0
  162. package/skills/filter-basics/SKILL.md +285 -0
  163. package/skills/filter-codegen/SKILL.md +246 -0
  164. package/skills/filter-definitions/SKILL.md +340 -0
  165. package/skills/filter-querying/SKILL.md +319 -0
  166. package/skills/filter-safety/SKILL.md +304 -0
  167. package/stubs/main.ts +4 -0
  168. package/stubs/make/filter/main.stub +45 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,417 @@
1
+ # @adonis-agora/filter
2
+
3
+ ## 0.0.0-beta-20261110131309
4
+
5
+ ### Patch Changes
6
+
7
+ - [#135](https://github.com/DavideCarvalho/adonis-agora-filter/pull/135) [`ed96cf0`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/ed96cf067d8e4d0983cde35c1c59eab81f94bac8) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Fix `node ace add @adonis-agora/filter`: the package's main entry now exports the `configure` hook, so the provider and the commands barrel get registered in `adonisrc.ts`.
8
+
9
+ Fix `node ace make:filter`, which crashed with `SyntaxError: Unexpected identifier 'filterable'` while compiling its stub.
10
+
11
+ ## 0.11.2
12
+
13
+ ### Patch Changes
14
+
15
+ - [#128](https://github.com/DavideCarvalho/adonis-agora-filter/pull/128) [`456d256`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/456d256e1f9001751a03193e4aa98cc4740781c0) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - perf(in-memory): read each row's sort values at most once (lazily memoized) instead of twice per comparison, memoize the reader used for to-one relation hops, and hoist per-field kind lookups out of the distinct/group-by row loops. Sorting 20k rows by a virtual field is ~2x faster and by a to-one relation path ~5x faster; results and error behaviour are unchanged.
16
+
17
+ ## 0.11.1
18
+
19
+ ### Patch Changes
20
+
21
+ - [#126](https://github.com/DavideCarvalho/adonis-agora-filter/pull/126) [`e03c7b7`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/e03c7b7a833ba4e3a8a2012dfbd3efae62162262) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Faster list queries, and a `notContains` fix.
22
+
23
+ - `filterPaginate` (the query macro and `Model.filterPaginate`) reads the page before the total and skips Lucid's `COUNT(*)` when the page comes back short, since `offset + rows` already is the total. Full pages, and empty pages past the first, still count. Models with `before:paginate` / `before:fetch` hooks, and select lists that can change the row count (`distinct`, aggregates, raw expressions, `union`), keep Lucid's own `paginate`. The paginator and its meta are unchanged.
24
+ - The keyset seek ANDs a redundant `c0 >= v0` (`<=` for a `desc` lead) in front of its OR-of-tiers predicate, so Postgres can seek a `(c0, id)` index to the cursor instead of scanning from the start of it. Skipped when the boundary value is NULL.
25
+ - A to-many `$count` filter that only tests emptiness (`gt 0`, `gte 1`, `notEquals 0`, `equals 0`, `lte 0`, `lt 1`) compiles to `EXISTS` / `NOT EXISTS` instead of a correlated `COUNT(*)`. Same rows.
26
+ - Fix: `notContains` compiled to a not-equals test. It now is the negation of `contains` — `field IS NOT NULL AND NOT (field ILIKE '%v%')` — with the same case-insensitivity and LIKE escaping. `QueryBuilderLike` gains the `whereNot(callback)` overload this needs (Lucid, the in-memory builder and `MockQueryBuilder` implement it).
27
+
28
+ ## 0.11.0
29
+
30
+ ### Minor Changes
31
+
32
+ - [#105](https://github.com/DavideCarvalho/adonis-agora-filter/pull/105) [`72ff139`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/72ff139e16c9ac05f326d490a997579ac5fb3273) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Drop Node.js 20 and 22: `engines.node` is now `>=24`. Node 20 reached end-of-life on 2026-04-30, and AdonisJS 7, which these packages require as a peer, needs Node 24 or later (`@adonisjs/core` declares `engines.node >=24.0.0`). The supported lines are the ones Node still maintains that AdonisJS 7 accepts: 24 and 26, both tested in CI.
33
+
34
+ ## 0.10.0
35
+
36
+ ### Minor Changes
37
+
38
+ - [#67](https://github.com/DavideCarvalho/adonis-agora-filter/pull/67) [`1dd11c3`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/1dd11c3563cd015d27627808cc3e23830ed4be50) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - In-memory adapter at `@adonis-agora/filter/in-memory`: `defineCollection({ fields, primaryKey, relations })` declares a row source (field types incl. `'string[]'`-style arrays, virtual fields, to-one/to-many relations), and `collection.query(rows)` is a `QueryBuilderLike` the existing runner drives unchanged — `applyFilter`, `applyCursor`, `applyFilterFromRequest`, `applyCursorFromRequest` and `groupByCountFromRequest` all work over a plain array with the same parsing, allow-listing and coercion. Evaluation mirrors the SQL the Lucid adapter emits (ILIKE, three-valued NULL logic, NULLS LAST asc / FIRST desc, EXISTS for to-many paths). `filterInMemory` / `cursorInMemory` do it in one call; `InMemoryQueryError` is thrown for SQL-only features (`computed`, `fullText`, `vectorSimilarity`).
39
+
40
+ ## 0.9.0
41
+
42
+ ### Minor Changes
43
+
44
+ - [#35](https://github.com/DavideCarvalho/adonis-agora-filter/pull/35) [`b6424ec`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/b6424ec696dbf8b50928e03a619d0227f5436345) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - One class style that does everything: `BaseFilter<TQuery>` — the generic base behind `BaseModelFilter` (Lucid) and custom backends alike — plus `applyCustomFilter` (methods per key over a caller-created draft, same envelope, loud rejections) and `groupByCount` end to end (`FilterQueryBuilder.groupByCount()` + `groupByCount[field]` envelope + `groupByCountFromRequest` over Lucid `GROUP BY` or a `GroupByCountAdapter`, with scope, search and paging). Lucid gains the `select`/`count`/`groupBy`/`offset` aggregation seam (optional, non-breaking).
45
+
46
+ ## 0.8.1
47
+
48
+ ### Patch Changes
49
+
50
+ - [#32](https://github.com/DavideCarvalho/adonis-agora-filter/pull/32) [`1fe94ce`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/1fe94cee8ef847c25b163b68f3eaba9eb4c1c7ac) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Declare `@adonisjs/core` as a required peer, not an optional one.
51
+
52
+ The package's main entry point reads the ambient `HttpContext` (the Lucid macros and the
53
+ `Filterable` mixin both resolve the request context from AsyncLocalStorage when no `ctx` is
54
+ passed), so importing `@adonis-agora/filter` without `@adonisjs/core` installed failed at load
55
+ with `ERR_MODULE_NOT_FOUND` rather than warning at install. The peer now says what the code does.
56
+
57
+ `@adonisjs/lucid` stays optional and structural — the library talks to any `QueryBuilderLike`. A
58
+ new test walks the barrel's import graph and fails on any value import of `@adonisjs/lucid`, which
59
+ is the promise that _is_ kept.
60
+
61
+ ## 0.8.0
62
+
63
+ ### Minor Changes
64
+
65
+ - [#29](https://github.com/DavideCarvalho/adonis-agora-filter/pull/29) [`2fd4700`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/2fd47000b87b025edf4c117ef97b1d9b731731a0) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Filter classes — the `adonis-lucid-filter` authoring shape, with the whole pipeline behind it.
66
+
67
+ A filter can now be a class:
68
+
69
+ ```ts
70
+ @inject()
71
+ export default class UserFilter extends BaseModelFilter {
72
+ declare $query: ModelQueryBuilderContract<typeof User>;
73
+ constructor(private tenants: TenantsService) {
74
+ super();
75
+ }
76
+
77
+ static model = User;
78
+ static filterable = ["name", "email", "status"];
79
+ static sortable = ["name", "createdAt"];
80
+ static searchable = ["name", "email"];
81
+
82
+ setup() {
83
+ this.$query.where("tenantId", this.tenants.current(this.$ctx));
84
+ }
85
+
86
+ fullName(value: string) {
87
+ this.$query.whereRaw("first_name || ' ' || last_name ilike ?", [
88
+ `%${value}%`,
89
+ ]);
90
+ }
91
+ }
92
+ ```
93
+
94
+ - **`BaseModelFilter`** — a method per request key, the builder on `this.$query`, a `setup()` that
95
+ runs before anything the request asked for. Writing the method is what exposes the key, so it
96
+ needs no allow-list entry; plain columns stay declarative in the statics.
97
+ - **Container-resolved** — the class is constructed through the request's IoC resolver, so
98
+ `@inject()` on the constructor works exactly as it does in a controller.
99
+ - **`Filterable` model mixin** — `static $filter = () => UserFilter`, then
100
+ `User.filterPaginate(ctx)` for the whole endpoint, or `const { query } = await User.filter(ctx)`
101
+ to keep composing and `query.filterPaginate()` to page it with what the request asked for.
102
+ - **The macros take a class** too, and `filterPaginate()` with no arguments pages a query a
103
+ previous call already filtered.
104
+ - **`node ace make:filter user`** scaffolds the class.
105
+ - Key matching follows the same conventions as `adonis-lucid-filter`: `static blacklist`,
106
+ `static dropId`, `static camelCase`, and a bare top-level key (`?minAge=21`) reaching a method of
107
+ that name. Keys the wire format owns (`sort`, `page`, `search`, …) are never dispatched.
108
+
109
+ Everything compiles to the same `FilterSpec` the declarative `defineFilter` produces, so classes
110
+ and specs share one runner — same allow-listing, operators, search, sort and page clamping.
111
+
112
+ Note for the class form: `applyFilterFromRequest(query, UserFilter, ctx)` and `User.filter(ctx)`
113
+ resolve to `{ page, size }` and `{ query, page, size }` respectively, never to the builder as the
114
+ promise's own value — a Lucid query builder is thenable, so a promise resolving to one would run
115
+ the query instead of handing it back.
116
+
117
+ - [#30](https://github.com/DavideCarvalho/adonis-agora-filter/pull/30) [`9796b85`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/9796b851b078d6d1aa977bbe5a396058d8059d55) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Decorators for the class form, and a `FilterClass` type fix.
118
+
119
+ `@filterFor('team.name')` binds a filter method to the request key(s) it answers, for keys a method
120
+ name could never spell and for one method answering several keys; a bound method stops answering to
121
+ its own name, so the public key survives a rename. A key the wire format owns (`sort`, `page`, …)
122
+ is refused when the class is defined.
123
+
124
+ `@filterable()`, `@sortable()` and `@searchable()` declare a model's filterable surface on the
125
+ columns themselves, stacked under Lucid's `@column()`; a filter picks them up through its
126
+ `static model`, and a static on the filter still replaces the list outright so a stricter filter can
127
+ narrow a shared model. `@filterable('number')` also declares the field's kind.
128
+
129
+ Both work under legacy (`experimentalDecorators`, what AdonisJS compiles) and standard TC39
130
+ decorators — the flavour is detected at call time.
131
+
132
+ Fixes `FilterClass`, which intersected `Record<string, unknown>`: no concrete
133
+ `class UserFilter extends BaseModelFilter` satisfied that, so `static $filter = () => UserFilter`
134
+ on a model did not typecheck. A type test over a real Lucid model now covers it.
135
+
136
+ ## 0.7.1
137
+
138
+ ### Patch Changes
139
+
140
+ - [#26](https://github.com/DavideCarvalho/adonis-agora-filter/pull/26) [`9b07c15`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/9b07c159716c5032812bbf5c2360ff82df7be4c5) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Ship TanStack Intent AI-agent skills: five core skills under `packages/core/skills/` (filter-basics, filter-safety, filter-definitions, filter-querying, filter-codegen) and one under `packages/client/skills/` (filter-query-builder), plus repo-level `_artifacts/` (domain map, skill spec, skill tree) and a `check-skills` GitHub workflow validating them on PRs. Skills are included in each package's `files` so they land in `node_modules` on install.
141
+
142
+ ## 0.7.0
143
+
144
+ ### Minor Changes
145
+
146
+ - [#23](https://github.com/DavideCarvalho/adonis-agora-filter/pull/23) [`2bd4e7b`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/2bd4e7baa50d3d0b4a25c834fc9edf612f7d854a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - `parseSpatieRequest` now parses `distinct`, matching `parseFilterRequest`.
147
+
148
+ `parseSpatieRequest` is documented as the additive counterpart to
149
+ `parseFilterRequest` — same filter, sort and search shapes, plus cursor
150
+ pagination, includes and sparse fieldsets — so a controller can swap one for the
151
+ other. It did not read `distinct`, even though the runner applies it. Swapping
152
+ the parser turned a working `?distinct=city` into a full, un-deduped result set
153
+ with nothing to signal that the parameter had been dropped.
154
+
155
+ Both string (`distinct=city,tier`) and repeated (`distinct[]=city&distinct[]=tier`)
156
+ forms now parse identically in the two parsers.
157
+
158
+ - [#23](https://github.com/DavideCarvalho/adonis-agora-filter/pull/23) [`2bd4e7b`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/2bd4e7baa50d3d0b4a25c834fc9edf612f7d854a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Add the `configure` hook, so `node ace add @adonis-agora/filter` actually wires the package.
159
+
160
+ The install instructions have always said to run `node ace add @adonis-agora/filter`,
161
+ but the package shipped no `configure` hook, so the command installed the
162
+ dependency and wired nothing. Two things silently did not happen: the provider
163
+ was never added to `adonisrc.ts` (no `applyFilterFromRequest` / `filterPaginate`
164
+ macros on `ModelQueryBuilder`), and the commands barrel was never registered, so
165
+ `make:filter-client` never appeared in `node ace list`.
166
+
167
+ `node ace add @adonis-agora/filter` — or `node ace configure @adonis-agora/filter`
168
+ on an already-installed package — now registers both:
169
+
170
+ ```ts
171
+ // adonisrc.ts
172
+ providers: [() => import('@adonis-agora/filter/filter_provider')],
173
+ commands: [() => import('@adonis-agora/filter/commands')],
174
+ ```
175
+
176
+ Nothing is published to `config/`: a filter policy is a per-model
177
+ `defineFilterSpec` call in your own code, not global configuration. Apps that
178
+ wired those two entries by hand need no change — the codemod is idempotent.
179
+
180
+ - [#23](https://github.com/DavideCarvalho/adonis-agora-filter/pull/23) [`2bd4e7b`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/2bd4e7baa50d3d0b4a25c834fc9edf612f7d854a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - `parseFilterRequest` now understands the client builder's structured shape, closing a silent unfiltered-response bug.
181
+
182
+ A POST search endpoint that hands `filterQuery()…build()` straight to
183
+ `parseFilterRequest` — the pairing the guides describe — did not work. The
184
+ builder returns
185
+
186
+ ```ts
187
+ { filter: { where: [{ field: 'status', operator: 'in', value: [...] }] },
188
+ sort: [{ field: 'createdAt', direction: 'desc' }],
189
+ paginate: { page: 2, size: 25 } }
190
+ ```
191
+
192
+ and the parser reshaped it as if `where` were a column: the entire condition list
193
+ became one `in` filter on a field named `where`, which the allow-list then
194
+ pruned, while `sort` and `paginate` were discarded for not being strings. The
195
+ endpoint answered with **every row, unsorted and unpaginated**, and raised
196
+ nothing — the failure looked like a working search with a broad result set.
197
+
198
+ The same shape reaches a plain GET too, because OR/AND groups serialize to a
199
+ top-level `where[0][field]=…`, so grouped queries were silently unfiltered as
200
+ well.
201
+
202
+ `parseFilterRequest` now recognises an already-structured condition list — under
203
+ `filter.where` or at the top level — and takes it as the filters, with nested
204
+ `AND`/`OR` groups intact. It also reads `sort` in the `[{ field, direction }]`
205
+ form and maps `paginate: { page, size }` onto `page`/`size`.
206
+
207
+ Existing requests are unaffected: `filter[field]=…`, `sort=-createdAt`,
208
+ `page`/`size` and `page[number]`/`page[size]` parse exactly as before, and a real
209
+ column named `where` (`filter[where]=lobby`, `filter[where][contains]=lob`,
210
+ `filter[where][]=a&filter[where][]=b`) is still treated as a column — only an
211
+ array of `{ field, operator }` records is read as a structured list, and a query
212
+ string cannot produce one by accident.
213
+
214
+ Note that `include` is still not consumed: eager-loading stays the caller's
215
+ `preload` call.
216
+
217
+ ### Patch Changes
218
+
219
+ - [#23](https://github.com/DavideCarvalho/adonis-agora-filter/pull/23) [`2bd4e7b`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/2bd4e7baa50d3d0b4a25c834fc9edf612f7d854a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Report the real version from the exported `VERSION` constant.
220
+
221
+ `VERSION` is a hand-written literal next to a "keep in sync with package.json"
222
+ comment, and it had not been touched since the first release: the package shipped
223
+ `0.2.0` through `0.6.0` while `VERSION` still answered `'0.1.0'`. Anything gating
224
+ on it — a feature check, a bug report, a diagnostics banner — got a wrong answer.
225
+
226
+ It now reads `0.6.0`, and a test compares it against `package.json` so the next
227
+ release cannot silently drift again.
228
+
229
+ - [#22](https://github.com/DavideCarvalho/adonis-agora-filter/pull/22) [`a33b2fe`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/a33b2febc48baa3dafcf6a703e4250bdebbde275) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Fix a 500 when `distinct` is given a relation path.
230
+
231
+ `distinct` was gated on the `allowed` list and nothing else. A relation path like
232
+ `posts.title` is filterable by design — declaring `relations: { posts: { filterable: ['title'] } }`
233
+ whitelists it, and `make:filter-client` enumerates it into the generated field
234
+ union — so `?distinct=posts.title` cleared the check and was handed to Lucid
235
+ verbatim:
236
+
237
+ ```sql
238
+ select distinct "posts"."title" from "users"
239
+ --> ERROR: missing FROM-clause entry for table "posts"
240
+ ```
241
+
242
+ Lucid filters a relation with a correlated `EXISTS` subquery, so the relation is
243
+ never joined into the outer `FROM` and there is no alias to project a column
244
+ from. A `whereHas` on the same relation in the same request does not help.
245
+
246
+ **If your `distinct` works today, nothing changes.** Root-table columns —
247
+ including ones qualified with the root table's own name — behave exactly as
248
+ before, as do the alias resolution and the allow-list drop.
249
+
250
+ **If you pass a relation path** (`posts.title`) or a to-many aggregate path
251
+ (`posts.$count`), you now get a defined refusal instead of a database error: the
252
+ field is dropped from the `distinct` list (the remaining fields still apply), or
253
+ raises `InvalidColumnFilterError` — a 400, not a 500 — when your spec sets
254
+ `throwOnInvalid`. The message names the cause: it is the missing join, not the
255
+ allow-list, so adding the path to `filterable` will not (and should not) change
256
+ it. Filtering on that path keeps working; only projecting it is refused.
257
+
258
+ - [#23](https://github.com/DavideCarvalho/adonis-agora-filter/pull/23) [`2bd4e7b`](https://github.com/DavideCarvalho/adonis-agora-filter/commit/2bd4e7baa50d3d0b4a25c834fc9edf612f7d854a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Declare a supported Node range in `engines` again, instead of one exact version.
259
+
260
+ Both packages shipped `"engines": { "node": "v26.7.0" }` — an exact version, and
261
+ plainly the output of `node -v`, leading `v` and all. Every consumer on any other
262
+ Node got an unsatisfied-engine warning on install, and anyone running with
263
+ `engine-strict` (or a package manager that treats it as fatal) could not install
264
+ at all. The pinned version was also higher than anything the project itself uses:
265
+ CI runs Node 22 and `.nvmrc` names Node 20.
266
+
267
+ `engines` states the floor the package actually supports, which is `>=20.6.0` —
268
+ the same range it declared before, and the one the rest of the Agora packages
269
+ use. Nothing about the code changed; this only stops a false incompatibility
270
+ signal.
271
+
272
+ ## 0.6.0
273
+
274
+ ### Minor Changes
275
+
276
+ - Parity sync from nestjs-filter: execute the server-side `distinct` projection (was a silent no-op), computed (virtual) fields for filter + sort (verbatim-string + `({alias}) => sql` forms), and native to-many aggregate fields (`$count`/`$sum`/`$avg`/`$min`/`$max`) auto-discovered from Lucid relation metadata — value stays parameterized (injection-safe), identifiers quoted.
277
+
278
+ ## 0.5.0
279
+
280
+ ### Minor Changes
281
+
282
+ - [#6](https://github.com/DavideCarvalho/adonis-filter/pull/6) [`e39da2c`](https://github.com/DavideCarvalho/adonis-filter/commit/e39da2c6f7e47553990f3295414b23d508894c9a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Os macros `query.applyFilterFromRequest(spec, ctx?)` e `query.filterPaginate(spec, ctx?)` passam a aceitar `ctx` opcional: quando omitido, leem o `HttpContext` ativo do AsyncLocalStorage do Adonis (`HttpContext.getOrFail()`). Nos controllers (99% dos casos) você chama `query.applyFilterFromRequest(spec)` sem passar o ctx. Fora de uma request (job/command), passe o ctx explicitamente. O default vive só no macro (camada Adonis); a função livre `applyFilterFromRequest` continua framework-agnostic, exigindo o ctx.
283
+
284
+ ## 0.4.0
285
+
286
+ ### Minor Changes
287
+
288
+ - [#4](https://github.com/DavideCarvalho/adonis-filter/pull/4) [`76b2ca8`](https://github.com/DavideCarvalho/adonis-filter/commit/76b2ca82d27811b09dbd98629be6964e9aee6167) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Add chainable Lucid query-builder macros via an optional `FilterProvider`
289
+
290
+ Register `@adonis-agora/filter/filter_provider` to get the method-call form of
291
+ `applyFilterFromRequest` on any Lucid query builder:
292
+
293
+ ```ts
294
+ // filter + sort + search, then keep chaining:
295
+ const rows = await User.query()
296
+ .where("tenantId", tenant.id)
297
+ .applyFilterFromRequest(userFilter, ctx)
298
+ .orderBy("createdAt", "desc");
299
+
300
+ // filter + paginate in one terminal call (returns Lucid's paginator):
301
+ const page = await User.query().filterPaginate(userFilter, ctx);
302
+ ```
303
+
304
+ `applyFilterFromRequest` applies the spec's server scope + allow-listed
305
+ filter/sort/search and returns the query for chaining (pagination resolved but
306
+ not applied); `filterPaginate` additionally calls `paginate(page, size)`. The
307
+ free functions are unchanged and work without the provider — the macros only add
308
+ the chainable sugar, so `@adonisjs/lucid` is an optional peer. `registerFilterMacros`
309
+ is also exported for manual registration.
310
+
311
+ ## 0.3.1
312
+
313
+ ### Patch Changes
314
+
315
+ - [`e6fb05c`](https://github.com/DavideCarvalho/adonis-filter/commit/e6fb05c0e137cd4ab3bad1f9ce5626216e670a8f) - Fix `QueryBuilderLike` rejecting every real Lucid query builder
316
+
317
+ Passing a Lucid builder to `applyFilterFromRequest` (or any adapter entry
318
+ point) failed to typecheck in consuming apps:
319
+
320
+ ```
321
+ Argument of type 'ModelQueryBuilderContract<typeof Post, Post>' is not
322
+ assignable to parameter of type 'QueryBuilderLike'.
323
+ Types of property 'where' are incompatible.
324
+ ```
325
+
326
+ The message blames `where`, but `where` was fine — TS reports the first member
327
+ it tries. The real culprit was `whereHas`, declared here as
328
+ `whereHas(relation: string, ...)`. Lucid types its own as
329
+ `<Name extends ExtractModelRelations<Model>>(relation: Name, ...)`, a union of
330
+ the model's literal relation names, and `string` is not assignable to that
331
+ union under the contravariant parameter check — so no real builder ever
332
+ satisfied the interface. Runtime was always fine; this was types-only.
333
+
334
+ `relation` is now `any`, which is the only type that both accepts the `string`
335
+ the adapter passes and is assignable to each model's relation-name union.
336
+ Marking the member optional does not help: an optional member that is present
337
+ is still checked.
338
+
339
+ A compile-time guard against real `@adonisjs/lucid` types now covers this
340
+ (`test/types/lucid_compat.types.ts`, run by `pnpm typecheck`). Lucid is a
341
+ devDependency only — nothing under `src/` imports it, so the package stays
342
+ framework-free. A hand-transcribed stub of Lucid's types was tried first and
343
+ compiled clean while real Lucid did not, so the guard uses Lucid's own `.d.ts`.
344
+
345
+ ## 0.3.0
346
+
347
+ ### Minor Changes
348
+
349
+ - [`d20245c`](https://github.com/DavideCarvalho/adonis-filter/commit/d20245cc6818120098d0f9027b59284380fd9f7e) - `filterable` accepts a colocated map: field name and its kind in one place
350
+
351
+ The array form makes every non-string field appear twice — once in `filterable`, once in
352
+ `fieldTypes` — which is ceremony for what is usually a short list:
353
+
354
+ ```ts
355
+ filterable: ['advisorId', 'dayOfWeek', 'isRecurring'],
356
+ fieldTypes: { dayOfWeek: { kind: 'number' }, isRecurring: { kind: 'boolean' } },
357
+ ```
358
+
359
+ `filterable` now also accepts a map, declaring both at once:
360
+
361
+ ```ts
362
+ filterable: { advisorId: 'string', dayOfWeek: 'number', isRecurring: 'boolean' },
363
+ ```
364
+
365
+ It desugars at the `defineFilter` boundary — the keys become the allow-list, the values become
366
+ `fieldTypes` — so everything downstream (predicates, runner, codegen) sees exactly the spec the
367
+ array form produces. An explicit `fieldTypes` entry still wins per field, which is how a caller
368
+ adds codegen-only richness (`enumValues`/`typeRef`) on top of a bare kind.
369
+
370
+ Both existing forms (`string[]` and `'*'`) are untouched and remain the right choice when no field
371
+ needs a declared type — `'string'` is the no-op kind, so a spec of only string columns should keep
372
+ using the array.
373
+
374
+ ## 0.2.0
375
+
376
+ ### Minor Changes
377
+
378
+ - [`058f0c0`](https://github.com/DavideCarvalho/adonis-filter/commit/058f0c0ef3dd224277663e6a5d40c0ef58e6bbd7) - `fieldTypes` on `defineFilter`: server-side value validation, and one type declaration for both ends
379
+
380
+ A filter value arriving over a query string is always a string, and Postgres implicitly casts the
381
+ benign cases — `day_of_week = '3'` and `is_recurring = 'false'` both work — so the gap stayed
382
+ invisible. It surfaces when a client sends something uncastable: `?filter[isRecurring][equals]=xyz`
383
+ becomes `is_recurring = 'xyz'`, which Postgres rejects with `invalid input syntax for type boolean`.
384
+ That is a **500 on a public endpoint, driven entirely by user input**. The allow-list guarded which
385
+ FIELD could be filtered; nothing guarded the VALUE that reached the column.
386
+
387
+ `defineFilter` now accepts `fieldTypes`, and a declared field has its value coerced before it ever
388
+ reaches the driver. An uncoercible value is treated exactly like a disallowed field — dropped by
389
+ default, or a loud `InvalidColumnFilterError` (→ 400 instead of 500) under `throwOnInvalid`. The
390
+ existing semantics are reused rather than a second error path invented.
391
+
392
+ ```ts
393
+ export const availabilityFilter = defineFilter({
394
+ filterable: ["advisorId", "dayOfWeek", "isRecurring"],
395
+ fieldTypes: {
396
+ dayOfWeek: { kind: "number" },
397
+ isRecurring: { kind: "boolean" },
398
+ },
399
+ });
400
+ ```
401
+
402
+ The same declaration now also feeds `make:filter-client`, which previously required repeating the
403
+ types in the codegen manifest. Declaring a kind once drives both value coercion and the client's
404
+ operator narrowing; an explicit manifest `fieldTypes` still wins when the client wants richer
405
+ codegen-only info (`enumValues`/`typeRef`).
406
+
407
+ Details:
408
+
409
+ - Array-valued operators (`in`, `between`, ...) coerce element-wise and fail as a whole if any
410
+ element fails — a partially-coerced list would filter on something the client never asked for.
411
+ - Pattern operators (`contains`, `startsWith`, ...) are never coerced: their argument is a LIKE
412
+ pattern, so turning `contains: '3'` into the number `3` would destroy it.
413
+ - `date` values are validated but handed back verbatim, never rewritten — converting `'2026-07-15'`
414
+ to a `Date` would silently re-zone a date-only value and shift the day for negative-offset clients.
415
+ - Undeclared fields are untouched, so this is backwards compatible and opt-in.
416
+ - `FilterFieldKind` moved from `generate_client.ts` to `types.ts` (it is no longer codegen-only) and
417
+ is re-exported from its old path.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Davi Carvalho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # `@adonis-agora/filter`
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@adonis-agora/filter)](https://www.npmjs.com/package/@adonis-agora/filter) [![npm downloads](https://img.shields.io/npm/dm/@adonis-agora/filter)](https://www.npmjs.com/package/@adonis-agora/filter) [![CI](https://github.com/DavideCarvalho/adonis-agora-filter/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/DavideCarvalho/adonis-agora-filter/actions/workflows/ci.yml) [![license](https://img.shields.io/npm/l/@adonis-agora/filter)](https://www.npmjs.com/package/@adonis-agora/filter)
4
+
5
+ Server-side query filtering, sorting, and pagination for AdonisJS. Parses the
6
+ Spatie/JSON:API query the `@adonis-agora/filter-client` builder emits, applies it to a
7
+ Lucid query through a field allow-list, and resolves offset or keyset (cursor)
8
+ pagination.
9
+
10
+ ```ts
11
+ import { parseFilterRequest, applyFilter } from '@adonis-agora/filter'
12
+
13
+ const input = parseFilterRequest(ctx.request.qs())
14
+ const query = User.query()
15
+ const { page, size } = applyFilter(query, input, {
16
+ allowed: ['name', 'email', 'age'],
17
+ searchable: ['name', 'email'],
18
+ })
19
+ return query.paginate(page, size)
20
+ ```
21
+
22
+ ### Chainable macros (optional)
23
+
24
+ Register the `FilterProvider` to get the method-call form of
25
+ `applyFilterFromRequest` on any Lucid query builder — the same operation, but
26
+ chainable:
27
+
28
+ ```ts
29
+ // adonisrc.ts → providers
30
+ () => import('@adonis-agora/filter/filter_provider')
31
+ ```
32
+
33
+ ```ts
34
+ // filter + sort + search, then keep chaining:
35
+ const rows = await User.query()
36
+ .where('tenantId', tenant.id)
37
+ .applyFilterFromRequest(userFilter, ctx)
38
+ .orderBy('createdAt', 'desc')
39
+
40
+ // filter + paginate in one terminal call (returns Lucid's paginator):
41
+ const page = await User.query().filterPaginate(userFilter, ctx)
42
+ ```
43
+
44
+ `applyFilterFromRequest` returns the query (pagination resolved but not applied);
45
+ `filterPaginate` pages it with the resolved values, returning Lucid's paginator — it
46
+ reads the page first and skips the `COUNT(*)` when the page comes back short. The free
47
+ functions work without the provider — the macros only add the chainable sugar, so
48
+ `@adonisjs/lucid` stays an optional peer.
49
+
50
+ Operators: equals/notEquals, contains/iContains/startsWith/endsWith, gt/gte/lt/lte,
51
+ between/notBetween, in/notIn/isAnyOf, isNull/isNotNull/isEmpty/isNotEmpty, plus
52
+ AND/OR composition. The `allowed`/`sortable`/`searchable` lists are the security
53
+ boundary. The Lucid binding is structural (`QueryBuilderLike`) so any Lucid query
54
+ builder works and the adapter is unit-testable.
55
+
56
+ ### In-memory lists
57
+
58
+ `@adonis-agora/filter/in-memory` runs the same input against a plain array — an
59
+ in-memory `QueryBuilderLike` the runner drives exactly like a Lucid query:
60
+
61
+ ```ts
62
+ import { defineCollection, filterInMemory } from '@adonis-agora/filter/in-memory'
63
+
64
+ const users = defineCollection<UserRow>({
65
+ fields: { name: 'string', age: 'number', fullName: { type: 'string', get: (u) => u.first + ' ' + u.last } },
66
+ })
67
+ const page = filterInMemory(users, rows, input, { allowed: ['name', 'age', 'fullName'] })
68
+ // { data, meta: { total, perPage, currentPage, lastPage, firstPage } }
69
+ ```
70
+
71
+ See the [repository README](https://github.com/DavideCarvalho/adonis-filter).
72
+
73
+ ## License
74
+
75
+ MIT © Davi Carvalho
@@ -0,0 +1,19 @@
1
+ import { ListLoader } from '@adonisjs/core/ace';
2
+ import MakeFilter from './make_filter.js';
3
+ import MakeFilterClient from './make_filter_client.js';
4
+ /**
5
+ * The commands barrel for `@adonis-agora/filter`. An app registers it in its
6
+ * `adonisrc` via `rcFile.addCommand('@adonis-agora/filter/commands')`. A
7
+ * {@link ListLoader} exposes the `make:filter-client` command's metadata and
8
+ * constructors to the ace kernel: `make:filter` (scaffold a filter class) and
9
+ * `make:filter-client` (emit the typed front-end builders).
10
+ *
11
+ * `@adonisjs/core` is an *optional* peer of this otherwise framework-free core —
12
+ * only this `./commands` subpath imports it, so the main entrypoint stays
13
+ * dependency-free.
14
+ */
15
+ declare const loader: ListLoader<typeof MakeFilter | typeof MakeFilterClient>;
16
+ export declare const getMetaData: () => Promise<import("@adonisjs/core/types/ace").CommandMetaData[]>;
17
+ export declare const getCommand: (metaData: import("@adonisjs/core/types/ace").CommandMetaData) => Promise<typeof MakeFilter | typeof MakeFilterClient | null>;
18
+ export default loader;
19
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,gBAAgB,MAAM,yBAAyB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,QAAA,MAAM,MAAM,yDAAiD,CAAC;AAE9D,eAAO,MAAM,WAAW,qEAAkC,CAAC;AAC3D,eAAO,MAAM,UAAU,+HAAiC,CAAC;eAE1C,MAAM"}
@@ -0,0 +1,19 @@
1
+ import { ListLoader } from '@adonisjs/core/ace';
2
+ import MakeFilter from './make_filter.js';
3
+ import MakeFilterClient from './make_filter_client.js';
4
+ /**
5
+ * The commands barrel for `@adonis-agora/filter`. An app registers it in its
6
+ * `adonisrc` via `rcFile.addCommand('@adonis-agora/filter/commands')`. A
7
+ * {@link ListLoader} exposes the `make:filter-client` command's metadata and
8
+ * constructors to the ace kernel: `make:filter` (scaffold a filter class) and
9
+ * `make:filter-client` (emit the typed front-end builders).
10
+ *
11
+ * `@adonisjs/core` is an *optional* peer of this otherwise framework-free core —
12
+ * only this `./commands` subpath imports it, so the main entrypoint stays
13
+ * dependency-free.
14
+ */
15
+ const loader = new ListLoader([MakeFilter, MakeFilterClient]);
16
+ export const getMetaData = loader.getMetaData.bind(loader);
17
+ export const getCommand = loader.getCommand.bind(loader);
18
+ export default loader;
19
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,gBAAgB,MAAM,yBAAyB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAE9D,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAe,MAAM,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { BaseCommand } from '@adonisjs/core/ace';
2
+ import type { CommandOptions } from '@adonisjs/core/types/ace';
3
+ /**
4
+ * `node ace make:filter <name>` — scaffold a filter class under `app/filters/`.
5
+ *
6
+ * The generated class extends `BaseModelFilter`: allow-lists as statics for the plain columns, a
7
+ * `setup()` for the scope the client cannot relax, and a method per key that needs SQL of its own.
8
+ * It is `@inject()`ed, so a constructor dependency resolves through the container exactly as a
9
+ * controller's does.
10
+ */
11
+ export default class MakeFilter extends BaseCommand {
12
+ static commandName: string;
13
+ static description: string;
14
+ static options: CommandOptions;
15
+ name: string;
16
+ run(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=make_filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"make_filter.d.ts","sourceRoot":"","sources":["../../commands/make_filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,OAAgB,WAAW,SAAiB;IAC5C,OAAgB,WAAW,SAA+B;IAC1D,OAAgB,OAAO,EAAE,cAAc,CAA+B;IAG9D,IAAI,EAAE,MAAM,CAAC;IAEN,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAMlC;CACF"}
@@ -0,0 +1,32 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { args, BaseCommand } from '@adonisjs/core/ace';
8
+ import { stubsRoot } from '../stubs/main.js';
9
+ /**
10
+ * `node ace make:filter <name>` — scaffold a filter class under `app/filters/`.
11
+ *
12
+ * The generated class extends `BaseModelFilter`: allow-lists as statics for the plain columns, a
13
+ * `setup()` for the scope the client cannot relax, and a method per key that needs SQL of its own.
14
+ * It is `@inject()`ed, so a constructor dependency resolves through the container exactly as a
15
+ * controller's does.
16
+ */
17
+ export default class MakeFilter extends BaseCommand {
18
+ static commandName = 'make:filter';
19
+ static description = 'Create a new filter class';
20
+ static options = { allowUnknownFlags: true };
21
+ async run() {
22
+ const codemods = await this.createCodemods();
23
+ await codemods.makeUsingStub(stubsRoot, 'make/filter/main.stub', {
24
+ flags: this.parsed.flags,
25
+ entity: this.app.generators.createEntity(this.name),
26
+ });
27
+ }
28
+ }
29
+ __decorate([
30
+ args.string({ description: 'Name of the filter (e.g. user)' })
31
+ ], MakeFilter.prototype, "name", void 0);
32
+ //# sourceMappingURL=make_filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"make_filter.js","sourceRoot":"","sources":["../../commands/make_filter.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,MAAM,CAAU,WAAW,GAAG,aAAa,CAAC;IAC5C,MAAM,CAAU,WAAW,GAAG,2BAA2B,CAAC;IAC1D,MAAM,CAAU,OAAO,GAAmB,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAK7D,KAAK,CAAC,GAAG;QAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC7C,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,uBAAuB,EAAE;YAC/D,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;SACpD,CAAC,CAAC;IACL,CAAC;CACF;AATS;IADP,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;wCAC1C"}