@reformer/core 10.2.0 → 11.0.0-beta.10

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 (285) hide show
  1. package/README.md +90 -68
  2. package/dist/behaviors-value-D05z3my6.js +109 -0
  3. package/dist/behaviors.d.ts +1 -1
  4. package/dist/behaviors.js +376 -211
  5. package/dist/create-model-DPSG3I0O.js +30 -0
  6. package/dist/date-utils-90-qZbse.js +43 -0
  7. package/dist/en-HP8XEych.js +44 -0
  8. package/dist/form/aggregate-signals.d.ts +21 -63
  9. package/dist/form/behaviors/collections.d.ts +84 -0
  10. package/dist/form/behaviors/context.d.ts +56 -0
  11. package/dist/form/behaviors/index.d.ts +30 -0
  12. package/dist/form/behaviors/internals.d.ts +41 -0
  13. package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +28 -5
  14. package/dist/form/behaviors/operators.d.ts +59 -0
  15. package/dist/form/behaviors/schema-operators.d.ts +83 -0
  16. package/dist/form/behaviors/types.d.ts +37 -0
  17. package/dist/form/create-form.d.ts +59 -70
  18. package/dist/form/flow/flow.d.ts +87 -0
  19. package/dist/form/form-bundle.d.ts +153 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +23 -27
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +159 -0
  37. package/dist/form/schema-subtree.d.ts +28 -0
  38. package/dist/form/signal-node-registry.d.ts +27 -22
  39. package/dist/form/submission.d.ts +93 -0
  40. package/dist/form/type-guards.d.ts +14 -73
  41. package/dist/form/types/contracts.d.ts +21 -91
  42. package/dist/form/types/field-config.d.ts +27 -0
  43. package/dist/form/types/index.d.ts +5 -91
  44. package/dist/form/types/nodes.d.ts +159 -0
  45. package/dist/form/types/schema-node.d.ts +148 -50
  46. package/dist/form/types/validation-schema.d.ts +24 -41
  47. package/dist/form/validation/context.d.ts +67 -0
  48. package/dist/form/validation/index.d.ts +24 -28
  49. package/dist/form/validation/operators.d.ts +101 -0
  50. package/dist/form/validation/run.d.ts +120 -0
  51. package/dist/form/validation/strategy.d.ts +89 -0
  52. package/dist/form/validation/types.d.ts +83 -0
  53. package/dist/form/validators/date-utils.d.ts +42 -0
  54. package/dist/form/validators/email.d.ts +23 -0
  55. package/dist/form/{validation/validators → validators}/file-type.d.ts +10 -10
  56. package/dist/form/{validation/validators → validators}/file-utils.d.ts +3 -2
  57. package/dist/form/validators/future-date.d.ts +23 -0
  58. package/dist/form/{validation/validators → validators}/index.d.ts +7 -4
  59. package/dist/form/validators/integer.d.ts +22 -0
  60. package/dist/form/validators/is-date.d.ts +24 -0
  61. package/dist/form/validators/is-number.d.ts +25 -0
  62. package/dist/form/validators/max-age.d.ts +28 -0
  63. package/dist/form/validators/max-date.d.ts +25 -0
  64. package/dist/form/{validation/validators → validators}/max-file-size.d.ts +10 -10
  65. package/dist/form/validators/max-files.d.ts +26 -0
  66. package/dist/form/validators/max-length.d.ts +33 -0
  67. package/dist/form/validators/max-total-file-size.d.ts +26 -0
  68. package/dist/form/validators/max.d.ts +26 -0
  69. package/dist/form/validators/min-age.d.ts +27 -0
  70. package/dist/form/validators/min-date.d.ts +25 -0
  71. package/dist/form/{validation/validators → validators}/min-file-size.d.ts +10 -10
  72. package/dist/form/validators/min-files.d.ts +27 -0
  73. package/dist/form/validators/min-length.d.ts +33 -0
  74. package/dist/form/validators/min.d.ts +26 -0
  75. package/dist/form/validators/multiple-of.d.ts +25 -0
  76. package/dist/form/validators/non-negative.d.ts +22 -0
  77. package/dist/form/validators/non-zero.d.ts +22 -0
  78. package/dist/form/validators/past-date.d.ts +23 -0
  79. package/dist/form/validators/pattern.d.ts +26 -0
  80. package/dist/form/{validation/validators → validators}/phone.d.ts +11 -11
  81. package/dist/form/validators/required.d.ts +29 -0
  82. package/dist/form/validators/url.d.ts +45 -0
  83. package/dist/form/validators/validation-issue.d.ts +8 -0
  84. package/dist/i18n/descriptor.d.ts +54 -0
  85. package/dist/i18n/en.json.d.ts +49 -0
  86. package/dist/i18n/loader.d.ts +71 -0
  87. package/dist/i18n/locale-store.d.ts +46 -0
  88. package/dist/i18n/locale.d.ts +90 -0
  89. package/dist/i18n/message-format.d.ts +136 -0
  90. package/dist/i18n/resolve-localized.d.ts +17 -0
  91. package/dist/i18n/ru.json.d.ts +49 -0
  92. package/dist/i18n/translator.d.ts +61 -0
  93. package/dist/i18n/validation-message.d.ts +53 -0
  94. package/dist/i18n.d.ts +26 -0
  95. package/dist/i18n.js +86 -0
  96. package/dist/index.d.ts +18 -6
  97. package/dist/index.js +932 -2779
  98. package/dist/loader-BWkigpVe.js +52 -0
  99. package/dist/locale/en.d.ts +2 -0
  100. package/dist/locale/en.js +5 -0
  101. package/dist/locale/en.json +45 -0
  102. package/dist/locale/index.d.ts +18 -0
  103. package/dist/locale/ru.d.ts +2 -0
  104. package/dist/locale/ru.js +44 -0
  105. package/dist/locale/ru.json +45 -0
  106. package/dist/locale-CG54AU2Q.js +219 -0
  107. package/dist/locale.d.ts +2 -0
  108. package/dist/locale.js +9 -0
  109. package/dist/{state → model}/behaviors-value.d.ts +24 -6
  110. package/dist/{state/form-model.d.ts → model/create-model.d.ts} +18 -4
  111. package/dist/{state → model}/derived-registry.d.ts +1 -1
  112. package/dist/model/index.d.ts +42 -0
  113. package/dist/model/model-nodes.d.ts +190 -0
  114. package/dist/model/model-signals-proxy.d.ts +63 -0
  115. package/dist/model/model-value-proxy.d.ts +84 -0
  116. package/dist/{state → model}/safe-effect.d.ts +50 -2
  117. package/dist/model/types.d.ts +239 -0
  118. package/dist/model.d.ts +2 -0
  119. package/dist/model.js +27 -0
  120. package/dist/node-kind-C-r6ZMdo.js +15 -0
  121. package/dist/platforms/react/field-binding/field-adapter.d.ts +87 -0
  122. package/dist/platforms/react/field-binding/field-handle.d.ts +53 -0
  123. package/dist/platforms/react/form-bundle-context.d.ts +27 -0
  124. package/dist/{form → platforms/react}/hooks/types.d.ts +60 -38
  125. package/dist/platforms/react/hooks/use-form-bundle.d.ts +41 -0
  126. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  127. package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +13 -7
  128. package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +6 -6
  129. package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +7 -9
  130. package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +18 -18
  131. package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +9 -1
  132. package/dist/platforms/react/i18n/context.d.ts +18 -0
  133. package/dist/platforms/react/i18n/hooks.d.ts +65 -0
  134. package/dist/platforms/react/i18n/provider.d.ts +59 -0
  135. package/dist/platforms/react/index.d.ts +30 -0
  136. package/dist/react.d.ts +22 -0
  137. package/dist/react.js +505 -0
  138. package/dist/runtime-token.d.ts +22 -0
  139. package/dist/safe-effect-BazLOv1g.js +669 -0
  140. package/dist/schema-controller-CpiO2JoK.js +143 -0
  141. package/dist/signals.d.ts +15 -9
  142. package/dist/signals.js +11 -6
  143. package/dist/strategy-DzvnsWLh.js +375 -0
  144. package/dist/type-guards-D-N_5Ddh.js +19 -0
  145. package/dist/validation-issue-B9mjI8jz.js +7 -0
  146. package/dist/validation-message-Ced9MGxs.js +194 -0
  147. package/dist/validation.d.ts +1 -1
  148. package/dist/validation.js +11 -165
  149. package/dist/validators/email.d.ts +1 -1
  150. package/dist/validators/email.js +5 -8
  151. package/dist/validators/future-date.d.ts +1 -1
  152. package/dist/validators/future-date.js +10 -13
  153. package/dist/validators/integer.d.ts +1 -1
  154. package/dist/validators/integer.js +4 -7
  155. package/dist/validators/is-date.d.ts +1 -1
  156. package/dist/validators/is-date.js +5 -8
  157. package/dist/validators/is-number.d.ts +1 -1
  158. package/dist/validators/is-number.js +4 -7
  159. package/dist/validators/max-age.d.ts +1 -1
  160. package/dist/validators/max-age.js +8 -12
  161. package/dist/validators/max-date.d.ts +1 -1
  162. package/dist/validators/max-date.js +10 -13
  163. package/dist/validators/max-length.d.ts +1 -1
  164. package/dist/validators/max-length.js +7 -10
  165. package/dist/validators/max.d.ts +1 -1
  166. package/dist/validators/max.js +4 -7
  167. package/dist/validators/min-age.d.ts +1 -1
  168. package/dist/validators/min-age.js +10 -11
  169. package/dist/validators/min-date.d.ts +1 -1
  170. package/dist/validators/min-date.js +8 -11
  171. package/dist/validators/min-length.d.ts +1 -1
  172. package/dist/validators/min-length.js +4 -7
  173. package/dist/validators/min.d.ts +1 -1
  174. package/dist/validators/min.js +4 -7
  175. package/dist/validators/multiple-of.d.ts +1 -1
  176. package/dist/validators/multiple-of.js +11 -10
  177. package/dist/validators/non-negative.d.ts +1 -1
  178. package/dist/validators/non-negative.js +2 -5
  179. package/dist/validators/non-zero.d.ts +1 -1
  180. package/dist/validators/non-zero.js +4 -7
  181. package/dist/validators/past-date.d.ts +1 -1
  182. package/dist/validators/past-date.js +10 -13
  183. package/dist/validators/pattern.d.ts +1 -1
  184. package/dist/validators/pattern.js +5 -8
  185. package/dist/validators/phone.d.ts +1 -1
  186. package/dist/validators/phone.js +10 -11
  187. package/dist/validators/required.d.ts +1 -1
  188. package/dist/validators/required.js +4 -7
  189. package/dist/validators/url.d.ts +1 -1
  190. package/dist/validators/url.js +48 -17
  191. package/dist/validators.d.ts +1 -5
  192. package/dist/validators.js +98 -154
  193. package/docs/llms/01-api-reference.md +89 -0
  194. package/docs/llms/02-quick-start.md +181 -0
  195. package/docs/llms/03-api-signatures.md +494 -0
  196. package/docs/llms/04-common-patterns.md +204 -0
  197. package/docs/llms/05-common-mistakes.md +192 -0
  198. package/docs/llms/06-troubleshooting.md +16 -0
  199. package/docs/llms/07-complete-import.md +130 -0
  200. package/docs/llms/08-form-types.md +54 -0
  201. package/docs/llms/09-formschema.md +191 -0
  202. package/docs/llms/10-arrays.md +241 -0
  203. package/docs/llms/11-async-watchfield.md +53 -0
  204. package/docs/llms/12-array-cleanup.md +31 -0
  205. package/docs/llms/13-multi-step.md +162 -0
  206. package/docs/llms/14-extended-mistakes.md +101 -0
  207. package/docs/llms/15-project-structure.md +279 -0
  208. package/docs/llms/16-ui-components.md +229 -0
  209. package/docs/llms/17-nonexistent-api.md +278 -0
  210. package/docs/llms/18-conditional-fields.md +222 -0
  211. package/docs/llms/19-reading-values.md +100 -0
  212. package/docs/llms/20-compute-vs-watch.md +153 -0
  213. package/docs/llms/21-array-operations.md +163 -0
  214. package/docs/llms/22-cycle-detection.md +86 -0
  215. package/docs/llms/23-copy-from.md +141 -0
  216. package/docs/llms/24-sync-fields.md +115 -0
  217. package/docs/llms/25-reset-when.md +122 -0
  218. package/docs/llms/26-transform-value.md +136 -0
  219. package/docs/llms/27-revalidate-when.md +133 -0
  220. package/docs/llms/28-submit-and-reset.md +409 -0
  221. package/docs/llms/29-async-preload.md +203 -0
  222. package/docs/llms/30-type-safety-recipes.md +213 -0
  223. package/docs/llms/31-async-validator-debounce.md +178 -0
  224. package/docs/llms/32-async-options-loading.md +108 -0
  225. package/docs/llms/33-validation-strategy.md +305 -0
  226. package/docs/llms/34-i18n.md +298 -0
  227. package/docs/llms/35-unified-contract.md +309 -0
  228. package/docs/llms/36-issue-messages.md +284 -0
  229. package/docs/llms/37-compiled-schema.md +332 -0
  230. package/docs/llms/38-form-flow.md +396 -0
  231. package/llms-index.json +1 -0
  232. package/llms.txt +10014 -3826
  233. package/package.json +77 -15
  234. package/dist/behaviors-node-DGEFMTQu.js +0 -18
  235. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  236. package/dist/date-utils-dTT_x_Be.js +0 -31
  237. package/dist/derived-registry-CluZCQ7U.js +0 -16
  238. package/dist/form/behaviors.d.ts +0 -133
  239. package/dist/form/error-handler.d.ts +0 -166
  240. package/dist/form/factories/index.d.ts +0 -6
  241. package/dist/form/factories/node-factory.d.ts +0 -188
  242. package/dist/form/form-submitter.d.ts +0 -125
  243. package/dist/form/nodes/array-node.d.ts +0 -360
  244. package/dist/form/nodes/field-node.d.ts +0 -294
  245. package/dist/form/nodes/form-node.d.ts +0 -340
  246. package/dist/form/nodes/group-node.d.ts +0 -288
  247. package/dist/form/nodes/model-array-node.d.ts +0 -121
  248. package/dist/form/status-machine.d.ts +0 -153
  249. package/dist/form/types/deep-schema.d.ts +0 -116
  250. package/dist/form/types/form-proxy.d.ts +0 -141
  251. package/dist/form/unique-id.d.ts +0 -53
  252. package/dist/form/validation/validators/date-utils.d.ts +0 -30
  253. package/dist/form/validation/validators/email.d.ts +0 -23
  254. package/dist/form/validation/validators/future-date.d.ts +0 -23
  255. package/dist/form/validation/validators/integer.d.ts +0 -22
  256. package/dist/form/validation/validators/is-date.d.ts +0 -23
  257. package/dist/form/validation/validators/is-number.d.ts +0 -24
  258. package/dist/form/validation/validators/max-age.d.ts +0 -25
  259. package/dist/form/validation/validators/max-date.d.ts +0 -25
  260. package/dist/form/validation/validators/max-files.d.ts +0 -26
  261. package/dist/form/validation/validators/max-length.d.ts +0 -27
  262. package/dist/form/validation/validators/max-total-file-size.d.ts +0 -26
  263. package/dist/form/validation/validators/max.d.ts +0 -25
  264. package/dist/form/validation/validators/min-age.d.ts +0 -25
  265. package/dist/form/validation/validators/min-date.d.ts +0 -25
  266. package/dist/form/validation/validators/min-files.d.ts +0 -26
  267. package/dist/form/validation/validators/min-length.d.ts +0 -27
  268. package/dist/form/validation/validators/min.d.ts +0 -25
  269. package/dist/form/validation/validators/multiple-of.d.ts +0 -24
  270. package/dist/form/validation/validators/non-negative.d.ts +0 -22
  271. package/dist/form/validation/validators/non-zero.d.ts +0 -22
  272. package/dist/form/validation/validators/past-date.d.ts +0 -23
  273. package/dist/form/validation/validators/pattern.d.ts +0 -33
  274. package/dist/form/validation/validators/required.d.ts +0 -32
  275. package/dist/form/validation/validators/url.d.ts +0 -43
  276. package/dist/form/validation-schema.d.ts +0 -106
  277. package/dist/form/validation-strategy.d.ts +0 -55
  278. package/dist/form/validators.d.ts +0 -6
  279. package/dist/form-model-DYCzGGKF.js +0 -286
  280. package/dist/state/index.d.ts +0 -31
  281. package/dist/state/subscription-manager.d.ts +0 -180
  282. package/dist/state/types.d.ts +0 -144
  283. package/dist/state.d.ts +0 -2
  284. package/dist/state.js +0 -21
  285. package/dist/subscription-manager-705bls97.js +0 -171
