@neutro/form 0.0.4 → 0.1.0

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.
@@ -29,9 +29,10 @@ module.exports = __toCommonJS(angular_exports);
29
29
  var import_core = require("@angular/core");
30
30
  function useAngularForm(form) {
31
31
  const formSignal = (0, import_core.signal)(form.getState());
32
- const unsubscribe = form.subscribe((s) => {
33
- formSignal.set(s);
34
- });
32
+ const zone = (0, import_core.inject)(import_core.NgZone, { optional: true });
33
+ const unsubscribe = form.subscribe(
34
+ (s) => zone ? zone.run(() => formSignal.set(s)) : formSignal.set(s)
35
+ );
35
36
  (0, import_core.inject)(import_core.DestroyRef).onDestroy(unsubscribe);
36
37
  return {
37
38
  state: formSignal.asReadonly(),
@@ -42,19 +43,36 @@ function useAngularForm(form) {
42
43
  handleSubmit: form.handleSubmit,
43
44
  reset: form.reset,
44
45
  batch: form.batch,
46
+ validate: form.validate,
47
+ subscribeToPath: form.subscribeToPath,
48
+ getPayload: form.getPayload,
49
+ getAriaProps: form.getAriaProps,
50
+ getFieldMode: form.getFieldMode,
51
+ getConnectedCount: form.getConnectedCount,
52
+ destroy: form.destroy,
45
53
  arrayAppend: form.arrayAppend,
46
54
  arrayInsert: form.arrayInsert,
47
55
  arrayRemove: form.arrayRemove,
48
56
  arrayMove: form.arrayMove,
49
- arraySwap: form.arraySwap
57
+ arraySwap: form.arraySwap,
58
+ setErrors: form.setErrors,
59
+ clearErrors: form.clearErrors
50
60
  };
51
61
  }
52
62
  function useAngularFormPath(form, path) {
53
63
  const value = (0, import_core.signal)(form.get(path));
54
64
  const fieldState = (0, import_core.signal)(null);
65
+ const zone = (0, import_core.inject)(import_core.NgZone, { optional: true });
55
66
  const unsubscribe = form.subscribeToPath(path, (v, fs) => {
56
- value.set(v);
57
- fieldState.set(fs);
67
+ if (zone) {
68
+ zone.run(() => {
69
+ value.set(v);
70
+ fieldState.set(fs);
71
+ });
72
+ } else {
73
+ value.set(v);
74
+ fieldState.set(fs);
75
+ }
58
76
  });
59
77
  (0, import_core.inject)(import_core.DestroyRef).onDestroy(unsubscribe);
60
78
  return { value: value.asReadonly(), fieldState: fieldState.asReadonly() };
@@ -1,6 +1,171 @@
1
- export * from '@neutro/form-angular';
2
- import '@neutro/form-react';
3
- import '@neutro/form-solid';
4
- import '@neutro/form-svelte';
5
- import '@neutro/form-vue';
6
- import '@neutro/form-core';
1
+ import { Signal } from '@angular/core';
2
+
3
+ /**
4
+ * @neutro/form-core
5
+ * High-Performance, Zero-Dependency, Framework-Agnostic Reactive Form Engine.
6
+ */
7
+ type Primitive = string | number | boolean | null | undefined | Date | File;
8
+ type Prev = [never, 0, 1, 2, 3, 4, 5, ...any[]];
9
+ type PathImpl<T, K extends keyof T, Depth extends number = 5> = [Depth] extends [never] ? never : K extends string ? T[K] extends Primitive ? K : T[K] extends Array<infer U> ? K | `${K}.${number}` | (U extends object ? `${K}.${number}.${PathImpl<U, keyof U, Prev[Depth]>}` : never) : NonNullable<T[K]> extends object ? K | `${K}.${PathImpl<NonNullable<T[K]>, keyof NonNullable<T[K]>, Prev[Depth]>}` : K : never;
10
+ type Path<T> = PathImpl<T, keyof T> & string;
11
+ type _GetPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? _GetPathValue<NonNullable<T[K]>, Rest> : T extends readonly any[] ? _GetPathValue<NonNullable<T[number]>, Rest> : unknown : P extends keyof T ? T[P] : T extends readonly any[] ? T[number] : unknown;
12
+ type GetPathValue<T, P extends string> = _GetPathValue<T, P>;
13
+ interface FormState<T> {
14
+ values: T;
15
+ errors: Record<string, string>;
16
+ touched: Record<string, boolean>;
17
+ dirty: Record<string, boolean>;
18
+ isSubmitting: boolean;
19
+ isValidating: boolean;
20
+ }
21
+ type FormSubscriber<T> = (state: FormState<T>) => void;
22
+ type PathSubscriber<V = any> = (value: V, fieldState: {
23
+ error?: string;
24
+ touched?: boolean;
25
+ dirty?: boolean;
26
+ }) => void;
27
+ type ValidationMode = 'onChange' | 'onBlur' | 'onTouched' | 'onSubmitOnly';
28
+ type FormAction = {
29
+ type: 'SET';
30
+ path: string;
31
+ value: unknown;
32
+ options?: {
33
+ touch?: boolean;
34
+ validate?: boolean;
35
+ };
36
+ } | {
37
+ type: 'VALIDATE';
38
+ paths?: string[];
39
+ } | {
40
+ type: 'SUBMIT';
41
+ } | {
42
+ type: 'RESET';
43
+ newValues?: unknown;
44
+ } | {
45
+ type: 'SET_ERRORS';
46
+ errors: Record<string, string>;
47
+ } | {
48
+ type: 'CONNECT';
49
+ path: string;
50
+ } | {
51
+ type: 'DISCONNECT';
52
+ path: string;
53
+ } | {
54
+ type: 'BLUR';
55
+ path: string;
56
+ } | {
57
+ type: 'BATCH_START';
58
+ } | {
59
+ type: 'BATCH_END';
60
+ } | {
61
+ type: 'ARRAY_APPEND';
62
+ path: string;
63
+ item: unknown;
64
+ } | {
65
+ type: 'ARRAY_INSERT';
66
+ path: string;
67
+ index: number;
68
+ item: unknown;
69
+ } | {
70
+ type: 'ARRAY_REMOVE';
71
+ path: string;
72
+ index: number;
73
+ } | {
74
+ type: 'ARRAY_MOVE';
75
+ path: string;
76
+ from: number;
77
+ to: number;
78
+ } | {
79
+ type: 'ARRAY_SWAP';
80
+ path: string;
81
+ i: number;
82
+ j: number;
83
+ } | {
84
+ type: 'CLEAR_ERRORS';
85
+ };
86
+ interface AriaPropsOptions {
87
+ required?: boolean;
88
+ errorId?: string;
89
+ }
90
+ interface AriaProps {
91
+ 'aria-invalid': 'true' | 'false';
92
+ 'aria-describedby': string | undefined;
93
+ 'aria-required': true | undefined;
94
+ }
95
+ interface ConnectOptions {
96
+ persist?: boolean;
97
+ format?: (val: string) => string;
98
+ validateOn?: ValidationMode;
99
+ }
100
+ interface FormInstance<T extends object> {
101
+ subscribe: (fn: FormSubscriber<T>) => () => void;
102
+ subscribeToPath<P extends Path<T>>(path: P, fn: PathSubscriber<GetPathValue<T, P>>): () => void;
103
+ subscribeToPath(path: string, fn: PathSubscriber): () => void;
104
+ get<P extends Path<T>>(path: P): GetPathValue<T, P>;
105
+ get(path: string | string[]): any;
106
+ set: (path: Path<T> | string | string[], val: any, options?: {
107
+ touch?: boolean;
108
+ validate?: boolean;
109
+ }) => void;
110
+ validate: (scopePaths?: Path<T>[] | string[] | string[][]) => Promise<boolean>;
111
+ connect: (path: Path<T> | string, el: HTMLElement, options?: ConnectOptions) => () => void;
112
+ submit: (onValid: (payload: Partial<T>) => void | Promise<void>) => Promise<boolean>;
113
+ handleSubmit: (onValid: (payload: Partial<T>) => void | Promise<void>, onInvalid?: (errors: Record<string, string>) => void) => (e?: Event) => void;
114
+ getState: () => FormState<T>;
115
+ getPayload: () => Partial<T>;
116
+ getAriaProps: (path: Path<T> | string, options?: AriaPropsOptions) => AriaProps;
117
+ batch: (fn: () => void) => void;
118
+ arrayAppend: (path: Path<T> | string | string[], item: any) => void;
119
+ arrayInsert: (path: Path<T> | string | string[], index: number, item: any) => void;
120
+ arrayRemove: (path: Path<T> | string | string[], index: number) => void;
121
+ arrayMove: (path: Path<T> | string | string[], fromIndex: number, toIndex: number) => void;
122
+ arraySwap: (path: Path<T> | string | string[], indexA: number, indexB: number) => void;
123
+ reset: (newValues?: T) => void;
124
+ getConnectedCount: () => number;
125
+ destroy: () => void;
126
+ setErrors: (errors: Record<Path<T> | (string & {}), string>) => void;
127
+ clearErrors: () => void;
128
+ /**
129
+ * Returns the effective ValidationMode for a field. Useful for debugging
130
+ * validation timing; framework adapters should rely on this only in custom
131
+ * event handlers, not in render logic.
132
+ */
133
+ getFieldMode: (path: string) => ValidationMode;
134
+ _subscribeToActions: (fn: (action: FormAction, state: FormState<T>) => void) => () => void;
135
+ }
136
+
137
+ interface AngularFormReturn<T extends object> {
138
+ state: Signal<FormState<T>>;
139
+ get: FormInstance<T>['get'];
140
+ set: FormInstance<T>['set'];
141
+ connect: FormInstance<T>['connect'];
142
+ submit: FormInstance<T>['submit'];
143
+ handleSubmit: FormInstance<T>['handleSubmit'];
144
+ reset: FormInstance<T>['reset'];
145
+ batch: FormInstance<T>['batch'];
146
+ validate: FormInstance<T>['validate'];
147
+ subscribeToPath: FormInstance<T>['subscribeToPath'];
148
+ getPayload: FormInstance<T>['getPayload'];
149
+ getAriaProps: FormInstance<T>['getAriaProps'];
150
+ getFieldMode: FormInstance<T>['getFieldMode'];
151
+ getConnectedCount: FormInstance<T>['getConnectedCount'];
152
+ destroy: FormInstance<T>['destroy'];
153
+ arrayAppend: FormInstance<T>['arrayAppend'];
154
+ arrayInsert: FormInstance<T>['arrayInsert'];
155
+ arrayRemove: FormInstance<T>['arrayRemove'];
156
+ arrayMove: FormInstance<T>['arrayMove'];
157
+ arraySwap: FormInstance<T>['arraySwap'];
158
+ setErrors: FormInstance<T>['setErrors'];
159
+ clearErrors: FormInstance<T>['clearErrors'];
160
+ }
161
+ declare function useAngularForm<T extends object>(form: FormInstance<T>): AngularFormReturn<T>;
162
+ declare function useAngularFormPath<T extends object>(form: FormInstance<T>, path: string): {
163
+ value: Signal<unknown>;
164
+ fieldState: Signal<{
165
+ error?: string;
166
+ touched?: boolean;
167
+ dirty?: boolean;
168
+ } | null>;
169
+ };
170
+
171
+ export { type AngularFormReturn, useAngularForm, useAngularFormPath };
@@ -1,6 +1,171 @@
1
- export * from '@neutro/form-angular';
2
- import '@neutro/form-react';
3
- import '@neutro/form-solid';
4
- import '@neutro/form-svelte';
5
- import '@neutro/form-vue';
6
- import '@neutro/form-core';
1
+ import { Signal } from '@angular/core';
2
+
3
+ /**
4
+ * @neutro/form-core
5
+ * High-Performance, Zero-Dependency, Framework-Agnostic Reactive Form Engine.
6
+ */
7
+ type Primitive = string | number | boolean | null | undefined | Date | File;
8
+ type Prev = [never, 0, 1, 2, 3, 4, 5, ...any[]];
9
+ type PathImpl<T, K extends keyof T, Depth extends number = 5> = [Depth] extends [never] ? never : K extends string ? T[K] extends Primitive ? K : T[K] extends Array<infer U> ? K | `${K}.${number}` | (U extends object ? `${K}.${number}.${PathImpl<U, keyof U, Prev[Depth]>}` : never) : NonNullable<T[K]> extends object ? K | `${K}.${PathImpl<NonNullable<T[K]>, keyof NonNullable<T[K]>, Prev[Depth]>}` : K : never;
10
+ type Path<T> = PathImpl<T, keyof T> & string;
11
+ type _GetPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? _GetPathValue<NonNullable<T[K]>, Rest> : T extends readonly any[] ? _GetPathValue<NonNullable<T[number]>, Rest> : unknown : P extends keyof T ? T[P] : T extends readonly any[] ? T[number] : unknown;
12
+ type GetPathValue<T, P extends string> = _GetPathValue<T, P>;
13
+ interface FormState<T> {
14
+ values: T;
15
+ errors: Record<string, string>;
16
+ touched: Record<string, boolean>;
17
+ dirty: Record<string, boolean>;
18
+ isSubmitting: boolean;
19
+ isValidating: boolean;
20
+ }
21
+ type FormSubscriber<T> = (state: FormState<T>) => void;
22
+ type PathSubscriber<V = any> = (value: V, fieldState: {
23
+ error?: string;
24
+ touched?: boolean;
25
+ dirty?: boolean;
26
+ }) => void;
27
+ type ValidationMode = 'onChange' | 'onBlur' | 'onTouched' | 'onSubmitOnly';
28
+ type FormAction = {
29
+ type: 'SET';
30
+ path: string;
31
+ value: unknown;
32
+ options?: {
33
+ touch?: boolean;
34
+ validate?: boolean;
35
+ };
36
+ } | {
37
+ type: 'VALIDATE';
38
+ paths?: string[];
39
+ } | {
40
+ type: 'SUBMIT';
41
+ } | {
42
+ type: 'RESET';
43
+ newValues?: unknown;
44
+ } | {
45
+ type: 'SET_ERRORS';
46
+ errors: Record<string, string>;
47
+ } | {
48
+ type: 'CONNECT';
49
+ path: string;
50
+ } | {
51
+ type: 'DISCONNECT';
52
+ path: string;
53
+ } | {
54
+ type: 'BLUR';
55
+ path: string;
56
+ } | {
57
+ type: 'BATCH_START';
58
+ } | {
59
+ type: 'BATCH_END';
60
+ } | {
61
+ type: 'ARRAY_APPEND';
62
+ path: string;
63
+ item: unknown;
64
+ } | {
65
+ type: 'ARRAY_INSERT';
66
+ path: string;
67
+ index: number;
68
+ item: unknown;
69
+ } | {
70
+ type: 'ARRAY_REMOVE';
71
+ path: string;
72
+ index: number;
73
+ } | {
74
+ type: 'ARRAY_MOVE';
75
+ path: string;
76
+ from: number;
77
+ to: number;
78
+ } | {
79
+ type: 'ARRAY_SWAP';
80
+ path: string;
81
+ i: number;
82
+ j: number;
83
+ } | {
84
+ type: 'CLEAR_ERRORS';
85
+ };
86
+ interface AriaPropsOptions {
87
+ required?: boolean;
88
+ errorId?: string;
89
+ }
90
+ interface AriaProps {
91
+ 'aria-invalid': 'true' | 'false';
92
+ 'aria-describedby': string | undefined;
93
+ 'aria-required': true | undefined;
94
+ }
95
+ interface ConnectOptions {
96
+ persist?: boolean;
97
+ format?: (val: string) => string;
98
+ validateOn?: ValidationMode;
99
+ }
100
+ interface FormInstance<T extends object> {
101
+ subscribe: (fn: FormSubscriber<T>) => () => void;
102
+ subscribeToPath<P extends Path<T>>(path: P, fn: PathSubscriber<GetPathValue<T, P>>): () => void;
103
+ subscribeToPath(path: string, fn: PathSubscriber): () => void;
104
+ get<P extends Path<T>>(path: P): GetPathValue<T, P>;
105
+ get(path: string | string[]): any;
106
+ set: (path: Path<T> | string | string[], val: any, options?: {
107
+ touch?: boolean;
108
+ validate?: boolean;
109
+ }) => void;
110
+ validate: (scopePaths?: Path<T>[] | string[] | string[][]) => Promise<boolean>;
111
+ connect: (path: Path<T> | string, el: HTMLElement, options?: ConnectOptions) => () => void;
112
+ submit: (onValid: (payload: Partial<T>) => void | Promise<void>) => Promise<boolean>;
113
+ handleSubmit: (onValid: (payload: Partial<T>) => void | Promise<void>, onInvalid?: (errors: Record<string, string>) => void) => (e?: Event) => void;
114
+ getState: () => FormState<T>;
115
+ getPayload: () => Partial<T>;
116
+ getAriaProps: (path: Path<T> | string, options?: AriaPropsOptions) => AriaProps;
117
+ batch: (fn: () => void) => void;
118
+ arrayAppend: (path: Path<T> | string | string[], item: any) => void;
119
+ arrayInsert: (path: Path<T> | string | string[], index: number, item: any) => void;
120
+ arrayRemove: (path: Path<T> | string | string[], index: number) => void;
121
+ arrayMove: (path: Path<T> | string | string[], fromIndex: number, toIndex: number) => void;
122
+ arraySwap: (path: Path<T> | string | string[], indexA: number, indexB: number) => void;
123
+ reset: (newValues?: T) => void;
124
+ getConnectedCount: () => number;
125
+ destroy: () => void;
126
+ setErrors: (errors: Record<Path<T> | (string & {}), string>) => void;
127
+ clearErrors: () => void;
128
+ /**
129
+ * Returns the effective ValidationMode for a field. Useful for debugging
130
+ * validation timing; framework adapters should rely on this only in custom
131
+ * event handlers, not in render logic.
132
+ */
133
+ getFieldMode: (path: string) => ValidationMode;
134
+ _subscribeToActions: (fn: (action: FormAction, state: FormState<T>) => void) => () => void;
135
+ }
136
+
137
+ interface AngularFormReturn<T extends object> {
138
+ state: Signal<FormState<T>>;
139
+ get: FormInstance<T>['get'];
140
+ set: FormInstance<T>['set'];
141
+ connect: FormInstance<T>['connect'];
142
+ submit: FormInstance<T>['submit'];
143
+ handleSubmit: FormInstance<T>['handleSubmit'];
144
+ reset: FormInstance<T>['reset'];
145
+ batch: FormInstance<T>['batch'];
146
+ validate: FormInstance<T>['validate'];
147
+ subscribeToPath: FormInstance<T>['subscribeToPath'];
148
+ getPayload: FormInstance<T>['getPayload'];
149
+ getAriaProps: FormInstance<T>['getAriaProps'];
150
+ getFieldMode: FormInstance<T>['getFieldMode'];
151
+ getConnectedCount: FormInstance<T>['getConnectedCount'];
152
+ destroy: FormInstance<T>['destroy'];
153
+ arrayAppend: FormInstance<T>['arrayAppend'];
154
+ arrayInsert: FormInstance<T>['arrayInsert'];
155
+ arrayRemove: FormInstance<T>['arrayRemove'];
156
+ arrayMove: FormInstance<T>['arrayMove'];
157
+ arraySwap: FormInstance<T>['arraySwap'];
158
+ setErrors: FormInstance<T>['setErrors'];
159
+ clearErrors: FormInstance<T>['clearErrors'];
160
+ }
161
+ declare function useAngularForm<T extends object>(form: FormInstance<T>): AngularFormReturn<T>;
162
+ declare function useAngularFormPath<T extends object>(form: FormInstance<T>, path: string): {
163
+ value: Signal<unknown>;
164
+ fieldState: Signal<{
165
+ error?: string;
166
+ touched?: boolean;
167
+ dirty?: boolean;
168
+ } | null>;
169
+ };
170
+
171
+ export { type AngularFormReturn, useAngularForm, useAngularFormPath };
@@ -1,10 +1,11 @@
1
1
  // ../adapters/angular/dist/index.js
2
- import { signal, DestroyRef, inject } from "@angular/core";
2
+ import { DestroyRef, inject, NgZone, signal } from "@angular/core";
3
3
  function useAngularForm(form) {
4
4
  const formSignal = signal(form.getState());
5
- const unsubscribe = form.subscribe((s) => {
6
- formSignal.set(s);
7
- });
5
+ const zone = inject(NgZone, { optional: true });
6
+ const unsubscribe = form.subscribe(
7
+ (s) => zone ? zone.run(() => formSignal.set(s)) : formSignal.set(s)
8
+ );
8
9
  inject(DestroyRef).onDestroy(unsubscribe);
9
10
  return {
10
11
  state: formSignal.asReadonly(),
@@ -15,19 +16,36 @@ function useAngularForm(form) {
15
16
  handleSubmit: form.handleSubmit,
16
17
  reset: form.reset,
17
18
  batch: form.batch,
19
+ validate: form.validate,
20
+ subscribeToPath: form.subscribeToPath,
21
+ getPayload: form.getPayload,
22
+ getAriaProps: form.getAriaProps,
23
+ getFieldMode: form.getFieldMode,
24
+ getConnectedCount: form.getConnectedCount,
25
+ destroy: form.destroy,
18
26
  arrayAppend: form.arrayAppend,
19
27
  arrayInsert: form.arrayInsert,
20
28
  arrayRemove: form.arrayRemove,
21
29
  arrayMove: form.arrayMove,
22
- arraySwap: form.arraySwap
30
+ arraySwap: form.arraySwap,
31
+ setErrors: form.setErrors,
32
+ clearErrors: form.clearErrors
23
33
  };
24
34
  }
25
35
  function useAngularFormPath(form, path) {
26
36
  const value = signal(form.get(path));
27
37
  const fieldState = signal(null);
38
+ const zone = inject(NgZone, { optional: true });
28
39
  const unsubscribe = form.subscribeToPath(path, (v, fs) => {
29
- value.set(v);
30
- fieldState.set(fs);
40
+ if (zone) {
41
+ zone.run(() => {
42
+ value.set(v);
43
+ fieldState.set(fs);
44
+ });
45
+ } else {
46
+ value.set(v);
47
+ fieldState.set(fs);
48
+ }
31
49
  });
32
50
  inject(DestroyRef).onDestroy(unsubscribe);
33
51
  return { value: value.asReadonly(), fieldState: fieldState.asReadonly() };
@@ -42,13 +42,17 @@ function useForm(form) {
42
42
  subscribeToPath: form.subscribeToPath,
43
43
  validate: form.validate,
44
44
  getPayload: form.getPayload,
45
+ getAriaProps: form.getAriaProps,
46
+ getFieldMode: form.getFieldMode,
45
47
  getConnectedCount: form.getConnectedCount,
46
48
  destroy: form.destroy,
47
49
  arrayAppend: form.arrayAppend,
48
50
  arrayInsert: form.arrayInsert,
49
51
  arrayRemove: form.arrayRemove,
50
52
  arrayMove: form.arrayMove,
51
- arraySwap: form.arraySwap
53
+ arraySwap: form.arraySwap,
54
+ setErrors: form.setErrors,
55
+ clearErrors: form.clearErrors
52
56
  };
53
57
  }
54
58
  function useFormPath(form, path) {
@@ -1 +1,139 @@
1
- export * from '@neutro/form-react';
1
+ /**
2
+ * @neutro/form-core
3
+ * High-Performance, Zero-Dependency, Framework-Agnostic Reactive Form Engine.
4
+ */
5
+ type Primitive = string | number | boolean | null | undefined | Date | File;
6
+ type Prev = [never, 0, 1, 2, 3, 4, 5, ...any[]];
7
+ type PathImpl<T, K extends keyof T, Depth extends number = 5> = [Depth] extends [never] ? never : K extends string ? T[K] extends Primitive ? K : T[K] extends Array<infer U> ? K | `${K}.${number}` | (U extends object ? `${K}.${number}.${PathImpl<U, keyof U, Prev[Depth]>}` : never) : NonNullable<T[K]> extends object ? K | `${K}.${PathImpl<NonNullable<T[K]>, keyof NonNullable<T[K]>, Prev[Depth]>}` : K : never;
8
+ type Path<T> = PathImpl<T, keyof T> & string;
9
+ type _GetPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? _GetPathValue<NonNullable<T[K]>, Rest> : T extends readonly any[] ? _GetPathValue<NonNullable<T[number]>, Rest> : unknown : P extends keyof T ? T[P] : T extends readonly any[] ? T[number] : unknown;
10
+ type GetPathValue<T, P extends string> = _GetPathValue<T, P>;
11
+ interface FormState<T> {
12
+ values: T;
13
+ errors: Record<string, string>;
14
+ touched: Record<string, boolean>;
15
+ dirty: Record<string, boolean>;
16
+ isSubmitting: boolean;
17
+ isValidating: boolean;
18
+ }
19
+ type FormSubscriber<T> = (state: FormState<T>) => void;
20
+ type PathSubscriber<V = any> = (value: V, fieldState: {
21
+ error?: string;
22
+ touched?: boolean;
23
+ dirty?: boolean;
24
+ }) => void;
25
+ type ValidationMode = 'onChange' | 'onBlur' | 'onTouched' | 'onSubmitOnly';
26
+ type FormAction = {
27
+ type: 'SET';
28
+ path: string;
29
+ value: unknown;
30
+ options?: {
31
+ touch?: boolean;
32
+ validate?: boolean;
33
+ };
34
+ } | {
35
+ type: 'VALIDATE';
36
+ paths?: string[];
37
+ } | {
38
+ type: 'SUBMIT';
39
+ } | {
40
+ type: 'RESET';
41
+ newValues?: unknown;
42
+ } | {
43
+ type: 'SET_ERRORS';
44
+ errors: Record<string, string>;
45
+ } | {
46
+ type: 'CONNECT';
47
+ path: string;
48
+ } | {
49
+ type: 'DISCONNECT';
50
+ path: string;
51
+ } | {
52
+ type: 'BLUR';
53
+ path: string;
54
+ } | {
55
+ type: 'BATCH_START';
56
+ } | {
57
+ type: 'BATCH_END';
58
+ } | {
59
+ type: 'ARRAY_APPEND';
60
+ path: string;
61
+ item: unknown;
62
+ } | {
63
+ type: 'ARRAY_INSERT';
64
+ path: string;
65
+ index: number;
66
+ item: unknown;
67
+ } | {
68
+ type: 'ARRAY_REMOVE';
69
+ path: string;
70
+ index: number;
71
+ } | {
72
+ type: 'ARRAY_MOVE';
73
+ path: string;
74
+ from: number;
75
+ to: number;
76
+ } | {
77
+ type: 'ARRAY_SWAP';
78
+ path: string;
79
+ i: number;
80
+ j: number;
81
+ } | {
82
+ type: 'CLEAR_ERRORS';
83
+ };
84
+ interface AriaPropsOptions {
85
+ required?: boolean;
86
+ errorId?: string;
87
+ }
88
+ interface AriaProps {
89
+ 'aria-invalid': 'true' | 'false';
90
+ 'aria-describedby': string | undefined;
91
+ 'aria-required': true | undefined;
92
+ }
93
+ interface ConnectOptions {
94
+ persist?: boolean;
95
+ format?: (val: string) => string;
96
+ validateOn?: ValidationMode;
97
+ }
98
+ interface FormInstance<T extends object> {
99
+ subscribe: (fn: FormSubscriber<T>) => () => void;
100
+ subscribeToPath<P extends Path<T>>(path: P, fn: PathSubscriber<GetPathValue<T, P>>): () => void;
101
+ subscribeToPath(path: string, fn: PathSubscriber): () => void;
102
+ get<P extends Path<T>>(path: P): GetPathValue<T, P>;
103
+ get(path: string | string[]): any;
104
+ set: (path: Path<T> | string | string[], val: any, options?: {
105
+ touch?: boolean;
106
+ validate?: boolean;
107
+ }) => void;
108
+ validate: (scopePaths?: Path<T>[] | string[] | string[][]) => Promise<boolean>;
109
+ connect: (path: Path<T> | string, el: HTMLElement, options?: ConnectOptions) => () => void;
110
+ submit: (onValid: (payload: Partial<T>) => void | Promise<void>) => Promise<boolean>;
111
+ handleSubmit: (onValid: (payload: Partial<T>) => void | Promise<void>, onInvalid?: (errors: Record<string, string>) => void) => (e?: Event) => void;
112
+ getState: () => FormState<T>;
113
+ getPayload: () => Partial<T>;
114
+ getAriaProps: (path: Path<T> | string, options?: AriaPropsOptions) => AriaProps;
115
+ batch: (fn: () => void) => void;
116
+ arrayAppend: (path: Path<T> | string | string[], item: any) => void;
117
+ arrayInsert: (path: Path<T> | string | string[], index: number, item: any) => void;
118
+ arrayRemove: (path: Path<T> | string | string[], index: number) => void;
119
+ arrayMove: (path: Path<T> | string | string[], fromIndex: number, toIndex: number) => void;
120
+ arraySwap: (path: Path<T> | string | string[], indexA: number, indexB: number) => void;
121
+ reset: (newValues?: T) => void;
122
+ getConnectedCount: () => number;
123
+ destroy: () => void;
124
+ setErrors: (errors: Record<Path<T> | (string & {}), string>) => void;
125
+ clearErrors: () => void;
126
+ /**
127
+ * Returns the effective ValidationMode for a field. Useful for debugging
128
+ * validation timing; framework adapters should rely on this only in custom
129
+ * event handlers, not in render logic.
130
+ */
131
+ getFieldMode: (path: string) => ValidationMode;
132
+ _subscribeToActions: (fn: (action: FormAction, state: FormState<T>) => void) => () => void;
133
+ }
134
+
135
+ declare function useForm<T extends object>(form: FormInstance<T>): FormState<T> & Omit<FormInstance<T>, 'subscribe' | 'getState' | '_subscribeToActions'>;
136
+ declare function useFormPath<T extends object, P extends Path<T>>(form: FormInstance<T>, path: P): GetPathValue<T, P>;
137
+ declare function useFormConnect(form: any): (path: string, options?: any) => (el: HTMLElement | null) => void;
138
+
139
+ export { useForm, useFormConnect, useFormPath };