@dudousxd/nestjs-filter 1.0.1

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 (116) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +108 -0
  4. package/dist/adapter/adapter.d.ts +158 -0
  5. package/dist/adapter/adapter.d.ts.map +1 -0
  6. package/dist/adapter/adapter.js +2 -0
  7. package/dist/adapter/adapter.js.map +1 -0
  8. package/dist/als-store.d.ts +15 -0
  9. package/dist/als-store.d.ts.map +1 -0
  10. package/dist/als-store.js +6 -0
  11. package/dist/als-store.js.map +1 -0
  12. package/dist/base-filter.d.ts +46 -0
  13. package/dist/base-filter.d.ts.map +1 -0
  14. package/dist/base-filter.js +95 -0
  15. package/dist/base-filter.js.map +1 -0
  16. package/dist/cli/generate.d.ts +3 -0
  17. package/dist/cli/generate.d.ts.map +1 -0
  18. package/dist/cli/generate.js +43 -0
  19. package/dist/cli/generate.js.map +1 -0
  20. package/dist/decorator/apply-filter.decorator.d.ts +11 -0
  21. package/dist/decorator/apply-filter.decorator.d.ts.map +1 -0
  22. package/dist/decorator/apply-filter.decorator.js +22 -0
  23. package/dist/decorator/apply-filter.decorator.js.map +1 -0
  24. package/dist/decorator/filter-for.decorator.d.ts +4 -0
  25. package/dist/decorator/filter-for.decorator.d.ts.map +1 -0
  26. package/dist/decorator/filter-for.decorator.js +29 -0
  27. package/dist/decorator/filter-for.decorator.js.map +1 -0
  28. package/dist/decorator/filterable.decorator.d.ts +5 -0
  29. package/dist/decorator/filterable.decorator.d.ts.map +1 -0
  30. package/dist/decorator/filterable.decorator.js +24 -0
  31. package/dist/decorator/filterable.decorator.js.map +1 -0
  32. package/dist/decorator/relations.decorator.d.ts +29 -0
  33. package/dist/decorator/relations.decorator.d.ts.map +1 -0
  34. package/dist/decorator/relations.decorator.js +40 -0
  35. package/dist/decorator/relations.decorator.js.map +1 -0
  36. package/dist/errors/exceptions.d.ts +32 -0
  37. package/dist/errors/exceptions.d.ts.map +1 -0
  38. package/dist/errors/exceptions.js +56 -0
  39. package/dist/errors/exceptions.js.map +1 -0
  40. package/dist/filter/filter-exception.filter.d.ts +6 -0
  41. package/dist/filter/filter-exception.filter.d.ts.map +1 -0
  42. package/dist/filter/filter-exception.filter.js +32 -0
  43. package/dist/filter/filter-exception.filter.js.map +1 -0
  44. package/dist/index.d.ts +23 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +19 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/input/dispatcher.d.ts +2 -0
  49. package/dist/input/dispatcher.d.ts.map +1 -0
  50. package/dist/input/dispatcher.js +25 -0
  51. package/dist/input/dispatcher.js.map +1 -0
  52. package/dist/input/normalizer.d.ts +9 -0
  53. package/dist/input/normalizer.d.ts.map +1 -0
  54. package/dist/input/normalizer.js +51 -0
  55. package/dist/input/normalizer.js.map +1 -0
  56. package/dist/input/source-resolver.d.ts +3 -0
  57. package/dist/input/source-resolver.d.ts.map +1 -0
  58. package/dist/input/source-resolver.js +49 -0
  59. package/dist/input/source-resolver.js.map +1 -0
  60. package/dist/input/validator.d.ts +3 -0
  61. package/dist/input/validator.d.ts.map +1 -0
  62. package/dist/input/validator.js +33 -0
  63. package/dist/input/validator.js.map +1 -0
  64. package/dist/interceptor/apply-filter.interceptor.d.ts +10 -0
  65. package/dist/interceptor/apply-filter.interceptor.d.ts.map +1 -0
  66. package/dist/interceptor/apply-filter.interceptor.js +81 -0
  67. package/dist/interceptor/apply-filter.interceptor.js.map +1 -0
  68. package/dist/module.d.ts +15 -0
  69. package/dist/module.d.ts.map +1 -0
  70. package/dist/module.js +111 -0
  71. package/dist/module.js.map +1 -0
  72. package/dist/operators/column-filter.dto.d.ts +22 -0
  73. package/dist/operators/column-filter.dto.d.ts.map +1 -0
  74. package/dist/operators/column-filter.dto.js +61 -0
  75. package/dist/operators/column-filter.dto.js.map +1 -0
  76. package/dist/operators/index.d.ts +5 -0
  77. package/dist/operators/index.d.ts.map +1 -0
  78. package/dist/operators/index.js +4 -0
  79. package/dist/operators/index.js.map +1 -0
  80. package/dist/operators/types.d.ts +21 -0
  81. package/dist/operators/types.d.ts.map +1 -0
  82. package/dist/operators/types.js +28 -0
  83. package/dist/operators/types.js.map +1 -0
  84. package/dist/operators/validate-column-filter.d.ts +24 -0
  85. package/dist/operators/validate-column-filter.d.ts.map +1 -0
  86. package/dist/operators/validate-column-filter.js +118 -0
  87. package/dist/operators/validate-column-filter.js.map +1 -0
  88. package/dist/runner.d.ts +105 -0
  89. package/dist/runner.d.ts.map +1 -0
  90. package/dist/runner.js +554 -0
  91. package/dist/runner.js.map +1 -0
  92. package/dist/testing/filter-testing.module.d.ts +7 -0
  93. package/dist/testing/filter-testing.module.d.ts.map +1 -0
  94. package/dist/testing/filter-testing.module.js +41 -0
  95. package/dist/testing/filter-testing.module.js.map +1 -0
  96. package/dist/testing/index.d.ts +3 -0
  97. package/dist/testing/index.d.ts.map +1 -0
  98. package/dist/testing/index.js +3 -0
  99. package/dist/testing/index.js.map +1 -0
  100. package/dist/testing/mock-query-builder.d.ts +6 -0
  101. package/dist/testing/mock-query-builder.d.ts.map +1 -0
  102. package/dist/testing/mock-query-builder.js +22 -0
  103. package/dist/testing/mock-query-builder.js.map +1 -0
  104. package/dist/tokens.d.ts +8 -0
  105. package/dist/tokens.d.ts.map +1 -0
  106. package/dist/tokens.js +8 -0
  107. package/dist/tokens.js.map +1 -0
  108. package/dist/types.d.ts +110 -0
  109. package/dist/types.d.ts.map +1 -0
  110. package/dist/types.js +2 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/utils/escape-like.d.ts +10 -0
  113. package/dist/utils/escape-like.d.ts.map +1 -0
  114. package/dist/utils/escape-like.js +15 -0
  115. package/dist/utils/escape-like.js.map +1 -0
  116. package/package.json +82 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ # @dudousxd/nestjs-filter
