@reformer/core 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/dist/behaviors/compute-from.d.ts +2 -0
  4. package/dist/behaviors/compute-from.js +31 -0
  5. package/dist/behaviors/copy-from.d.ts +2 -0
  6. package/dist/behaviors/copy-from.js +29 -0
  7. package/dist/behaviors/enable-when.d.ts +2 -0
  8. package/dist/behaviors/enable-when.js +25 -0
  9. package/dist/behaviors/reset-when.d.ts +2 -0
  10. package/dist/behaviors/reset-when.js +24 -0
  11. package/dist/behaviors/revalidate-when.d.ts +2 -0
  12. package/dist/behaviors/revalidate-when.js +18 -0
  13. package/dist/behaviors/sync-fields.d.ts +2 -0
  14. package/dist/behaviors/sync-fields.js +41 -0
  15. package/dist/behaviors/transform-value.d.ts +2 -0
  16. package/dist/behaviors/transform-value.js +45 -0
  17. package/dist/behaviors/watch-field.d.ts +2 -0
  18. package/dist/behaviors/watch-field.js +21 -0
  19. package/dist/behaviors.d.ts +6 -0
  20. package/dist/behaviors.js +29 -0
  21. package/dist/core/behavior/behavior-context.d.ts +47 -0
  22. package/dist/core/behavior/behavior-registry.d.ts +85 -0
  23. package/dist/core/behavior/behaviors/compute-from.d.ts +70 -0
  24. package/dist/core/behavior/behaviors/copy-from.d.ts +56 -0
  25. package/dist/core/behavior/behaviors/enable-when.d.ts +118 -0
  26. package/dist/core/behavior/behaviors/index.d.ts +11 -0
  27. package/dist/core/behavior/behaviors/reset-when.d.ts +64 -0
  28. package/dist/core/behavior/behaviors/revalidate-when.d.ts +53 -0
  29. package/dist/core/behavior/behaviors/sync-fields.d.ts +48 -0
  30. package/dist/core/behavior/behaviors/transform-value.d.ts +192 -0
  31. package/dist/core/behavior/behaviors/watch-field.d.ts +80 -0
  32. package/dist/core/behavior/compose-behavior.d.ts +96 -0
  33. package/dist/core/behavior/index.d.ts +11 -0
  34. package/dist/core/behavior/types.d.ts +146 -0
  35. package/dist/core/factories/index.d.ts +6 -0
  36. package/dist/core/factories/node-factory.d.ts +186 -0
  37. package/dist/core/nodes/array-node.d.ts +328 -0
  38. package/dist/core/nodes/field-node.d.ts +294 -0
  39. package/dist/core/nodes/form-node.d.ts +340 -0
  40. package/dist/core/nodes/group-node.d.ts +319 -0
  41. package/dist/core/types/deep-schema.d.ts +68 -0
  42. package/dist/core/types/field-path.d.ts +42 -0
  43. package/dist/core/types/form-context.d.ts +89 -0
  44. package/dist/core/types/form-proxy.d.ts +105 -0
  45. package/dist/core/types/index.d.ts +209 -0
  46. package/dist/core/types/validation-schema.d.ts +95 -0
  47. package/dist/core/utils/abstract-registry.d.ts +74 -0
  48. package/dist/core/utils/aggregate-signals.d.ts +71 -0
  49. package/dist/core/utils/create-form.d.ts +44 -0
  50. package/dist/core/utils/error-handler.d.ts +163 -0
  51. package/dist/core/utils/field-path-navigator.d.ts +240 -0
  52. package/dist/core/utils/field-path.d.ts +60 -0
  53. package/dist/core/utils/form-observer.d.ts +176 -0
  54. package/dist/core/utils/form-proxy-builder.d.ts +25 -0
  55. package/dist/core/utils/form-submitter.d.ts +121 -0
  56. package/dist/core/utils/index.d.ts +22 -0
  57. package/dist/core/utils/registry-helpers.d.ts +43 -0
  58. package/dist/core/utils/registry-stack.d.ts +69 -0
  59. package/dist/core/utils/safe-effect.d.ts +73 -0
  60. package/dist/core/utils/status-machine.d.ts +153 -0
  61. package/dist/core/utils/subscription-manager.d.ts +180 -0
  62. package/dist/core/utils/type-guards.d.ts +98 -0
  63. package/dist/core/utils/unique-id.d.ts +53 -0
  64. package/dist/core/validation/core/apply-when.d.ts +22 -0
  65. package/dist/core/validation/core/apply.d.ts +52 -0
  66. package/dist/core/validation/core/index.d.ts +8 -0
  67. package/dist/core/validation/core/validate-async.d.ts +36 -0
  68. package/dist/core/validation/core/validate-tree.d.ts +35 -0
  69. package/dist/core/validation/core/validate.d.ts +26 -0
  70. package/dist/core/validation/index.d.ts +27 -0
  71. package/dist/core/validation/validate-form.d.ts +48 -0
  72. package/dist/core/validation/validation-applicator.d.ts +70 -0
  73. package/dist/core/validation/validation-context.d.ts +86 -0
  74. package/dist/core/validation/validation-registry.d.ts +142 -0
  75. package/dist/core/validation/validators/array-validators.d.ts +53 -0
  76. package/dist/core/validation/validators/date-utils.d.ts +26 -0
  77. package/dist/core/validation/validators/email.d.ts +37 -0
  78. package/dist/core/validation/validators/future-date.d.ts +35 -0
  79. package/dist/core/validation/validators/index.d.ts +20 -0
  80. package/dist/core/validation/validators/is-date.d.ts +36 -0
  81. package/dist/core/validation/validators/max-age.d.ts +36 -0
  82. package/dist/core/validation/validators/max-date.d.ts +36 -0
  83. package/dist/core/validation/validators/max-length.d.ts +38 -0
  84. package/dist/core/validation/validators/max.d.ts +38 -0
  85. package/dist/core/validation/validators/min-age.d.ts +36 -0
  86. package/dist/core/validation/validators/min-date.d.ts +36 -0
  87. package/dist/core/validation/validators/min-length.d.ts +38 -0
  88. package/dist/core/validation/validators/min.d.ts +38 -0
  89. package/dist/core/validation/validators/number.d.ts +31 -0
  90. package/dist/core/validation/validators/past-date.d.ts +35 -0
  91. package/dist/core/validation/validators/pattern.d.ts +40 -0
  92. package/dist/core/validation/validators/phone.d.ts +27 -0
  93. package/dist/core/validation/validators/required.d.ts +41 -0
  94. package/dist/core/validation/validators/url.d.ts +22 -0
  95. package/dist/date-utils-xUWFslTj.js +29 -0
  96. package/dist/field-path-DuKdGcIE.js +66 -0
  97. package/dist/hooks/types.d.ts +328 -0
  98. package/dist/hooks/useArrayLength.d.ts +31 -0
  99. package/dist/hooks/useFormControl.d.ts +24 -0
  100. package/dist/hooks/useFormControlValue.d.ts +167 -0
  101. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  102. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  103. package/dist/index-D25LsbRm.js +73 -0
  104. package/dist/index.d.ts +17 -0
  105. package/dist/index.js +3271 -0
  106. package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
  107. package/dist/safe-effect-Dh8uw81c.js +20 -0
  108. package/dist/validate-C3XiA_zf.js +10 -0
  109. package/dist/validators/email.d.ts +2 -0
  110. package/dist/validators/email.js +13 -0
  111. package/dist/validators/future-date.d.ts +2 -0
  112. package/dist/validators/future-date.js +20 -0
  113. package/dist/validators/is-date.d.ts +2 -0
  114. package/dist/validators/is-date.js +12 -0
  115. package/dist/validators/max-age.d.ts +2 -0
  116. package/dist/validators/max-age.js +20 -0
  117. package/dist/validators/max-date.d.ts +2 -0
  118. package/dist/validators/max-date.js +20 -0
  119. package/dist/validators/max-length.d.ts +2 -0
  120. package/dist/validators/max-length.js +11 -0
  121. package/dist/validators/max.d.ts +2 -0
  122. package/dist/validators/max.js +11 -0
  123. package/dist/validators/min-age.d.ts +2 -0
  124. package/dist/validators/min-age.js +20 -0
  125. package/dist/validators/min-date.d.ts +2 -0
  126. package/dist/validators/min-date.js +20 -0
  127. package/dist/validators/min-length.d.ts +2 -0
  128. package/dist/validators/min-length.js +11 -0
  129. package/dist/validators/min.d.ts +2 -0
  130. package/dist/validators/min.js +11 -0
  131. package/dist/validators/number.d.ts +2 -0
  132. package/dist/validators/number.js +35 -0
  133. package/dist/validators/past-date.d.ts +2 -0
  134. package/dist/validators/past-date.js +20 -0
  135. package/dist/validators/pattern.d.ts +2 -0
  136. package/dist/validators/pattern.js +11 -0
  137. package/dist/validators/phone.d.ts +2 -0
  138. package/dist/validators/phone.js +35 -0
  139. package/dist/validators/required.d.ts +2 -0
  140. package/dist/validators/required.js +15 -0
  141. package/dist/validators/url.d.ts +2 -0
  142. package/dist/validators/url.js +19 -0
  143. package/dist/validators-BGsNOgT1.js +207 -0
  144. package/dist/validators.d.ts +6 -0
  145. package/dist/validators.js +56 -0
  146. package/llms.txt +9351 -0
  147. package/package.json +166 -0
