@querykitjs/class-validator 1.0.0

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.
@@ -0,0 +1,325 @@
1
+ import { FilterOperator, Filter, Sort, SortDirection } from '@querykitjs/core';
2
+ import { ValidationOptions, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
3
+
4
+ /**
5
+ * Xabar kataloglari — **yagona manba**. Har bir til bir xil kalitlar to'plamini
6
+ * to'ldiradi; kalitlarning o'zi shu yerdagi inglizcha katalogdan chiqadi.
7
+ *
8
+ * Paket xatoni kalit bilan qaytargani uchun tillar soni cheklanmagan: tarjima
9
+ * har doim iste'molchi tomonida (`nestjs-i18n` `I18nValidationPipe` yoki oddiy
10
+ * `formatMessage(key, args, catalog)`). Bu yerdagi uchta katalog shunchaki eng
11
+ * ko'p kerak bo'ladiganini qutidan beradi — hech biri majburiy emas.
12
+ *
13
+ * `QueryKitMessageCatalog` ataylab qat'iy (`Record<QueryKitMessageKey, string>`):
14
+ * inglizchaga yangi kalit qo'shilsa, qolgan kataloglar **compile-time**da yorilib
15
+ * beradi va eskirib qololmaydi.
16
+ */
17
+ /**
18
+ * Inglizcha katalog — kalitlarning kanonik ro'yxati va default fallback.
19
+ * `{placeholder}` lar `formatMessage` bilan to'ldiriladi.
20
+ */
21
+ declare const QUERYKIT_MESSAGES_EN: {
22
+ readonly "querykit.filter.invalid_node": "{path} must be a filter condition or an and/or/not group";
23
+ readonly "querykit.filter.missing_key": "{path} is missing the required `key` string";
24
+ readonly "querykit.filter.unknown_key": "{path}: `{key}` is not an allowed filter key";
25
+ readonly "querykit.filter.invalid_operator": "{path}: `{operation}` is not a supported filter operator";
26
+ readonly "querykit.filter.invalid_value": "{path} must be a string, number, boolean, null, or an array of those";
27
+ readonly "querykit.filter.invalid_group": "{path} must be an array of filter nodes";
28
+ readonly "querykit.filter.max_depth": "{path} exceeds the maximum filter depth of {max}";
29
+ readonly "querykit.filter.value_requires_array": "{path}: `{operation}` requires an array value";
30
+ readonly "querykit.filter.value_requires_tuple": "{path}: `{operation}` requires exactly two values";
31
+ readonly "querykit.filter.value_forbidden": "{path}: `{operation}` must not carry a value";
32
+ readonly "querykit.sort.not_array": "{path} must be an array of { key, direction } items";
33
+ readonly "querykit.sort.invalid_item": "{path} must be an object with a `key` string";
34
+ readonly "querykit.sort.missing_key": "{path} is missing the required `key` string";
35
+ readonly "querykit.sort.unknown_key": "{path}: `{key}` is not an allowed sort key";
36
+ readonly "querykit.sort.invalid_direction": "{path}: direction must be `asc` or `desc`";
37
+ };
38
+ /**
39
+ * Har bir validatsiya xatosining i18n kaliti. Union inglizcha katalogdan chiqadi —
40
+ * yangi xabar qo'shilsa tip o'zi kengayadi, qo'lda ro'yxat yuritilmaydi.
41
+ */
42
+ type QueryKitMessageKey = keyof typeof QUERYKIT_MESSAGES_EN;
43
+ /** Bitta tilning to'liq katalogi — barcha kalitlar majburiy. */
44
+ type QueryKitMessageCatalog = Record<QueryKitMessageKey, string>;
45
+ /** O'zbekcha katalog. Placeholder nomlari inglizchasi bilan bir xil. */
46
+ declare const QUERYKIT_MESSAGES_UZ: QueryKitMessageCatalog;
47
+ /** Ruscha katalog. */
48
+ declare const QUERYKIT_MESSAGES_RU: QueryKitMessageCatalog;
49
+ /**
50
+ * Til kodi → katalog. Qutidagi tillar; iste'molchi o'z katalogini shu shaklda
51
+ * qo'shib, `formatMessage`ga uzata oladi.
52
+ */
53
+ declare const QUERYKIT_LOCALES: {
54
+ readonly en: {
55
+ readonly "querykit.filter.invalid_node": "{path} must be a filter condition or an and/or/not group";
56
+ readonly "querykit.filter.missing_key": "{path} is missing the required `key` string";
57
+ readonly "querykit.filter.unknown_key": "{path}: `{key}` is not an allowed filter key";
58
+ readonly "querykit.filter.invalid_operator": "{path}: `{operation}` is not a supported filter operator";
59
+ readonly "querykit.filter.invalid_value": "{path} must be a string, number, boolean, null, or an array of those";
60
+ readonly "querykit.filter.invalid_group": "{path} must be an array of filter nodes";
61
+ readonly "querykit.filter.max_depth": "{path} exceeds the maximum filter depth of {max}";
62
+ readonly "querykit.filter.value_requires_array": "{path}: `{operation}` requires an array value";
63
+ readonly "querykit.filter.value_requires_tuple": "{path}: `{operation}` requires exactly two values";
64
+ readonly "querykit.filter.value_forbidden": "{path}: `{operation}` must not carry a value";
65
+ readonly "querykit.sort.not_array": "{path} must be an array of { key, direction } items";
66
+ readonly "querykit.sort.invalid_item": "{path} must be an object with a `key` string";
67
+ readonly "querykit.sort.missing_key": "{path} is missing the required `key` string";
68
+ readonly "querykit.sort.unknown_key": "{path}: `{key}` is not an allowed sort key";
69
+ readonly "querykit.sort.invalid_direction": "{path}: direction must be `asc` or `desc`";
70
+ };
71
+ readonly uz: QueryKitMessageCatalog;
72
+ readonly ru: QueryKitMessageCatalog;
73
+ };
74
+ /** Qutidan chiqadigan til kodlari. */
75
+ type QueryKitLocale = keyof typeof QUERYKIT_LOCALES;
76
+
77
+ /**
78
+ * Xabar formatlash. Kataloglarning o'zi `locales.ts`da — bu fayl faqat
79
+ * `{placeholder}` interpolatsiyasini biladi va hech qanday tilga bog'lanmagan.
80
+ *
81
+ * Har bir {@link ValidationIssue} uchta narsani olib yuradi: `key` (tarjima
82
+ * kaliti), `message` (tayyor matn) va `args` (interpolatsiya qiymatlari). Shu
83
+ * sababli paket i18n'ga **majburlamaydi**, lekin unga to'liq mos.
84
+ */
85
+
86
+ /** Shablon interpolatsiyasi argumentlari — `I18nValidationPipe`ga ham shu shaklda uzatiladi. */
87
+ type MessageArgs = Record<string, string | number>;
88
+ /**
89
+ * Kalitni matnga aylantiradi: katalogdan shablon olinadi va `{placeholder}` lar
90
+ * `args` bilan almashtiriladi. Mos argument topilmasa placeholder o'z holicha
91
+ * qoladi — xabar hech qachon bo'sh chiqmaydi.
92
+ *
93
+ * @param catalog Til katalogi. Default — inglizcha; `QUERYKIT_MESSAGES_UZ`,
94
+ * `QUERYKIT_MESSAGES_RU` yoki o'zingizniki uzatilishi mumkin.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * formatMessage(issue.key, issue.args, QUERYKIT_MESSAGES_UZ);
99
+ * ```
100
+ */
101
+ declare function formatMessage(key: QueryKitMessageKey, args?: MessageArgs, catalog?: QueryKitMessageCatalog): string;
102
+ /**
103
+ * Issue'ni tanlangan tilda qayta formatlaydi — `message` inglizcha yozilgan
104
+ * bo'lsa ham, javobni boshqa tilda berish uchun.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * issues.map(issue => localizeIssue(issue, QUERYKIT_MESSAGES_RU));
109
+ * ```
110
+ */
111
+ declare function localizeIssue(issue: {
112
+ key: QueryKitMessageKey;
113
+ args: MessageArgs;
114
+ }, catalog: QueryKitMessageCatalog): string;
115
+
116
+ /** Filter daraxti chuqurligi default limiti — cheklovsiz rekursiya DoS yuzasi. */
117
+ declare const DEFAULT_MAX_FILTER_DEPTH = 5;
118
+ interface QueryFilterOptions {
119
+ /**
120
+ * Berilsa — faqat shu kalitlarga ruxsat; noma'lum kalit **rad etiladi**.
121
+ * Backend adapterlari noma'lum kalitni jimgina tashlaydi, bu qatlam esa
122
+ * qat'iyroq: mijoz mavjud bo'lmagan maydon bo'yicha filtr yubora olmaydi.
123
+ * Berilmasa — istalgan string kalit o'tadi (`@querykitjs/zod` bilan parity).
124
+ */
125
+ allowedKeys?: readonly string[];
126
+ /** Ruxsat etilgan operatorlar. Default — core `FILTER_OPERATORS`ning hammasi. */
127
+ allowedOperators?: readonly FilterOperator[];
128
+ /** `and`/`or`/`not` tugunlarining chuqurlik limiti. Default — {@link DEFAULT_MAX_FILTER_DEPTH}. */
129
+ maxDepth?: number;
130
+ /**
131
+ * Operator↔qiymat shakli tekshiruvi (opt-in): `in`/`notIn` massiv talab qiladi,
132
+ * `between`/`notBetween` — aynan ikki element, `isNull`/`isNotNull` — qiymatsiz.
133
+ * Default `false`, ya'ni `@querykitjs/zod` bilan bir xil qat'iylik.
134
+ */
135
+ strictValue?: boolean;
136
+ /**
137
+ * `path`ning ildizi. Default `"filter"`. Dekorator buni DTO property nomiga
138
+ * qo'yadi, ya'ni `where` deb nomlangan maydon xatosi `filter.operation` emas,
139
+ * `where.operation` deb chiqadi.
140
+ */
141
+ rootPath?: string;
142
+ }
143
+ interface QuerySortOptions {
144
+ /** Berilsa — faqat shu kalitlar bo'yicha saralashga ruxsat. */
145
+ allowedKeys?: readonly string[];
146
+ /** `path`ning ildizi. Default `"sort"`. Qarang: {@link QueryFilterOptions.rootPath}. */
147
+ rootPath?: string;
148
+ }
149
+ /** Bitta validatsiya xatosi. */
150
+ interface ValidationIssue {
151
+ /** Xato joyi, masalan `filter.and[1].or[0].value`. */
152
+ path: string;
153
+ /** i18n kaliti — iste'molchi tarjima faylida shu kalit qidiriladi. */
154
+ key: QueryKitMessageKey;
155
+ /** Tayyor inglizcha matn — tarjimasiz ham o'qiladi. */
156
+ message: string;
157
+ /** Interpolatsiya argumentlari (`path` har doim bor) — `I18nValidationPipe` uchun. */
158
+ args: MessageArgs;
159
+ }
160
+ /**
161
+ * Filter daraxtini core kontraktiga tekshiradi. Bo'sh massiv = valid.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * const issues = validateFilter(body.filter, { allowedKeys: ["id", "name"] });
166
+ * if (issues.length) throw new BadRequestException(issues);
167
+ * ```
168
+ */
169
+ declare function validateFilter(value: unknown, options?: QueryFilterOptions): ValidationIssue[];
170
+ /**
171
+ * Sort massivini core kontraktiga tekshiradi (`{ key, direction }[]`). Bo'sh massiv = valid.
172
+ */
173
+ declare function validateSort(value: unknown, options?: QuerySortOptions): ValidationIssue[];
174
+
175
+ /** Dekorator darajasidagi qo'shimcha — yadro buni o'qimaydi. */
176
+ interface MessageStyleOptions {
177
+ /**
178
+ * Xato matni qanday shaklda qaytsin:
179
+ * - `"text"` (default) — tayyor inglizcha jumla, i18n'siz app ham o'qiydi;
180
+ * - `"key"` — `querykit.filter.*` kaliti, `nestjs-i18n`ning `I18nValidationPipe`i
181
+ * uni so'rovning tiliga tarjima qiladi.
182
+ */
183
+ messageStyle?: "text" | "key";
184
+ }
185
+ type IsQueryFilterOptions = QueryFilterOptions & MessageStyleOptions;
186
+ type IsQuerySortOptions = QuerySortOptions & MessageStyleOptions;
187
+ /** `@IsQueryFilter()` ortidagi konstraint — test va qayta ishlatish uchun ochiq. */
188
+ declare class IsQueryFilterConstraint implements ValidatorConstraintInterface {
189
+ validate(value: unknown, args?: ValidationArguments): boolean;
190
+ defaultMessage(args?: ValidationArguments): string;
191
+ }
192
+ /** `@IsQuerySort()` ortidagi konstraint. */
193
+ declare class IsQuerySortConstraint implements ValidatorConstraintInterface {
194
+ validate(value: unknown, args?: ValidationArguments): boolean;
195
+ defaultMessage(args?: ValidationArguments): string;
196
+ }
197
+ /**
198
+ * Property'ni querykit `Filter` kontraktiga tekshiradi: flat massiv (implicit AND),
199
+ * nested `and`/`or`/`not` daraxti va core `FILTER_OPERATORS`ning to'liq to'plami.
200
+ *
201
+ * `undefined` qiymatni **rad etmaydi** — maydonni ixtiyoriy qilish uchun
202
+ * `@IsOptional()` bilan birga ishlating (DTO'lardagi kabi).
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * class ListUsersDto extends OffsetParamsDto {
207
+ * @IsOptional()
208
+ * @IsQueryFilter({ allowedKeys: ["id", "name", "createdAt"] })
209
+ * declare filter?: Filter;
210
+ * }
211
+ * ```
212
+ */
213
+ declare function IsQueryFilter(options?: IsQueryFilterOptions, validationOptions?: ValidationOptions): PropertyDecorator;
214
+ /**
215
+ * Property'ni querykit `Sort` kontraktiga tekshiradi — `{ key, direction }[]`.
216
+ * Qarang: {@link IsQueryFilter}.
217
+ */
218
+ declare function IsQuerySort(options?: IsQuerySortOptions, validationOptions?: ValidationOptions): PropertyDecorator;
219
+
220
+ /** Paginatsiyasiz umumiy params — `filter` + `sort`. */
221
+ declare class BaseParamsDto {
222
+ filter?: Filter;
223
+ sort?: Sort;
224
+ }
225
+ /** Offset (sahifali) params — `page` + `perPage` (`perPage` ≤ core cap). */
226
+ declare class OffsetParamsDto extends BaseParamsDto {
227
+ page?: number;
228
+ perPage?: number;
229
+ }
230
+ /** Infinite-scroll params — `limit` + `offset`. */
231
+ declare class InfiniteParamsDto extends BaseParamsDto {
232
+ limit?: number;
233
+ offset?: number;
234
+ }
235
+ /**
236
+ * Cursor (keyset) params. `sort` **yo'q**: tartib `order` + `direction` bilan
237
+ * boshqariladi. Shu sababli `BaseParamsDto`dan meros olinmaydi — aks holda
238
+ * `sort` kontraktga qarshi ravishda meros bo'lib qolardi.
239
+ */
240
+ declare class CursorParamsDto {
241
+ filter?: Filter;
242
+ limit?: number;
243
+ cursor?: string | null;
244
+ cursorKey?: string;
245
+ order?: SortDirection;
246
+ direction?: "forward" | "backward";
247
+ }
248
+ /** Server'ga tegishli, default'da clientdan qabul qilinmaydigan maydonlar. */
249
+ type ServerOwnedField = "columns" | "with" | "withDeleted";
250
+ /** `allow` bilan ochiladigan maydonlarning shakli. */
251
+ interface ServerOwnedParams {
252
+ columns?: Record<string, boolean>;
253
+ with?: Record<string, unknown>;
254
+ withDeleted?: boolean;
255
+ }
256
+ interface ParamsDtoOptions {
257
+ /** `filter` uchun to'liq opsiyalar — `allowedKeys`, `allowedOperators`, `maxDepth`, `strictValue`, `messageStyle`. */
258
+ filter?: IsQueryFilterOptions;
259
+ /** `sort` uchun opsiyalar — `allowedKeys`, `messageStyle`. */
260
+ sort?: IsQuerySortOptions;
261
+ /**
262
+ * `filter` va `sort` uchun umumiy allowlist qisqartmasi — odatda ikkalasi bir xil.
263
+ * Aniq berilgan `filter.allowedKeys` / `sort.allowedKeys` bundan **ustun**.
264
+ */
265
+ allowedKeys?: readonly string[];
266
+ /** `perPage` yuqori chegarasi. Default — core `DEFAULT_MAX_PER_PAGE` (200). `Infinity` — cap yo'q (⚠️ DoS). */
267
+ maxPerPage?: number;
268
+ /** `limit` yuqori chegarasi. Default — core `DEFAULT_MAX_LIMIT` (200). */
269
+ maxLimit?: number;
270
+ /** Server-owned maydonlarni **ataylab** ochish. Default — bo'sh. */
271
+ allow?: readonly ServerOwnedField[];
272
+ }
273
+ /**
274
+ * Offset (sahifali) params DTO'si — `allowedKeys` va cap'lar bilan.
275
+ *
276
+ * @example
277
+ * ```ts
278
+ * const ListBuyersDto = makeOffsetParamsDto({ allowedKeys: ["buyerName", "createdAt"] });
279
+ * type ListBuyersDto = InstanceType<typeof ListBuyersDto>;
280
+ *
281
+ * @Post("list")
282
+ * list(@Body() params: ListBuyersDto) {
283
+ * return this.buyersRepository.findList(params);
284
+ * }
285
+ * ```
286
+ */
287
+ declare function makeOffsetParamsDto(options?: ParamsDtoOptions): {
288
+ new (): {
289
+ filter?: Filter;
290
+ sort?: Sort;
291
+ page?: number;
292
+ perPage?: number;
293
+ columns?: Record<string, boolean>;
294
+ with?: Record<string, unknown>;
295
+ withDeleted?: boolean;
296
+ };
297
+ };
298
+ /** Infinite-scroll params DTO'si. Qarang: {@link makeOffsetParamsDto}. */
299
+ declare function makeInfiniteParamsDto(options?: ParamsDtoOptions): {
300
+ new (): {
301
+ filter?: Filter;
302
+ sort?: Sort;
303
+ limit?: number;
304
+ offset?: number;
305
+ columns?: Record<string, boolean>;
306
+ with?: Record<string, unknown>;
307
+ withDeleted?: boolean;
308
+ };
309
+ };
310
+ /** Cursor (keyset) params DTO'si — `sort` yo'q. Qarang: {@link makeOffsetParamsDto}. */
311
+ declare function makeCursorParamsDto(options?: ParamsDtoOptions): {
312
+ new (): {
313
+ filter?: Filter;
314
+ limit?: number;
315
+ cursor?: string | null;
316
+ cursorKey?: string;
317
+ order?: SortDirection;
318
+ direction?: "forward" | "backward";
319
+ columns?: Record<string, boolean>;
320
+ with?: Record<string, unknown>;
321
+ withDeleted?: boolean;
322
+ };
323
+ };
324
+
325
+ export { BaseParamsDto, CursorParamsDto, DEFAULT_MAX_FILTER_DEPTH, InfiniteParamsDto, IsQueryFilter, IsQueryFilterConstraint, type IsQueryFilterOptions, IsQuerySort, IsQuerySortConstraint, type IsQuerySortOptions, type MessageArgs, type MessageStyleOptions, OffsetParamsDto, type ParamsDtoOptions, QUERYKIT_LOCALES, QUERYKIT_MESSAGES_EN, QUERYKIT_MESSAGES_RU, QUERYKIT_MESSAGES_UZ, type QueryFilterOptions, type QueryKitLocale, type QueryKitMessageCatalog, type QueryKitMessageKey, type QuerySortOptions, type ServerOwnedField, type ServerOwnedParams, type ValidationIssue, formatMessage, localizeIssue, makeCursorParamsDto, makeInfiniteParamsDto, makeOffsetParamsDto, validateFilter, validateSort };
@@ -0,0 +1,325 @@
1
+ import { FilterOperator, Filter, Sort, SortDirection } from '@querykitjs/core';
2
+ import { ValidationOptions, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
3
+
4
+ /**
5
+ * Xabar kataloglari — **yagona manba**. Har bir til bir xil kalitlar to'plamini
6
+ * to'ldiradi; kalitlarning o'zi shu yerdagi inglizcha katalogdan chiqadi.
7
+ *
8
+ * Paket xatoni kalit bilan qaytargani uchun tillar soni cheklanmagan: tarjima
9
+ * har doim iste'molchi tomonida (`nestjs-i18n` `I18nValidationPipe` yoki oddiy
10
+ * `formatMessage(key, args, catalog)`). Bu yerdagi uchta katalog shunchaki eng
11
+ * ko'p kerak bo'ladiganini qutidan beradi — hech biri majburiy emas.
12
+ *
13
+ * `QueryKitMessageCatalog` ataylab qat'iy (`Record<QueryKitMessageKey, string>`):
14
+ * inglizchaga yangi kalit qo'shilsa, qolgan kataloglar **compile-time**da yorilib
15
+ * beradi va eskirib qololmaydi.
16
+ */
17
+ /**
18
+ * Inglizcha katalog — kalitlarning kanonik ro'yxati va default fallback.
19
+ * `{placeholder}` lar `formatMessage` bilan to'ldiriladi.
20
+ */
21
+ declare const QUERYKIT_MESSAGES_EN: {
22
+ readonly "querykit.filter.invalid_node": "{path} must be a filter condition or an and/or/not group";
23
+ readonly "querykit.filter.missing_key": "{path} is missing the required `key` string";
24
+ readonly "querykit.filter.unknown_key": "{path}: `{key}` is not an allowed filter key";
25
+ readonly "querykit.filter.invalid_operator": "{path}: `{operation}` is not a supported filter operator";
26
+ readonly "querykit.filter.invalid_value": "{path} must be a string, number, boolean, null, or an array of those";
27
+ readonly "querykit.filter.invalid_group": "{path} must be an array of filter nodes";
28
+ readonly "querykit.filter.max_depth": "{path} exceeds the maximum filter depth of {max}";
29
+ readonly "querykit.filter.value_requires_array": "{path}: `{operation}` requires an array value";
30
+ readonly "querykit.filter.value_requires_tuple": "{path}: `{operation}` requires exactly two values";
31
+ readonly "querykit.filter.value_forbidden": "{path}: `{operation}` must not carry a value";
32
+ readonly "querykit.sort.not_array": "{path} must be an array of { key, direction } items";
33
+ readonly "querykit.sort.invalid_item": "{path} must be an object with a `key` string";
34
+ readonly "querykit.sort.missing_key": "{path} is missing the required `key` string";
35
+ readonly "querykit.sort.unknown_key": "{path}: `{key}` is not an allowed sort key";
36
+ readonly "querykit.sort.invalid_direction": "{path}: direction must be `asc` or `desc`";
37
+ };
38
+ /**
39
+ * Har bir validatsiya xatosining i18n kaliti. Union inglizcha katalogdan chiqadi —
40
+ * yangi xabar qo'shilsa tip o'zi kengayadi, qo'lda ro'yxat yuritilmaydi.
41
+ */
42
+ type QueryKitMessageKey = keyof typeof QUERYKIT_MESSAGES_EN;
43
+ /** Bitta tilning to'liq katalogi — barcha kalitlar majburiy. */
44
+ type QueryKitMessageCatalog = Record<QueryKitMessageKey, string>;
45
+ /** O'zbekcha katalog. Placeholder nomlari inglizchasi bilan bir xil. */
46
+ declare const QUERYKIT_MESSAGES_UZ: QueryKitMessageCatalog;
47
+ /** Ruscha katalog. */
48
+ declare const QUERYKIT_MESSAGES_RU: QueryKitMessageCatalog;
49
+ /**
50
+ * Til kodi → katalog. Qutidagi tillar; iste'molchi o'z katalogini shu shaklda
51
+ * qo'shib, `formatMessage`ga uzata oladi.
52
+ */
53
+ declare const QUERYKIT_LOCALES: {
54
+ readonly en: {
55
+ readonly "querykit.filter.invalid_node": "{path} must be a filter condition or an and/or/not group";
56
+ readonly "querykit.filter.missing_key": "{path} is missing the required `key` string";
57
+ readonly "querykit.filter.unknown_key": "{path}: `{key}` is not an allowed filter key";
58
+ readonly "querykit.filter.invalid_operator": "{path}: `{operation}` is not a supported filter operator";
59
+ readonly "querykit.filter.invalid_value": "{path} must be a string, number, boolean, null, or an array of those";
60
+ readonly "querykit.filter.invalid_group": "{path} must be an array of filter nodes";
61
+ readonly "querykit.filter.max_depth": "{path} exceeds the maximum filter depth of {max}";
62
+ readonly "querykit.filter.value_requires_array": "{path}: `{operation}` requires an array value";
63
+ readonly "querykit.filter.value_requires_tuple": "{path}: `{operation}` requires exactly two values";
64
+ readonly "querykit.filter.value_forbidden": "{path}: `{operation}` must not carry a value";
65
+ readonly "querykit.sort.not_array": "{path} must be an array of { key, direction } items";
66
+ readonly "querykit.sort.invalid_item": "{path} must be an object with a `key` string";
67
+ readonly "querykit.sort.missing_key": "{path} is missing the required `key` string";
68
+ readonly "querykit.sort.unknown_key": "{path}: `{key}` is not an allowed sort key";
69
+ readonly "querykit.sort.invalid_direction": "{path}: direction must be `asc` or `desc`";
70
+ };
71
+ readonly uz: QueryKitMessageCatalog;
72
+ readonly ru: QueryKitMessageCatalog;
73
+ };
74
+ /** Qutidan chiqadigan til kodlari. */
75
+ type QueryKitLocale = keyof typeof QUERYKIT_LOCALES;
76
+
77
+ /**
78
+ * Xabar formatlash. Kataloglarning o'zi `locales.ts`da — bu fayl faqat
79
+ * `{placeholder}` interpolatsiyasini biladi va hech qanday tilga bog'lanmagan.
80
+ *
81
+ * Har bir {@link ValidationIssue} uchta narsani olib yuradi: `key` (tarjima
82
+ * kaliti), `message` (tayyor matn) va `args` (interpolatsiya qiymatlari). Shu
83
+ * sababli paket i18n'ga **majburlamaydi**, lekin unga to'liq mos.
84
+ */
85
+
86
+ /** Shablon interpolatsiyasi argumentlari — `I18nValidationPipe`ga ham shu shaklda uzatiladi. */
87
+ type MessageArgs = Record<string, string | number>;
88
+ /**
89
+ * Kalitni matnga aylantiradi: katalogdan shablon olinadi va `{placeholder}` lar
90
+ * `args` bilan almashtiriladi. Mos argument topilmasa placeholder o'z holicha
91
+ * qoladi — xabar hech qachon bo'sh chiqmaydi.
92
+ *
93
+ * @param catalog Til katalogi. Default — inglizcha; `QUERYKIT_MESSAGES_UZ`,
94
+ * `QUERYKIT_MESSAGES_RU` yoki o'zingizniki uzatilishi mumkin.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * formatMessage(issue.key, issue.args, QUERYKIT_MESSAGES_UZ);
99
+ * ```
100
+ */
101
+ declare function formatMessage(key: QueryKitMessageKey, args?: MessageArgs, catalog?: QueryKitMessageCatalog): string;
102
+ /**
103
+ * Issue'ni tanlangan tilda qayta formatlaydi — `message` inglizcha yozilgan
104
+ * bo'lsa ham, javobni boshqa tilda berish uchun.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * issues.map(issue => localizeIssue(issue, QUERYKIT_MESSAGES_RU));
109
+ * ```
110
+ */
111
+ declare function localizeIssue(issue: {
112
+ key: QueryKitMessageKey;
113
+ args: MessageArgs;
114
+ }, catalog: QueryKitMessageCatalog): string;
115
+
116
+ /** Filter daraxti chuqurligi default limiti — cheklovsiz rekursiya DoS yuzasi. */
117
+ declare const DEFAULT_MAX_FILTER_DEPTH = 5;
118
+ interface QueryFilterOptions {
119
+ /**
120
+ * Berilsa — faqat shu kalitlarga ruxsat; noma'lum kalit **rad etiladi**.
121
+ * Backend adapterlari noma'lum kalitni jimgina tashlaydi, bu qatlam esa
122
+ * qat'iyroq: mijoz mavjud bo'lmagan maydon bo'yicha filtr yubora olmaydi.
123
+ * Berilmasa — istalgan string kalit o'tadi (`@querykitjs/zod` bilan parity).
124
+ */
125
+ allowedKeys?: readonly string[];
126
+ /** Ruxsat etilgan operatorlar. Default — core `FILTER_OPERATORS`ning hammasi. */
127
+ allowedOperators?: readonly FilterOperator[];
128
+ /** `and`/`or`/`not` tugunlarining chuqurlik limiti. Default — {@link DEFAULT_MAX_FILTER_DEPTH}. */
129
+ maxDepth?: number;
130
+ /**
131
+ * Operator↔qiymat shakli tekshiruvi (opt-in): `in`/`notIn` massiv talab qiladi,
132
+ * `between`/`notBetween` — aynan ikki element, `isNull`/`isNotNull` — qiymatsiz.
133
+ * Default `false`, ya'ni `@querykitjs/zod` bilan bir xil qat'iylik.
134
+ */
135
+ strictValue?: boolean;
136
+ /**
137
+ * `path`ning ildizi. Default `"filter"`. Dekorator buni DTO property nomiga
138
+ * qo'yadi, ya'ni `where` deb nomlangan maydon xatosi `filter.operation` emas,
139
+ * `where.operation` deb chiqadi.
140
+ */
141
+ rootPath?: string;
142
+ }
143
+ interface QuerySortOptions {
144
+ /** Berilsa — faqat shu kalitlar bo'yicha saralashga ruxsat. */
145
+ allowedKeys?: readonly string[];
146
+ /** `path`ning ildizi. Default `"sort"`. Qarang: {@link QueryFilterOptions.rootPath}. */
147
+ rootPath?: string;
148
+ }
149
+ /** Bitta validatsiya xatosi. */
150
+ interface ValidationIssue {
151
+ /** Xato joyi, masalan `filter.and[1].or[0].value`. */
152
+ path: string;
153
+ /** i18n kaliti — iste'molchi tarjima faylida shu kalit qidiriladi. */
154
+ key: QueryKitMessageKey;
155
+ /** Tayyor inglizcha matn — tarjimasiz ham o'qiladi. */
156
+ message: string;
157
+ /** Interpolatsiya argumentlari (`path` har doim bor) — `I18nValidationPipe` uchun. */
158
+ args: MessageArgs;
159
+ }
160
+ /**
161
+ * Filter daraxtini core kontraktiga tekshiradi. Bo'sh massiv = valid.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * const issues = validateFilter(body.filter, { allowedKeys: ["id", "name"] });
166
+ * if (issues.length) throw new BadRequestException(issues);
167
+ * ```
168
+ */
169
+ declare function validateFilter(value: unknown, options?: QueryFilterOptions): ValidationIssue[];
170
+ /**
171
+ * Sort massivini core kontraktiga tekshiradi (`{ key, direction }[]`). Bo'sh massiv = valid.
172
+ */
173
+ declare function validateSort(value: unknown, options?: QuerySortOptions): ValidationIssue[];
174
+
175
+ /** Dekorator darajasidagi qo'shimcha — yadro buni o'qimaydi. */
176
+ interface MessageStyleOptions {
177
+ /**
178
+ * Xato matni qanday shaklda qaytsin:
179
+ * - `"text"` (default) — tayyor inglizcha jumla, i18n'siz app ham o'qiydi;
180
+ * - `"key"` — `querykit.filter.*` kaliti, `nestjs-i18n`ning `I18nValidationPipe`i
181
+ * uni so'rovning tiliga tarjima qiladi.
182
+ */
183
+ messageStyle?: "text" | "key";
184
+ }
185
+ type IsQueryFilterOptions = QueryFilterOptions & MessageStyleOptions;
186
+ type IsQuerySortOptions = QuerySortOptions & MessageStyleOptions;
187
+ /** `@IsQueryFilter()` ortidagi konstraint — test va qayta ishlatish uchun ochiq. */
188
+ declare class IsQueryFilterConstraint implements ValidatorConstraintInterface {
189
+ validate(value: unknown, args?: ValidationArguments): boolean;
190
+ defaultMessage(args?: ValidationArguments): string;
191
+ }
192
+ /** `@IsQuerySort()` ortidagi konstraint. */
193
+ declare class IsQuerySortConstraint implements ValidatorConstraintInterface {
194
+ validate(value: unknown, args?: ValidationArguments): boolean;
195
+ defaultMessage(args?: ValidationArguments): string;
196
+ }
197
+ /**
198
+ * Property'ni querykit `Filter` kontraktiga tekshiradi: flat massiv (implicit AND),
199
+ * nested `and`/`or`/`not` daraxti va core `FILTER_OPERATORS`ning to'liq to'plami.
200
+ *
201
+ * `undefined` qiymatni **rad etmaydi** — maydonni ixtiyoriy qilish uchun
202
+ * `@IsOptional()` bilan birga ishlating (DTO'lardagi kabi).
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * class ListUsersDto extends OffsetParamsDto {
207
+ * @IsOptional()
208
+ * @IsQueryFilter({ allowedKeys: ["id", "name", "createdAt"] })
209
+ * declare filter?: Filter;
210
+ * }
211
+ * ```
212
+ */
213
+ declare function IsQueryFilter(options?: IsQueryFilterOptions, validationOptions?: ValidationOptions): PropertyDecorator;
214
+ /**
215
+ * Property'ni querykit `Sort` kontraktiga tekshiradi — `{ key, direction }[]`.
216
+ * Qarang: {@link IsQueryFilter}.
217
+ */
218
+ declare function IsQuerySort(options?: IsQuerySortOptions, validationOptions?: ValidationOptions): PropertyDecorator;
219
+
220
+ /** Paginatsiyasiz umumiy params — `filter` + `sort`. */
221
+ declare class BaseParamsDto {
222
+ filter?: Filter;
223
+ sort?: Sort;
224
+ }
225
+ /** Offset (sahifali) params — `page` + `perPage` (`perPage` ≤ core cap). */
226
+ declare class OffsetParamsDto extends BaseParamsDto {
227
+ page?: number;
228
+ perPage?: number;
229
+ }
230
+ /** Infinite-scroll params — `limit` + `offset`. */
231
+ declare class InfiniteParamsDto extends BaseParamsDto {
232
+ limit?: number;
233
+ offset?: number;
234
+ }
235
+ /**
236
+ * Cursor (keyset) params. `sort` **yo'q**: tartib `order` + `direction` bilan
237
+ * boshqariladi. Shu sababli `BaseParamsDto`dan meros olinmaydi — aks holda
238
+ * `sort` kontraktga qarshi ravishda meros bo'lib qolardi.
239
+ */
240
+ declare class CursorParamsDto {
241
+ filter?: Filter;
242
+ limit?: number;
243
+ cursor?: string | null;
244
+ cursorKey?: string;
245
+ order?: SortDirection;
246
+ direction?: "forward" | "backward";
247
+ }
248
+ /** Server'ga tegishli, default'da clientdan qabul qilinmaydigan maydonlar. */
249
+ type ServerOwnedField = "columns" | "with" | "withDeleted";
250
+ /** `allow` bilan ochiladigan maydonlarning shakli. */
251
+ interface ServerOwnedParams {
252
+ columns?: Record<string, boolean>;
253
+ with?: Record<string, unknown>;
254
+ withDeleted?: boolean;
255
+ }
256
+ interface ParamsDtoOptions {
257
+ /** `filter` uchun to'liq opsiyalar — `allowedKeys`, `allowedOperators`, `maxDepth`, `strictValue`, `messageStyle`. */
258
+ filter?: IsQueryFilterOptions;
259
+ /** `sort` uchun opsiyalar — `allowedKeys`, `messageStyle`. */
260
+ sort?: IsQuerySortOptions;
261
+ /**
262
+ * `filter` va `sort` uchun umumiy allowlist qisqartmasi — odatda ikkalasi bir xil.
263
+ * Aniq berilgan `filter.allowedKeys` / `sort.allowedKeys` bundan **ustun**.
264
+ */
265
+ allowedKeys?: readonly string[];
266
+ /** `perPage` yuqori chegarasi. Default — core `DEFAULT_MAX_PER_PAGE` (200). `Infinity` — cap yo'q (⚠️ DoS). */
267
+ maxPerPage?: number;
268
+ /** `limit` yuqori chegarasi. Default — core `DEFAULT_MAX_LIMIT` (200). */
269
+ maxLimit?: number;
270
+ /** Server-owned maydonlarni **ataylab** ochish. Default — bo'sh. */
271
+ allow?: readonly ServerOwnedField[];
272
+ }
273
+ /**
274
+ * Offset (sahifali) params DTO'si — `allowedKeys` va cap'lar bilan.
275
+ *
276
+ * @example
277
+ * ```ts
278
+ * const ListBuyersDto = makeOffsetParamsDto({ allowedKeys: ["buyerName", "createdAt"] });
279
+ * type ListBuyersDto = InstanceType<typeof ListBuyersDto>;
280
+ *
281
+ * @Post("list")
282
+ * list(@Body() params: ListBuyersDto) {
283
+ * return this.buyersRepository.findList(params);
284
+ * }
285
+ * ```
286
+ */
287
+ declare function makeOffsetParamsDto(options?: ParamsDtoOptions): {
288
+ new (): {
289
+ filter?: Filter;
290
+ sort?: Sort;
291
+ page?: number;
292
+ perPage?: number;
293
+ columns?: Record<string, boolean>;
294
+ with?: Record<string, unknown>;
295
+ withDeleted?: boolean;
296
+ };
297
+ };
298
+ /** Infinite-scroll params DTO'si. Qarang: {@link makeOffsetParamsDto}. */
299
+ declare function makeInfiniteParamsDto(options?: ParamsDtoOptions): {
300
+ new (): {
301
+ filter?: Filter;
302
+ sort?: Sort;
303
+ limit?: number;
304
+ offset?: number;
305
+ columns?: Record<string, boolean>;
306
+ with?: Record<string, unknown>;
307
+ withDeleted?: boolean;
308
+ };
309
+ };
310
+ /** Cursor (keyset) params DTO'si — `sort` yo'q. Qarang: {@link makeOffsetParamsDto}. */
311
+ declare function makeCursorParamsDto(options?: ParamsDtoOptions): {
312
+ new (): {
313
+ filter?: Filter;
314
+ limit?: number;
315
+ cursor?: string | null;
316
+ cursorKey?: string;
317
+ order?: SortDirection;
318
+ direction?: "forward" | "backward";
319
+ columns?: Record<string, boolean>;
320
+ with?: Record<string, unknown>;
321
+ withDeleted?: boolean;
322
+ };
323
+ };
324
+
325
+ export { BaseParamsDto, CursorParamsDto, DEFAULT_MAX_FILTER_DEPTH, InfiniteParamsDto, IsQueryFilter, IsQueryFilterConstraint, type IsQueryFilterOptions, IsQuerySort, IsQuerySortConstraint, type IsQuerySortOptions, type MessageArgs, type MessageStyleOptions, OffsetParamsDto, type ParamsDtoOptions, QUERYKIT_LOCALES, QUERYKIT_MESSAGES_EN, QUERYKIT_MESSAGES_RU, QUERYKIT_MESSAGES_UZ, type QueryFilterOptions, type QueryKitLocale, type QueryKitMessageCatalog, type QueryKitMessageKey, type QuerySortOptions, type ServerOwnedField, type ServerOwnedParams, type ValidationIssue, formatMessage, localizeIssue, makeCursorParamsDto, makeInfiniteParamsDto, makeOffsetParamsDto, validateFilter, validateSort };