@@ -1,28 +1,24 @@
1
- export { required } from './validators/required';
2
- export { min } from './validators/min';
3
- export { max } from './validators/max';
4
- export { minLength } from './validators/min-length';
5
- export { maxLength } from './validators/max-length';
6
- export { email } from './validators/email';
7
- export { pattern } from './validators/pattern';
8
- export { url } from './validators/url';
9
- export { phone, type PhoneFormat } from './validators/phone';
10
- export { isNumber } from './validators/is-number';
11
- export { integer } from './validators/integer';
12
- export { multipleOf } from './validators/multiple-of';
13
- export { nonNegative } from './validators/non-negative';
14
- export { nonZero } from './validators/non-zero';
15
- export { isDate } from './validators/is-date';
16
- export { minDate } from './validators/min-date';
17
- export { maxDate } from './validators/max-date';
18
- export { pastDate } from './validators/past-date';
19
- export { futureDate } from './validators/future-date';
20
- export { minAge } from './validators/min-age';
21
- export { maxAge } from './validators/max-age';
22
- export { maxFileSize } from './validators/max-file-size';
23
- export { minFileSize } from './validators/min-file-size';
24
- export { fileType } from './validators/file-type';
25
- export { maxFiles } from './validators/max-files';
26
- export { minFiles } from './validators/min-files';
27
- export { maxTotalFileSize } from './validators/max-total-file-size';
28
- export { isFileLike, matchesFileAccept, toFileArray, type FileLike } from './validators/file-utils';
1
+ /**
2
+ * Декларативный контракт СХЕМЫ ВАЛИДАЦИИ — `@reformer/core/validation`.
3
+ *
4
+ * Схема валидации — обычная функция `({ model, cross }) => void`. Внутри вызываются свободные операторы
5
+ * (`validate`/`validateAsync`/`validateWhen`/`apply`/`applyEach`) и `cross` области, которые САМИ пишут ошибки в
6
+ * ambient-сток текущего прогона — автор не видит ни коллектора, ни `getNodeForSignal`, ни `.push`.
7
+ * Раннер открывает ambient-окно на время СИНХРОННОГО прогона схемы и дожидается async-правил
8
+ * ({@link runValidation} — сбор результата), затем разносит ошибки по нодам формы, гася поля, ставшие
9
+ * валидными ({@link applyValidationResult}); {@link validateModel} делает оба шага и отвечает `boolean`.
10
+ *
11
+ * Зеркалит контракт поведения (`@reformer/core/behaviors`): тот же ambient-стиль, но отдельный слой —
12
+ * валидация НЕ реактивна (прогон по требованию: submit/шаг), поведение — реактивно (живые подписки).
13
+ *
14
+ * Модуль разложен по зонам ответственности: `./types` — контракт правил и схемы, `./context` —
15
+ * ambient-сток прогона, `./operators` — операторы схемы, `./run` — раннер, `./strategy` — КОГДА
16
+ * запускать (реэкспортируется отсюда, тот же сабпат).
17
+ *
18
+ * @group Validation
19
+ * @module form/validation
20
+ */
21
+ export type { Rule, AsyncRule, ValidationDefinition, ValidationScope, ValidationStatus, ValidationFailure, ValidationResult, } from './types';
22
+ export { validate, validateAsync, validateWhen, apply, applyEach } from './operators';
23
+ export { defineValidation, runValidation, applyValidationResult, validateModel } from './run';
24
+ export * from './strategy';
@@ -0,0 +1,101 @@
1
+ import { FormModel, ModelArray, PathAwareSignal } from '../../model/types';
2
+ import { AsyncRule, Rule, ValidationDefinition, ValidationScope } from './types';
3
+ /**
4
+ * Синхронные правила поля.
5
+ *
6
+ * Поле — лист либо массив целиком: `model.$.<массив>` — такая же ручка значения, и правило
7
+ * получает массив (мультивыбор, теги, список файлов).
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * validate(model.$.loanAmount, [required({ code: 'loanAmount.required' }), min(50000)]);
12
+ * validate(model.$.tags, [required(), maxLength(3)]); // tags: string[] — массив как значение
13
+ * ```
14
+ */
15
+ export declare function validate<TField>(sig: PathAwareSignal<TField>, rules: Rule<TField>[]): void;
16
+ /**
17
+ * Асинхронные правила поля. Раннер дожидается их и прокидывает `AbortSignal` для отмены
18
+ * устаревших ответов.
19
+ *
20
+ * Отклонённое правило (сеть, исключение) — сбой проверки: он попадает в `failures` результата
21
+ * прогона, статус прогона становится `error`, а на поле ложится блокирующая ошибка `ruleFailed`
22
+ * (текст — из словаря локали). Отмена устаревшего прогона сбоем не считается.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * validateAsync(model.$.username, [async (v, { signal }) => {
27
+ * const r = await fetch(`/api/free?u=${v}`, { signal });
28
+ * return (await r.json()).free ? null : { code: 'taken' };
29
+ * }]);
30
+ * ```
31
+ */
32
+ export declare function validateAsync<TField>(sig: PathAwareSignal<TField>, rules: AsyncRule<TField>[]): void;
33
+ /**
34
+ * Условная валидация: правила внутри `cb` активны, только пока `cond()` истинно; иначе их поля
35
+ * ГАСЯТСЯ (пустой bucket → `setIssues([])`). Не трогает включение/сброс поля — это дело поведения (`enableWhen`).
36
+ */
37
+ export declare function validateWhen(cond: () => boolean, cb: () => void): void;
38
+ /**
39
+ * Область схемы над моделью: сама модель и `cross`, снимок которого — эта модель.
40
+ *
41
+ * @internal
42
+ */
43
+ export declare function scopeOf<T>(model: FormModel<T>): ValidationScope<T>;
44
+ /** Привязка к группе: ручка `model.$.<группа>` (тип значения — из её `peek()`). */
45
+ interface GroupBinding<V> {
46
+ peek(): V;
47
+ }
48
+ /** Привязка к массиву под-форм: ручка `model.$.<массив>`. */
49
+ interface ArrayBinding<U> {
50
+ peek(): readonly U[] | null | undefined;
51
+ }
52
+ /**
53
+ * Применить схему к КАЖДОМУ элементу массива под-форм. Схема элемента — обычная
54
+ * `ValidationDefinition` над типом элемента: тот же набор правил подключается и к группе через
55
+ * {@link apply}.
56
+ *
57
+ * У каждого элемента своя область: `cross` внутри схемы получает снапшот элемента.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const propertyRules = defineValidation<Property>(({ model, cross }) => {
62
+ * validate(model.$.type, [required()]);
63
+ * cross(model.$.estimatedValue, (property) =>
64
+ * property.hasEncumbrance && property.estimatedValue < 100_000 ? tooCheap : null
65
+ * );
66
+ * });
67
+ *
68
+ * applyEach(model.$.properties, propertyRules);
69
+ * ```
70
+ */
71
+ export declare function applyEach<U extends object>(array: ArrayBinding<U> | ModelArray<U>, schema: ValidationDefinition<U>): void;
72
+ /**
73
+ * Подключить схему подформы к группе модели — одной или нескольким.
74
+ *
75
+ * Схема запускается в области группы: `model` в ней — под-модель, `cross` получает снапшот
76
+ * группы. Привязка — ручка `model.$.<группа>`, как у узла-подформы схемы и у `apply` поведения.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * const addressRules = defineValidation<Address>(({ model }) => {
81
+ * validate(model.$.city, [required()]);
82
+ * });
83
+ *
84
+ * apply(model.$.registrationAddress, addressRules);
85
+ * validateWhen(
86
+ * () => !model.sameAsRegistration,
87
+ * () => apply(model.$.residenceAddress, addressRules)
88
+ * );
89
+ * ```
90
+ */
91
+ export declare function apply<V extends object | null | undefined>(group: GroupBinding<V> | GroupBinding<V>[], schema: ValidationDefinition<NonNullable<V>>): void;
92
+ /**
93
+ * Композиция схем над ТОЙ ЖЕ моделью области — так форма собирается из схем шагов.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const formRules = defineValidation<Form>(() => apply(loanRules, contactsRules));
98
+ * ```
99
+ */
100
+ export declare function apply<T>(...schemas: ValidationDefinition<T>[]): void;
101
+ export {};
@@ -0,0 +1,120 @@
1
+ import { FormModel } from '../../model/types';
2
+ import { ValidationResult, ValidationDefinition } from './types';
3
+ /**
4
+ * Тонкая identity-обёртка для типизации/discoverability (как `defineFormBehavior`). Возвращает схему как есть.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * export const step1 = defineValidation<LoanForm>(({ model }) => {
9
+ * validate(model.$.loanAmount, [required(), min(50000)]);
10
+ * });
11
+ * ```
12
+ */
13
+ export declare function defineValidation<T>(schema: ValidationDefinition<T>): ValidationDefinition<T>;
14
+ /**
15
+ * Отменить идущий прогон пары (model, schema): его `AbortSignal` срабатывает, ожидание async-правил
16
+ * размыкается, результат получает статус `cancelled` и по нодам не разносится. Контроллер живой
17
+ * проверки зовёт это при снятии — иначе ответ правила лёг бы на ноды уже снятой формы.
18
+ *
19
+ * @internal
20
+ */
21
+ export declare function cancelRun(model: object, schema: object): void;
22
+ /**
23
+ * Какие поля пометить тронутыми при разносе: `false` — никакие, `true` — проверенные правилами,
24
+ * `'all'` — проверенные, а при отказе (`invalid`, `error`) ещё и всю форму.
25
+ *
26
+ * @internal
27
+ */
28
+ export type ValidationTouch = boolean | 'all';
29
+ /** Идёт ли сейчас касание полей от имени проверки ({@link touchByValidation}). @internal */
30
+ export declare const isTouchByValidation: () => boolean;
31
+ /**
32
+ * Собрать результат проверки модели схемой — без формы и без побочных эффектов на нодах.
33
+ *
34
+ * Открывает ambient-окно на время синхронного прогона `schema`, дожидается async-правил и
35
+ * возвращает {@link ValidationResult}. Нод формы не трогает: ошибки по ним разносит
36
+ * {@link applyValidationResult}. Подходит для проверки данных без UI (сервер, тест, предпросмотр).
37
+ *
38
+ * Статус результата:
39
+ * - `cancelled` — прогон устарел: пока он шёл, для той же пары (model, schema) запущен следующий;
40
+ * - `error` — хотя бы одно async-правило не вернуло результат (сеть, исключение) — см. `failures`;
41
+ * - `invalid` — есть блокирующие ошибки (`severity: 'warning'` не блокирует);
42
+ * - `valid` — блокирующих ошибок нет.
43
+ *
44
+ * ⚠️ `schema` должна быть СТАБИЛЬНОЙ ссылкой: отмена устаревших прогонов ключится по идентичности
45
+ * `schema`. Держите схемы в `const` / `defineValidation`.
46
+ *
47
+ * @param model - Модель данных формы.
48
+ * @param schema - Схема валидации (стабильная ссылка).
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const result = await runValidation(model, formRules);
53
+ * if (result.status === 'error') showToast('Не удалось проверить форму, повторите попытку');
54
+ * else if (result.status !== 'cancelled') applyValidationResult(result, { touch: true });
55
+ * ```
56
+ */
57
+ export declare function runValidation<T>(model: FormModel<T>, schema: ValidationDefinition<T>): Promise<ValidationResult>;
58
+ /**
59
+ * Разнести собранный результат по нодам формы: поставить ошибки проверенным полям и погасить поля,
60
+ * которые были с ошибкой в прошлом разнесённом прогоне той же пары (model, schema), а теперь не
61
+ * проверялись (удалённая строка массива, выключенная ветка).
62
+ *
63
+ * Отменённый результат (`status: 'cancelled'`) не разносится: его async-часть оборвана.
64
+ *
65
+ * `options.touch`: пометить `touched` ИМЕННО поля, которые схема проверяла (не всё поддерево):
66
+ * поля выключенной ветки `validateWhen` и, в проверке уровня формы, выключенные поля не метятся —
67
+ * пользователь их не видел, и после включения их ошибка не должна быть видна с первой проверки.
68
+ * Замечания становятся видимыми (`shouldShowIssues`: есть замечания и `touched || dirty`) без
69
+ * ручного `form.markAsTouched()`; при пошаговой валидации следующий шаг не показывается
70
+ * «тронутым».
71
+ *
72
+ * @param result - Результат {@link runValidation}.
73
+ * @param options - `{ touch }` — пометить проверенные поля `touched` для показа ошибок.
74
+ */
75
+ export declare function applyValidationResult(result: ValidationResult, options?: {
76
+ touch?: boolean;
77
+ }): void;
78
+ /**
79
+ * Прогон с разносом и полным результатом — общий движок {@link validateModel}, контроллера
80
+ * стратегии и прогонов шага. На время async-правил держит ноды их полей в `pending`.
81
+ * `skipDisabled` — проверка уровня формы: поля выключенных нод не учитываются.
82
+ *
83
+ * `touch`: `true` — пометить тронутыми проверенные поля; `'all'` — то же, а при отказе (`invalid`,
84
+ * `error`) ещё и всю форму: так попытка отправки раскрывает замечания полей, которых правила не
85
+ * касались. Отменённый результат не разносится и ничего не трогает.
86
+ *
87
+ * @internal
88
+ */
89
+ export declare function runAndApply<T>(model: FormModel<T>, schema: ValidationDefinition<T>, options?: {
90
+ touch?: ValidationTouch;
91
+ skipDisabled?: boolean;
92
+ }): Promise<ValidationResult>;
93
+ /**
94
+ * Провалидировать модель ЛЮБОЙ схемой (шаг или вся форма): собрать результат и разнести ошибки по
95
+ * нодам формы. Возвращает `true` только для статуса `valid`: ошибки, сбой правила и отмена дают
96
+ * `false` (**fail-closed**). Чтобы различить их, берите {@link runValidation} либо
97
+ * `bundle.validate()` / `bundle.validateStep(id)` сборки формы.
98
+ *
99
+ * Гашение — на пару (model, schema): `validateModel(model, step2)` и `validateModel(model, form)`
100
+ * не мешают друг другу. Устаревший прогон (быстрый повторный вызов той же (model, schema))
101
+ * отменяется через `AbortSignal`.
102
+ *
103
+ * ⚠️ `schema` должна быть СТАБИЛЬНОЙ ссылкой: отмена устаревших прогонов ключится по идентичности
104
+ * `schema`. Инлайн-стрелка (`validateModel(model, ({ model }) => …)`) каждый раз создаёт НОВЫЙ
105
+ * прогон без дедупликации — держите схемы в `const` / `defineValidation`.
106
+ *
107
+ * @param model - Модель данных формы.
108
+ * @param schema - Схема валидации (стабильная ссылка).
109
+ * @param options - `{ touch }` — пометить провалидированные поля `touched` для показа ошибок.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const ok = await validateModel(model, step2Validation); // один шаг
114
+ * const all = await validateModel(model, formValidation); // вся форма
115
+ * const stepOk = await validateModel(model, step2Validation, { touch: true }); // + показать ошибки шага
116
+ * ```
117
+ */
118
+ export declare function validateModel<T>(model: FormModel<T>, schema: ValidationDefinition<T>, options?: {
119
+ touch?: boolean;
120
+ }): Promise<boolean>;
@@ -0,0 +1,89 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { FormModel } from '../../model/types';
3
+ import { ValidationResult, ValidationDefinition } from './types';
4
+ /**
5
+ * Когда запускается schema-валидация:
6
+ * - `submit` (по умолчанию) — только явным прогоном (`validate()`, отправка);
7
+ * - `blur` — при потере фокуса поля (каждый уход из любого листа, и повторный тоже);
8
+ * - `change` — на каждый ввод (с опц. `debounce`);
9
+ * - `afterFirstSubmit` — тихо до первой попытки отправки (`run({ submit: true })`), затем live
10
+ * (см. `liveAfterSubmit`). Проверка без отправки (`validate()`) фазу не переключает.
11
+ */
12
+ export type ValidationStrategyKind = 'submit' | 'blur' | 'change' | 'afterFirstSubmit';
13
+ export interface ValidationStrategyOptions {
14
+ /** Стратегия запуска. Default `'submit'`. */
15
+ strategy?: ValidationStrategyKind;
16
+ /** Debounce (мс) для live-фаз (`change` и live-часть `afterFirstSubmit`). Default `0`. */
17
+ debounce?: number;
18
+ /** Режим live-фазы для `afterFirstSubmit`. Default `'change'`. */
19
+ liveAfterSubmit?: 'change' | 'blur';
20
+ }
21
+ /**
22
+ * Опции полного прогона {@link FormValidationController.run}.
23
+ */
24
+ export interface FormValidationRunOptions {
25
+ /**
26
+ * Какие поля пометить тронутыми: `true` (по умолчанию) — проверенные правилами; `'all'` — они же,
27
+ * а при отказе (`invalid`, `error`) ещё и вся форма.
28
+ */
29
+ touch?: true | 'all';
30
+ /**
31
+ * Прогон — попытка отправки: переводит `afterFirstSubmit` в живую фазу. Проверка без отправки
32
+ * (`validate()`, кнопка «Проверить») фазу не трогает.
33
+ */
34
+ submit?: boolean;
35
+ }
36
+ export interface FormValidationController {
37
+ /**
38
+ * Полный прогон схемы с раскрытием замечаний проверенных полей (`touch: true`). Попыткой
39
+ * отправки не считается: `afterFirstSubmit` остаётся в тихой фазе. Возвращает `true` только для
40
+ * статуса `valid`: ошибки, сбой правила и отмена дают `false`.
41
+ */
42
+ validate(): Promise<boolean>;
43
+ /**
44
+ * Полный прогон с полным результатом: по статусу отличают ошибки (`invalid`) от сбоя правила
45
+ * (`error`) и отмены (`cancelled`). Без опций — тот же прогон, что
46
+ * {@link FormValidationController.validate}; `{ submit: true }` — попытка отправки,
47
+ * `{ touch: 'all' }` — при отказе раскрыть всю форму.
48
+ */
49
+ run(options?: FormValidationRunOptions): Promise<ValidationResult>;
50
+ /**
51
+ * Армировать реактивные подписки стратегии. Идемпотентно. Возвращает `dispose`. НЕ звать при SSR.
52
+ *
53
+ * После {@link FormValidationController.dispose} контроллер армируется ЗАНОВО: React монтирует
54
+ * компоненты повторно (StrictMode в разработке, remount по роуту), и «одноразовый» контроллер
55
+ * после такого цикла молча оставался бы без живой валидации.
56
+ */
57
+ start(): () => void;
58
+ /**
59
+ * Снять подписки и таймеры, отменить идущий прогон (его `validate()` или `run()` получит
60
+ * `cancelled`). Идемпотентно; после него `start()` снова армирует стратегию.
61
+ */
62
+ dispose(): void;
63
+ /** Идёт ли прогон (submit или live) — снапшот. */
64
+ readonly isValidating: boolean;
65
+ /** Реактивный сигнал состояния прогона (для тонкой подписки в UI). */
66
+ readonly validating: ReadonlySignal<boolean>;
67
+ }
68
+ /**
69
+ * Собрать контроллер валидации формы с выбранной стратегией запуска. Движок прогона — тот же, что у
70
+ * `validateModel`.
71
+ *
72
+ * Фабрика ЧИСТАЯ до `start()` (никаких подписок) — безопасна для SSR/headless. Реактивные триггеры
73
+ * арминуются только в `start()` (в React — из `useEffect`).
74
+ *
75
+ * @typeParam T - Форма данных модели.
76
+ * @param model - Модель данных.
77
+ * @param schema - Схема валидации. **Стабильная ссылка** (иначе устаревший прогон не отменится).
78
+ * @param options - {@link ValidationStrategyOptions}.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const ctrl = createFormValidation(model, schema, { strategy: 'afterFirstSubmit', debounce: 300 });
83
+ * const dispose = ctrl.start(); // на клиенте
84
+ * const ok = await ctrl.validate(); // проверка: замечания проверенных полей
85
+ * const sent = await ctrl.run({ touch: 'all', submit: true }); // попытка отправки: живая фаза
86
+ * dispose();
87
+ * ```
88
+ */
89
+ export declare function createFormValidation<T>(model: FormModel<T>, schema: ValidationDefinition<T>, options?: ValidationStrategyOptions): FormValidationController;
@@ -0,0 +1,83 @@
1
+ import { FormModel, PathAwareSignal } from '../../model/types';
2
+ import { ValidationIssue } from '../types/contracts';
3
+ /**
4
+ * Синхронное правило поля типа `TField`: получает значение, возвращает ошибку или `null`.
5
+ *
6
+ * Правило, написанное для более широкого типа, подходит полю с более узким: `min()` принимает
7
+ * `number | null | undefined` и годится и полю `number`, и полю `number | null`; `required()`
8
+ * принимает любое значение. Обратное — ошибка компиляции: `validate(model.$.age, [email()])`
9
+ * подсветится, потому что `email` ждёт строку, а поле числовое.
10
+ *
11
+ * Правило над несколькими полями — оператор `cross` области схемы: он получает снимок модели.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const AMOUNT_RULES: Rule<number | null>[] = [required(), min(50000)];
16
+ * const notReserved: Rule<string> = (value) =>
17
+ * value === 'admin' ? { code: 'reserved' } : null;
18
+ * ```
19
+ */
20
+ export type Rule<TField> = (value: TField) => ValidationIssue | null;
21
+ /**
22
+ * Асинхронное правило поля. Получает `AbortSignal` для отмены устаревших ответов (быстрый повторный
23
+ * прогон той же схемы отменяет предыдущий).
24
+ *
25
+ * Отклонённое правило (сеть, исключение) — это сбой проверки, а не «ошибок нет»: прогон получает
26
+ * статус `error`, поле — блокирующую ошибку `ruleFailed`. Если недоступность сервиса не должна
27
+ * мешать отправке, ловите ошибку в правиле и возвращайте `null` — это решение автора правила.
28
+ */
29
+ export type AsyncRule<TField> = (value: TField, ctx: {
30
+ signal: AbortSignal;
31
+ }) => Promise<ValidationIssue | null>;
32
+ /**
33
+ * Область схемы валидации — то, что схема получает аргументом.
34
+ *
35
+ * В корне прогона область — вся форма; в схеме, подключённой через `apply` / `applyEach`, —
36
+ * под-модель группы или строки массива. Поэтому правила подформы пишутся один раз и не знают, где
37
+ * она стоит.
38
+ */
39
+ export interface ValidationScope<T> {
40
+ /** Модель области. Привязки правил — через её `$`: `validate(model.$.email, [...])`. */
41
+ readonly model: FormModel<T>;
42
+ /**
43
+ * Правило над несколькими полями: `check` получает снимок модели области (`model.get()`) и
44
+ * вешает ошибку на поле `handle`. Тип снимка выведен из схемы — приводить его не нужно.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * defineValidation<Loan>(({ model, cross }) => {
49
+ * cross(model.$.initialPayment, (loan) =>
50
+ * loan.initialPayment > loan.propertyValue ? tooHigh : null
51
+ * );
52
+ * });
53
+ * ```
54
+ */
55
+ cross(handle: PathAwareSignal<unknown>, check: (snapshot: T) => ValidationIssue | null): void;
56
+ }
57
+ /** Схема валидации — обычная функция над областью. First-class значение (можно `apply`/тестировать/переиспользовать). */
58
+ export type ValidationDefinition<T> = (scope: ValidationScope<T>) => void;
59
+ /**
60
+ * Чем закончился прогон схемы:
61
+ * - `valid` — блокирующих ошибок нет (`severity: 'warning'` не блокирует);
62
+ * - `invalid` — есть блокирующие ошибки;
63
+ * - `error` — хотя бы одно async-правило не вернуло результат (сеть, исключение);
64
+ * - `cancelled` — прогон устарел: для той же пары (model, schema) запущен следующий.
65
+ *
66
+ * Приоритет при нескольких причинах: `cancelled` → `error` → `invalid` → `valid`.
67
+ */
68
+ export type ValidationStatus = 'valid' | 'invalid' | 'cancelled' | 'error';
69
+ /** Правило, которое не вернуло результат. */
70
+ export interface ValidationFailure {
71
+ /** Ручка поля `model.$.…`, чьё правило отклонилось. */
72
+ readonly handle: PathAwareSignal<unknown>;
73
+ /** То, чем правило отклонилось (ошибка сети, исключение). */
74
+ readonly error: unknown;
75
+ }
76
+ /** Результат прогона схемы валидации — см. `runValidation`. */
77
+ export interface ValidationResult {
78
+ readonly status: ValidationStatus;
79
+ /** Ошибки по ручке поля `model.$.…`; путь — метаданные ручки (`handle.__path`). */
80
+ readonly issues: ReadonlyMap<PathAwareSignal<unknown>, readonly ValidationIssue[]>;
81
+ /** Правила, которые не вернули результат (сеть, исключение). */
82
+ readonly failures: readonly ValidationFailure[];
83
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Утилиты для работы с датами в валидаторах
3
+ *
4
+ * @group Validation
5
+ * @category Validators
6
+ * @module form/validators/date-utils
7
+ */
8
+ /**
9
+ * Парсит значение в объект Date. Строгий формат: `Date` либо строка `YYYY-MM-DD` (значение
10
+ * `<input type="date">`); любая другая строка — не дата. Разбор движка (`new Date(строка)`) не
11
+ * используется: он угадывает дату по обрывкам (`'hello 1'`, `'March 7'`), и результат зависит от
12
+ * движка.
13
+ *
14
+ * Строка `YYYY-MM-DD` — локальная полночь этого дня: `new Date('2024-01-01')` дал бы полночь UTC,
15
+ * и западнее UTC правила дат сравнивали бы предыдущий день. Несуществующая дата (`2023-02-29`)
16
+ * не разбирается — движок перенёс бы её на 1 марта.
17
+ *
18
+ * @param value - Значение для парсинга (Date, string или undefined)
19
+ * @returns Date объект или null если парсинг не удался
20
+ */
21
+ export declare function parseDate(value: string | Date | null | undefined): Date | null;
22
+ /**
23
+ * Возвращает сегодняшнюю дату с обнулённым временем
24
+ */
25
+ export declare function getToday(): Date;
26
+ /**
27
+ * Нормализует дату, обнуляя время
28
+ */
29
+ export declare function normalizeDate(date: Date): Date;
30
+ /** Дата рождения позже сегодняшнего дня. */
31
+ export declare function isFutureBirth(birthDate: Date): boolean;
32
+ /**
33
+ * Вычисляет полный возраст (в годах) по календарным полям.
34
+ *
35
+ * Считаем по годам/месяцам/дням, а не делением миллисекунд на усреднённый год (365.25 дн): усреднение
36
+ * даёт off-by-one на границе — в день N-летия накопленных реальных дней меньше `N * 365.25`, и `floor`
37
+ * роняет возраст до `N - 1` (т.е. `minAge(18)` ошибочно отклонял 18-летнего в его день рождения).
38
+ *
39
+ * Родившийся 29 февраля в невисокосный год становится на год старше 28 февраля: срок, исчисляемый
40
+ * годами, истекает в последний день месяца, если соответствующего числа в нём нет (ГК РФ, ст. 192).
41
+ */
42
+ export declare function calculateAge(birthDate: Date): number;
@@ -0,0 +1,23 @@
1
+ import { RuleOptions } from '../types/validation-schema';
2
+ import { Rule } from '../validation/types';
3
+ /**
4
+ * Фабрика валидатора формата email.
5
+ *
6
+ * Проверяет по упрощённому regex `^[^\s@]+@[^\s@]+\.[^\s@]+$`. Пустые значения
7
+ * (`''`/`null`/`undefined`) пропускаются (используйте {@link required} для обязательности).
8
+ *
9
+ * @param options - Опции валидатора ({@link RuleOptions}): `code`, `params`
10
+ * @returns Правило {@link Rule} для строкового поля
11
+ *
12
+ * @example Проверка формата email
13
+ * ```typescript
14
+ * import { defineValidation, validate } from '@reformer/core/validation';
15
+ * import { required, email } from '@reformer/core/validators';
16
+ *
17
+ * // Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
18
+ * const validation = defineValidation<MyForm>(({ model }) => {
19
+ * validate(model.$.email, [required(), email({ code: 'email.email' })]);
20
+ * });
21
+ * ```
22
+ */
23
+ export declare function email(options?: RuleOptions): Rule<string | null | undefined>;
@@ -1,4 +1,5 @@
1
- import { Validator, ValidateOptions } from '../../types/validation-schema';
1
+ import { RuleOptions } from '../types/validation-schema';
2
+ import { Rule } from '../validation/types';
2
3
  /**
3
4
  * Фабрика валидатора типа файла.
4
5
  *
@@ -12,20 +13,19 @@ import { Validator, ValidateOptions } from '../../types/validation-schema';
12
13
  * пропускаются (используйте {@link required} для обязательности).
13
14
  *
14
15
  * @param accept - Accept-строка допустимых типов, например `'image/*,.pdf'`
15
- * @param options - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
16
+ * @param options - Опции валидатора ({@link RuleOptions}). В `params` ошибки автоматически
16
17
  * попадают `accept` и `fileName` (первый нарушивший файл).
17
- * @returns Чистый валидатор {@link Validator} для файла или массива файлов
18
+ * @returns Правило {@link Rule} для файла или массива файлов
18
19
  *
19
20
  * @example Только изображения и PDF
20
21
  * ```typescript
22
+ * import { defineValidation, validate } from '@reformer/core/validation';
21
23
  * import { fileType } from '@reformer/core/validators';
22
24
  *
23
- * // Внутри FieldConfig схемы формы:
24
- * documents: {
25
- * value: model.$.documents,
26
- * component: FileUploadField,
27
- * validators: [fileType('image/*,.pdf', { message: 'Только изображения или PDF' })],
28
- * },
25
+ * // Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
26
+ * const validation = defineValidation<MyForm>(({ model }) => {
27
+ * validate(model.$.documents, [fileType('image/*,.pdf', { code: 'documents.fileType' })]);
28
+ * });
29
29
  * ```
30
30
  */
31
- export declare function fileType<TForm = unknown, TField = unknown>(accept: string, options?: ValidateOptions): Validator<TForm, TField>;
31
+ export declare function fileType(accept: string, options?: RuleOptions): Rule<unknown>;
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @group Validation
9
9
  * @category Validators
10
- * @module validators/file-utils
10
+ * @module form/validators/file-utils
11
11
  */
12
12
  /**
13
13
  * Минимальный файлоподобный контракт, с которым работают file-валидаторы:
@@ -39,7 +39,8 @@ export declare function toFileArray(value: unknown): FileLike[] | null;
39
39
  /**
40
40
  * Проверяет файл против accept-паттерна в синтаксисе нативного атрибута
41
41
  * `<input type="file" accept>`: список через запятую из расширений (`.pdf`),
42
- * точных MIME (`image/png`) и wildcard-категорий (`image/*`). Регистронезависимо.
42
+ * точных MIME (`image/png`) и wildcard-категорий (`image/*`); шаблон «любой тип» (звёздочка,
43
+ * слэш, звёздочка) пропускает любой файл. Регистронезависимо.
43
44
  *
44
45
  * Нужен собственный матчер, потому что `accept` у нативного input — только подсказка
45
46
  * пикеру: на drag-and-drop и на программное значение он не действует.
@@ -0,0 +1,23 @@
1
+ import { RuleOptions } from '../types/validation-schema';
2
+ import { Rule } from '../validation/types';
3
+ /**
4
+ * Фабрика валидатора, проверяющего что дата не в прошлом.
5
+ *
6
+ * Дата не должна быть раньше сегодняшнего дня (сравнение по нормализованным датам).
7
+ * Пустые и невалидные даты пропускаются (используйте {@link required} и {@link isDate}).
8
+ *
9
+ * @param options - Опции валидатора ({@link RuleOptions}): `code`, `params`
10
+ * @returns Правило {@link Rule} для поля даты (`string | Date`)
11
+ *
12
+ * @example Дата не в прошлом
13
+ * ```typescript
14
+ * import { defineValidation, validate } from '@reformer/core/validation';
15
+ * import { required, futureDate } from '@reformer/core/validators';
16
+ *
17
+ * // Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
18
+ * const validation = defineValidation<MyForm>(({ model }) => {
19
+ * validate(model.$.appointmentDate, [required(), futureDate({ code: 'appointmentDate.futureDate' })]);
20
+ * });
21
+ * ```
22
+ */
23
+ export declare function futureDate(options?: RuleOptions): Rule<string | Date | null | undefined>;
@@ -1,9 +1,12 @@
1
1
  /**
2
- * Reusable validator factories.
2
+ * Точка входа сабпата `@reformer/core/validators` — все фабрики правил одним импортом.
3
3
  *
4
- * Все примитивы — фабрики, возвращающие чистый Validator<TForm, TField>.
5
- * Импортируются из `@reformer/core/validators` и передаются в массив `validators`
6
- * поля схемы формы: `validators: [required(), min(18)]`.
4
+ * Все примитивы — фабрики, возвращающие чистый `Validator<TForm, TField>`; передаются вторым
5
+ * аргументом оператора `validate` внутри схемы валидации: `validate(model.$.age, [required(), min(18)])`.
6
+ * В layout-схему формы правила НЕ кладутся — поля `validators` у узла нет.
7
+ *
8
+ * Гранулярные сабпаты (`@reformer/core/validators/required` и ещё 20) собираются отдельными
9
+ * entry для tree-shaking; этот модуль отдаёт полный набор для случаев, когда правил много.
7
10
  */
8
11
  export { required } from './required';
9
12
  export { min } from './min';