@@ -0,0 +1,176 @@
1
+ import { FormFields, FormValue, FieldStatus, ValidationError } from '../types';
2
+ /**
3
+ * Тип события изменения в форме
4
+ */
5
+ export type FormChangeType = 'value' | 'status' | 'errors' | 'touched' | 'dirty';
6
+ /**
7
+ * Событие изменения в форме
8
+ */
9
+ export interface FormChangeEvent {
10
+ /** Тип изменения */
11
+ type: FormChangeType;
12
+ /** Путь к полю */
13
+ path: string;
14
+ /** Timestamp события */
15
+ timestamp: number;
16
+ /** Старое значение */
17
+ oldValue?: unknown;
18
+ /** Новое значение */
19
+ newValue: unknown;
20
+ }
21
+ /**
22
+ * Интерфейс узла формы для observer
23
+ */
24
+ export interface ObservableFormNode {
25
+ value: {
26
+ value: FormValue;
27
+ };
28
+ status: {
29
+ value: FieldStatus;
30
+ };
31
+ errors: {
32
+ value: ValidationError[];
33
+ };
34
+ touched: {
35
+ value: boolean;
36
+ };
37
+ dirty: {
38
+ value: boolean;
39
+ };
40
+ }
41
+ /**
42
+ * Интерфейс формы для observer
43
+ */
44
+ export interface ObservableForm<T extends FormFields> {
45
+ value: {
46
+ value: T;
47
+ };
48
+ status: {
49
+ value: FieldStatus;
50
+ };
51
+ errors: {
52
+ value: ValidationError[];
53
+ };
54
+ touched: {
55
+ value: boolean;
56
+ };
57
+ dirty: {
58
+ value: boolean;
59
+ };
60
+ getFieldByPath(path: string): ObservableFormNode | undefined;
61
+ }
62
+ /**
63
+ * Callback для обработки событий изменения
64
+ */
65
+ export type FormChangeCallback = (event: FormChangeEvent) => void;
66
+ /**
67
+ * Опции для FormObserver
68
+ */
69
+ export interface FormObserverOptions {
70
+ /** Включить логирование в консоль (по умолчанию true в DEV) */
71
+ enableLogging?: boolean;
72
+ /** Фильтр типов событий */
73
+ eventTypes?: FormChangeType[];
74
+ /** Фильтр путей полей (regex или массив) */
75
+ pathFilter?: RegExp | string[];
76
+ }
77
+ /**
78
+ * FormObserver - мониторинг изменений в форме
79
+ *
80
+ * Полезен для:
81
+ * - Отладки сложных форм
82
+ * - Логирования изменений для аудита
83
+ * - Синхронизации с внешними системами
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const observer = new FormObserver(form, {
88
+ * enableLogging: true,
89
+ * eventTypes: ['value', 'errors']
90
+ * });
91
+ *
92
+ * // Подписка на события
93
+ * const unsubscribe = observer.subscribe((event) => {
94
+ * console.log(`${event.type} at ${event.path}:`, event.newValue);
95
+ * });
96
+ *
97
+ * // Включить трассировку
98
+ * const disposeTracing = observer.enableTracing();
99
+ *
100
+ * // Cleanup
101
+ * unsubscribe();
102
+ * disposeTracing();
103
+ * ```
104
+ */
105
+ export declare class FormObserver<T extends FormFields> {
106
+ private readonly form;
107
+ private listeners;
108
+ private disposers;
109
+ private options;
110
+ /**
111
+ * @param form - Форма для наблюдения
112
+ * @param options - Опции observer
113
+ */
114
+ constructor(form: ObservableForm<T>, options?: FormObserverOptions);
115
+ /**
116
+ * Подписаться на события изменения
117
+ *
118
+ * @param callback - Функция обработки события
119
+ * @returns Функция отписки
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const unsubscribe = observer.subscribe((event) => {
124
+ * // Отправить событие в analytics
125
+ * analytics.track('form_change', event);
126
+ * });
127
+ * ```
128
+ */
129
+ subscribe(callback: FormChangeCallback): () => void;
130
+ /**
131
+ * Включить трассировку формы
132
+ *
133
+ * Подписывается на изменения основных сигналов формы
134
+ * и вызывает listeners при каждом изменении
135
+ *
136
+ * @returns Функция для отключения трассировки
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const dispose = observer.enableTracing();
141
+ *
142
+ * // Позже, для отключения
143
+ * dispose();
144
+ * ```
145
+ */
146
+ enableTracing(): () => void;
147
+ /**
148
+ * Наблюдать за конкретным полем
149
+ *
150
+ * @param path - Путь к полю
151
+ * @returns Функция для отключения наблюдения
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * // Наблюдать за полем email
156
+ * const dispose = observer.watchField('email');
157
+ * ```
158
+ */
159
+ watchField(path: string): () => void;
160
+ /**
161
+ * Отправить событие всем listeners
162
+ */
163
+ private emit;
164
+ /**
165
+ * Проверить, нужно ли отслеживать тип события
166
+ */
167
+ private shouldTrack;
168
+ /**
169
+ * Проверить, соответствует ли путь фильтру
170
+ */
171
+ private matchesPathFilter;
172
+ /**
173
+ * Очистить все подписки и disposers
174
+ */
175
+ dispose(): void;
176
+ }
@@ -0,0 +1,25 @@
1
+ import { FormNode } from '../nodes/form-node';
2
+ import { FormProxy } from '../types/form-proxy';
3
+ /**
4
+ * Создать Proxy для типобезопасного доступа к полям GroupNode
5
+ *
6
+ * Proxy обеспечивает:
7
+ * - Доступ к полям через точечную нотацию (form.email, form.address.city)
8
+ * - Приоритет собственных свойств GroupNode над полями
9
+ * - Цепочку proxy для вложенных GroupNode
10
+ * - Блокировку прямого присваивания полям (только через setValue/patchValue)
11
+ *
12
+ * @param target - GroupNode для которого создаётся proxy
13
+ * @param fields - Map полей формы
14
+ * @returns Типизированный Proxy
15
+ *
16
+ * @internal
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const proxy = buildFormProxy(groupNode, groupNode._fields);
21
+ * proxy.email.setValue('test@example.com');
22
+ * console.log(proxy.email.value.value);
23
+ * ```
24
+ */
25
+ export declare function buildFormProxy<T>(target: FormNode<T>, fields: Map<keyof T, FormNode<unknown>>): FormProxy<T>;
@@ -0,0 +1,121 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { FormFields } from '../types';
3
+ /**
4
+ * Интерфейс формы для FormSubmitter
5
+ * Минимальный контракт для работы с любой формой
6
+ */
7
+ export interface SubmittableForm<T extends FormFields> {
8
+ /** Пометить все поля как touched */
9
+ markAsTouched(): void;
10
+ /** Валидировать форму */
11
+ validate(): Promise<boolean>;
12
+ /** Получить значения формы */
13
+ getValue(): T;
14
+ }
15
+ /**
16
+ * Опции для submit
17
+ */
18
+ export interface SubmitOptions {
19
+ /** Пропустить валидацию перед submit */
20
+ skipValidation?: boolean;
21
+ /** Пропустить markAsTouched перед submit */
22
+ skipTouch?: boolean;
23
+ }
24
+ /**
25
+ * Результат submit
26
+ */
27
+ export interface SubmitResult<R> {
28
+ /** Успешно ли выполнен submit */
29
+ success: boolean;
30
+ /** Результат от onSubmit callback */
31
+ data: R | null;
32
+ /** Ошибка, если submit не удался */
33
+ error?: Error;
34
+ }
35
+ /**
36
+ * FormSubmitter - управляет процессом отправки формы
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const submitter = new FormSubmitter(form);
41
+ *
42
+ * // Простой submit
43
+ * const result = await submitter.submit(async (values) => {
44
+ * return await api.saveForm(values);
45
+ * });
46
+ *
47
+ * // Проверка состояния
48
+ * if (submitter.submitting.value) {
49
+ * console.log('Форма отправляется...');
50
+ * }
51
+ * ```
52
+ */
53
+ export declare class FormSubmitter<T extends FormFields> {
54
+ private readonly form;
55
+ /** Внутренний сигнал состояния отправки */
56
+ private readonly _submitting;
57
+ /** Публичный read-only сигнал состояния отправки */
58
+ readonly submitting: ReadonlySignal<boolean>;
59
+ /**
60
+ * @param form - Форма для отправки
61
+ */
62
+ constructor(form: SubmittableForm<T>);
63
+ /**
64
+ * Отправить форму
65
+ *
66
+ * Процесс:
67
+ * 1. Помечает все поля как touched (для отображения ошибок)
68
+ * 2. Валидирует форму
69
+ * 3. Если валидация успешна - вызывает onSubmit
70
+ * 4. Управляет состоянием submitting
71
+ *
72
+ * @param onSubmit - Callback для отправки данных
73
+ * @param options - Опции submit
74
+ * @returns Результат от onSubmit или null если валидация не пройдена
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const result = await submitter.submit(async (values) => {
79
+ * const response = await fetch('/api/form', {
80
+ * method: 'POST',
81
+ * body: JSON.stringify(values)
82
+ * });
83
+ * return response.json();
84
+ * });
85
+ *
86
+ * if (result === null) {
87
+ * console.log('Форма не прошла валидацию');
88
+ * }
89
+ * ```
90
+ */
91
+ submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
92
+ /**
93
+ * Отправить форму с расширенным результатом
94
+ *
95
+ * В отличие от submit(), возвращает объект с информацией об успехе/ошибке
96
+ *
97
+ * @param onSubmit - Callback для отправки данных
98
+ * @param options - Опции submit
99
+ * @returns Объект SubmitResult с данными и статусом
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const result = await submitter.submitWithResult(async (values) => {
104
+ * return await api.saveForm(values);
105
+ * });
106
+ *
107
+ * if (result.success) {
108
+ * console.log('Сохранено:', result.data);
109
+ * } else if (result.error) {
110
+ * console.error('Ошибка:', result.error.message);
111
+ * } else {
112
+ * console.log('Валидация не пройдена');
113
+ * }
114
+ * ```
115
+ */
116
+ submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
117
+ /**
118
+ * Проверить, идет ли отправка формы
119
+ */
120
+ isSubmitting(): boolean;
121
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Утилиты для работы с формами
3
+ *
4
+ * Централизованные вспомогательные классы и функции.
5
+ */
6
+ export { FieldPathNavigator, type PathSegment } from './field-path-navigator';
7
+ export { createFieldPath, extractPath, extractKey, toFieldPath } from './field-path';
8
+ export { SubscriptionManager } from './subscription-manager';
9
+ export { getCurrentValidationRegistry, getCurrentBehaviorRegistry } from './registry-helpers';
10
+ export { RegistryStack } from './registry-stack';
11
+ export { isFormNode, isFieldNode, isGroupNode, isArrayNode, getNodeType } from './type-guards';
12
+ export { FormErrorHandler, ErrorStrategy } from './error-handler';
13
+ export { createForm } from './create-form';
14
+ export { uniqueId } from './unique-id';
15
+ export { safeCallback, runOutsideEffect, safeDebouncedCallback } from './safe-effect';
16
+ export { AbstractRegistry } from './abstract-registry';
17
+ export { FormSubmitter } from './form-submitter';
18
+ export type { SubmittableForm, SubmitOptions, SubmitResult } from './form-submitter';
19
+ export { FormStatusMachine } from './status-machine';
20
+ export type { StatusEvent } from './status-machine';
21
+ export { FormObserver } from './form-observer';
22
+ export type { FormChangeType, FormChangeEvent, FormChangeCallback, FormObserverOptions, ObservableForm, ObservableFormNode, } from './form-observer';
@@ -0,0 +1,43 @@
1
+ import { ValidationRegistry } from '../validation/validation-registry';
2
+ import { BehaviorRegistry } from '../behavior/behavior-registry';
3
+ /**
4
+ * Получить текущий активный ValidationRegistry из context stack
5
+ *
6
+ * Используется внутри validation schema функций (validate, validateAsync, required и т.д.)
7
+ * для регистрации валидаторов в активном контексте GroupNode
8
+ *
9
+ * @returns Текущий ValidationRegistry или заглушка в production
10
+ * @throws Error если нет активного контекста (только в DEV режиме)
11
+ *
12
+ * @internal
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * function required(fieldPath, options) {
17
+ * const registry = getCurrentValidationRegistry();
18
+ * registry.registerSync(path, validator, options);
19
+ * }
20
+ * ```
21
+ */
22
+ export declare function getCurrentValidationRegistry(): ValidationRegistry;
23
+ /**
24
+ * Получить текущий активный BehaviorRegistry из context stack
25
+ *
26
+ * Используется внутри behavior schema функций (copyFrom, enableWhen, computeFrom и т.д.)
27
+ * для регистрации поведений в активном контексте GroupNode
28
+ *
29
+ * @returns Текущий BehaviorRegistry или заглушка в production
30
+ * @throws Error если нет активного контекста (только в DEV режиме)
31
+ *
32
+ * @internal
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * function copyFrom(target, source, options) {
37
+ * const registry = getCurrentBehaviorRegistry();
38
+ * const handler = createCopyBehavior(target, source, options);
39
+ * registry.register(handler, { debounce: options?.debounce });
40
+ * }
41
+ * ```
42
+ */
43
+ export declare function getCurrentBehaviorRegistry(): BehaviorRegistry;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Generic Registry Stack - утилита для управления стеком регистрации
3
+ *
4
+ * Используется ValidationRegistry и BehaviorRegistry для tracking активного контекста.
5
+ * Устраняет дублирование кода между параллельными системами.
6
+ *
7
+ * @template T - Тип элементов в стеке (ValidationRegistry или BehaviorRegistry)
8
+ *
9
+ * @internal
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * class ValidationRegistry {
14
+ * private static registryStack = new RegistryStack<ValidationRegistry>();
15
+ *
16
+ * static getCurrent() {
17
+ * return ValidationRegistry.registryStack.getCurrent();
18
+ * }
19
+ *
20
+ * beginRegistration() {
21
+ * ValidationRegistry.registryStack.push(this);
22
+ * }
23
+ *
24
+ * endRegistration() {
25
+ * ValidationRegistry.registryStack.verify(this, 'ValidationRegistry');
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ export declare class RegistryStack<T> {
31
+ private stack;
32
+ /**
33
+ * Добавить элемент в стек
34
+ * @param item - Элемент для добавления
35
+ */
36
+ push(item: T): void;
37
+ /**
38
+ * Извлечь элемент из стека
39
+ * @returns Извлеченный элемент или undefined если стек пуст
40
+ */
41
+ pop(): T | undefined;
42
+ /**
43
+ * Получить текущий элемент (вершину стека) без извлечения
44
+ * @returns Текущий элемент или null если стек пуст
45
+ */
46
+ getCurrent(): T | null;
47
+ /**
48
+ * Извлечь элемент из стека с проверкой
49
+ * Выводит предупреждение в DEV режиме если извлеченный элемент не совпадает с ожидаемым
50
+ *
51
+ * @param expected - Ожидаемый элемент
52
+ * @param name - Имя реестра для отладки (например, 'ValidationRegistry')
53
+ */
54
+ verify(expected: T, name: string): void;
55
+ /**
56
+ * Получить длину стека
57
+ * @returns Количество элементов в стеке
58
+ */
59
+ get length(): number;
60
+ /**
61
+ * Проверить, пуст ли стек
62
+ * @returns true если стек пуст
63
+ */
64
+ isEmpty(): boolean;
65
+ /**
66
+ * Очистить стек
67
+ */
68
+ clear(): void;
69
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Утилиты для безопасной работы с сигналами в effect контексте
3
+ *
4
+ * Эти утилиты решают проблему "Cycle detected" в @preact/signals-core,
5
+ * которая возникает при модификации сигналов внутри effect.
6
+ *
7
+ * @group Utils
8
+ * @module utils/safe-effect
9
+ */
10
+ /**
11
+ * Создает callback, который выполняется вне контекста effect
12
+ *
13
+ * Использует queueMicrotask для отложенного выполнения,
14
+ * что позволяет модифицировать сигналы без вызова "Cycle detected"
15
+ *
16
+ * @param callback - Функция для выполнения
17
+ * @returns Обёрнутая функция, безопасная для вызова внутри effect
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // Вместо:
22
+ * effect(() => {
23
+ * queueMicrotask(() => {
24
+ * callback(value, context);
25
+ * });
26
+ * });
27
+ *
28
+ * // Используем:
29
+ * effect(() => {
30
+ * safeCallback(callback)(value, context);
31
+ * });
32
+ * ```
33
+ */
34
+ export declare function safeCallback<TArgs extends unknown[]>(callback: (...args: TArgs) => void | Promise<void>): (...args: TArgs) => void;
35
+ /**
36
+ * Выполняет функцию вне контекста effect
37
+ *
38
+ * Более простой вариант для одиночного вызова
39
+ *
40
+ * @param fn - Функция для выполнения
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * effect(() => {
45
+ * const value = signal.value;
46
+ * runOutsideEffect(() => {
47
+ * otherSignal.value = transform(value);
48
+ * });
49
+ * });
50
+ * ```
51
+ */
52
+ export declare function runOutsideEffect(fn: () => void | Promise<void>): void;
53
+ /**
54
+ * Создает версию callback с поддержкой debounce, безопасную для effect
55
+ *
56
+ * Комбинирует debounce логику с выходом из effect контекста
57
+ *
58
+ * @param callback - Функция для выполнения
59
+ * @param withDebounce - Функция debounce обёртки из BehaviorRegistry
60
+ * @returns Обёрнутая функция
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * return effect(() => {
65
+ * const value = node.value.value;
66
+ * safeDebouncedCallback(
67
+ * () => callback(value, context),
68
+ * withDebounce
69
+ * )();
70
+ * });
71
+ * ```
72
+ */
73
+ export declare function safeDebouncedCallback(callback: () => void | Promise<void>, withDebounce: (fn: () => void) => void): () => void;
@@ -0,0 +1,153 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { FieldStatus } from '../types';
3
+ /**
4
+ * События для State Machine
5
+ */
6
+ export type StatusEvent = {
7
+ type: 'START_VALIDATION';
8
+ } | {
9
+ type: 'VALIDATION_SUCCESS';
10
+ } | {
11
+ type: 'VALIDATION_FAILURE';
12
+ } | {
13
+ type: 'DISABLE';
14
+ } | {
15
+ type: 'ENABLE';
16
+ hasErrors?: boolean;
17
+ } | {
18
+ type: 'SET_ERRORS';
19
+ hasErrors: boolean;
20
+ };
21
+ /**
22
+ * FormStatusMachine - управляет состоянием поля формы
23
+ *
24
+ * Предоставляет:
25
+ * - Единый источник истины для статуса
26
+ * - Computed signals для derived состояний (valid, invalid, pending, disabled)
27
+ * - Валидацию переходов между состояниями
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const statusMachine = new FormStatusMachine('valid');
32
+ *
33
+ * // Начало валидации
34
+ * statusMachine.startValidation();
35
+ * console.log(statusMachine.pending.value); // true
36
+ *
37
+ * // Завершение валидации с ошибками
38
+ * statusMachine.completeValidation(true);
39
+ * console.log(statusMachine.invalid.value); // true
40
+ *
41
+ * // Отключение поля
42
+ * statusMachine.disable();
43
+ * console.log(statusMachine.disabled.value); // true
44
+ * ```
45
+ */
46
+ export declare class FormStatusMachine {
47
+ /** Внутренний сигнал статуса */
48
+ private readonly _status;
49
+ /** Публичный read-only сигнал статуса */
50
+ readonly status: ReadonlySignal<FieldStatus>;
51
+ /** Поле валидно */
52
+ readonly valid: ReadonlySignal<boolean>;
53
+ /** Поле невалидно */
54
+ readonly invalid: ReadonlySignal<boolean>;
55
+ /** Идет валидация */
56
+ readonly pending: ReadonlySignal<boolean>;
57
+ /** Поле отключено */
58
+ readonly disabled: ReadonlySignal<boolean>;
59
+ /**
60
+ * @param initial - Начальный статус (по умолчанию 'valid')
61
+ */
62
+ constructor(initial?: FieldStatus);
63
+ /**
64
+ * Начать валидацию
65
+ *
66
+ * Переводит статус в 'pending' если поле не отключено
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * statusMachine.startValidation();
71
+ * // status: 'pending'
72
+ * ```
73
+ */
74
+ startValidation(): void;
75
+ /**
76
+ * Завершить валидацию
77
+ *
78
+ * @param hasErrors - Есть ли ошибки валидации
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // Валидация успешна
83
+ * statusMachine.completeValidation(false);
84
+ * // status: 'valid'
85
+ *
86
+ * // Есть ошибки
87
+ * statusMachine.completeValidation(true);
88
+ * // status: 'invalid'
89
+ * ```
90
+ */
91
+ completeValidation(hasErrors: boolean): void;
92
+ /**
93
+ * Установить ошибки напрямую (без перехода через pending)
94
+ *
95
+ * Используется для синхронной валидации или установки ошибок извне
96
+ *
97
+ * @param hasErrors - Есть ли ошибки
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * statusMachine.setErrors(true);
102
+ * // status: 'invalid'
103
+ * ```
104
+ */
105
+ setErrors(hasErrors: boolean): void;
106
+ /**
107
+ * Отключить поле
108
+ *
109
+ * Переводит статус в 'disabled'
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * statusMachine.disable();
114
+ * // status: 'disabled'
115
+ * ```
116
+ */
117
+ disable(): void;
118
+ /**
119
+ * Включить поле
120
+ *
121
+ * @param hasErrors - Есть ли ошибки (определяет valid/invalid)
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * statusMachine.enable(false);
126
+ * // status: 'valid'
127
+ *
128
+ * statusMachine.enable(true);
129
+ * // status: 'invalid'
130
+ * ```
131
+ */
132
+ enable(hasErrors?: boolean): void;
133
+ /**
134
+ * Обработать событие (альтернативный API)
135
+ *
136
+ * @param event - Событие для обработки
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * statusMachine.dispatch({ type: 'START_VALIDATION' });
141
+ * statusMachine.dispatch({ type: 'VALIDATION_FAILURE' });
142
+ * ```
143
+ */
144
+ dispatch(event: StatusEvent): void;
145
+ /**
146
+ * Получить текущий статус
147
+ */
148
+ getStatus(): FieldStatus;
149
+ /**
150
+ * Проверить, можно ли начать валидацию
151
+ */
152
+ canValidate(): boolean;
153
+ }