@reformer/core 1.1.0 → 2.0.0-beta.11

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 (221) hide show
  1. package/dist/behaviors/compute-from.d.ts +2 -0
  2. package/dist/behaviors/compute-from.js +31 -0
  3. package/dist/behaviors/copy-from.d.ts +2 -0
  4. package/dist/behaviors/copy-from.js +29 -0
  5. package/dist/behaviors/enable-when.d.ts +2 -0
  6. package/dist/behaviors/enable-when.js +25 -0
  7. package/dist/behaviors/reset-when.d.ts +2 -0
  8. package/dist/behaviors/reset-when.js +24 -0
  9. package/dist/behaviors/revalidate-when.d.ts +2 -0
  10. package/dist/behaviors/revalidate-when.js +18 -0
  11. package/dist/behaviors/sync-fields.d.ts +2 -0
  12. package/dist/behaviors/sync-fields.js +41 -0
  13. package/dist/behaviors/transform-value.d.ts +2 -0
  14. package/dist/behaviors/transform-value.js +45 -0
  15. package/dist/behaviors/watch-field.d.ts +2 -0
  16. package/dist/behaviors/watch-field.js +21 -0
  17. package/dist/behaviors.d.ts +6 -2
  18. package/dist/behaviors.js +27 -228
  19. package/dist/core/behavior/behavior-context.d.ts +32 -14
  20. package/dist/core/behavior/behavior-registry.d.ts +15 -27
  21. package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
  22. package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
  23. package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
  24. package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
  25. package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
  26. package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
  27. package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
  28. package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
  29. package/dist/core/behavior/compose-behavior.d.ts +2 -12
  30. package/dist/core/behavior/index.d.ts +0 -1
  31. package/dist/core/behavior/types.d.ts +2 -8
  32. package/dist/core/factories/node-factory.d.ts +6 -29
  33. package/dist/core/nodes/array-node.d.ts +42 -22
  34. package/dist/core/nodes/field-node.d.ts +51 -26
  35. package/dist/core/nodes/form-node.d.ts +18 -20
  36. package/dist/core/nodes/group-node.d.ts +37 -212
  37. package/dist/core/types/deep-schema.d.ts +2 -12
  38. package/dist/core/types/field-path.d.ts +1 -1
  39. package/dist/core/types/form-context.d.ts +36 -30
  40. package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
  41. package/dist/core/types/index.d.ts +52 -6
  42. package/dist/core/types/validation-schema.d.ts +3 -12
  43. package/dist/core/utils/abstract-registry.d.ts +74 -0
  44. package/dist/core/utils/aggregate-signals.d.ts +71 -0
  45. package/dist/core/utils/create-form.d.ts +3 -20
  46. package/dist/core/utils/error-handler.d.ts +1 -18
  47. package/dist/core/utils/field-path-navigator.d.ts +1 -1
  48. package/dist/core/{validation → utils}/field-path.d.ts +23 -6
  49. package/dist/core/utils/form-observer.d.ts +176 -0
  50. package/dist/core/utils/form-proxy-builder.d.ts +25 -0
  51. package/dist/core/utils/form-submitter.d.ts +121 -0
  52. package/dist/core/utils/index.d.ts +10 -2
  53. package/dist/core/utils/registry-helpers.d.ts +0 -7
  54. package/dist/core/utils/safe-effect.d.ts +73 -0
  55. package/dist/core/utils/status-machine.d.ts +153 -0
  56. package/dist/core/utils/type-guards.d.ts +5 -23
  57. package/dist/core/utils/unique-id.d.ts +53 -0
  58. package/dist/core/validation/core/apply-when.d.ts +3 -9
  59. package/dist/core/validation/core/apply.d.ts +2 -13
  60. package/dist/core/validation/core/validate-async.d.ts +2 -8
  61. package/dist/core/validation/core/validate-tree.d.ts +10 -10
  62. package/dist/core/validation/core/validate.d.ts +1 -7
  63. package/dist/core/validation/index.d.ts +8 -2
  64. package/dist/core/validation/validate-form.d.ts +1 -38
  65. package/dist/core/validation/validation-applicator.d.ts +2 -21
  66. package/dist/core/validation/validation-context.d.ts +67 -28
  67. package/dist/core/validation/validation-registry.d.ts +11 -25
  68. package/dist/core/validation/validators/array-validators.d.ts +2 -12
  69. package/dist/core/validation/validators/date-utils.d.ts +26 -0
  70. package/dist/core/validation/validators/email.d.ts +2 -9
  71. package/dist/core/validation/validators/future-date.d.ts +35 -0
  72. package/dist/core/validation/validators/index.d.ts +7 -1
  73. package/dist/core/validation/validators/is-date.d.ts +36 -0
  74. package/dist/core/validation/validators/max-age.d.ts +36 -0
  75. package/dist/core/validation/validators/max-date.d.ts +36 -0
  76. package/dist/core/validation/validators/max-length.d.ts +3 -10
  77. package/dist/core/validation/validators/max.d.ts +3 -10
  78. package/dist/core/validation/validators/min-age.d.ts +36 -0
  79. package/dist/core/validation/validators/min-date.d.ts +36 -0
  80. package/dist/core/validation/validators/min-length.d.ts +3 -10
  81. package/dist/core/validation/validators/min.d.ts +3 -10
  82. package/dist/core/validation/validators/number.d.ts +2 -9
  83. package/dist/core/validation/validators/past-date.d.ts +35 -0
  84. package/dist/core/validation/validators/pattern.d.ts +2 -9
  85. package/dist/core/validation/validators/phone.d.ts +2 -9
  86. package/dist/core/validation/validators/required.d.ts +2 -9
  87. package/dist/core/validation/validators/url.d.ts +2 -9
  88. package/dist/date-utils-xUWFslTj.js +29 -0
  89. package/dist/field-path-DuKdGcIE.js +66 -0
  90. package/dist/hooks/types.d.ts +328 -0
  91. package/dist/hooks/useArrayLength.d.ts +31 -0
  92. package/dist/hooks/useFormControl.d.ts +15 -39
  93. package/dist/hooks/useFormControlValue.d.ts +167 -0
  94. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  95. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  96. package/dist/index-D25LsbRm.js +73 -0
  97. package/dist/index.d.ts +8 -1
  98. package/dist/index.js +3271 -8
  99. package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
  100. package/dist/safe-effect-Dh8uw81c.js +20 -0
  101. package/dist/validate-C3XiA_zf.js +10 -0
  102. package/dist/validators/email.d.ts +2 -0
  103. package/dist/validators/email.js +13 -0
  104. package/dist/validators/future-date.d.ts +2 -0
  105. package/dist/validators/future-date.js +20 -0
  106. package/dist/validators/is-date.d.ts +2 -0
  107. package/dist/validators/is-date.js +12 -0
  108. package/dist/validators/max-age.d.ts +2 -0
  109. package/dist/validators/max-age.js +20 -0
  110. package/dist/validators/max-date.d.ts +2 -0
  111. package/dist/validators/max-date.js +20 -0
  112. package/dist/validators/max-length.d.ts +2 -0
  113. package/dist/validators/max-length.js +11 -0
  114. package/dist/validators/max.d.ts +2 -0
  115. package/dist/validators/max.js +11 -0
  116. package/dist/validators/min-age.d.ts +2 -0
  117. package/dist/validators/min-age.js +20 -0
  118. package/dist/validators/min-date.d.ts +2 -0
  119. package/dist/validators/min-date.js +20 -0
  120. package/dist/validators/min-length.d.ts +2 -0
  121. package/dist/validators/min-length.js +11 -0
  122. package/dist/validators/min.d.ts +2 -0
  123. package/dist/validators/min.js +11 -0
  124. package/dist/validators/number.d.ts +2 -0
  125. package/dist/validators/number.js +35 -0
  126. package/dist/validators/past-date.d.ts +2 -0
  127. package/dist/validators/past-date.js +20 -0
  128. package/dist/validators/pattern.d.ts +2 -0
  129. package/dist/validators/pattern.js +11 -0
  130. package/dist/validators/phone.d.ts +2 -0
  131. package/dist/validators/phone.js +35 -0
  132. package/dist/validators/required.d.ts +2 -0
  133. package/dist/validators/required.js +15 -0
  134. package/dist/validators/url.d.ts +2 -0
  135. package/dist/validators/url.js +19 -0
  136. package/dist/validators-BGsNOgT1.js +207 -0
  137. package/dist/validators.d.ts +6 -2
  138. package/dist/validators.js +54 -296
  139. package/llms.txt +8887 -59
  140. package/package.json +87 -8
  141. package/dist/core/behavior/behavior-applicator.d.ts +0 -71
  142. package/dist/core/behavior/behavior-applicator.js +0 -92
  143. package/dist/core/behavior/behavior-context.js +0 -38
  144. package/dist/core/behavior/behavior-registry.js +0 -198
  145. package/dist/core/behavior/behaviors/compute-from.js +0 -84
  146. package/dist/core/behavior/behaviors/copy-from.js +0 -64
  147. package/dist/core/behavior/behaviors/enable-when.js +0 -81
  148. package/dist/core/behavior/behaviors/index.js +0 -11
  149. package/dist/core/behavior/behaviors/reset-when.js +0 -63
  150. package/dist/core/behavior/behaviors/revalidate-when.js +0 -51
  151. package/dist/core/behavior/behaviors/sync-fields.js +0 -66
  152. package/dist/core/behavior/behaviors/transform-value.js +0 -110
  153. package/dist/core/behavior/behaviors/watch-field.js +0 -56
  154. package/dist/core/behavior/compose-behavior.js +0 -166
  155. package/dist/core/behavior/create-field-path.d.ts +0 -20
  156. package/dist/core/behavior/create-field-path.js +0 -69
  157. package/dist/core/behavior/index.js +0 -17
  158. package/dist/core/behavior/types.js +0 -7
  159. package/dist/core/context/form-context-impl.d.ts +0 -29
  160. package/dist/core/context/form-context-impl.js +0 -37
  161. package/dist/core/factories/index.js +0 -6
  162. package/dist/core/factories/node-factory.js +0 -281
  163. package/dist/core/nodes/array-node.js +0 -534
  164. package/dist/core/nodes/field-node.js +0 -510
  165. package/dist/core/nodes/form-node.js +0 -343
  166. package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
  167. package/dist/core/nodes/group-node/field-registry.js +0 -215
  168. package/dist/core/nodes/group-node/index.d.ts +0 -11
  169. package/dist/core/nodes/group-node/index.js +0 -11
  170. package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
  171. package/dist/core/nodes/group-node/proxy-builder.js +0 -161
  172. package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
  173. package/dist/core/nodes/group-node/state-manager.js +0 -265
  174. package/dist/core/nodes/group-node.js +0 -770
  175. package/dist/core/types/deep-schema.js +0 -11
  176. package/dist/core/types/field-path.js +0 -4
  177. package/dist/core/types/form-context.js +0 -25
  178. package/dist/core/types/group-node-proxy.js +0 -31
  179. package/dist/core/types/index.js +0 -4
  180. package/dist/core/types/validation-schema.js +0 -10
  181. package/dist/core/utils/create-form.js +0 -24
  182. package/dist/core/utils/debounce.d.ts +0 -160
  183. package/dist/core/utils/debounce.js +0 -197
  184. package/dist/core/utils/error-handler.js +0 -226
  185. package/dist/core/utils/field-path-navigator.js +0 -374
  186. package/dist/core/utils/index.js +0 -14
  187. package/dist/core/utils/registry-helpers.js +0 -79
  188. package/dist/core/utils/registry-stack.js +0 -86
  189. package/dist/core/utils/resources.d.ts +0 -41
  190. package/dist/core/utils/resources.js +0 -69
  191. package/dist/core/utils/subscription-manager.js +0 -214
  192. package/dist/core/utils/type-guards.js +0 -169
  193. package/dist/core/validation/core/apply-when.js +0 -41
  194. package/dist/core/validation/core/apply.js +0 -38
  195. package/dist/core/validation/core/index.js +0 -8
  196. package/dist/core/validation/core/validate-async.js +0 -45
  197. package/dist/core/validation/core/validate-tree.js +0 -37
  198. package/dist/core/validation/core/validate.js +0 -38
  199. package/dist/core/validation/field-path.js +0 -147
  200. package/dist/core/validation/index.js +0 -33
  201. package/dist/core/validation/validate-form.js +0 -152
  202. package/dist/core/validation/validation-applicator.js +0 -217
  203. package/dist/core/validation/validation-context.js +0 -75
  204. package/dist/core/validation/validation-registry.js +0 -298
  205. package/dist/core/validation/validators/array-validators.js +0 -86
  206. package/dist/core/validation/validators/date.d.ts +0 -38
  207. package/dist/core/validation/validators/date.js +0 -117
  208. package/dist/core/validation/validators/email.js +0 -60
  209. package/dist/core/validation/validators/index.js +0 -14
  210. package/dist/core/validation/validators/max-length.js +0 -60
  211. package/dist/core/validation/validators/max.js +0 -60
  212. package/dist/core/validation/validators/min-length.js +0 -60
  213. package/dist/core/validation/validators/min.js +0 -60
  214. package/dist/core/validation/validators/number.js +0 -90
  215. package/dist/core/validation/validators/pattern.js +0 -62
  216. package/dist/core/validation/validators/phone.js +0 -58
  217. package/dist/core/validation/validators/required.js +0 -69
  218. package/dist/core/validation/validators/url.js +0 -55
  219. package/dist/create-field-path-CdPF3lIK.js +0 -704
  220. package/dist/hooks/useFormControl.js +0 -298
  221. package/dist/node-factory-D7DOnSSN.js +0 -3200
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reformer/core",
3
- "version": "1.1.0",
3
+ "version": "2.0.0-beta.11",
4
4
  "description": "Reactive form state management library for React with signals-based architecture",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,13 +18,91 @@
