@modulify/validator 0.1.0 → 0.2.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 (61) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +324 -107
  3. package/dist/assert.cjs +66 -0
  4. package/dist/assert.d.ts +16 -0
  5. package/dist/assert.mjs +66 -0
  6. package/dist/assertions.cjs +190 -92
  7. package/dist/assertions.d.ts +58 -2
  8. package/dist/assertions.mjs +191 -93
  9. package/dist/checkers.d.ts +8 -0
  10. package/dist/combinators.cjs +341 -0
  11. package/dist/combinators.d.ts +17 -0
  12. package/dist/combinators.mjs +341 -0
  13. package/dist/constraints.d.ts +4 -0
  14. package/dist/extractors.d.ts +2 -0
  15. package/dist/index.cjs +172 -61
  16. package/dist/index.d.ts +10 -4
  17. package/dist/index.mjs +176 -64
  18. package/dist/json-schema.cjs +514 -0
  19. package/dist/json-schema.d.ts +14 -0
  20. package/dist/json-schema.mjs +514 -0
  21. package/dist/metadata.cjs +8 -0
  22. package/dist/metadata.cjs.js +130 -0
  23. package/dist/metadata.d.ts +8 -0
  24. package/dist/metadata.es.js +131 -0
  25. package/dist/metadata.mjs +8 -0
  26. package/dist/predicates.cjs +40 -5
  27. package/dist/predicates.d.ts +25 -3
  28. package/dist/predicates.mjs +40 -5
  29. package/dist/violations.d.ts +29 -0
  30. package/docs/en/00-index.md +14 -0
  31. package/docs/en/01-shape-api.md +348 -0
  32. package/docs/en/02-metadata-and-introspection.md +276 -0
  33. package/docs/en/03-violations.md +267 -0
  34. package/docs/en/04-json-schema-export.md +264 -0
  35. package/docs/en/05-public-api.md +123 -0
  36. package/docs/en/06-common-recipes.md +273 -0
  37. package/docs/en/07-ai-reference.md +215 -0
  38. package/docs/en/08-violation-code-types.md +241 -0
  39. package/docs/ru/00-index.md +15 -0
  40. package/docs/ru/01-shape-api.md +348 -0
  41. package/docs/ru/02-metadata-and-introspection.md +276 -0
  42. package/docs/ru/03-violations.md +267 -0
  43. package/docs/ru/04-json-schema-export.md +264 -0
  44. package/docs/ru/05-public-api.md +123 -0
  45. package/docs/ru/06-common-recipes.md +273 -0
  46. package/docs/ru/07-ai-reference.md +215 -0
  47. package/docs/ru/08-violation-code-types.md +241 -0
  48. package/docs/ru/README.md +371 -0
  49. package/package.json +51 -33
  50. package/types/index.d.ts +789 -30
  51. package/types/json-schema.d.ts +75 -0
  52. package/dist/assertions/Assert.d.ts +0 -2
  53. package/dist/assertions/HasLength.d.ts +0 -7
  54. package/dist/assertions/check.d.ts +0 -3
  55. package/dist/assertions/index.d.ts +0 -16
  56. package/dist/runners/Each.d.ts +0 -3
  57. package/dist/runners/HasProperties.d.ts +0 -6
  58. package/dist/runners/index.d.ts +0 -2
  59. package/dist/runners.cjs +0 -32
  60. package/dist/runners.d.ts +0 -2
  61. package/dist/runners.mjs +0 -32
