@querykitjs/class-validator 1.0.0 → 2.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.
@@ -0,0 +1,5 @@
1
+ export { BaseParamsDto, CursorParamsDto, InfiniteParamsDto, OffsetParamsDto } from './chunk-XF6C4EG2.js';
2
+ import './chunk-V4E73HMQ.js';
3
+ import './chunk-QMFPUYJO.js';
4
+ //# sourceMappingURL=legacy-dto.js.map
5
+ //# sourceMappingURL=legacy-dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"legacy-dto.js"}
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ var chunkHKLXL3NW_cjs = require('./chunk-HKLXL3NW.cjs');
4
+
5
+
6
+
7
+ Object.defineProperty(exports, "DEFAULT_MAX_FILTER_DEPTH", {
8
+ enumerable: true,
9
+ get: function () { return chunkHKLXL3NW_cjs.DEFAULT_MAX_FILTER_DEPTH; }
10
+ });
11
+ Object.defineProperty(exports, "validateFilter", {
12
+ enumerable: true,
13
+ get: function () { return chunkHKLXL3NW_cjs.validateFilter; }
14
+ });
15
+ Object.defineProperty(exports, "validateSort", {
16
+ enumerable: true,
17
+ get: function () { return chunkHKLXL3NW_cjs.validateSort; }
18
+ });
19
+ //# sourceMappingURL=validate.cjs.map
20
+ //# sourceMappingURL=validate.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"validate.cjs"}
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_MAX_FILTER_DEPTH, validateFilter, validateSort } from './chunk-QMFPUYJO.js';
2
+ //# sourceMappingURL=validate.js.map
3
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"validate.js"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@querykitjs/class-validator",
3
- "version": "1.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "class-validator decorators and DTOs that validate the querykit request contract (filters, sort, pagination) — types stay in sync with @querykitjs/core.",
5
5
  "type": "module",