18
18
  "./validators": {
19
19
  "types": "./dist/validators.d.ts",
20
20
  "import": "./dist/validators.js"
21
+ },
22
+ "./validators/required": {
23
+ "types": "./dist/validators/required.d.ts",
24
+ "import": "./dist/validators/required.js"
25
+ },
26
+ "./validators/email": {
27
+ "types": "./dist/validators/email.d.ts",
28
+ "import": "./dist/validators/email.js"
29
+ },
30
+ "./validators/min": {
31
+ "types": "./dist/validators/min.d.ts",
32
+ "import": "./dist/validators/min.js"
33
+ },
34
+ "./validators/max": {
35
+ "types": "./dist/validators/max.d.ts",
36
+ "import": "./dist/validators/max.js"
37
+ },
38
+ "./validators/min-length": {
39
+ "types": "./dist/validators/min-length.d.ts",
40
+ "import": "./dist/validators/min-length.js"
41
+ },
42
+ "./validators/max-length": {
43
+ "types": "./dist/validators/max-length.d.ts",
44
+ "import": "./dist/validators/max-length.js"
45
+ },
46
+ "./validators/pattern": {
47
+ "types": "./dist/validators/pattern.d.ts",
48
+ "import": "./dist/validators/pattern.js"
49
+ },
50
+ "./validators/url": {
51
+ "types": "./dist/validators/url.d.ts",
52
+ "import": "./dist/validators/url.js"
53
+ },
54
+ "./validators/phone": {
55
+ "types": "./dist/validators/phone.d.ts",
56
+ "import": "./dist/validators/phone.js"
57
+ },
58
+ "./validators/number": {
59
+ "types": "./dist/validators/number.d.ts",
60
+ "import": "./dist/validators/number.js"
61
+ },
62
+ "./validators/date": {
63
+ "types": "./dist/validators/date.d.ts",
64
+ "import": "./dist/validators/date.js"
65
+ },
66
+ "./behaviors/copy-from": {
67
+ "types": "./dist/behaviors/copy-from.d.ts",
68
+ "import": "./dist/behaviors/copy-from.js"
69
+ },
70
+ "./behaviors/enable-when": {
71
+ "types": "./dist/behaviors/enable-when.d.ts",
72
+ "import": "./dist/behaviors/enable-when.js"
73
+ },
74
+ "./behaviors/compute-from": {
75
+ "types": "./dist/behaviors/compute-from.d.ts",
76
+ "import": "./dist/behaviors/compute-from.js"
77
+ },
78
+ "./behaviors/watch-field": {
79
+ "types": "./dist/behaviors/watch-field.d.ts",
80
+ "import": "./dist/behaviors/watch-field.js"
81
+ },
82
+ "./behaviors/revalidate-when": {
83
+ "types": "./dist/behaviors/revalidate-when.d.ts",
84
+ "import": "./dist/behaviors/revalidate-when.js"
85
+ },
86
+ "./behaviors/sync-fields": {
87
+ "types": "./dist/behaviors/sync-fields.d.ts",
88
+ "import": "./dist/behaviors/sync-fields.js"
89
+ },
90
+ "./behaviors/reset-when": {
91
+ "types": "./dist/behaviors/reset-when.d.ts",
92
+ "import": "./dist/behaviors/reset-when.js"
93
+ },
94
+ "./behaviors/transform-value": {
95
+ "types": "./dist/behaviors/transform-value.d.ts",
96
+ "import": "./dist/behaviors/transform-value.js"
21
97
  }
