@reformer/core 1.1.0-beta.4 → 1.1.0-beta.6

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.
@@ -0,0 +1,156 @@
1
+ import { V as d } from "./registry-helpers-BfCZcMkO.js";
2
+ function g() {
3
+ return l("");
4
+ }
5
+ function l(t) {
6
+ return new Proxy({}, {
7
+ get(e, i) {
8
+ if (typeof i == "symbol")
9
+ return;
10
+ if (i === "__path")
11
+ return t || i;
12
+ if (i === "__key") {
13
+ const a = t.split(".");
14
+ return a[a.length - 1] || i;
15
+ }
16
+ if (i === "then" || i === "catch" || i === "finally" || i === "constructor" || i === "toString" || i === "valueOf" || i === "toJSON")
17
+ return;
18
+ const n = t ? `${t}.${i}` : i, o = {
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ __key: i,
21
+ __path: n,
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ __formType: void 0,
24
+ __fieldType: void 0
25
+ };
26
+ return new Proxy(o, {
27
+ get(a, r) {
28
+ if (typeof r != "symbol") {
29
+ if (r === "__path") return n;
30
+ if (r === "__key") return i;
31
+ if (r !== "__formType" && r !== "__fieldType" && !(r === "then" || r === "catch" || r === "finally" || r === "constructor" || r === "toString" || r === "valueOf" || r === "toJSON"))
32
+ return l(`${n}.${r}`);
33
+ }
34
+ }
35
+ });
36
+ }
37
+ });
38
+ }
39
+ function m(t) {
40
+ if (typeof t == "string")
41
+ return t;
42
+ if (t && typeof t == "object") {
43
+ const e = t.__path;
44
+ if (typeof e == "string")
45
+ return e;
46
+ }
47
+ throw new Error("Invalid field path node: " + JSON.stringify(t));
48
+ }
49
+ function F(t) {
50
+ const e = m(t);
51
+ return l(e);
52
+ }
53
+ function V(t) {
54
+ if (t && typeof t == "object" && "__key" in t)
55
+ return t.__key;
56
+ if (typeof t == "string") {
57
+ const e = t.split(".");
58
+ return e[e.length - 1];
59
+ }
60
+ throw new Error("Invalid field path node");
61
+ }
62
+ function s(t) {
63
+ return t == null ? !1 : typeof t == "object" && "value" in t && "setValue" in t && "getValue" in t && "validate" in t;
64
+ }
65
+ function c(t) {
66
+ return t == null ? !1 : s(t) && "validators" in t && "asyncValidators" in t && // FieldNode имеет markAsTouched метод
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ typeof t.markAsTouched == "function" && // У FieldNode нет fields или items
69
+ !("fields" in t) && !("items" in t);
70
+ }
71
+ function u(t) {
72
+ return t == null ? !1 : s(t) && "applyValidationSchema" in t && "applyBehaviorSchema" in t && "getFieldByPath" in t && // GroupNode НЕ имеет items/push/removeAt (это ArrayNode)
73
+ !("items" in t) && !("push" in t) && !("removeAt" in t);
74
+ }
75
+ function y(t) {
76
+ return t == null ? !1 : s(t) && "items" in t && "length" in t && "push" in t && "removeAt" in t && "at" in t && // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ typeof t.push == "function" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ typeof t.removeAt == "function";
79
+ }
80
+ function N(t) {
81
+ return c(t) ? "FieldNode" : u(t) ? "GroupNode" : y(t) ? "ArrayNode" : s(t) ? "FormNode" : "Unknown";
82
+ }
83
+ function f(t) {
84
+ return c(t) ? [t] : u(t) ? Array.from(t.getAllFields()).flatMap(f) : y(t) ? t.map((e) => f(e)).flat() : [];
85
+ }
86
+ async function w(t, e) {
87
+ const i = new d();
88
+ i.beginRegistration();
89
+ let n = [], o = !1;
90
+ try {
91
+ const a = g();
92
+ e(a), n = i.getCurrentContext()?.getValidators() || [], i.cancelRegistration(), o = !0, t.clearErrors();
93
+ const h = f(t);
94
+ return await Promise.all(h.map((_) => _.validate())), n.length > 0 && await t.applyContextualValidators(n), t.valid.value;
95
+ } catch (a) {
96
+ throw o || i.cancelRegistration(), a;
97
+ }
98
+ }
99
+ class A {
100
+ _form;
101
+ control;
102
+ /**
103
+ * Форма с типизированным Proxy-доступом к полям
104
+ */
105
+ form;
106
+ constructor(e, i, n) {
107
+ this._form = e, this.control = n, this.form = e._proxyInstance || e.getProxy();
108
+ }
109
+ /**
110
+ * Получить текущее значение поля (внутренний метод для validation-applicator)
111
+ * @internal
112
+ */
113
+ value() {
114
+ return this.control.value.value;
115
+ }
116
+ /**
117
+ * Безопасно установить значение поля по строковому пути
118
+ * Автоматически использует emitEvent: false для предотвращения циклов
119
+ */
120
+ setFieldValue(e, i) {
121
+ const n = this._form.getFieldByPath(e);
122
+ n && s(n) && n.setValue(i, { emitEvent: !1 });
123
+ }
124
+ }
125
+ class T {
126
+ _form;
127
+ /**
128
+ * Форма с типизированным Proxy-доступом к полям
129
+ */
130
+ form;
131
+ constructor(e) {
132
+ this._form = e, this.form = e._proxyInstance || e.getProxy();
133
+ }
134
+ /**
135
+ * Безопасно установить значение поля по строковому пути
136
+ * Автоматически использует emitEvent: false для предотвращения циклов
137
+ */
138
+ setFieldValue(e, i) {
139
+ const n = this._form.getFieldByPath(e);
140
+ n && s(n) && n.setValue(i, { emitEvent: !1 });
141
+ }
142
+ }
143
+ export {
144
+ T,
145
+ A as V,
146
+ s as a,
147
+ u as b,
148
+ g as c,
149
+ y as d,
150
+ m as e,
151
+ V as f,
152
+ N as g,
153
+ c as i,
154
+ F as t,
155
+ w as v
156
+ };
@@ -1,7 +1,7 @@
1
- import { h as c, t as l, j as i } from "./node-factory-DYXIgJmW.js";
2
- import { T as H, V as P, k as W, v as j } from "./node-factory-DYXIgJmW.js";
3
- import { g as u } from "./create-field-path-DcXDTWil.js";
4
- import { V as M } from "./create-field-path-DcXDTWil.js";
1
+ import { e as c, t as l, c as i } from "./validation-context-cWXmh_Ho.js";
2
+ import { T as H, V as P, f as W, v as M } from "./validation-context-cWXmh_Ho.js";
3
+ import { g as u } from "./registry-helpers-BfCZcMkO.js";
4
+ import { V as j } from "./registry-helpers-BfCZcMkO.js";
5
5
  function n(r, e, a) {
6
6
  if (!r) return;
7
7
  const m = c(r);
@@ -256,7 +256,7 @@ function I(r, e) {
256
256
  export {
257
257
  H as TreeValidationContextImpl,
258
258
  P as ValidationContextImpl,
259
- M as ValidationRegistry,
259
+ j as ValidationRegistry,
260
260
  $ as apply,
261
261
  o as applyWhen,
262
262
  i as createFieldPath,
@@ -277,7 +277,7 @@ export {
277
277
  T as url,
278
278
  n as validate,
279
279
  A as validateAsync,
280
- j as validateForm,
280
+ M as validateForm,
281
281
  I as validateItems,
282
282
  _ as validateTree
283
283
  };
package/llms.txt CHANGED
@@ -43,6 +43,78 @@ const loanType = useFormControlValue(control.loanType);
43
43
  const { value, errors, disabled } = useFormControl(control.loanType);
44
44
  ```
45
45
 
46
+ ## 1.5 QUICK START - Minimal Working Form
47
+
48
+ ```typescript
49
+ import { createForm, useFormControl } from '@reformer/core';
50
+ import { required, email } from '@reformer/core/validators';
51
+ import type { GroupNodeWithControls } from '@reformer/core';
52
+
53
+ // 1. Define form type
54
+ interface ContactForm {
55
+ name: string;
56
+ email: string;
57
+ }
58
+
59
+ // 2. Create form schema with validation
60
+ const form = createForm<ContactForm>({
61
+ form: {
62
+ name: { value: '', component: Input },
63
+ email: { value: '', component: Input },
64
+ },
65
+ validation: (path) => {
66
+ required(path.name, { message: 'Name is required' });
67
+ required(path.email, { message: 'Email is required' });
68
+ email(path.email, { message: 'Invalid email format' });
69
+ },
70
+ });
71
+
72
+ // 3. Use in React component
73
+ function ContactFormComponent() {
74
+ const nameCtrl = useFormControl(form.name);
75
+ const emailCtrl = useFormControl(form.email);
76
+
77
+ const handleSubmit = async () => {
78
+ await form.submit((values) => {
79
+ console.log('Form submitted:', values);
80
+ });
81
+ };
82
+
83
+ return (
84
+ <form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
85
+ <div>
86
+ <input
87
+ value={nameCtrl.value}
88
+ onChange={(e) => form.name.setValue(e.target.value)}
89
+ disabled={nameCtrl.disabled}
90
+ />
91
+ {nameCtrl.errors.map((err) => <span key={err.code}>{err.message}</span>)}
92
+ </div>
93
+ <div>
94
+ <input
95
+ value={emailCtrl.value}
96
+ onChange={(e) => form.email.setValue(e.target.value)}
97
+ disabled={emailCtrl.disabled}
98
+ />
99
+ {emailCtrl.errors.map((err) => <span key={err.code}>{err.message}</span>)}
100
+ </div>
101
+ <button type="submit">Send</button>
102
+ </form>
103
+ );
104
+ }
105
+
106
+ // 4. Pass form to child components via props (NOT context!)
107
+ interface FormStepProps {
108
+ form: GroupNodeWithControls<ContactForm>;
109
+ }
110
+
111
+ function FormStep({ form }: FormStepProps) {
112
+ // Access form fields directly
113
+ const { value } = useFormControl(form.name);
114
+ return <div>Name: {value}</div>;
115
+ }
116
+ ```
117
+
46
118
  ## 2. API SIGNATURES
47
119
 
48
120
  ### Validators
@@ -68,9 +140,48 @@ validate(path, validator: (value, ctx) => ValidationError | null)
68
140
  validateAsync(path, validator: async (value, ctx) => ValidationError | null)
69
141
  validateTree(validator: (ctx) => ValidationError | null, options?: { targetField?: string })
70
142
 
71
- // Conditional validation
143
+ // Conditional validation (3 arguments!)
72
144
  applyWhen(fieldPath, condition: (fieldValue) => boolean, validatorsFn: (path) => void)
73
145
 
146
+ // ⚠️ applyWhen Examples - CRITICALLY IMPORTANT
147
+ // applyWhen takes 3 arguments: triggerField, condition, validators
148
+
149
+ // Example 1: Simple condition
150
+ applyWhen(
151
+ path.loanType, // 1st: field to watch
152
+ (type) => type === 'mortgage', // 2nd: condition on field value
153
+ (p) => { // 3rd: validators to apply
154
+ required(p.propertyValue);
155
+ min(p.propertyValue, 100000);
156
+ }
157
+ );
158
+
159
+ // Example 2: Nested field as trigger
160
+ applyWhen(
161
+ path.address.country,
162
+ (country) => country === 'US',
163
+ (p) => {
164
+ required(p.address.state);
165
+ pattern(p.address.zip, /^\d{5}(-\d{4})?$/);
166
+ }
167
+ );
168
+
169
+ // Example 3: Boolean trigger
170
+ applyWhen(
171
+ path.hasInsurance,
172
+ (has) => has === true,
173
+ (p) => {
174
+ required(p.insuranceCompany);
175
+ required(p.policyNumber);
176
+ }
177
+ );
178
+
179
+ // ❌ WRONG - only 2 arguments (React Hook Form pattern)
180
+ applyWhen(
181
+ (form) => form.loanType === 'mortgage', // WRONG!
182
+ () => { required(path.propertyValue); }
183
+ );
184
+
74
185
  // Array validators
75
186
  notEmpty(path, options?: { message?: string })
76
187
  validateItems(arrayPath, itemValidatorsFn: (itemPath) => void)
@@ -392,6 +503,25 @@ form.address.city.value.value; // Get current value
392
503
  form.items.push({ id: '1', name: 'Item' }); // Array operations
393
504
  ```
394
505
 
506
+ ### ⚠️ createForm Returns a Proxy
507
+
508
+ ```typescript
509
+ // createForm() returns GroupNodeWithControls<T> (a Proxy wrapper around GroupNode)
510
+ // This enables type-safe field access:
511
+ const form = createForm<MyForm>({...});
512
+
513
+ form.email // FieldNode<string> - TypeScript knows the type!
514
+ form.address.city // FieldNode<string> - nested access works
515
+ form.items.at(0) // GroupNodeWithControls<ItemType> - array items
516
+
517
+ // ⚠️ IMPORTANT: Proxy doesn't pass instanceof checks!
518
+ // Use type guards instead:
519
+ import { isFieldNode, isGroupNode, isArrayNode } from '@reformer/core';
520
+
521
+ if (isFieldNode(node)) { /* ... */ } // ✅ Works with Proxy
522
+ if (node instanceof FieldNode) { /* ... */ } // ❌ Fails with Proxy!
523
+ ```
524
+
395
525
  ## 9. ARRAY SCHEMA FORMAT
396
526
 
397
527
  **Array items are sub-forms!** Each array element is a complete sub-form with its own fields, validation, and behavior.
@@ -522,6 +652,61 @@ export const fullValidation: ValidationSchemaFn<Form> = (path) => {
522
652
  step1Validation(path);
523
653
  step2Validation(path);
524
654
  };
655
+
656
+ // Using validateForm() for step validation
657
+ import { validateForm } from '@reformer/core';
658
+
659
+ const goToNextStep = async () => {
660
+ const currentValidation = STEP_VALIDATIONS[currentStep];
661
+ const isValid = await validateForm(form, currentValidation);
662
+
663
+ if (!isValid) {
664
+ form.markAsTouched(); // Show errors on current step fields
665
+ return;
666
+ }
667
+
668
+ setCurrentStep(currentStep + 1);
669
+ };
670
+
671
+ // Full form submit with all validations
672
+ const handleSubmit = async () => {
673
+ const isValid = await validateForm(form, fullValidation);
674
+
675
+ if (isValid) {
676
+ await form.submit(onSubmit);
677
+ }
678
+ };
679
+ ```
680
+
681
+ ### Multi-Step Component Example
682
+
683
+ ```tsx
684
+ function MultiStepForm() {
685
+ const [step, setStep] = useState(1);
686
+
687
+ const nextStep = async () => {
688
+ const validation = STEP_VALIDATIONS[step];
689
+ if (await validateForm(form, validation)) {
690
+ setStep(step + 1);
691
+ } else {
692
+ form.markAsTouched();
693
+ }
694
+ };
695
+
696
+ return (
697
+ <div>
698
+ {step === 1 && <Step1Fields form={form} />}
699
+ {step === 2 && <Step2Fields form={form} />}
700
+
701
+ <button onClick={() => setStep(step - 1)} disabled={step === 1}>
702
+ Back
703
+ </button>
704
+ <button onClick={step === 2 ? handleSubmit : nextStep}>
705
+ {step === 2 ? 'Submit' : 'Next'}
706
+ </button>
707
+ </div>
708
+ );
709
+ }
525
710
  ```
526
711
 
527
712
  ## 13. ⚠️ EXTENDED COMMON MISTAKES
@@ -642,6 +827,123 @@ export const creditApplicationValidation: ValidationSchemaFn<Form> = (path) => {
642
827
  | Medium | Separate files: `type.ts`, `schema.ts`, `validators.ts`, `Form.tsx` |
643
828
  | Complex | Full colocation with `steps/` and `sub-forms/` |
644
829
 
830
+ ## 14.5 UI COMPONENT PATTERNS
831
+
832
+ ReFormer does NOT provide UI components - you create them yourself or use a UI library.
833
+
834
+ ### Generic FormField Component
835
+
836
+ ```tsx
837
+ import type { FieldNode } from '@reformer/core';
838
+ import { useFormControl } from '@reformer/core';
839
+
840
+ interface FormFieldProps<T> {
841
+ control: FieldNode<T>;
842
+ label?: string;
843
+ type?: 'text' | 'email' | 'number' | 'password';
844
+ placeholder?: string;
845
+ }
846
+
847
+ function FormField<T extends string | number>({
848
+ control,
849
+ label,
850
+ type = 'text',
851
+ placeholder
852
+ }: FormFieldProps<T>) {
853
+ const { value, errors, disabled, touched } = useFormControl(control);
854
+ const showError = touched && errors.length > 0;
855
+
856
+ return (
857
+ <div className="form-field">
858
+ {label && <label>{label}</label>}
859
+ <input
860
+ type={type}
861
+ value={value ?? ''}
862
+ onChange={(e) => {
863
+ const val = type === 'number'
864
+ ? Number(e.target.value) as T
865
+ : e.target.value as T;
866
+ control.setValue(val);
867
+ }}
868
+ onBlur={() => control.markAsTouched()}
869
+ disabled={disabled}
870
+ placeholder={placeholder}
871
+ className={showError ? 'error' : ''}
872
+ />
873
+ {showError && (
874
+ <span className="error-message">{errors[0].message}</span>
875
+ )}
876
+ </div>
877
+ );
878
+ }
879
+
880
+ // Usage
881
+ <FormField control={form.email} label="Email" type="email" />
882
+ <FormField control={form.age} label="Age" type="number" />
883
+ ```
884
+
885
+ ### FormField for Select
886
+
887
+ ```tsx
888
+ interface SelectFieldProps<T extends string> {
889
+ control: FieldNode<T>;
890
+ label?: string;
891
+ options: Array<{ value: T; label: string }>;
892
+ }
893
+
894
+ function SelectField<T extends string>({
895
+ control,
896
+ label,
897
+ options
898
+ }: SelectFieldProps<T>) {
899
+ const { value, errors, disabled, touched } = useFormControl(control);
900
+
901
+ return (
902
+ <div className="form-field">
903
+ {label && <label>{label}</label>}
904
+ <select
905
+ value={value}
906
+ onChange={(e) => control.setValue(e.target.value as T)}
907
+ disabled={disabled}
908
+ >
909
+ {options.map((opt) => (
910
+ <option key={opt.value} value={opt.value}>
911
+ {opt.label}
912
+ </option>
913
+ ))}
914
+ </select>
915
+ {touched && errors[0] && (
916
+ <span className="error-message">{errors[0].message}</span>
917
+ )}
918
+ </div>
919
+ );
920
+ }
921
+ ```
922
+
923
+ ### Integration with UI Libraries
924
+
925
+ ```tsx
926
+ // With shadcn/ui
927
+ import { Input } from '@/components/ui/input';
928
+ import { Label } from '@/components/ui/label';
929
+
930
+ function ShadcnFormField({ control, label }: FormFieldProps<string>) {
931
+ const { value, errors, disabled } = useFormControl(control);
932
+
933
+ return (
934
+ <div className="space-y-2">
935
+ <Label>{label}</Label>
936
+ <Input
937
+ value={value}
938
+ onChange={(e) => control.setValue(e.target.value)}
939
+ disabled={disabled}
940
+ />
941
+ {errors[0] && <p className="text-red-500">{errors[0].message}</p>}
942
+ </div>
943
+ );
944
+ }
945
+ ```
946
+
645
947
  ## 15. NON-EXISTENT API (DO NOT USE)
646
948
 
647
949
  ⚠️ **The following APIs do NOT exist in @reformer/core:**
@@ -652,6 +954,13 @@ export const creditApplicationValidation: ValidationSchemaFn<Form> = (path) => {
652
954
  | `FieldSchema` | `FieldConfig<T>` | Type for individual field config |
653
955
  | `when()` | `applyWhen()` | Conditional validation function |
654
956
  | `FormFields` | `FieldNode<T>` | Type for field nodes |
957
+ | `FormInstance<T>` | `GroupNodeWithControls<T>` | Form type for component props |
958
+ | `useArrayField()` | `form.items.push/map/removeAt` | Use ArrayNode methods directly |
959
+ | `FormProvider` | `<Component form={form} />` | Pass form via props, no context |
960
+ | `formState` | `form.valid`, `form.dirty`, etc. | Separate signals on form |
961
+ | `control` prop | Not needed | Form IS the control |
962
+ | `register('field')` | `useFormControl(form.field)` | Type-safe field access |
963
+ | `getFieldValue()` | `ctx.form.field.value.value` | Read via signals |
655
964
 
656
965
  ### Common Import Errors
657
966
 
@@ -692,6 +1001,89 @@ const schema: FormSchema<MyForm> = {
692
1001
  };
693
1002
  ```
694
1003
 
1004
+ ## 15.5 REACT HOOK FORM MIGRATION
1005
+
1006
+ If you're familiar with React Hook Form, here's how to translate patterns to ReFormer:
1007
+
1008
+ | React Hook Form | ReFormer | Notes |
1009
+ |-----------------|----------|-------|
1010
+ | `const { register, handleSubmit } = useForm()` | `const form = createForm({...})` | Call OUTSIDE component |
1011
+ | `register('fieldName')` | `useFormControl(form.fieldName)` | Returns control object |
1012
+ | `watch('fieldName')` | `useFormControlValue(form.fieldName)` | Returns value directly (NOT `{ value }`) |
1013
+ | `setValue('field', value)` | `form.field.setValue(value)` | Direct method call |
1014
+ | `getValues('field')` | `form.field.value.value` | Signal-based |
1015
+ | `formState.errors` | `useFormControl(form.field).errors` | Per-field errors array |
1016
+ | `formState.isValid` | `form.valid.value` | Signal |
1017
+ | `formState.isDirty` | `form.dirty.value` | Signal |
1018
+ | `handleSubmit(onSubmit)` | `form.submit(onSubmit)` | Built-in validation |
1019
+ | `<FormProvider form={...}>` | `<Component form={form} />` | Props, NOT context |
1020
+ | `useFormContext()` | Not needed | Pass form via props |
1021
+ | `useFieldArray({ name: 'items' })` | `form.items` (ArrayNode) | Direct array access |
1022
+ | `fields.map((field, index) => ...)` | `form.items.map((item, index) => ...)` | item is sub-form |
1023
+ | `append(data)` | `form.items.push(data)` | Add item |
1024
+ | `remove(index)` | `form.items.removeAt(index)` | Remove item |
1025
+ | `control` prop | Not needed | Form IS the control |
1026
+
1027
+ ### Key Differences
1028
+
1029
+ 1. **No Provider/Context** - Pass form directly via props
1030
+ ```typescript
1031
+ // ❌ React Hook Form pattern - doesn't exist in ReFormer
1032
+ <FormProvider form={form}>
1033
+ <NestedComponent />
1034
+ </FormProvider>
1035
+
1036
+ // ✅ ReFormer pattern - pass via props
1037
+ <NestedComponent form={form} />
1038
+ ```
1039
+
1040
+ 2. **Form Creation Location** - Create outside component
1041
+ ```typescript
1042
+ // ❌ WRONG - creates new form on every render
1043
+ function MyComponent() {
1044
+ const form = createForm({...});
1045
+ }
1046
+
1047
+ // ✅ CORRECT - create once, outside component
1048
+ const form = createForm({...});
1049
+ function MyComponent() {
1050
+ const ctrl = useFormControl(form.name);
1051
+ }
1052
+ ```
1053
+
1054
+ 3. **Type-Safe Field Access** - No string paths
1055
+ ```typescript
1056
+ // React Hook Form
1057
+ register('user.address.city')
1058
+
1059
+ // ReFormer - fully typed
1060
+ form.user.address.city.setValue('New York')
1061
+ ```
1062
+
1063
+ 4. **Array Items are Sub-Forms**
1064
+ ```typescript
1065
+ // React Hook Form
1066
+ fields.map((field, index) => (
1067
+ <input {...register(`items.${index}.name`)} />
1068
+ ))
1069
+
1070
+ // ReFormer - each item is a typed GroupNode
1071
+ form.items.map((item, index) => (
1072
+ <FormField control={item.name} /> // item.name is FieldNode
1073
+ ))
1074
+ ```
1075
+
1076
+ 5. **Reading Values in Behaviors**
1077
+ ```typescript
1078
+ // React Hook Form
1079
+ watch('fieldName')
1080
+
1081
+ // ReFormer in watchField callback
1082
+ watchField(path.trigger, (value, ctx) => {
1083
+ const other = ctx.form.otherField.value.value; // Signal access
1084
+ });
1085
+ ```
1086
+
695
1087
  ## 16. READING FIELD VALUES (CRITICALLY IMPORTANT)
696
1088
 
697
1089
  ### Why .value.value?
@@ -806,6 +1198,25 @@ watchField(path.loanAmount, (amount, ctx) => {
806
1198
 
807
1199
  ## 18. ARRAY OPERATIONS
808
1200
 
1201
+ ### ⚠️ Array Access - CRITICAL
1202
+
1203
+ ```typescript
1204
+ // ❌ WRONG - bracket notation does NOT work!
1205
+ const first = form.items[0]; // undefined or error
1206
+ const second = form.items[1]; // undefined or error
1207
+
1208
+ // ✅ CORRECT - use .at() method
1209
+ const first = form.items.at(0); // GroupNodeWithControls<ItemType> | undefined
1210
+ const second = form.items.at(1); // GroupNodeWithControls<ItemType> | undefined
1211
+
1212
+ // ✅ CORRECT - iterate with map (most common pattern)
1213
+ form.items.map((item, index) => {
1214
+ // item is fully typed GroupNode
1215
+ item.name.setValue('New Name');
1216
+ item.price.value.value; // read value
1217
+ });
1218
+ ```
1219
+
809
1220
  ### Array Methods
810
1221
 
811
1222
  ```typescript
@@ -820,10 +1231,10 @@ form.items.clear(); // Remove all items
820
1231
  // Reorder
821
1232
  form.items.move(fromIndex, toIndex); // Move item
822
1233
 
823
- // Access
1234
+ // Access (use .at(), NOT brackets!)
824
1235
  form.items.length.value; // Current length (Signal)
825
1236
  form.items.map((item, index) => ...); // Iterate items
826
- form.items.at(index); // Get item at index
1237
+ form.items.at(index); // Get item at index (NOT items[index]!)
827
1238
  ```
828
1239
 
829
1240
  ### Rendering Arrays
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reformer/core",
3
- "version": "1.1.0-beta.4",
3
+ "version": "1.1.0-beta.6",
4
4
  "description": "Reactive form state management library for React with signals-based architecture",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",