@reformer/core 1.0.0-beta.3

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 (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/behaviors.d.ts +2 -0
  4. package/dist/behaviors.js +230 -0
  5. package/dist/core/behavior/behavior-applicator.d.ts +71 -0
  6. package/dist/core/behavior/behavior-applicator.js +92 -0
  7. package/dist/core/behavior/behavior-context.d.ts +29 -0
  8. package/dist/core/behavior/behavior-context.js +38 -0
  9. package/dist/core/behavior/behavior-registry.d.ts +97 -0
  10. package/dist/core/behavior/behavior-registry.js +198 -0
  11. package/dist/core/behavior/behaviors/compute-from.d.ts +41 -0
  12. package/dist/core/behavior/behaviors/compute-from.js +84 -0
  13. package/dist/core/behavior/behaviors/copy-from.d.ts +31 -0
  14. package/dist/core/behavior/behaviors/copy-from.js +64 -0
  15. package/dist/core/behavior/behaviors/enable-when.d.ts +49 -0
  16. package/dist/core/behavior/behaviors/enable-when.js +81 -0
  17. package/dist/core/behavior/behaviors/index.d.ts +11 -0
  18. package/dist/core/behavior/behaviors/index.js +11 -0
  19. package/dist/core/behavior/behaviors/reset-when.d.ts +51 -0
  20. package/dist/core/behavior/behaviors/reset-when.js +63 -0
  21. package/dist/core/behavior/behaviors/revalidate-when.d.ts +30 -0
  22. package/dist/core/behavior/behaviors/revalidate-when.js +51 -0
  23. package/dist/core/behavior/behaviors/sync-fields.d.ts +28 -0
  24. package/dist/core/behavior/behaviors/sync-fields.js +66 -0
  25. package/dist/core/behavior/behaviors/transform-value.d.ts +120 -0
  26. package/dist/core/behavior/behaviors/transform-value.js +110 -0
  27. package/dist/core/behavior/behaviors/watch-field.d.ts +35 -0
  28. package/dist/core/behavior/behaviors/watch-field.js +56 -0
  29. package/dist/core/behavior/compose-behavior.d.ts +106 -0
  30. package/dist/core/behavior/compose-behavior.js +166 -0
  31. package/dist/core/behavior/create-field-path.d.ts +20 -0
  32. package/dist/core/behavior/create-field-path.js +69 -0
  33. package/dist/core/behavior/index.d.ts +12 -0
  34. package/dist/core/behavior/index.js +17 -0
  35. package/dist/core/behavior/types.d.ts +152 -0
  36. package/dist/core/behavior/types.js +7 -0
  37. package/dist/core/context/form-context-impl.d.ts +29 -0
  38. package/dist/core/context/form-context-impl.js +37 -0
  39. package/dist/core/factories/index.d.ts +6 -0
  40. package/dist/core/factories/index.js +6 -0
  41. package/dist/core/factories/node-factory.d.ts +209 -0
  42. package/dist/core/factories/node-factory.js +281 -0
  43. package/dist/core/nodes/array-node.d.ts +308 -0
  44. package/dist/core/nodes/array-node.js +534 -0
  45. package/dist/core/nodes/field-node.d.ts +269 -0
  46. package/dist/core/nodes/field-node.js +510 -0
  47. package/dist/core/nodes/form-node.d.ts +342 -0
  48. package/dist/core/nodes/form-node.js +343 -0
  49. package/dist/core/nodes/group-node/field-registry.d.ts +191 -0
  50. package/dist/core/nodes/group-node/field-registry.js +215 -0
  51. package/dist/core/nodes/group-node/index.d.ts +11 -0
  52. package/dist/core/nodes/group-node/index.js +11 -0
  53. package/dist/core/nodes/group-node/proxy-builder.d.ts +71 -0
  54. package/dist/core/nodes/group-node/proxy-builder.js +161 -0
  55. package/dist/core/nodes/group-node/state-manager.d.ts +184 -0
  56. package/dist/core/nodes/group-node/state-manager.js +265 -0
  57. package/dist/core/nodes/group-node.d.ts +494 -0
  58. package/dist/core/nodes/group-node.js +770 -0
  59. package/dist/core/types/deep-schema.d.ts +78 -0
  60. package/dist/core/types/deep-schema.js +11 -0
  61. package/dist/core/types/field-path.d.ts +42 -0
  62. package/dist/core/types/field-path.js +4 -0
  63. package/dist/core/types/form-context.d.ts +83 -0
  64. package/dist/core/types/form-context.js +25 -0
  65. package/dist/core/types/group-node-proxy.d.ts +135 -0
  66. package/dist/core/types/group-node-proxy.js +31 -0
  67. package/dist/core/types/index.d.ts +163 -0
  68. package/dist/core/types/index.js +4 -0
  69. package/dist/core/types/validation-schema.d.ts +104 -0
  70. package/dist/core/types/validation-schema.js +10 -0
  71. package/dist/core/utils/create-form.d.ts +61 -0
  72. package/dist/core/utils/create-form.js +24 -0
  73. package/dist/core/utils/debounce.d.ts +160 -0
  74. package/dist/core/utils/debounce.js +197 -0
  75. package/dist/core/utils/error-handler.d.ts +180 -0
  76. package/dist/core/utils/error-handler.js +226 -0
  77. package/dist/core/utils/field-path-navigator.d.ts +240 -0
  78. package/dist/core/utils/field-path-navigator.js +374 -0
  79. package/dist/core/utils/index.d.ts +14 -0
  80. package/dist/core/utils/index.js +14 -0
  81. package/dist/core/utils/registry-helpers.d.ts +50 -0
  82. package/dist/core/utils/registry-helpers.js +79 -0
  83. package/dist/core/utils/registry-stack.d.ts +69 -0
  84. package/dist/core/utils/registry-stack.js +86 -0
  85. package/dist/core/utils/resources.d.ts +41 -0
  86. package/dist/core/utils/resources.js +69 -0
  87. package/dist/core/utils/subscription-manager.d.ts +180 -0
  88. package/dist/core/utils/subscription-manager.js +214 -0
  89. package/dist/core/utils/type-guards.d.ts +116 -0
  90. package/dist/core/utils/type-guards.js +169 -0
  91. package/dist/core/validation/core/apply-when.d.ts +28 -0
  92. package/dist/core/validation/core/apply-when.js +41 -0
  93. package/dist/core/validation/core/apply.d.ts +63 -0
  94. package/dist/core/validation/core/apply.js +38 -0
  95. package/dist/core/validation/core/index.d.ts +8 -0
  96. package/dist/core/validation/core/index.js +8 -0
  97. package/dist/core/validation/core/validate-async.d.ts +42 -0
  98. package/dist/core/validation/core/validate-async.js +45 -0
  99. package/dist/core/validation/core/validate-tree.d.ts +35 -0
  100. package/dist/core/validation/core/validate-tree.js +37 -0
  101. package/dist/core/validation/core/validate.d.ts +32 -0
  102. package/dist/core/validation/core/validate.js +38 -0
  103. package/dist/core/validation/field-path.d.ts +43 -0
  104. package/dist/core/validation/field-path.js +147 -0
  105. package/dist/core/validation/index.d.ts +21 -0
  106. package/dist/core/validation/index.js +33 -0
  107. package/dist/core/validation/validate-form.d.ts +85 -0
  108. package/dist/core/validation/validate-form.js +152 -0
  109. package/dist/core/validation/validation-applicator.d.ts +89 -0
  110. package/dist/core/validation/validation-applicator.js +217 -0
  111. package/dist/core/validation/validation-context.d.ts +47 -0
  112. package/dist/core/validation/validation-context.js +75 -0
  113. package/dist/core/validation/validation-registry.d.ts +156 -0
  114. package/dist/core/validation/validation-registry.js +298 -0
  115. package/dist/core/validation/validators/array-validators.d.ts +63 -0
  116. package/dist/core/validation/validators/array-validators.js +86 -0
  117. package/dist/core/validation/validators/date.d.ts +38 -0
  118. package/dist/core/validation/validators/date.js +117 -0
  119. package/dist/core/validation/validators/email.d.ts +44 -0
  120. package/dist/core/validation/validators/email.js +60 -0
  121. package/dist/core/validation/validators/index.d.ts +14 -0
  122. package/dist/core/validation/validators/index.js +14 -0
  123. package/dist/core/validation/validators/max-length.d.ts +45 -0
  124. package/dist/core/validation/validators/max-length.js +60 -0
  125. package/dist/core/validation/validators/max.d.ts +45 -0
  126. package/dist/core/validation/validators/max.js +60 -0
  127. package/dist/core/validation/validators/min-length.d.ts +45 -0
  128. package/dist/core/validation/validators/min-length.js +60 -0
  129. package/dist/core/validation/validators/min.d.ts +45 -0
  130. package/dist/core/validation/validators/min.js +60 -0
  131. package/dist/core/validation/validators/number.d.ts +38 -0
  132. package/dist/core/validation/validators/number.js +90 -0
  133. package/dist/core/validation/validators/pattern.d.ts +47 -0
  134. package/dist/core/validation/validators/pattern.js +62 -0
  135. package/dist/core/validation/validators/phone.d.ts +34 -0
  136. package/dist/core/validation/validators/phone.js +58 -0
  137. package/dist/core/validation/validators/required.d.ts +48 -0
  138. package/dist/core/validation/validators/required.js +69 -0
  139. package/dist/core/validation/validators/url.d.ts +29 -0
  140. package/dist/core/validation/validators/url.js +55 -0
  141. package/dist/create-field-path-CdPF3lIK.js +704 -0
  142. package/dist/hooks/useFormControl.d.ts +48 -0
  143. package/dist/hooks/useFormControl.js +298 -0
  144. package/dist/index.d.ts +10 -0
  145. package/dist/index.js +8 -0
  146. package/dist/node-factory-D7DOnSSN.js +3200 -0
  147. package/dist/validators.d.ts +2 -0
  148. package/dist/validators.js +298 -0
  149. package/llms.txt +847 -0
  150. package/package.json +86 -0
@@ -0,0 +1,191 @@
1
+ /**
2
+ * FieldRegistry - управление полями в GroupNode
3
+ *
4
+ * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
+ * Отвечает только за хранение и управление коллекцией полей формы.
6
+ *
7
+ * @template T Тип формы (объект)
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const registry = new FieldRegistry<{ email: string; name: string }>();
12
+ * registry.set('email', emailField);
13
+ * registry.set('name', nameField);
14
+ *
15
+ * const emailField = registry.get('email');
16
+ * registry.forEach((field, key) => {
17
+ * console.log(key, field.value.value);
18
+ * });
19
+ * ```
20
+ */
21
+ import type { FormNode } from '../form-node';
22
+ import type { FormValue } from '../../types';
23
+ /**
24
+ * Реестр полей формы
25
+ *
26
+ * Предоставляет типобезопасный доступ к полям формы
27
+ * через Map-подобный интерфейс
28
+ *
29
+ * @template T Тип формы (объект)
30
+ */
31
+ export declare class FieldRegistry<T> {
32
+ /**
33
+ * Внутреннее хранилище полей
34
+ * Map обеспечивает быструю lookup производительность O(1)
35
+ */
36
+ private fields;
37
+ /**
38
+ * Установить поле в реестр
39
+ *
40
+ * @param key - Ключ поля (имя свойства в типе T)
41
+ * @param node - FormNode для этого поля
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * registry.set('email', new FieldNode({ value: '' }));
46
+ * ```
47
+ */
48
+ set<K extends keyof T>(key: K, node: FormNode<T[K]>): void;
49
+ /**
50
+ * Получить поле из реестра
51
+ *
52
+ * @param key - Ключ поля
53
+ * @returns FormNode или undefined, если поле не найдено
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const emailField = registry.get('email');
58
+ * if (emailField) {
59
+ * console.log(emailField.value.value);
60
+ * }
61
+ * ```
62
+ */
63
+ get<K extends keyof T>(key: K): FormNode<T[K]> | undefined;
64
+ /**
65
+ * Проверить наличие поля в реестре
66
+ *
67
+ * @param key - Ключ поля
68
+ * @returns true если поле существует
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * if (registry.has('email')) {
73
+ * console.log('Email field exists');
74
+ * }
75
+ * ```
76
+ */
77
+ has(key: keyof T): boolean;
78
+ /**
79
+ * Удалить поле из реестра
80
+ *
81
+ * @param key - Ключ поля
82
+ * @returns true если поле было удалено, false если поля не было
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * registry.delete('email');
87
+ * ```
88
+ */
89
+ delete(key: keyof T): boolean;
90
+ /**
91
+ * Перебрать все поля
92
+ *
93
+ * @param callback - Функция обратного вызова для каждого поля
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * registry.forEach((field, key) => {
98
+ * console.log(`${key}: ${field.value.value}`);
99
+ * });
100
+ * ```
101
+ */
102
+ forEach(callback: (field: FormNode<FormValue>, key: keyof T) => void): void;
103
+ /**
104
+ * Получить итератор значений (полей)
105
+ *
106
+ * @returns Итератор по всем полям
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * for (const field of registry.values()) {
111
+ * await field.validate();
112
+ * }
113
+ * ```
114
+ */
115
+ values(): IterableIterator<FormNode<FormValue>>;
116
+ /**
117
+ * Получить итератор пар [ключ, значение]
118
+ *
119
+ * @returns Итератор по всем записям
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * for (const [key, field] of registry.entries()) {
124
+ * console.log(key, field.value.value);
125
+ * }
126
+ * ```
127
+ */
128
+ entries(): IterableIterator<[keyof T, FormNode<FormValue>]>;
129
+ /**
130
+ * Получить итератор ключей полей
131
+ *
132
+ * @returns Итератор по всем ключам
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const fieldNames = Array.from(registry.keys());
137
+ * // ['email', 'name', 'age']
138
+ * ```
139
+ */
140
+ keys(): IterableIterator<keyof T>;
141
+ /**
142
+ * Получить количество полей
143
+ *
144
+ * @returns Количество зарегистрированных полей
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * console.log(`Form has ${registry.size()} fields`);
149
+ * ```
150
+ */
151
+ size(): number;
152
+ /**
153
+ * Очистить все поля
154
+ *
155
+ * Удаляет все поля из реестра
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * registry.clear();
160
+ * console.log(registry.size()); // 0
161
+ * ```
162
+ */
163
+ clear(): void;
164
+ /**
165
+ * Получить все поля как массив
166
+ *
167
+ * Полезно для операций, требующих работу с массивом
168
+ *
169
+ * @returns Массив всех полей
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * const allValid = registry.toArray().every(field => field.valid.value);
174
+ * ```
175
+ */
176
+ toArray(): FormNode<FormValue>[];
177
+ /**
178
+ * Получить Map-представление реестра (readonly)
179
+ *
180
+ * Используйте для совместимости с существующим кодом
181
+ *
182
+ * @returns ReadonlyMap с полями
183
+ * @internal
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * const mapView = registry.asMap();
188
+ * ```
189
+ */
190
+ asMap(): ReadonlyMap<keyof T, FormNode<FormValue>>;
191
+ }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * FieldRegistry - управление полями в GroupNode
3
+ *
4
+ * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
+ * Отвечает только за хранение и управление коллекцией полей формы.
6
+ *
7
+ * @template T Тип формы (объект)
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const registry = new FieldRegistry<{ email: string; name: string }>();
12
+ * registry.set('email', emailField);
13
+ * registry.set('name', nameField);
14
+ *
15
+ * const emailField = registry.get('email');
16
+ * registry.forEach((field, key) => {
17
+ * console.log(key, field.value.value);
18
+ * });
19
+ * ```
20
+ */
21
+ /**
22
+ * Реестр полей формы
23
+ *
24
+ * Предоставляет типобезопасный доступ к полям формы
25
+ * через Map-подобный интерфейс
26
+ *
27
+ * @template T Тип формы (объект)
28
+ */
29
+ export class FieldRegistry {
30
+ /**
31
+ * Внутреннее хранилище полей
32
+ * Map обеспечивает быструю lookup производительность O(1)
33
+ */
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ fields = new Map();
36
+ /**
37
+ * Установить поле в реестр
38
+ *
39
+ * @param key - Ключ поля (имя свойства в типе T)
40
+ * @param node - FormNode для этого поля
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * registry.set('email', new FieldNode({ value: '' }));
45
+ * ```
46
+ */
47
+ set(key, node) {
48
+ this.fields.set(key, node);
49
+ }
50
+ /**
51
+ * Получить поле из реестра
52
+ *
53
+ * @param key - Ключ поля
54
+ * @returns FormNode или undefined, если поле не найдено
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const emailField = registry.get('email');
59
+ * if (emailField) {
60
+ * console.log(emailField.value.value);
61
+ * }
62
+ * ```
63
+ */
64
+ get(key) {
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ return this.fields.get(key);
67
+ }
68
+ /**
69
+ * Проверить наличие поля в реестре
70
+ *
71
+ * @param key - Ключ поля
72
+ * @returns true если поле существует
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * if (registry.has('email')) {
77
+ * console.log('Email field exists');
78
+ * }
79
+ * ```
80
+ */
81
+ has(key) {
82
+ return this.fields.has(key);
83
+ }
84
+ /**
85
+ * Удалить поле из реестра
86
+ *
87
+ * @param key - Ключ поля
88
+ * @returns true если поле было удалено, false если поля не было
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * registry.delete('email');
93
+ * ```
94
+ */
95
+ delete(key) {
96
+ return this.fields.delete(key);
97
+ }
98
+ /**
99
+ * Перебрать все поля
100
+ *
101
+ * @param callback - Функция обратного вызова для каждого поля
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * registry.forEach((field, key) => {
106
+ * console.log(`${key}: ${field.value.value}`);
107
+ * });
108
+ * ```
109
+ */
110
+ forEach(callback) {
111
+ this.fields.forEach(callback);
112
+ }
113
+ /**
114
+ * Получить итератор значений (полей)
115
+ *
116
+ * @returns Итератор по всем полям
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * for (const field of registry.values()) {
121
+ * await field.validate();
122
+ * }
123
+ * ```
124
+ */
125
+ values() {
126
+ return this.fields.values();
127
+ }
128
+ /**
129
+ * Получить итератор пар [ключ, значение]
130
+ *
131
+ * @returns Итератор по всем записям
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * for (const [key, field] of registry.entries()) {
136
+ * console.log(key, field.value.value);
137
+ * }
138
+ * ```
139
+ */
140
+ entries() {
141
+ return this.fields.entries();
142
+ }
143
+ /**
144
+ * Получить итератор ключей полей
145
+ *
146
+ * @returns Итератор по всем ключам
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * const fieldNames = Array.from(registry.keys());
151
+ * // ['email', 'name', 'age']
152
+ * ```
153
+ */
154
+ keys() {
155
+ return this.fields.keys();
156
+ }
157
+ /**
158
+ * Получить количество полей
159
+ *
160
+ * @returns Количество зарегистрированных полей
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * console.log(`Form has ${registry.size()} fields`);
165
+ * ```
166
+ */
167
+ size() {
168
+ return this.fields.size;
169
+ }
170
+ /**
171
+ * Очистить все поля
172
+ *
173
+ * Удаляет все поля из реестра
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * registry.clear();
178
+ * console.log(registry.size()); // 0
179
+ * ```
180
+ */
181
+ clear() {
182
+ this.fields.clear();
183
+ }
184
+ /**
185
+ * Получить все поля как массив
186
+ *
187
+ * Полезно для операций, требующих работу с массивом
188
+ *
189
+ * @returns Массив всех полей
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const allValid = registry.toArray().every(field => field.valid.value);
194
+ * ```
195
+ */
196
+ toArray() {
197
+ return Array.from(this.fields.values());
198
+ }
199
+ /**
200
+ * Получить Map-представление реестра (readonly)
201
+ *
202
+ * Используйте для совместимости с существующим кодом
203
+ *
204
+ * @returns ReadonlyMap с полями
205
+ * @internal
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const mapView = registry.asMap();
210
+ * ```
211
+ */
212
+ asMap() {
213
+ return this.fields;
214
+ }
215
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * GroupNode modules
3
+ *
4
+ * Модульная структура для GroupNode:
5
+ * - FieldRegistry: Управление коллекцией полей
6
+ * - ProxyBuilder: Создание Proxy для типобезопасного доступа к полям
7
+ * - StateManager: Управление состоянием формы (signals)
8
+ */
9
+ export { FieldRegistry } from './field-registry';
10
+ export { ProxyBuilder } from './proxy-builder';
11
+ export { StateManager } from './state-manager';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * GroupNode modules
3
+ *
4
+ * Модульная структура для GroupNode:
5
+ * - FieldRegistry: Управление коллекцией полей
6
+ * - ProxyBuilder: Создание Proxy для типобезопасного доступа к полям
7
+ * - StateManager: Управление состоянием формы (signals)
8
+ */
9
+ export { FieldRegistry } from './field-registry';
10
+ export { ProxyBuilder } from './proxy-builder';
11
+ export { StateManager } from './state-manager';
@@ -0,0 +1,71 @@
1
+ /**
2
+ * ProxyBuilder - создание Proxy для типобезопасного доступа к полям GroupNode
3
+ *
4
+ * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
+ * Отвечает только за создание Proxy с расширенной поддержкой операций.
6
+ *
7
+ * @template T Тип формы (объект)
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const fieldRegistry = new FieldRegistry<FormType>();
12
+ * const proxyBuilder = new ProxyBuilder(fieldRegistry);
13
+ * const proxy = proxyBuilder.build(groupNode);
14
+ *
15
+ * // Теперь можно обращаться к полям напрямую:
16
+ * console.log(proxy.email.value);
17
+ * console.log('email' in proxy); // true
18
+ * ```
19
+ */
20
+ import type { GroupNode } from '../group-node';
21
+ import type { GroupNodeWithControls } from '../../types/group-node-proxy';
22
+ import type { FieldRegistry } from './field-registry';
23
+ /**
24
+ * Строитель Proxy для GroupNode
25
+ *
26
+ * Создает Proxy с поддержкой:
27
+ * - get: Прямой доступ к полям через точечную нотацию
28
+ * - set: Предупреждение о попытке прямой установки полей
29
+ * - has: Проверка существования поля ('email' in form)
30
+ * - ownKeys: Перечисление всех ключей (Object.keys(form))
31
+ * - getOwnPropertyDescriptor: Получение дескрипторов полей
32
+ *
33
+ * @template T Тип формы (объект)
34
+ */
35
+ export declare class ProxyBuilder<T> {
36
+ private fieldRegistry;
37
+ /**
38
+ * @param fieldRegistry - Реестр полей для доступа к коллекции
39
+ */
40
+ constructor(fieldRegistry: FieldRegistry<T>);
41
+ /**
42
+ * Создать Proxy для GroupNode
43
+ *
44
+ * Proxy позволяет обращаться к полям формы напрямую:
45
+ * - form.email вместо form.fields.get('email')
46
+ * - form.address.city вместо form.fields.get('address').fields.get('city')
47
+ *
48
+ * @param target - GroupNode для которого создается Proxy
49
+ * @returns Proxy с типобезопасным доступом к полям
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const proxy = proxyBuilder.build(groupNode);
54
+ *
55
+ * // Доступ к полям
56
+ * console.log(proxy.email.value); // Работает!
57
+ * console.log(proxy.name.value); // Работает!
58
+ *
59
+ * // Доступ к методам GroupNode
60
+ * await proxy.validate(); // Работает!
61
+ * proxy.markAsTouched(); // Работает!
62
+ *
63
+ * // Проверка существования
64
+ * if ('email' in proxy) { ... }
65
+ *
66
+ * // Перечисление ключей
67
+ * Object.keys(proxy); // ['email', 'name', ...]
68
+ * ```
69
+ */
70
+ build(target: GroupNode<T>): GroupNodeWithControls<T>;
71
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * ProxyBuilder - создание Proxy для типобезопасного доступа к полям GroupNode
3
+ *
4
+ * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
+ * Отвечает только за создание Proxy с расширенной поддержкой операций.
6
+ *
7
+ * @template T Тип формы (объект)
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const fieldRegistry = new FieldRegistry<FormType>();
12
+ * const proxyBuilder = new ProxyBuilder(fieldRegistry);
13
+ * const proxy = proxyBuilder.build(groupNode);
14
+ *
15
+ * // Теперь можно обращаться к полям напрямую:
16
+ * console.log(proxy.email.value);
17
+ * console.log('email' in proxy); // true
18
+ * ```
19
+ */
20
+ /**
21
+ * Строитель Proxy для GroupNode
22
+ *
23
+ * Создает Proxy с поддержкой:
24
+ * - get: Прямой доступ к полям через точечную нотацию
25
+ * - set: Предупреждение о попытке прямой установки полей
26
+ * - has: Проверка существования поля ('email' in form)
27
+ * - ownKeys: Перечисление всех ключей (Object.keys(form))
28
+ * - getOwnPropertyDescriptor: Получение дескрипторов полей
29
+ *
30
+ * @template T Тип формы (объект)
31
+ */
32
+ export class ProxyBuilder {
33
+ fieldRegistry;
34
+ /**
35
+ * @param fieldRegistry - Реестр полей для доступа к коллекции
36
+ */
37
+ constructor(fieldRegistry) {
38
+ this.fieldRegistry = fieldRegistry;
39
+ }
40
+ /**
41
+ * Создать Proxy для GroupNode
42
+ *
43
+ * Proxy позволяет обращаться к полям формы напрямую:
44
+ * - form.email вместо form.fields.get('email')
45
+ * - form.address.city вместо form.fields.get('address').fields.get('city')
46
+ *
47
+ * @param target - GroupNode для которого создается Proxy
48
+ * @returns Proxy с типобезопасным доступом к полям
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const proxy = proxyBuilder.build(groupNode);
53
+ *
54
+ * // Доступ к полям
55
+ * console.log(proxy.email.value); // Работает!
56
+ * console.log(proxy.name.value); // Работает!
57
+ *
58
+ * // Доступ к методам GroupNode
59
+ * await proxy.validate(); // Работает!
60
+ * proxy.markAsTouched(); // Работает!
61
+ *
62
+ * // Проверка существования
63
+ * if ('email' in proxy) { ... }
64
+ *
65
+ * // Перечисление ключей
66
+ * Object.keys(proxy); // ['email', 'name', ...]
67
+ * ```
68
+ */
69
+ build(target) {
70
+ return new Proxy(target, {
71
+ /**
72
+ * Get trap: Перехват доступа к свойствам
73
+ *
74
+ * Приоритет:
75
+ * 1. Собственные свойства и методы GroupNode (validate, setValue и т.д.)
76
+ * 2. Поля формы из fieldRegistry
77
+ * 3. undefined для несуществующих свойств
78
+ */
79
+ get: (target, prop) => {
80
+ // Приоритет 1: Собственные свойства и методы GroupNode
81
+ // Это важно, чтобы методы validate(), setValue() и т.д. работали корректно
82
+ if (prop in target) {
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ return target[prop];
85
+ }
86
+ // Приоритет 2: Поля формы
87
+ // Используем fieldRegistry для типобезопасного доступа
88
+ if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
89
+ return this.fieldRegistry.get(prop);
90
+ }
91
+ // Fallback для несуществующих свойств
92
+ return undefined;
93
+ },
94
+ /**
95
+ * Set trap: Перехват установки свойств
96
+ *
97
+ * Запрещает прямую установку значений полей через form.email = value
98
+ * Пользователь должен использовать form.email.setValue(value) или form.setValue({...})
99
+ */
100
+ set: (target, prop, value) => {
101
+ // Запретить прямое изменение полей
102
+ if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
103
+ if (import.meta.env.DEV) {
104
+ console.warn(`[GroupNode] Cannot set field "${prop}" directly. Use .setValue() or .patchValue() instead.\n` +
105
+ `Example: form.setValue({ ${prop}: ${JSON.stringify(value)} })`);
106
+ }
107
+ return false;
108
+ }
109
+ // Разрешить установку других свойств
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ target[prop] = value;
112
+ return true;
113
+ },
114
+ /**
115
+ * Has trap: Перехват оператора 'in'
116
+ *
117
+ * Позволяет проверять существование полей:
118
+ * if ('email' in form) { ... }
119
+ */
120
+ has: (target, prop) => {
121
+ // Проверяем наличие в полях
122
+ if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
123
+ return true;
124
+ }
125
+ // Проверяем наличие в самом объекте
126
+ return prop in target;
127
+ },
128
+ /**
129
+ * OwnKeys trap: Перехват Object.keys() / Object.getOwnPropertyNames()
130
+ *
131
+ * Возвращает объединенный список:
132
+ * - Ключей самого GroupNode
133
+ * - Ключей полей из fieldRegistry
134
+ */
135
+ ownKeys: (target) => {
136
+ const nodeKeys = Reflect.ownKeys(target);
137
+ const fieldKeys = Array.from(this.fieldRegistry.keys());
138
+ // Используем Set для удаления дубликатов
139
+ return [...new Set([...nodeKeys, ...fieldKeys])];
140
+ },
141
+ /**
142
+ * GetOwnPropertyDescriptor trap: Перехват Object.getOwnPropertyDescriptor()
143
+ *
144
+ * Возвращает дескриптор свойства для полей и свойств GroupNode
145
+ * Важно для корректной работы Object.keys() и других рефлексивных операций
146
+ */
147
+ getOwnPropertyDescriptor: (target, prop) => {
148
+ // Дескриптор для полей формы
149
+ if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
150
+ return {
151
+ enumerable: true, // Поле должно быть перечисляемым
152
+ configurable: true, // Поле может быть удалено
153
+ // Не указываем writable, т.к. это accessor property через get/set traps
154
+ };
155
+ }
156
+ // Дескриптор для свойств самого объекта
157
+ return Reflect.getOwnPropertyDescriptor(target, prop);
158
+ },
159
+ });
160
+ }
161
+ }