22
98
  },
23
99
  "sideEffects": false,
24
100
  "scripts": {
25
- "build": "vite build && tsc -p tsconfig.json",
101
+ "generate:llms": "node ../../scripts/generate-llms-txt .",
102
+ "build": "npm run generate:llms && vite build",
103
+ "build:stackblitz": "vite build",
26
104
  "dev": "vite",
27
- "test": "vitest",
105
+ "test": "node ../../scripts/run-vitest.mjs",
28
106
  "test:watch": "vitest watch"
29
107
  },
30
108
  "keywords": [
@@ -63,18 +141,19 @@
63
141
  "LLMs.txt"
64
142
  ],
65
143
  "peerDependencies": {
66
- "react": "^18.0.0 || ^19.0.0",
67
- "react-dom": "^18.0.0 || ^19.0.0"
144
+ "@preact/signals-core": "^1.8.0",
145
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
146
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
68
147
  },
69
148
  "dependencies": {
70
- "@preact/signals-core": "^1.8.0",
71
- "uuid": "^13.0.0"
149
+ "use-sync-external-store": "^1.2.0"
72
150
  },
73
151
  "devDependencies": {
152
+ "@types/use-sync-external-store": "^0.0.6",
74
153
  "@types/node": "^24.10.1",
154
+ "tsx": "^4.19.2",
75
155
  "@types/react": "^19.2.7",
76
156
  "@types/react-dom": "^19.2.3",
77
- "@types/uuid": "^10.0.0",
78
157
  "@vitejs/plugin-react": "^5.1.0",
79
158
  "@vitest/utils": "^4.0.8",
80
159
  "react": "^19.2.1",
@@ -1,71 +0,0 @@
1
- /**
2
- * Применение behavior схемы к форме
3
- *
4
- * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
- * Управляет процессом регистрации и применения behaviors.
6
- *
7
- * @template T Тип формы
8
- *
9
- * @example
10
- * ```typescript
11
- * class GroupNode {
12
- * private readonly behaviorApplicator = new BehaviorApplicator(this);
13
- *
14
- * applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void {
15
- * return this.behaviorApplicator.apply(schemaFn);
16
- * }
17
- * }
18
- * ```
19
- */
20
- import type { GroupNode } from '../nodes/group-node';
21
- import type { BehaviorSchemaFn } from './types';
22
- import { BehaviorRegistry } from './behavior-registry';
23
- /**
24
- * Класс для применения behavior схемы к форме
25
- *
26
- * Выполняет:
27
- * 1. Начало регистрации behaviors (beginRegistration)
28
- * 2. Выполнение схемы (регистрация behaviors)
29
- * 3. Завершение регистрации (endRegistration) - применение behaviors
30
- * 4. Возврат функции cleanup для отписки
31
- *
32
- * @template T Тип формы (объект)
33
- */
34
- export declare class BehaviorApplicator<T> {
35
- private readonly form;
36
- private readonly behaviorRegistry;
37
- constructor(form: GroupNode<T>, behaviorRegistry: BehaviorRegistry);
38
- /**
39
- * Применить behavior схему к форме
40
- *
41
- * Этапы:
42
- * 1. Начать регистрацию (beginRegistration)
43
- * 2. Выполнить схему (регистрация behaviors)
44
- * 3. Завершить регистрацию (endRegistration) - применить behaviors
45
- * 4. Вернуть функцию cleanup для отписки
46
- *
47
- * @param schemaFn Функция-схема behavior
48
- * @returns Функция отписки от всех behaviors
49
- *
50
- * @example
51
- * ```typescript
52
- * const cleanup = behaviorApplicator.apply((path) => {
53
- * copyFrom(path.residenceAddress, path.registrationAddress, {
54
- * when: (form) => form.sameAsRegistration === true
55
- * });
56
- *
57
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
58
- *
59
- * computeFrom(
60
- * path.initialPayment,
61
- * [path.propertyValue],
62
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
63
- * );
64
- * });
65
- *
66
- * // Cleanup при unmount
67
- * useEffect(() => cleanup, []);
68
- * ```
69
- */
70
- apply(schemaFn: BehaviorSchemaFn<T>): () => void;
71
- }
@@ -1,92 +0,0 @@
1
- /**
2
- * Применение behavior схемы к форме
3
- *
4
- * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
- * Управляет процессом регистрации и применения behaviors.
6
- *
7
- * @template T Тип формы
8
- *
9
- * @example
10
- * ```typescript
11
- * class GroupNode {
12
- * private readonly behaviorApplicator = new BehaviorApplicator(this);
13
- *
14
- * applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void {
15
- * return this.behaviorApplicator.apply(schemaFn);
16
- * }
17
- * }
18
- * ```
19
- */
20
- import { createFieldPath as createBehaviorFieldPath } from './create-field-path';
21
- import { FormErrorHandler, ErrorStrategy } from '../utils/error-handler';
22
- /**
23
- * Класс для применения behavior схемы к форме
24
- *
25
- * Выполняет:
26
- * 1. Начало регистрации behaviors (beginRegistration)
27
- * 2. Выполнение схемы (регистрация behaviors)
28
- * 3. Завершение регистрации (endRegistration) - применение behaviors
29
- * 4. Возврат функции cleanup для отписки
30
- *
31
- * @template T Тип формы (объект)
32
- */
33
- export class BehaviorApplicator {
34
- form;
35
- behaviorRegistry;
36
- constructor(form, behaviorRegistry) {
37
- this.form = form;
38
- this.behaviorRegistry = behaviorRegistry;
39
- }
40
- /**
41
- * Применить behavior схему к форме
42
- *
43
- * Этапы:
44
- * 1. Начать регистрацию (beginRegistration)
45
- * 2. Выполнить схему (регистрация behaviors)
46
- * 3. Завершить регистрацию (endRegistration) - применить behaviors
47
- * 4. Вернуть функцию cleanup для отписки
48
- *
49
- * @param schemaFn Функция-схема behavior
50
- * @returns Функция отписки от всех behaviors
51
- *
52
- * @example
53
- * ```typescript
54
- * const cleanup = behaviorApplicator.apply((path) => {
55
- * copyFrom(path.residenceAddress, path.registrationAddress, {
56
- * when: (form) => form.sameAsRegistration === true
57
- * });
58
- *
59
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
60
- *
61
- * computeFrom(
62
- * path.initialPayment,
63
- * [path.propertyValue],
64
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
65
- * );
66
- * });
67
- *
68
- * // Cleanup при unmount
69
- * useEffect(() => cleanup, []);
70
- * ```
71
- */
72
- apply(schemaFn) {
73
- this.behaviorRegistry.beginRegistration();
74
- try {
75
- // 1. Создать field path для type-safe доступа к полям
76
- const path = createBehaviorFieldPath();
77
- // 2. Выполнить схему (регистрация behaviors)
78
- schemaFn(path);
79
- // 3. Завершить регистрацию и применить behaviors
80
- // Используем публичный метод getProxy() для получения proxy-инстанса
81
- const formToUse = this.form.getProxy();
82
- const result = this.behaviorRegistry.endRegistration(formToUse);
83
- // 4. Вернуть функцию cleanup
84
- return result.cleanup;
85
- }
86
- catch (error) {
87
- FormErrorHandler.handle(error, 'BehaviorApplicator', ErrorStrategy.THROW);
88
- // TypeScript требует return, но код никогда не дойдет сюда
89
- throw error;
90
- }
91
- }
92
- }
@@ -1,38 +0,0 @@
1
- /**
2
- * BehaviorContext - контекст для behavior callback функций
3
- *
4
- * Реализует FormContext для behavior схем
5
- */
6
- /**
7
- * Реализация BehaviorContext (FormContext)
8
- *
9
- * Предоставляет:
10
- * - `form` - прямой типизированный доступ к форме
11
- * - `setFieldValue` - безопасная установка значения (emitEvent: false)
12
- */
13
- export class BehaviorContextImpl {
14
- /**
15
- * Форма с типизированным Proxy-доступом к полям
16
- */
17
- form;
18
- _form;
19
- constructor(form) {
20
- this._form = form;
21
- // Используем _proxyInstance если доступен, иначе fallback на form
22
- const proxy = (form
23
- ._proxyInstance || form);
24
- this.form = proxy;
25
- }
26
- /**
27
- * Безопасно установить значение поля по строковому пути
28
- *
29
- * Автоматически использует emitEvent: false для предотвращения циклов
30
- */
31
- setFieldValue(path, value) {
32
- const node = this._form.getFieldByPath(path);
33
- if (node) {
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- node.setValue(value, { emitEvent: false });
36
- }
37
- }
38
- }
@@ -1,198 +0,0 @@
1
- /**
2
- * BehaviorRegistry - регистрация и управление behavior схемами
3
- *
4
- * Аналогично ValidationRegistry, но для реактивного поведения форм
5
- */
6
- import { BehaviorContextImpl } from './behavior-context';
7
- import { RegistryStack } from '../utils/registry-stack';
8
- /**
9
- * Реестр behaviors для формы
10
- *
11
- * Каждый экземпляр GroupNode создает собственный реестр (композиция).
12
- * Устраняет race conditions и изолирует формы друг от друга.
13
- *
14
- * Context stack используется для tracking текущего активного реестра:
15
- * - beginRegistration() помещает this в stack
16
- * - endRegistration() извлекает из stack
17
- * - getCurrent() возвращает текущий активный реестр
18
- *
19
- * @example
20
- * ```typescript
21
- * class GroupNode {
22
- * private readonly behaviorRegistry = new BehaviorRegistry();
23
- *
24
- * applyBehaviorSchema(schemaFn) {
25
- * this.behaviorRegistry.beginRegistration(); // Pushes this to stack
26
- * schemaFn(createBehaviorFieldPath(this)); // Uses getCurrent()
27
- * return this.behaviorRegistry.endRegistration(this); // Pops from stack
28
- * }
29
- * }
30
- * ```
31
- */
32
- export class BehaviorRegistry {
33
- /**
34
- * Stack активных контекстов регистрации
35
- * Используется для изоляции форм друг от друга
36
- */
37
- static contextStack = new RegistryStack();
38
- registrations = [];
39
- isRegistering = false;
40
- /**
41
- * Получить текущий активный реестр из context stack
42
- *
43
- * @returns Текущий активный реестр или null
44
- *
45
- * @example
46
- * ```typescript
47
- * // В schema-behaviors.ts
48
- * export function copyFrom(...) {
49
- * const registry = BehaviorRegistry.getCurrent();
50
- * if (registry) {
51
- * registry.register({ ... });
52
- * }
53
- * }
54
- * ```
55
- */
56
- static getCurrent() {
57
- return BehaviorRegistry.contextStack.getCurrent();
58
- }
59
- /**
60
- * Начать регистрацию behaviors
61
- * Вызывается перед применением схемы
62
- *
63
- * Помещает this в context stack для изоляции форм
64
- */
65
- beginRegistration() {
66
- this.isRegistering = true;
67
- this.registrations = [];
68
- // Помещаем this в stack для tracking текущего активного реестра
69
- BehaviorRegistry.contextStack.push(this);
70
- }
71
- /**
72
- * Зарегистрировать behavior handler
73
- * Вызывается функциями из schema-behaviors.ts
74
- *
75
- * @param handler - BehaviorHandlerFn функция
76
- * @param options - Опции behavior (debounce)
77
- *
78
- * @example
79
- * ```typescript
80
- * const handler = createCopyBehavior(target, source, { when: ... });
81
- * registry.register(handler, { debounce: 300 });
82
- * ```
83
- */
84
- register(handler, options) {
85
- if (!this.isRegistering) {
86
- if (import.meta.env.DEV) {
87
- throw new Error('BehaviorRegistry: call beginRegistration() before registering behaviors');
88
- }
89
- return;
90
- }
91
- this.registrations.push({
92
- // Type assertion безопасен: handler будет вызван с правильным типом формы в createEffect
93
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
- handler: handler,
95
- debounce: options?.debounce,
96
- });
97
- }
98
- /**
99
- * Завершить регистрацию и применить behaviors к форме
100
- * Создает effect подписки для всех зарегистрированных behaviors
101
- *
102
- * Извлекает this из context stack
103
- *
104
- * @param form - GroupNode формы
105
- * @returns Количество зарегистрированных behaviors и функция cleanup
106
- */
107
- endRegistration(form) {
108
- this.isRegistering = false;
109
- // Извлекаем из stack с проверкой
110
- BehaviorRegistry.contextStack.verify(this, 'BehaviorRegistry');
111
- const context = new BehaviorContextImpl(form);
112
- const disposeCallbacks = [];
113
- // Создаем effect подписки для каждого behavior
114
- for (const registered of this.registrations) {
115
- const dispose = this.createEffect(registered, form, context);
116
- if (dispose) {
117
- disposeCallbacks.push(dispose);
118
- }
119
- }
120
- // Функция cleanup для отписки от всех effects
121
- const cleanup = () => {
122
- disposeCallbacks.forEach((dispose) => dispose());
123
- };
124
- return {
125
- count: this.registrations.length,
126
- cleanup,
127
- };
128
- }
129
- /**
130
- * Создать effect подписку для behavior
131
- * @private
132
- */
133
- createEffect(registered, form, context) {
134
- const { handler, debounce: debounceMs = 0 } = registered;
135
- let debounceTimer = null;
136
- // Обертка для debounce
137
- const withDebounce = (callback) => {
138
- if (debounceMs > 0) {
139
- if (debounceTimer)
140
- clearTimeout(debounceTimer);
141
- debounceTimer = setTimeout(callback, debounceMs);
142
- }
143
- else {
144
- callback();
145
- }
146
- };
147
- // Cleanup функция для debounce таймера
148
- const cleanupDebounce = () => {
149
- if (debounceTimer) {
150
- clearTimeout(debounceTimer);
151
- debounceTimer = null;
152
- }
153
- };
154
- // Вызываем handler напрямую
155
- // Type assertion необходим из-за contravariance: handler хранится как
156
- // BehaviorHandlerFn<FormFields>, но вызывается с более специфичным типом T.
157
- // Используем any для обхода ограничений TypeScript при хранении generic handlers в массиве.
158
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
- const effectDispose = handler(form, context, withDebounce);
160
- if (!effectDispose) {
161
- return null;
162
- }
163
- // Возвращаем комбинированный cleanup
164
- // который очищает и effect, и debounce таймер
165
- return () => {
166
- cleanupDebounce();
167
- if (effectDispose) {
168
- effectDispose();
169
- }
170
- };
171
- }
172
- }
173
- // ============================================================================
174
- // Глобальный экземпляр BehaviorRegistry УДАЛЕН
175
- // ============================================================================
176
- //
177
- // Ранее здесь был глобальный Singleton экземпляр BehaviorRegistry,
178
- // который создавал race conditions и нарушал изоляцию форм.
179
- //
180
- // Теперь каждый GroupNode создает собственный экземпляр BehaviorRegistry:
181
- //
182
- // @example
183
- // ```typescript
184
- // class GroupNode {
185
- // private readonly behaviorRegistry = new BehaviorRegistry();
186
- //
187
- // applyBehaviorSchema(schemaFn) {
188
- // this.behaviorRegistry.beginRegistration();
189
- // schemaFn(createBehaviorFieldPath(this));
190
- // return this.behaviorRegistry.endRegistration(this);
191
- // }
192
- // }
193
- // ```
194
- //
195
- // Это обеспечивает:
196
- // - Полную изоляцию форм друг от друга
197
- // - Отсутствие race conditions при параллельной регистрации
198
- // - Возможность применять разные behavior схемы к разным формам одновременно
@@ -1,84 +0,0 @@
1
- /**
2
- * Вычисляемые поля
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/computeFrom
7
- */
8
- import { effect } from '@preact/signals-core';
9
- import { getCurrentBehaviorRegistry } from '../../utils/registry-helpers';
10
- /**
11
- * Автоматически вычисляет значение поля на основе других полей
12
- *
13
- * @group Behaviors
14
- * @category Behavior Rules
15
- *
16
- * @param sources - Массив полей-зависимостей
17
- * @param target - Поле для записи результата
18
- * @param computeFn - Функция вычисления (принимает объект с именами полей)
19
- * @param options - Опции
20
- *
21
- * @example
22
- * ```typescript
23
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
24
- * // Автоматический расчет минимального взноса
25
- * computeFrom(
26
- * [path.propertyValue],
27
- * path.initialPayment,
28
- * (values) => values.propertyValue ? values.propertyValue * 0.2 : null,
29
- * { debounce: 300 }
30
- * );
31
- *
32
- * // Общая стоимость = цена * количество
33
- * computeFrom(
34
- * [path.price, path.quantity],
35
- * path.total,
36
- * (values) => values.price * values.quantity
37
- * );
38
- * };
39
- * ```
40
- */
41
- export function computeFrom(
42
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- sources, target, computeFn, options) {
44
- const { debounce, condition } = options || {};
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
- const handler = (form, _context, withDebounce) => {
47
- const targetNode = form.getFieldByPath(target.__path);
48
- if (!targetNode)
49
- return null;
50
- // Разрешаем source узлы
51
- const sourceNodes = sources
52
- .map((field) => form.getFieldByPath(field.__path))
53
- .filter((node) => node !== undefined);
54
- if (sourceNodes.length === 0)
55
- return null;
56
- return effect(() => {
57
- // Читаем значения всех source полей
58
- const sourceValues = sourceNodes.map((node) => node.value.value);
59
- withDebounce(() => {
60
- // Проверка условия
61
- if (condition) {
62
- const formValue = form.getValue();
63
- if (!condition(formValue))
64
- return;
65
- }
66
- // Создаем объект с именами полей для computeFn
67
- // computeFn ожидает объект вида { fieldName: value, ... }
68
- const sourceValuesObject = {};
69
- sources.forEach((source, index) => {
70
- // Извлекаем имя поля из пути (последний сегмент)
71
- const fieldName = source.__path.split('.').pop() || source.__path;
72
- sourceValuesObject[fieldName] = sourceValues[index];
73
- });
74
- // Вычисляем новое значение
75
- const computedValue = computeFn(sourceValuesObject);
76
- // Устанавливаем значение без триггера событий
77
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
- targetNode.setValue(computedValue, { emitEvent: false });
79
- });
80
- });
81
- };
82
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
- getCurrentBehaviorRegistry().register(handler, { debounce });
84
- }
@@ -1,64 +0,0 @@
1
- /**
2
- * Копирование значений между полями
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/copyFrom
7
- */
8
- import { watchField } from './watch-field';
9
- /**
10
- * Копирует значения из одного поля/группы в другое при выполнении условия
11
- *
12
- * @group Behaviors
13
- * @category Behavior Rules
14
- *
15
- * @param source - Откуда копировать
16
- * @param target - Куда копировать
17
- * @param options - Опции копирования
18
- *
19
- * @example
20
- * ```typescript
21
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
22
- * // Копировать адрес регистрации в адрес проживания
23
- * copyFrom(path.registrationAddress, path.residenceAddress, {
24
- * when: (form) => form.sameAsRegistration === true,
25
- * fields: 'all'
26
- * });
27
- * };
28
- * ```
29
- */
30
- export function copyFrom(source, target, options) {
31
- const { when, fields = 'all', transform, debounce } = options || {};
32
- watchField(source, (sourceValue, ctx) => {
33
- // Проверка условия
34
- if (when) {
35
- const formValue = ctx.form.getValue();
36
- if (!when(formValue))
37
- return;
38
- }
39
- // Трансформация значения
40
- const value = transform ? transform(sourceValue) : sourceValue;
41
- // Получаем target node
42
- const targetNode = ctx.form.getFieldByPath(target.__path);
43
- if (!targetNode)
44
- return;
45
- // Копирование
46
- if (fields === 'all' || !fields) {
47
- targetNode.setValue(value, { emitEvent: false });
48
- }
49
- else {
50
- // Частичное копирование для групп
51
- const patch = {};
52
- fields.forEach((key) => {
53
- if (sourceValue && typeof sourceValue === 'object') {
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- patch[key] = sourceValue[key];
56
- }
57
- });
58
- if ('patchValue' in targetNode) {
59
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
- targetNode.patchValue(patch);
61
- }
62
- }
63
- }, { debounce });
64
- }