6
6
  "author": {
@@ -23,13 +23,19 @@
23
23
  "types": "./dist/index.d.ts",
24
24
  "exports": {
25
25
  ".": {
26
- "types": "./dist/index.d.ts",
27
- "import": "./dist/index.js",
28
- "require": "./dist/index.cjs"
26
+ "import": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "require": {
31
+ "types": "./dist/index.d.cts",
32
+ "default": "./dist/index.cjs"
33
+ }
29
34
  }
30
35
  },
31
36
  "files": [
32
37
  "dist",
38
+ "skills/querykit-class-validator",
33
39
  "README.md",
34
40
  "README.en.md",
35
41
  "LICENSE"
@@ -38,6 +44,9 @@
38
44
  "publishConfig": {
39
45
  "access": "public"
40
46
  },
47
+ "engines": {
48
+ "node": ">=22.12"
49
+ },
41
50
  "scripts": {
42
51
  "build": "tsup",
43
52
  "dev": "tsup --watch",
@@ -45,12 +54,12 @@
45
54
  "test:smoke": "cd test/.bun && bun ../smoke.ts"
46
55
  },
47
56
  "dependencies": {
48
- "@querykitjs/core": "1.2.0"
57
+ "@querykitjs/core": "^2.1.0"
49
58
  },
50
59
  "peerDependencies": {
51
- "class-transformer": ">=0.5",
52
- "class-validator": ">=0.14",
53
- "reflect-metadata": ">=0.1.13"
60
+ "class-transformer": "^0.5",
61
+ "class-validator": ">=0.14 <0.16",
62
+ "reflect-metadata": "^0.1.13 || ^0.2"
54
63
  },
55
64
  "peerDependenciesMeta": {
56
65
  "reflect-metadata": {
@@ -58,9 +67,9 @@
58
67
  }
59
68
  },
60
69
  "devDependencies": {
61
- "class-transformer": "^0.5.1",
62
- "class-validator": "^0.15.1",
63
- "reflect-metadata": "^0.2.2"
70
+ "class-transformer": "0.5.1",
71
+ "class-validator": "0.15.1",
72
+ "reflect-metadata": "0.2.2"
64
73
  },
65
74
  "keywords": [
66
75
  "querykit",
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: querykit-class-validator
3
+ description: Use when code validates a querykit list request with class-validator DTOs (@querykitjs/class-validator) - makeOffsetParamsDto / makeInfiniteParamsDto / makeCursorParamsDto, allowedKeys, maxDepth and limits, the allow option for columns / with / withDeleted, NestJS ValidationPipe settings, IsQueryFilter / IsQuerySort, i18n message catalogs (QUERYKIT_LOCALES), or validateFilter / validateSort without Nest.
4
+ ---
5
+
6
+ ## Safety rules
7
+
8
+ - DO build DTOs with the factories `makeOffsetParamsDto`, `makeInfiniteParamsDto`, `makeCursorParamsDto`. DON'T use the deprecated constant DTOs in new code.
9
+ - DO run Nest's `ValidationPipe` with `whitelist: true` and `transform: true`. Without `whitelist` the server-owned `columns`, `with` and `withDeleted` survive on the object and reach the repository; without `transform` numeric query-string fields stay strings.
10
+ - DON'T open `columns`, `with` or `withDeleted` by default. Only `allow: ["withDeleted"]` (or `"columns"`, `"with"`) opens one, on purpose, and the repository's `forcedColumns` / `allowedColumns` / `relations` still decide.
11
+ - DO pass `allowedKeys` (the filter and sort fields a client may name). An unknown key is then rejected, and `[]` allows nothing. Without it any key passes.
12
+ - DO keep `maxDepth` (default `5`) and the other request limits from core `DEFAULT_QUERY_LIMITS`: conditions `30`, array items `200`, string length `256`, offset `10000`, sort fields `3`. `perPage` and `limit` are capped at `200`. Raise one with `limits` on the factory. DON'T write your own size checks.
13
+ - DO treat the DTO as the first line only: the repository's `filterable` / `sortable` policy on the server is still the authority on which fields and operators a client may use.
14
+
15
+ ## When to use
16
+
17
+ - A NestJS controller that receives the JSON body `@querykitjs/web` sends and passes it to a querykit repository.
18
+ - Express, Hono or any other framework with `validateFilter` / `validateSort` (no Nest, no decorators).
19
+ - Validation messages in more than one language (`en`, `ru`, `uz` catalogs, or your own).
20
+
21
+ With zod use `@querykitjs/zod`; the request contract is the same.
22
+
23
+ ## Setup
24
+
25
+ ```ts
26
+ import { makeOffsetParamsDto } from "@querykitjs/class-validator";
27
+
28
+ // main.ts: app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
29
+ export const ListBuyersDto = makeOffsetParamsDto({ allowedKeys: ["buyerName", "createdAt", "status"] });
30
+ export type ListBuyersDto = InstanceType<typeof ListBuyersDto>;
31
+
32
+ declare function findList(params: ListBuyersDto): Promise<{ data: unknown[] }>;
33
+
34
+ // @Post("list") list(@Body() params: ListBuyersDto) { … }
35
+ export const list = (params: ListBuyersDto) => findList(params); // core-aligned, no cast
36
+ ```
37
+
38
+ ## Common tasks
39
+
40
+ ### Infinite scroll and cursor DTOs
41
+
42
+ ```ts
43
+ import { makeCursorParamsDto, makeInfiniteParamsDto } from "@querykitjs/class-validator";
44
+
45
+ export const FeedDto = makeInfiniteParamsDto({ allowedKeys: ["status"], maxLimit: 50 }); // limit / offset
46
+ export const TimelineDto = makeCursorParamsDto({ allowedKeys: ["status"] }); // limit / cursor / cursorKey / order / direction
47
+ export type TimelineDto = InstanceType<typeof TimelineDto>;
48
+ ```
49
+
50
+ ### Add a field of your own with a decorator
51
+
52
+ ```ts
53
+ import { IsOptional, IsString, MaxLength } from "class-validator";
54
+ import { makeOffsetParamsDto } from "@querykitjs/class-validator";
55
+
56
+ const BaseListDto = makeOffsetParamsDto({ allowedKeys: ["buyerName", "status"] });
57
+
58
+ export class ListBuyersDto extends BaseListDto {
59
+ @IsOptional()
60
+ @IsString()
61
+ @MaxLength(64)
62
+ search?: string; // decorated, so whitelist: true keeps it
63
+ }
64
+ ```
65
+
66
+ ### Tighten the limits and the operators
67
+
68
+ ```ts
69
+ import { makeOffsetParamsDto } from "@querykitjs/class-validator";
70
+
71
+ export const ReportDto = makeOffsetParamsDto({
72
+ allowedKeys: ["status", "createdAt"],
73
+ maxPerPage: 100,
74
+ limits: { maxConditions: 10, maxInItems: 50 },
75
+ filter: { maxDepth: 3, allowedOperators: ["=", "in", "between"], strictValue: true },
76
+ });
77
+ ```
78
+
79
+ ### Skip the count query with `withTotal`
80
+
81
+ ```ts
82
+ import { makeOffsetParamsDto } from "@querykitjs/class-validator";
83
+
84
+ const ListDto = makeOffsetParamsDto({ allowedKeys: ["status"] });
85
+ const params: InstanceType<typeof ListDto> = { page: 3, perPage: 50, withTotal: false };
86
+ // withTotal: false ⇒ the repository runs no count; meta.total_items / total_pages are null
87
+ console.log(params.withTotal);
88
+ ```
89
+
90
+ ### Messages in the request's language
91
+
92
+ ```ts
93
+ import { formatMessage, makeOffsetParamsDto, QUERYKIT_LOCALES, QUERYKIT_MESSAGES_UZ } from "@querykitjs/class-validator";
94
+
95
+ // messageStyle "key" returns i18n keys (for nestjs-i18n's I18nValidationPipe) instead of sentences
96
+ export const ListDto = makeOffsetParamsDto({ allowedKeys: ["buyerName"], filter: { messageStyle: "key" }, sort: { messageStyle: "key" } });
97
+
98
+ console.log(Object.keys(QUERYKIT_LOCALES)); // en, ru, uz
99
+ console.log(formatMessage("querykit.filter.unknown_key", { path: "filter[0]", key: "zzz" }, QUERYKIT_MESSAGES_UZ));
100
+ ```
101
+
102
+ ### Validate without Nest
103
+
104
+ ```ts
105
+ import { validateFilter, validateSort } from "@querykitjs/class-validator";
106
+
107
+ declare const body: { filter?: unknown; sort?: unknown };
108
+ const allowedKeys = ["status", "createdAt"];
109
+
110
+ const issues = [...validateFilter(body.filter, { allowedKeys, strictValue: true }), ...validateSort(body.sort, { allowedKeys })];
111
+ if (issues.length > 0) console.log(issues.map(issue => `${issue.path}: ${issue.message}`)); // return 400 with { path, key, message, args }
112
+ ```
113
+
114
+ ## Request contract
115
+
116
+ The same JSON shape as every querykit package (`@querykitjs/core`): conditions `{ key, operation?, value? }`, groups `{ and }` / `{ or }` / `{ not }` (a flat array = AND), `sort` always `[{ key, direction? }]`, offset `page` / `perPage` / `withTotal`, infinite `limit` / `offset`, cursor `limit` / `cursor` / `cursorKey` / `order` / `direction` (no `sort`). Meta is snake_case: `total_items`, `total_pages`, `has_next`, `has_more`, `next_offset`, `next_cursor`, `prev_cursor`. The body types are `WireOffsetParams`, `WireInfiniteParams` and `WireCursorParams`; validate with `@querykitjs/zod` or `@querykitjs/class-validator`, then pass the body as is. Refusals are `QueryKitError` with `{ source, site, key, operation?, reason }`.
117
+
118
+ ## Mistakes to avoid
119
+
120
+ - Using the deprecated constant DTOs `OffsetParamsDto`, `InfiniteParamsDto`, `CursorParamsDto`, `BaseParamsDto` in new code.
121
+ - Forgetting `whitelist: true` or `transform: true` on `ValidationPipe`.
122
+ - Overriding `filter` in a subclass without `declare`: the property declaration shadows the inherited value.
123
+ - Sending a `Date` object, `NaN` or `Infinity` as a filter value: the wire is JSON, so they are rejected; send ISO strings.
124
+ - Expecting `allowedKeys` to stop a field the repository allows: it only narrows the request; the repository's `filterable` is checked again on the server.