2
+
3
+ ## 1.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`6b22e31`](https://github.com/DavideCarvalho/nestjs-filter/commit/6b22e3153f1f0b275bd579f6fade8facae61b8a7) - Initial release of nestjs-filter.
8
+
9
+ Structured input format with three top-level keys: filter, include, search.
10
+
11
+ Core features:
12
+
13
+ - Declarative filter classes with @FilterFor, @Filterable, @ApplyFilter
14
+ - Auto-fields with entity metadata introspection
15
+ - 22 built-in operators with AND/OR composition
16
+ - Dot-notation relation filtering (posts.title)
17
+ - Eager loading via ?include=role,posts
18
+ - Global search via ?search=term (ILIKE or tsvector)
19
+ - applyDynamic() for querying any entity without a filter class
20
+ - AsyncLocalStorage state isolation
21
+ - class-validator integration
22
+ - FilterTestingModule + makeMockQueryBuilder
23
+
24
+ Adapters: MikroORM 7, TypeORM 0.3+
25
+ Client: Zero-dependency fluent query builder
26
+
27
+ ## 1.0.0
28
+
29
+ ### Minor Changes
30
+
31
+ - [`0cd738a`](https://github.com/DavideCarvalho/nestjs-filter/commit/0cd738a41105812bad6bee876d4c707bf815258f) - Initial release. Declarative ORM-agnostic filter classes for NestJS.
32
+
33
+ Core: BaseFilter, FilterRunner, @Filterable, @FilterFor, @ApplyFilter decorators, FilterModule, auto-fields with entity metadata introspection, dot-notation relation filtering, 22 built-in operators with AND/OR composition, bracket notation query string support, class-validator integration, FilterExceptionFilter, FilterTestingModule, makeMockQueryBuilder.
34
+
35
+ Adapters: MikroORM 7 and TypeORM with full operator support, entity metadata introspection, and relation filtering.
36
+
37
+ Client: Zero-dependency fluent query builder for browser and Node.js with type-safe operator validation.
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,108 @@
1
+ # @dudousxd/nestjs-filter
2
+
3
+ Core package for nestjs-filter -- declarative, ORM-agnostic filter classes for NestJS.
4
+
5
+ Provides `BaseFilter`, `FilterRunner`, decorators (`@Filterable`, `@FilterFor`, `@ApplyFilter`), `FilterModule`, exception handling, and testing utilities.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @dudousxd/nestjs-filter
11
+ ```
12
+
13
+ You also need an ORM adapter package:
14
+
15
+ ```bash
16
+ # MikroORM
17
+ pnpm add @dudousxd/nestjs-filter-mikro-orm
18
+
19
+ # TypeORM
20
+ pnpm add @dudousxd/nestjs-filter-typeorm
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { Injectable } from '@nestjs/common';
27
+ import { Filterable, FilterFor, BaseFilter, FilterModule, ApplyFilter } from '@dudousxd/nestjs-filter';
28
+
29
+ // 1. Define a filter
30
+ @Injectable()
31
+ @Filterable({ entity: User })
32
+ class UserFilter extends BaseFilter<QueryBuilder> {
33
+ @FilterFor('name')
34
+ applyName(value: string) {
35
+ this.$query.andWhere({ name: value });
36
+ }
37
+ }
38
+
39
+ // 2. Register
40
+ @Module({
41
+ imports: [
42
+ FilterModule.forRoot({ inputNormalizer: 'camelCase' }),
43
+ FilterModule.forFeature([UserFilter]),
44
+ ],
45
+ })
46
+ class AppModule {}
47
+
48
+ // 3. Use in controller
49
+ @Controller('users')
50
+ class UsersController {
51
+ @Get()
52
+ list(@ApplyFilter(UserFilter) qb: QueryBuilder) {
53
+ return qb.getResultList();
54
+ }
55
+ }
56
+ ```
57
+
58
+ ## API Reference
59
+
60
+ ### Decorators
61
+
62
+ - **`@Filterable({ entity, allowed?, blocked? })`** -- Class decorator. Associates a filter with an entity. `allowed` whitelists keys; `blocked` blacklists them.
63
+ - **`@FilterFor(inputKey?)`** -- Method decorator. Maps an input key to the method. Defaults to the method name if omitted.
64
+ - **`@ApplyFilter(FilterClass, options?)`** -- Parameter decorator. Resolves input from the request, runs the filter, and injects the QueryBuilder. Options: `source` (`'auto'|'query'|'body'|Function`), `dto`, `resolve` (dynamic filter selection).
65
+
66
+ ### Classes
67
+
68
+ - **`BaseFilter<TQuery>`** -- Abstract base class. Provides `$query`, `$input`, `$context`, `$adapter` via AsyncLocalStorage. Optional `setup()` hook.
69
+ - **`FilterRunner`** -- Injectable service. `apply(FilterClass, input, qb, context?)` runs a filter programmatically.
70
+ - **`FilterModule`** -- `forRoot(options?)` registers global infrastructure. `forFeature(filters)` registers filter classes.
71
+
72
+ ### Exceptions
73
+
74
+ - `FilterException` -- Abstract base.
75
+ - `FilterNotRegisteredException` -- Filter class not in DI container.
76
+ - `FilterMissingEntityException` -- Missing `@Filterable({ entity })`.
77
+ - `FilterStateUnavailableException` -- Accessing `$query` outside `FilterRunner.apply()`.
78
+ - `UnknownFilterKeyException` -- Unknown key when `onUnknownKey: 'throw'`.
79
+ - `FilterValidationException` -- class-validator validation failed.
80
+ - `FilterMethodException` -- A filter method (or `setup()`) threw an error.
81
+
82
+ ### Exception Filter
83
+
84
+ - **`FilterExceptionFilter`** -- Catches `FilterValidationException` and returns `{ statusCode: 400, message, errors }`.
85
+
86
+ ### Testing (from `@dudousxd/nestjs-filter/testing`)
87
+
88
+ - **`FilterTestingModule`** -- `forRoot(options?)` and `forFeature(filters)`. Defaults to `validation: 'off'`.
89
+ - **`makeMockQueryBuilder<E>()`** -- Proxy-based mock QB that records all calls. Access via `qb.calls`.
90
+
91
+ ### Types
92
+
93
+ - **`FilterInput<F>`** -- Extracts the input shape from a filter class.
94
+ - **`FilterContext`** -- `{ req?, user?, raw? }`.
95
+ - **`FilterModuleOptions`** -- `{ inputNormalizer?, dropId?, onUnknownKey?, validation? }`.
96
+ - **`ApplyFilterOptions`** -- `{ source?, dto?, resolve? }`.
97
+ - **`InputSource`** -- `'auto' | 'query' | 'body' | ((req) => Record<string, unknown>)`.
98
+
99
+ ## Configuration
100
+
101
+ | Option | Type | Default | Description |
102
+ |--------|------|---------|-------------|
103
+ | `inputNormalizer` | `'camelCase' \| 'snakeCase' \| fn` | `'camelCase'` | Normalize input keys. |
104
+ | `dropId` | `boolean` | `false` | Strip trailing `Id`/`_id`. |
105
+ | `onUnknownKey` | `'ignore' \| 'warn' \| 'throw'` | `'ignore'` | Policy for unrecognized keys. |
106
+ | `validation` | `'auto' \| 'off'` | `'auto'` | Validate with class-validator if installed. |
107
+
108
+ See the [root README](../../README.md) for full documentation.
@@ -0,0 +1,158 @@
1
+ import type { Type } from '@nestjs/common';
2
+ import type { ColumnFilter } from '../operators/types.js';
3
+ /**
4
+ * Describes a single scalar field on an entity, as reported by the ORM's
5
+ * metadata layer. Used by `autoFields: true` to validate that incoming
6
+ * filter keys correspond to real entity columns.
7
+ */
8
+ export interface EntityFieldInfo {
9
+ /** Property name on the entity class (e.g. 'name', 'email', 'createdAt'). */
10
+ name: string;
11
+ /** Actual database column name (e.g. 'name', 'email', 'created_at'). */
12
+ columnName: string;
13
+ /** Simplified type classification for the column. */
14
+ type: 'string' | 'number' | 'boolean' | 'date' | 'json' | 'unknown';
15
+ }
16
+ /**
17
+ * Describes a relation on an entity, as reported by the ORM's metadata layer.
18
+ * Used by dot-notation relation filtering to auto-join and filter by related
19
+ * entity fields (e.g. `posts.title` joins `posts` and filters by `title`).
20
+ */
21
+ export interface EntityRelationInfo {
22
+ /** Relation property name on the entity class (e.g. 'posts', 'author'). */
23
+ name: string;
24
+ /** Target entity name (e.g. 'Post', 'User'). */
25
+ targetEntity: string;
26
+ /** Relation cardinality type. */
27
+ type: 'one-to-one' | 'many-to-one' | 'one-to-many' | 'many-to-many';
28
+ }
29
+ export interface FilterAdapter {
30
+ createQueryBuilder<E>(entity: Type<E>): unknown;
31
+ /**
32
+ * Applies a constraint on a relation. The adapter joins (without selecting)
33
+ * the given relation and passes the query builder to the callback, which adds
34
+ * WHERE conditions for the related entity's fields.
35
+ *
36
+ * Implementations should use an inner join (not a left join with select) to
37
+ * avoid eagerly loading relation data and producing duplicate parent rows.
38
+ *
39
+ * Optional — adapters that don't support relation filtering should not implement this.
40
+ */
41
+ applyRelationConstraint?(qb: unknown, relationName: string, callback: (relationQb: unknown) => Promise<void>): Promise<void>;
42
+ /**
43
+ * Applies an array of generic ColumnFilter conditions to a query builder.
44
+ *
45
+ * Each adapter translates the operator-based filters into ORM-specific
46
+ * syntax (MikroORM FilterQuery objects, TypeORM andWhere/Brackets, etc.).
47
+ *
48
+ * Optional — adapters that don't support operator-based filtering should
49
+ * not implement this.
50
+ *
51
+ * @param qb - The query builder instance.
52
+ * @param filters - Array of ColumnFilter conditions to apply.
53
+ */
54
+ applyColumnFilters?(qb: unknown, filters: ColumnFilter[]): void;
55
+ /**
56
+ * Applies an auto-field value to the query builder.
57
+ *
58
+ * Auto-fields are input keys that have no @FilterFor mapping but are
59
+ * explicitly declared in the filter class via `autoFields`.
60
+ *
61
+ * The adapter handles three value shapes:
62
+ * - Single value → `equals` (andWhere)
63
+ * - Array value → `in` ($in / IN)
64
+ * - Object with operator keys → apply those operators
65
+ *
66
+ * Optional — adapters that don't support auto-fields should not implement this.
67
+ *
68
+ * @param qb - The query builder instance.
69
+ * @param field - The field/column name.
70
+ * @param value - The filter value (scalar, array, or operator object).
71
+ */
72
+ applyAutoField?(qb: unknown, field: string, value: unknown): void;
73
+ /**
74
+ * Introspects the ORM's metadata for the given entity class and returns
75
+ * an array of scalar (non-relation) field descriptors.
76
+ *
77
+ * Used by `autoFields: true` to restrict accepted input keys to actual
78
+ * entity columns, preventing unknown-column probing and SQL errors.
79
+ *
80
+ * Optional — when absent or when it returns `null`, `autoFields: true`
81
+ * falls back to accepting any key (legacy behavior) with a logged warning.
82
+ *
83
+ * @param entity - The entity class to introspect.
84
+ * @returns Array of field descriptors, or `null` if metadata is unavailable.
85
+ */
86
+ getEntityFields?(entity: Type<unknown>): EntityFieldInfo[] | null;
87
+ /**
88
+ * Introspects the ORM's metadata for the given entity class and returns
89
+ * an array of relation descriptors.
90
+ *
91
+ * Used by dot-notation relation filtering (`posts.title`) to discover
92
+ * which input key prefixes correspond to real entity relations.
93
+ *
94
+ * Optional — when absent or when it returns `null`, dot-notation
95
+ * relation filtering is disabled.
96
+ *
97
+ * @param entity - The entity class to introspect.
98
+ * @returns Array of relation descriptors, or `null` if metadata is unavailable.
99
+ */
100
+ getEntityRelations?(entity: Type<unknown>): EntityRelationInfo[] | null;
101
+ /**
102
+ * Applies a dot-notation relation field filter to the query builder.
103
+ *
104
+ * Auto-joins the relation (if not already joined) and applies a WHERE
105
+ * condition on the related entity's field.
106
+ *
107
+ * The value follows the same shapes as `applyAutoField`:
108
+ * - Single value → equals
109
+ * - Array value → IN
110
+ * - Object with operator keys → apply those operators
111
+ *
112
+ * Optional — adapters that don't support dot-notation relation filtering
113
+ * should not implement this.
114
+ *
115
+ * @param qb - The query builder instance.
116
+ * @param relationName - The relation property name (e.g. 'posts').
117
+ * @param field - The field name on the related entity (e.g. 'title').
118
+ * @param value - The filter value (scalar, array, or operator object).
119
+ */
120
+ applyAutoRelationField?(qb: unknown, relationName: string, field: string, value: unknown): void;
121
+ /**
122
+ * Applies eager loading for the given relation paths.
123
+ *
124
+ * Each include path is a dot-separated relation name (e.g. 'posts', 'posts.comments').
125
+ * The adapter should left-join-and-select these relations on the query builder.
126
+ *
127
+ * Optional — adapters that don't support includes should not implement this.
128
+ *
129
+ * @param qb - The query builder instance.
130
+ * @param includes - Array of relation paths to eagerly load.
131
+ * @param entity - The root entity class.
132
+ */
133
+ applyIncludes?(qb: unknown, includes: string[], entity: Type<unknown>): void;
134
+ /**
135
+ * Applies a global ILIKE search across the given columns.
136
+ *
137
+ * Generates an OR condition: `col1 ILIKE '%term%' OR col2 ILIKE '%term%' OR ...`
138
+ *
139
+ * Optional — adapters that don't support search should not implement this.
140
+ *
141
+ * @param qb - The query builder instance.
142
+ * @param term - The search term (already trimmed).
143
+ * @param columns - Column names to search across.
144
+ * @param entity - The root entity class.
145
+ */
146
+ applySearch?(qb: unknown, term: string, columns: string[], entity: Type<unknown>): void;
147
+ /**
148
+ * Applies a full-text vector search using a tsvector column.
149
+ *
150
+ * Optional — adapters that don't support vector search should not implement this.
151
+ *
152
+ * @param qb - The query builder instance.
153
+ * @param term - The search term.
154
+ * @param vectorColumn - The tsvector column name.
155
+ */
156
+ applyVectorSearch?(qb: unknown, term: string, vectorColumn: string): void;
157
+ }
158
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACrE;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC;CACrE;AAED,MAAM,WAAW,aAAa;IAC5B,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAEhD;;;;;;;;;OASG;IACH,uBAAuB,CAAC,CACtB,EAAE,EAAE,OAAO,EACX,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAEhE;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAElE;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,eAAe,EAAE,GAAG,IAAI,CAAC;IAElE;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,kBAAkB,EAAE,GAAG,IAAI,CAAC;IAExE;;;;;;;;;;;;;;;;;;OAkBG;IACH,sBAAsB,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAEhG;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAE7E;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAExF;;;;;;;;OAQG;IACH,iBAAiB,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/adapter/adapter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import type { FilterAdapter } from './adapter/adapter.js';
3
+ import type { FilterContext } from './types.js';
4
+ export interface FilterState {
5
+ $query: unknown;
6
+ $input: Readonly<Record<string, unknown>>;
7
+ $context: FilterContext;
8
+ $adapter: FilterAdapter | null;
9
+ $whitelisted: Set<string>;
10
+ $blacklisted: Set<string>;
11
+ $pushed: Array<[string, unknown]>;
12
+ }
13
+ export declare const filterAls: AsyncLocalStorage<FilterState>;
14
+ export declare function runWithFilterState<T>(state: FilterState, fn: () => T): T;
15
+ //# sourceMappingURL=als-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"als-store.d.ts","sourceRoot":"","sources":["../src/als-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1C,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACnC;AAED,eAAO,MAAM,SAAS,gCAAuC,CAAC;AAE9D,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAExE"}
@@ -0,0 +1,6 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ export const filterAls = new AsyncLocalStorage();
3
+ export function runWithFilterState(state, fn) {
4
+ return filterAls.run(state, fn);
5
+ }
6
+ //# sourceMappingURL=als-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"als-store.js","sourceRoot":"","sources":["../src/als-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAcrD,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,iBAAiB,EAAe,CAAC;AAE9D,MAAM,UAAU,kBAAkB,CAAI,KAAkB,EAAE,EAAW;IACnE,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { FilterAdapter } from './adapter/adapter.js';
2
+ import type { FilterContext } from './types.js';
3
+ export declare abstract class BaseFilter<TQuery = unknown> {
4
+ get $query(): TQuery;
5
+ get $input(): Readonly<Record<string, unknown>>;
6
+ get $context(): FilterContext;
7
+ get $adapter(): FilterAdapter | null;
8
+ /**
9
+ * Dynamically whitelist a filter key at runtime (typically called in setup()).
10
+ * Whitelisted keys bypass static allowed/blocked checks in the dispatcher.
11
+ */
12
+ protected whitelistMethod(key: string): void;
13
+ /**
14
+ * Dynamically blacklist a filter key at runtime (typically called in setup()).
15
+ * Blacklisted keys are skipped during dispatch regardless of static configuration.
16
+ */
17
+ protected blacklistMethod(key: string): void;
18
+ /**
19
+ * Returns the full input object, a single input value by key,
20
+ * or a default value if the key is not present.
21
+ */
22
+ input(): Readonly<Record<string, unknown>>;
23
+ input(key: string): unknown;
24
+ input(key: string, defaultValue: unknown): unknown;
25
+ /**
26
+ * Injects additional key/value pairs into the filter input queue.
27
+ * Pushed entries are dispatched after the current dispatch loop completes.
28
+ */
29
+ protected push(key: string, value: unknown): void;
30
+ protected push(input: Record<string, unknown>): void;
31
+ /**
32
+ * Imperatively constrain a relation. Delegates to the adapter's
33
+ * `applyRelationConstraint` with the given conditions applied via callback.
34
+ *
35
+ * @param relationName - The relation property name on the entity (e.g. 'posts').
36
+ * @param conditions - A record of column/value pairs to match on the related entity.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * await this.related('posts', { status: 'published' });
41
+ * ```
42
+ */
43
+ protected related(relationName: string, conditions: Record<string, unknown>): Promise<void>;
44
+ setup?(): void | Promise<void>;
45
+ }
46
+ //# sourceMappingURL=base-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-filter.d.ts","sourceRoot":"","sources":["../src/base-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,8BAAsB,UAAU,CAAC,MAAM,GAAG,OAAO;IAC/C,IAAI,MAAM,IAAI,MAAM,CAInB;IAED,IAAI,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAI9C;IAED,IAAI,QAAQ,IAAI,aAAa,CAI5B;IAED,IAAI,QAAQ,IAAI,aAAa,GAAG,IAAI,CAInC;IAED;;;OAGG;IACH,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAM5C;;;OAGG;IACH,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAM5C;;;OAGG;IACH,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAC3B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO;IAOlD;;;OAGG;IACH,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IACjD,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAapD;;;;;;;;;;;OAWG;cACa,OAAO,CACrB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC;IAsBhB,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAC/B"}
@@ -0,0 +1,95 @@
1
+ import { filterAls } from './als-store.js';
2
+ import { FilterStateUnavailableException } from './errors/exceptions.js';
3
+ export class BaseFilter {
4
+ get $query() {
5
+ const s = filterAls.getStore();
6
+ if (!s)
7
+ throw new FilterStateUnavailableException();
8
+ return s.$query;
9
+ }
10
+ get $input() {
11
+ const s = filterAls.getStore();
12
+ if (!s)
13
+ throw new FilterStateUnavailableException();
14
+ return s.$input;
15
+ }
16
+ get $context() {
17
+ const s = filterAls.getStore();
18
+ if (!s)
19
+ throw new FilterStateUnavailableException();
20
+ return s.$context;
21
+ }
22
+ get $adapter() {
23
+ const s = filterAls.getStore();
24
+ if (!s)
25
+ throw new FilterStateUnavailableException();
26
+ return s.$adapter;
27
+ }
28
+ /**
29
+ * Dynamically whitelist a filter key at runtime (typically called in setup()).
30
+ * Whitelisted keys bypass static allowed/blocked checks in the dispatcher.
31
+ */
32
+ whitelistMethod(key) {
33
+ const s = filterAls.getStore();
34
+ if (!s)
35
+ throw new FilterStateUnavailableException();
36
+ s.$whitelisted.add(key);
37
+ }
38
+ /**
39
+ * Dynamically blacklist a filter key at runtime (typically called in setup()).
40
+ * Blacklisted keys are skipped during dispatch regardless of static configuration.
41
+ */
42
+ blacklistMethod(key) {
43
+ const s = filterAls.getStore();
44
+ if (!s)
45
+ throw new FilterStateUnavailableException();
46
+ s.$blacklisted.add(key);
47
+ }
48
+ input(key, defaultValue) {
49
+ const inp = this.$input;
50
+ if (key === undefined)
51
+ return inp;
52
+ return key in inp ? inp[key] : defaultValue;
53
+ }
54
+ push(keyOrInput, value) {
55
+ const state = filterAls.getStore();
56
+ if (!state)
57
+ throw new FilterStateUnavailableException();
58
+ if (typeof keyOrInput === 'string') {
59
+ state.$pushed.push([keyOrInput, value]);
60
+ }
61
+ else {
62
+ for (const [k, v] of Object.entries(keyOrInput)) {
63
+ state.$pushed.push([k, v]);
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * Imperatively constrain a relation. Delegates to the adapter's
69
+ * `applyRelationConstraint` with the given conditions applied via callback.
70
+ *
71
+ * @param relationName - The relation property name on the entity (e.g. 'posts').
72
+ * @param conditions - A record of column/value pairs to match on the related entity.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * await this.related('posts', { status: 'published' });
77
+ * ```
78
+ */
79
+ async related(relationName, conditions) {
80
+ const adapter = this.$adapter;
81
+ if (!adapter?.applyRelationConstraint) {
82
+ throw new Error(`Adapter does not support relation constraints. Use $query directly to filter relation "${relationName}".`);
83
+ }
84
+ await adapter.applyRelationConstraint(this.$query, relationName, async (relationQb) => {
85
+ // Apply each condition as an andWhere on the relation query builder.
86
+ // The exact shape depends on the adapter -- TypeORM and MikroORM both
87
+ // support object-based andWhere calls.
88
+ const qb = relationQb;
89
+ for (const [column, value] of Object.entries(conditions)) {
90
+ qb.andWhere({ [column]: value });
91
+ }
92
+ });
93
+ }
94
+ }
95
+ //# sourceMappingURL=base-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-filter.js","sourceRoot":"","sources":["../src/base-filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AAGzE,MAAM,OAAgB,UAAU;IAC9B,IAAI,MAAM;QACR,MAAM,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,+BAA+B,EAAE,CAAC;QACpD,OAAO,CAAC,CAAC,MAAgB,CAAC;IAC5B,CAAC;IAED,IAAI,MAAM;QACR,MAAM,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,+BAA+B,EAAE,CAAC;QACpD,OAAO,CAAC,CAAC,MAAM,CAAC;IAClB,CAAC;IAED,IAAI,QAAQ;QACV,MAAM,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,+BAA+B,EAAE,CAAC;QACpD,OAAO,CAAC,CAAC,QAAQ,CAAC;IACpB,CAAC;IAED,IAAI,QAAQ;QACV,MAAM,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,+BAA+B,EAAE,CAAC;QACpD,OAAO,CAAC,CAAC,QAAQ,CAAC;IACpB,CAAC;IAED;;;OAGG;IACO,eAAe,CAAC,GAAW;QACnC,MAAM,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,+BAA+B,EAAE,CAAC;QACpD,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACO,eAAe,CAAC,GAAW;QACnC,MAAM,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,+BAA+B,EAAE,CAAC;QACpD,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IASD,KAAK,CAAC,GAAY,EAAE,YAAsB;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC;QACxB,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC;QAClC,OAAO,GAAG,IAAK,GAAc,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IAC1D,CAAC;IAQS,IAAI,CAAC,UAA4C,EAAE,KAAe;QAC1E,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,+BAA+B,EAAE,CAAC;QACxD,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;YACnC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChD,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACO,KAAK,CAAC,OAAO,CACrB,YAAoB,EACpB,UAAmC;QAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,OAAO,EAAE,uBAAuB,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CACb,0FAA0F,YAAY,IAAI,CAC3G,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,CAAC,uBAAuB,CACnC,IAAI,CAAC,MAAM,EACX,YAAY,EACZ,KAAK,EAAE,UAAmB,EAAE,EAAE;YAC5B,qEAAqE;YACrE,sEAAsE;YACtE,uCAAuC;YACvC,MAAM,EAAE,GAAG,UAAwD,CAAC;YACpE,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBACzD,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACnC,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;CAGF"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ const args = process.argv.slice(2);
5
+ const command = args[0];
6
+ const name = args[1];
7
+ const ormArg = args.find((a) => a.startsWith('--orm='));
8
+ const orm = ormArg?.split('=')[1] ?? 'mikro-orm';
9
+ if (!command || !name || !['generate', 'g'].includes(command)) {
10
+ console.log('Usage: npx nestjs-filter generate <name> [--orm=mikro-orm|typeorm]');
11
+ process.exit(1);
12
+ }
13
+ const pascal = name.charAt(0).toUpperCase() + name.slice(1);
14
+ const filterImport = orm === 'typeorm'
15
+ ? "import { TypeOrmFilter } from '@dudousxd/nestjs-filter-typeorm';"
16
+ : "import { MikroOrmFilter } from '@dudousxd/nestjs-filter-mikro-orm';";
17
+ const baseClass = orm === 'typeorm' ? `TypeOrmFilter<${pascal}>` : `MikroOrmFilter<${pascal}>`;
18
+ const template = `import { Injectable } from '@nestjs/common';
19
+ import { Filterable, FilterFor } from '@dudousxd/nestjs-filter';
20
+ ${filterImport}
21
+ import { ${pascal} } from './${name}.entity.js';
22
+
23
+ @Injectable()
24
+ @Filterable({ entity: ${pascal} })
25
+ export class ${pascal}Filter extends ${baseClass} {
26
+ // @FilterFor('fieldName')
27
+ // applyField(value: string) {
28
+ // this.$query.andWhere({ field: value });
29
+ // }
30
+ }
31
+ `;
32
+ const outDir = join(process.cwd(), 'src');
33
+ const outPath = join(outDir, `${name}.filter.ts`);
34
+ if (existsSync(outPath)) {
35
+ console.error(`File already exists: ${outPath}`);
36
+ process.exit(1);
37
+ }
38
+ if (!existsSync(outDir)) {
39
+ mkdirSync(outDir, { recursive: true });
40
+ }
41
+ writeFileSync(outPath, template);
42
+ console.log(`Created ${outPath}`);
43
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AACxB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AACrB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AACxD,MAAM,GAAG,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC;AAEjD,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;IAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC5D,MAAM,YAAY,GAChB,GAAG,KAAK,SAAS;IACf,CAAC,CAAC,kEAAkE;IACpE,CAAC,CAAC,qEAAqE,CAAC;AAC5E,MAAM,SAAS,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,MAAM,GAAG,CAAC,CAAC,CAAC,kBAAkB,MAAM,GAAG,CAAC;AAE/F,MAAM,QAAQ,GAAG;;EAEf,YAAY;WACH,MAAM,cAAc,IAAI;;;wBAGX,MAAM;eACf,MAAM,kBAAkB,SAAS;;;;;;CAM/C,CAAC;AAEF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;AAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,CAAC;AAElD,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,OAAO,CAAC,KAAK,CAAC,wBAAwB,OAAO,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;IACxB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AACjC,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import 'reflect-metadata';
2
+ import { type Type } from '@nestjs/common';
3
+ import type { ApplyFilterOptions } from '../types.js';
4
+ export interface ApplyFilterMetadataEntry {
5
+ filterClass: Type<unknown>;
6
+ options: ApplyFilterOptions;
7
+ paramIndex: number;
8
+ }
9
+ export declare function ApplyFilter(filterClass: Type<unknown>, options?: ApplyFilterOptions): ParameterDecorator;
10
+ export declare function getApplyFilterMetadata(controllerCtor: Function, methodName: string | symbol): ApplyFilterMetadataEntry[];
11
+ //# sourceMappingURL=apply-filter.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-filter.decorator.d.ts","sourceRoot":"","sources":["../../src/decorator/apply-filter.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAyB,KAAK,IAAI,EAAwB,MAAM,gBAAgB,CAAC;AAExF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,WAAW,CACzB,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,EAC1B,OAAO,GAAE,kBAAuB,GAC/B,kBAAkB,CAsBpB;AAED,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,QAAQ,EACxB,UAAU,EAAE,MAAM,GAAG,MAAM,GAC1B,wBAAwB,EAAE,CAG5B"}
@@ -0,0 +1,22 @@
1
+ import 'reflect-metadata';
2
+ import { createParamDecorator } from '@nestjs/common';
3
+ import { APPLY_FILTER_METADATA, APPLY_FILTER_REQ_KEY } from '../tokens.js';
4
+ export function ApplyFilter(filterClass, options = {}) {
5
+ return (target, propertyKey, parameterIndex) => {
6
+ const existing = (Reflect.getOwnMetadata(APPLY_FILTER_METADATA, target.constructor, propertyKey) ?? []);
7
+ existing.push({ filterClass, options, paramIndex: parameterIndex });
8
+ Reflect.defineMetadata(APPLY_FILTER_METADATA, existing, target.constructor, propertyKey);
9
+ return createParamDecorator((_data, ctx) => {
10
+ const req = ctx.switchToHttp().getRequest();
11
+ const slot = req[APPLY_FILTER_REQ_KEY];
12
+ if (!slot)
13
+ return undefined;
14
+ return slot[parameterIndex];
15
+ })()(target, propertyKey, parameterIndex);
16
+ };
17
+ }
18
+ export function getApplyFilterMetadata(controllerCtor, methodName) {
19
+ return (Reflect.getOwnMetadata(APPLY_FILTER_METADATA, controllerCtor, methodName) ??
20
+ []);
21
+ }
22
+ //# sourceMappingURL=apply-filter.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-filter.decorator.js","sourceRoot":"","sources":["../../src/decorator/apply-filter.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAoC,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAS3E,MAAM,UAAU,WAAW,CACzB,WAA0B,EAC1B,UAA8B,EAAE;IAEhC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE;QAC7C,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,cAAc,CACtC,qBAAqB,EACrB,MAAM,CAAC,WAAW,EAClB,WAAqB,CACtB,IAAI,EAAE,CAA+B,CAAC;QACvC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,cAAc,CACpB,qBAAqB,EACrB,QAAQ,EACR,MAAM,CAAC,WAAW,EAClB,WAAqB,CACtB,CAAC;QAEF,OAAO,oBAAoB,CAAC,CAAC,KAAc,EAAE,GAAqB,EAAE,EAAE;YACpE,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,UAAU,EAA6B,CAAC;YACvE,MAAM,IAAI,GAAG,GAAG,CAAC,oBAAoB,CAA0B,CAAC;YAChE,IAAI,CAAC,IAAI;gBAAE,OAAO,SAAS,CAAC;YAC5B,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9B,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;IAC5C,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,cAAwB,EACxB,UAA2B;IAE3B,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,qBAAqB,EAAE,cAAc,EAAE,UAAoB,CAAC;QACzF,EAAE,CAA+B,CAAC;AACtC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import 'reflect-metadata';
2
+ export declare function FilterFor(inputKey?: string): MethodDecorator;
3
+ export declare function getFilterForMap(target: object): Map<string, string>;
4
+ //# sourceMappingURL=filter-for.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-for.decorator.d.ts","sourceRoot":"","sources":["../../src/decorator/filter-for.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAG1B,wBAAgB,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe,CAU5D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAenE"}