@@ -0,0 +1,371 @@
1
+ # <img src="../../logo.png" alt="Logo" width="36" /> `@modulify/validator`
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@modulify/validator.svg)](https://www.npmjs.com/package/@modulify/validator)
4
+ [![codecov](https://codecov.io/gh/modulify/validator/branch/main/graph/badge.svg)](https://codecov.io/gh/modulify/validator)
5
+ [![Tests Status](https://github.com/modulify/validator/actions/workflows/tests.yml/badge.svg)](https://github.com/modulify/validator/actions)
6
+
7
+ [README на английском](../../README.md)
8
+ [Индекс русской документации](./00-index.md)
9
+
10
+ `@modulify/validator` — небольшая TypeScript-библиотека для валидации, построенная вокруг трёх отдельных слоёв:
11
+
12
+ - predicates для проверок во время выполнения и сужения типов;
13
+ - assertions для машиночитаемых результатов валидации с ошибкой;
14
+ - combinators для композиции схем, включая структурную рекурсию по массивам и объектам.
15
+
16
+ Проект сознательно строится вокруг структурированных метаданных, а не вокруг встроенных человекочитаемых сообщений об ошибках.
17
+
18
+ ## Что это за проект
19
+
20
+ Эта библиотека рассчитана на случаи, когда вам нужно:
21
+
22
+ - сохранить простые type guards полезными сами по себе;
23
+ - валидировать вложенные данные рекурсивно;
24
+ - получать структурированные violations вместо текстовых сообщений;
25
+ - позже самостоятельно решать, как эти violations показывать или преобразовывать.
26
+
27
+ Типичные результаты слоя валидации можно преобразовать в:
28
+
29
+ - локализованные сообщения об ошибках;
30
+ - состояние ошибок полей формы;
31
+ - API payloads с ошибками;
32
+ - данные для аналитики или отладки;
33
+ - собственный UI или узлы virtual DOM.
34
+
35
+ ## Идея
36
+
37
+ Проект разделяет две задачи, которые часто смешиваются в одной абстракции.
38
+
39
+ ### Предикаты
40
+
41
+ Predicates — это небольшие проверки во время выполнения, которые одновременно работают как TypeScript type guards.
42
+
43
+ Они отвечают на вопросы вроде:
44
+
45
+ - является ли это значение строкой?
46
+ - является ли это значение объектом с определённой формой?
47
+ - выполняет ли это значение базовое логическое условие?
48
+
49
+ Этот слой должен оставаться простым и полезным сам по себе даже вне контура валидации.
50
+
51
+ ### Валидаторы и assertions
52
+
53
+ Assertions — это проверки, которые могут вернуть violation со структурированными метаданными.
54
+
55
+ Вместо генерации текстового сообщения assertion возвращает данные, которые описывают:
56
+
57
+ - что именно сломалось;
58
+ - где это произошло;
59
+ - какой семантический код сработал;
60
+ - какие аргументы или границы были задействованы.
61
+
62
+ Так представление результата остаётся за пределами библиотеки.
63
+
64
+ ### Почему это существует рядом с `zod`-подобными библиотеками
65
+
66
+ Библиотеки вроде `zod`, `yup` и других schema-oriented инструментов хорошо известны и отлично решают большой класс задач.
67
+
68
+ Задача этого проекта в другом.
69
+
70
+ Он в первую очередь не пытается быть:
71
+
72
+ - schema-definition DSL;
73
+ - form library со встроенной семантикой сообщений;
74
+ - all-in-one parsing и слой представления.
75
+
76
+ Вместо этого проект фокусируется на:
77
+
78
+ - маленьких predicates для narrowing;
79
+ - отдельном слое assertions для diagnostics;
80
+ - composable schema combinators;
81
+ - machine-readable violations, которые потребитель может преобразовывать как угодно.
82
+
83
+ Короткое описание направления проекта:
84
+
85
+ > Type-safe predicates for narrowing, and validators for machine-readable diagnostics.
86
+
87
+ Или ещё короче:
88
+
89
+ > No messages, only meaning.
90
+
91
+ ## Установка
92
+
93
+ Через `yarn`:
94
+
95
+ ```bash
96
+ yarn add @modulify/validator
97
+ ```
98
+
99
+ Через `npm`:
100
+
101
+ ```bash
102
+ npm install @modulify/validator --save
103
+ ```
104
+
105
+ ## Быстрый пример
106
+
107
+ ```typescript
108
+ import {
109
+ each,
110
+ shape,
111
+ exact,
112
+ hasLength,
113
+ isDefined,
114
+ isString,
115
+ nullable,
116
+ optional,
117
+ validate,
118
+ } from '@modulify/validator'
119
+
120
+ const [ok, validated, violations] = await validate({
121
+ form: {
122
+ nickname: undefined,
123
+ title: null,
124
+ password: '',
125
+ role: 'admin',
126
+ },
127
+ }, shape({
128
+ form: [
129
+ isDefined,
130
+ shape({
131
+ nickname: optional([isString, hasLength({ min: 4 })]),
132
+ title: nullable(isString),
133
+ password: [isString, hasLength({ min: 6 })],
134
+ role: exact('admin'),
135
+ }),
136
+ ],
137
+ }))
138
+
139
+ if (ok) {
140
+ validated.form.nickname.toUpperCase()
141
+ } else {
142
+ console.log(violations)
143
+ }
144
+ ```
145
+
146
+ Синхронная валидация:
147
+
148
+ ```typescript
149
+ const [ok, validated, violations] = validate.sync({
150
+ form: {
151
+ nickname: '',
152
+ password: '',
153
+ },
154
+ }, shape({
155
+ form: [
156
+ isDefined,
157
+ shape({
158
+ nickname: [isString, hasLength({ min: 4 })],
159
+ password: [isString, hasLength({ min: 6 })],
160
+ }),
161
+ ],
162
+ }))
163
+
164
+ if (ok) {
165
+ validated.form.password.toUpperCase()
166
+ }
167
+ ```
168
+
169
+ Сужение исходной переменной в sync-коде:
170
+
171
+ ```typescript
172
+ import {
173
+ isDefined,
174
+ isString,
175
+ matches,
176
+ } from '@modulify/validator'
177
+
178
+ const value: unknown = 'nickname'
179
+
180
+ if (matches.sync(value, [isDefined, isString])) {
181
+ value.toUpperCase()
182
+ }
183
+ ```
184
+
185
+ Сильная типизация успешной ветки прямо из `validate`:
186
+
187
+ ```typescript
188
+ import {
189
+ shape,
190
+ isDefined,
191
+ isString,
192
+ validate,
193
+ } from '@modulify/validator'
194
+
195
+ const schema = shape({
196
+ name: [isDefined, isString],
197
+ })
198
+
199
+ const [ok, validated, violations] = await validate({ name: 'Kirill' }, schema)
200
+
201
+ if (ok) {
202
+ validated.name.toUpperCase()
203
+ } else {
204
+ console.log(violations)
205
+ }
206
+ ```
207
+
208
+ ## API объектных схем
209
+
210
+ `shape(...)` — это переиспользуемый API объектных схем. Он валидирует вложенные record-like objects и предоставляет небольшой неизменяемый API вроде `strict()`, `pick()`, `omit()`, `partial()`, `extend()`, `merge()`, `refine()` и `fieldsMatch(...)`.
211
+
212
+ ```typescript
213
+ import {
214
+ isString,
215
+ optional,
216
+ shape,
217
+ validate,
218
+ } from '@modulify/validator'
219
+
220
+ const profile = shape({
221
+ id: isString,
222
+ nickname: optional(isString),
223
+ })
224
+
225
+ const [ok] = validate.sync({
226
+ id: 'u1',
227
+ nickname: 'neo',
228
+ }, profile)
229
+ ```
230
+
231
+ Подробные руководства:
232
+
233
+ - [Руководство по API объектных схем](./01-shape-api.md)
234
+
235
+ ## Метаданные и интроспекция
236
+
237
+ `meta(...)` добавляет машиночитаемые метаданные к любому constraint, а `describe(...)` возвращает стабильное рекурсивное дерево descriptors для built-in constraints и совместимых custom validators.
238
+
239
+ ```typescript
240
+ import {
241
+ describe,
242
+ isString,
243
+ meta,
244
+ optional,
245
+ shape,
246
+ } from '@modulify/validator'
247
+
248
+ const registration = meta(shape({
249
+ email: meta(isString, {
250
+ title: 'Email',
251
+ placeholder: 'name@example.com',
252
+ }),
253
+ nickname: optional(isString),
254
+ }).strict(), {
255
+ title: 'Registration form',
256
+ })
257
+
258
+ const node = describe(registration)
259
+ ```
260
+
261
+ Подробные руководства:
262
+
263
+ - [Руководство по метаданным и интроспекции](./02-metadata-and-introspection.md)
264
+ - [Руководство по типам кодов нарушений](./08-violation-code-types.md)
265
+
266
+ ## Ментальная модель
267
+
268
+ Практически про библиотеку удобно думать так:
269
+
270
+ - predicates отвечают: удовлетворяет ли значение условию `X`?
271
+ - assertions отвечают: если нет, то что именно сломалось?
272
+ - combinators отвечают: как объединять constraints в более крупную схему, включая рекурсивный обход объектов и массивов?
273
+
274
+ В текущем API это обычно выглядит так:
275
+
276
+ - leaf checks через assertions вроде `isString`, `isDefined`, `hasLength`, `oneOf`;
277
+ - композиция схем через combinators вроде `exact`, `optional`, `nullable`, `nullish`, `shape(...)`, `each(...)`;
278
+ - типизированная валидация через `validate(...)` или `validate.sync(...)`;
279
+ - narrowing исходной переменной в sync-коде через `matches.sync(...)`.
280
+
281
+ ## Нарушения
282
+
283
+ `validate(...)` возвращает машиночитаемый список `Violation[]`, а `collection(...)` может обернуть его в небольшой helper API для точного поиска по path и обхода дерева.
284
+
285
+ ```typescript
286
+ import {
287
+ collection,
288
+ isString,
289
+ shape,
290
+ validate,
291
+ } from '@modulify/validator'
292
+
293
+ const [ok, validated, violations] = validate.sync({
294
+ profile: {
295
+ email: '',
296
+ },
297
+ }, shape({
298
+ profile: shape({
299
+ email: isString,
300
+ }),
301
+ }))
302
+
303
+ const errors = collection(violations)
304
+
305
+ const rootErrors = errors.at([])
306
+ const emailErrors = errors.at(['profile', 'email'])
307
+ ```
308
+
309
+ Подробные руководства:
310
+
311
+ - [Руководство по нарушениям](./03-violations.md)
312
+ - [Руководство по типам кодов нарушений](./08-violation-code-types.md)
313
+
314
+ ## Экспорт JSON Schema
315
+
316
+ `toJsonSchema(...)` строит представление JSON Schema поверх того же публичного дерева descriptors, которое возвращает `describe(...)`.
317
+
318
+ ```typescript
319
+ import {
320
+ isNumber,
321
+ isString,
322
+ meta,
323
+ optional,
324
+ shape,
325
+ } from '@modulify/validator'
326
+ import { toJsonSchema } from '@modulify/validator/json-schema'
327
+
328
+ const profile = meta(shape({
329
+ email: meta(isString, {
330
+ title: 'Email',
331
+ format: 'email',
332
+ }),
333
+ age: optional(isNumber),
334
+ }).strict(), {
335
+ title: 'Profile',
336
+ })
337
+
338
+ const jsonSchema = toJsonSchema(profile)
339
+ ```
340
+
341
+ Подробные руководства:
342
+
343
+ - [Руководство по экспорту JSON Schema](./04-json-schema-export.md)
344
+
345
+ ## Публичный API
346
+
347
+ Подробное руководство по public API описывает root exports, специализированные subpath exports, validation result tuple и то, как package surface разделён между validation, predicates, violations, metadata и экспортом JSON Schema.
348
+
349
+ Подробные руководства:
350
+
351
+ - [Руководство по публичному API](./05-public-api.md)
352
+
353
+ ## Практические рецепты и справка для AI
354
+
355
+ Есть ещё два руководства, которые полезны, когда нужен более быстрый практический вход вместо последовательного чтения всей концептуальной документации.
356
+
357
+ - [Практические рецепты](./06-common-recipes.md) - практические примеры для валидации payload, выбора wrappers, переиспользуемых shape, сопоставления ошибок формы и экспорта JSON Schema.
358
+ - [Справка для AI](./07-ai-reference.md) - компактное описание контракта для agents, инструментов и быстрого поиска семантики библиотеки.
359
+
360
+ ## Заметки
361
+
362
+ - Assertions возвращают структурированные метаданные вместо сообщений.
363
+ - Combinators — это тонкие вспомогательные средства для построения схем поверх assertions и validators; `each` и `shape` в этой модели являются structural combinators.
364
+ - Predicates должны оставаться полезными независимо от слоя валидации.
365
+ - Библиотекой проще пользоваться, когда во всём проекте сохраняется один стабильный violation format.
366
+ - `validate(...)` сужает тип `validated` в tuple, а не исходную входную переменную.
367
+ - Чтобы сузить исходную переменную в sync-коде, используйте `matches.sync(...)`.
368
+
369
+ ## Переводы
370
+
371
+ - [English](../../README.md)
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "description": "Declarative validation util for JavaScript",
5
5
  "license": "MIT",
6
- "version": "0.1.0",
6
+ "version": "0.2.1",
7
7
  "exports": {
8
8
  ".": {
9
9
  "types": "./dist/index.d.ts",
@@ -17,17 +17,23 @@
17
17
  "require": "./dist/assertions.cjs",
18
18
  "default": "./dist/assertions.mjs"
19
19
  },
20
+ "./combinators": {
21
+ "types": "./dist/combinators.d.ts",
22
+ "import": "./dist/combinators.mjs",
23
+ "require": "./dist/combinators.cjs",
24
+ "default": "./dist/combinators.mjs"
25
+ },
26
+ "./json-schema": {
27
+ "types": "./dist/json-schema.d.ts",
28
+ "import": "./dist/json-schema.mjs",
29
+ "require": "./dist/json-schema.cjs",
30
+ "default": "./dist/json-schema.mjs"
31
+ },
20
32
  "./predicates": {
21
33
  "types": "./dist/predicates.d.ts",
22
34
  "import": "./dist/predicates.mjs",
23
35
  "require": "./dist/predicates.cjs",
24
36
  "default": "./dist/predicates.mjs"
25
- },
26
- "./runners": {
27
- "types": "./dist/runners.d.ts",
28
- "import": "./dist/runners.mjs",
29
- "require": "./dist/runners.cjs",
30
- "default": "./dist/runners.mjs"
31
37
  }
32
38
  },
33
39
  "types": "dist/index.d.ts",
@@ -36,11 +42,14 @@
36
42
  "assertions": [
37
43
  "./dist/assertions.d.ts"
38
44
  ],
45
+ "combinators": [
46
+ "./dist/combinators.d.ts"
47
+ ],
48
+ "json-schema": [
49
+ "./dist/json-schema.d.ts"
50
+ ],
39
51
  "predicates": [
40
52
  "./dist/predicates.d.ts"
41
- ],
42
- "runners": [
43
- "./dist/runners.d.ts"
44
53
  ]
45
54
  }
46
55
  },
@@ -48,37 +57,50 @@
48
57
  "build": "vite build",
49
58
  "lint": "eslint src tests types",
50
59
  "prepare": "husky",
51
- "release": "standard-version",
52
- "release:minor": "standard-version --release-as minor",
53
- "release:patch": "standard-version --release-as patch",
54
- "release:major": "standard-version --release-as major",
60
+ "release": "npx --yes standard-version",
61
+ "release:minor": "npx --yes standard-version --release-as minor",
62
+ "release:patch": "npx --yes standard-version --release-as patch",
63
+ "release:major": "npx --yes standard-version --release-as major",
55
64
  "stats": "gzip -c ./dist/index.mjs | wc -c",
56
65
  "test": "vitest run",
66
+ "test:d": "vitest run --typecheck.only",
57
67
  "test:coverage": "vitest run --coverage",
58
- "test:coverage:html": "vitest run --coverage --reporter=html --outputFile.html=./reports/html/report.html"
68
+ "test:coverage:html": "vitest run --coverage --reporter=html --outputFile.html=./reports/html/report.html",
69
+ "typecheck": "tsc --noEmit"
59
70
  },
60
71
  "devDependencies": {
61
- "@commitlint/cli": "^17.7.1",
62
- "@commitlint/config-conventional": "^17.7.0",
63
- "@eslint/js": "^9.17.0",
64
- "@types/node": "^18.15 || ^20.11",
65
- "@vitest/coverage-istanbul": "2.1.8",
66
- "@vitest/ui": "2.1.8",
67
- "eslint": "^9.17.0",
68
- "globals": "^15.14.0",
72
+ "@commitlint/cli": "^20.4.3",
73
+ "@commitlint/config-conventional": "^20.4.3",
74
+ "@eslint/js": "^10.0.1",
75
+ "@types/node": "^25.3.5",
76
+ "@vitest/coverage-v8": "4.0.18",
77
+ "@vitest/ui": "4.0.18",
78
+ "eslint": "^10.0.3",
79
+ "globals": "^17.4.0",
69
80
  "husky": "^9.1.7",
70
- "standard-version": "^9.5.0",
71
81
  "ts-node": "^10.9.2",
72
82
  "tslib": "^2.8.1",
73
83
  "typescript": "^5.5.4",
74
- "typescript-eslint": "^8.18.1",
75
- "vite": "^5.4.11",
76
- "vite-plugin-dts": "^4.4.0",
77
- "vitest": "^2.1.8"
84
+ "typescript-eslint": "^8.56.1",
85
+ "vite": "^7.3.1",
86
+ "vite-plugin-dts": "^4.5.4",
87
+ "vitest": "^4.0.18"
88
+ },
89
+ "resolutions": {
90
+ "minimatch@npm:10.2.1": "npm:10.2.4",
91
+ "minimatch@npm:^9.0.3": "npm:9.0.7"
78
92
  },
79
93
  "publishConfig": {
80
94
  "access": "public"
81
95
  },
96
+ "files": [
97
+ "dist",
98
+ "types",
99
+ "docs",
100
+ "README.md",
101
+ "CHANGELOG.md",
102
+ "logo.png"
103
+ ],
82
104
  "keywords": [
83
105
  "validate",
84
106
  "validator",
@@ -92,9 +114,5 @@
92
114
  "type": "git",
93
115
  "url": "https://github.com/modulify/validator.git"
94
116
  },
95
- "husky": {
96
- "hooks": {
97
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
98
- }
99
- }
117
+ "packageManager": "yarn@4.12.0"
100
118
  }