@reformer/core 1.1.0-beta.1 → 1.1.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 (2) hide show
  1. package/llms.txt +250 -29
  2. package/package.json +1 -1
package/llms.txt CHANGED
@@ -6,9 +6,16 @@
6
6
 
7
7
  | What | Where |
8
8
  | ------------------------------------------------------------------------------------------- | --------------------------- |
9
+ | `createForm`, `useFormControl`, `useFormControlValue` | `@reformer/core` |
9
10
  | `ValidationSchemaFn`, `BehaviorSchemaFn`, `FieldPath`, `GroupNodeWithControls`, `FieldNode` | `@reformer/core` |
10
- | `required`, `min`, `max`, `minLength`, `email`, `validate`, `validateTree` | `@reformer/core/validators` |
11
- | `computeFrom`, `enableWhen`, `disableWhen`, `copyFrom`, `watchField` | `@reformer/core/behaviors` |
11
+ | `FormSchema`, `FieldConfig`, `ArrayNode` | `@reformer/core` |
12
+ | `required`, `min`, `max`, `minLength`, `maxLength`, `email` | `@reformer/core/validators` |
13
+ | `pattern`, `url`, `phone`, `number`, `date` | `@reformer/core/validators` |
14
+ | `validate`, `validateAsync`, `validateTree`, `applyWhen` | `@reformer/core/validators` |
15
+ | `notEmpty`, `validateItems` | `@reformer/core/validators` |
16
+ | `computeFrom`, `enableWhen`, `disableWhen`, `watchField`, `copyFrom` | `@reformer/core/behaviors` |
17
+ | `resetWhen`, `revalidateWhen`, `syncFields` | `@reformer/core/behaviors` |
18
+ | `transformValue`, `transformers` | `@reformer/core/behaviors` |
12
19
 
13
20
  ### Type Values
14
21
 
@@ -21,31 +28,68 @@
21
28
  ### Validators
22
29
 
23
30
  ```typescript
31
+ // Basic validators
24
32
  required(path, options?: { message?: string })
25
33
  min(path, value: number, options?: { message?: string })
26
34
  max(path, value: number, options?: { message?: string })
27
35
  minLength(path, length: number, options?: { message?: string })
28
36
  maxLength(path, length: number, options?: { message?: string })
29
37
  email(path, options?: { message?: string })
30
- validate(path, validator: (value) => ValidationError | null)
31
- validateTree(validator: (ctx) => ValidationError | null)
38
+
39
+ // Additional validators
40
+ pattern(path, regex: RegExp, options?: { message?: string })
41
+ url(path, options?: { message?: string })
42
+ phone(path, options?: { message?: string; format?: PhoneFormat })
43
+ number(path, options?: { message?: string })
44
+ date(path, options?: { message?: string; minAge?: number; maxAge?: number; noFuture?: boolean; noPast?: boolean })
45
+
46
+ // Custom validators
47
+ validate(path, validator: (value, ctx) => ValidationError | null)
48
+ validateAsync(path, validator: async (value, ctx) => ValidationError | null)
49
+ validateTree(validator: (ctx) => ValidationError | null, options?: { targetField?: string })
50
+
51
+ // Conditional validation
32
52
  applyWhen(fieldPath, condition: (fieldValue) => boolean, validatorsFn: (path) => void)
53
+
54
+ // Array validators
55
+ notEmpty(path, options?: { message?: string })
56
+ validateItems(arrayPath, itemValidatorsFn: (itemPath) => void)
33
57
  ```
34
58
 
35
59
  ### Behaviors
36
60
 
37
61
  ```typescript
62
+ // Enable/disable fields conditionally
38
63
  enableWhen(path, condition: (form) => boolean, options?: { resetOnDisable?: boolean })
39
64
  disableWhen(path, condition: (form) => boolean)
40
- computeFrom(sourcePaths[], targetPath, compute: (values) => result)
41
- watchField(path, callback: (value, ctx: BehaviorContext) => void)
42
- copyFrom(sourcePath, targetPath, options?: { when?, fields?, transform? })
65
+
66
+ // Computed fields (same nesting level)
67
+ computeFrom(sourcePaths[], targetPath, compute: (values) => result, options?: { debounce?: number; condition?: (form) => boolean })
68
+
69
+ // Watch field changes
70
+ watchField(path, callback: (value, ctx: BehaviorContext) => void, options?: { immediate?: boolean; debounce?: number })
71
+
72
+ // Copy values between fields
73
+ copyFrom(sourcePath, targetPath, options?: { when?: (form) => boolean; fields?: string[]; transform?: (value) => value })
74
+
75
+ // Reset field when condition met
76
+ resetWhen(path, condition: (form) => boolean, options?: { toValue?: any })
77
+
78
+ // Re-validate when another field changes
79
+ revalidateWhen(triggerPath, targetPath)
80
+
81
+ // Sync multiple fields
82
+ syncFields(paths[], options?: { bidirectional?: boolean })
83
+
84
+ // Transform values
85
+ transformValue(path, transformer: (value) => value, options?: { on?: 'change' | 'blur' })
86
+ transformers.trim, transformers.toUpperCase, transformers.toLowerCase, transformers.toNumber
43
87
 
44
88
  // BehaviorContext interface:
45
89
  interface BehaviorContext<TForm> {
46
- form: TForm; // Current form state
47
- setFieldValue: (path, value) => void; // Set field value
48
- getFieldValue: (path) => unknown; // Get field value
90
+ form: GroupNodeWithControls<TForm>; // Form proxy with typed field access
91
+ setFieldValue: (path: string, value: any) => void;
92
+ getFieldValue: (path: string) => unknown;
49
93
  }
50
94
  ```
51
95
 
@@ -184,47 +228,174 @@ interface MyForm {
184
228
  }
185
229
  ```
186
230
 
187
- ## 8. CREATEFORM API
231
+ ## 8. FORMSCHEMA FORMAT (CRITICALLY IMPORTANT)
232
+
233
+ ⚠️ **Every field MUST have `value` and `component` properties!**
234
+
235
+ ### FieldConfig Interface
236
+
237
+ ```typescript
238
+ interface FieldConfig<T> {
239
+ value: T | null; // Initial value (REQUIRED)
240
+ component: ComponentType; // React component (REQUIRED)
241
+ componentProps?: object; // Props passed to component
242
+ disabled?: boolean; // Disable field initially
243
+ validators?: ValidatorFn[]; // Sync validators
244
+ asyncValidators?: AsyncValidatorFn[]; // Async validators
245
+ updateOn?: 'change' | 'blur' | 'submit';
246
+ debounce?: number;
247
+ }
248
+ ```
249
+
250
+ ### Primitive Fields
251
+
252
+ ```typescript
253
+ import { Input, Select, Checkbox } from '@/components/ui';
254
+
255
+ const schema: FormSchema<MyForm> = {
256
+ // String field
257
+ name: {
258
+ value: '', // Initial value (REQUIRED)
259
+ component: Input, // React component (REQUIRED)
260
+ componentProps: {
261
+ label: 'Name',
262
+ placeholder: 'Enter name',
263
+ },
264
+ },
265
+
266
+ // Number field (optional)
267
+ age: {
268
+ value: undefined, // Use undefined, NOT null
269
+ component: Input,
270
+ componentProps: { type: 'number', label: 'Age' },
271
+ },
272
+
273
+ // Boolean field
274
+ agree: {
275
+ value: false,
276
+ component: Checkbox,
277
+ componentProps: { label: 'I agree to terms' },
278
+ },
279
+
280
+ // Enum/Select field
281
+ status: {
282
+ value: 'active',
283
+ component: Select,
284
+ componentProps: {
285
+ label: 'Status',
286
+ options: [
287
+ { value: 'active', label: 'Active' },
288
+ { value: 'inactive', label: 'Inactive' },
289
+ ],
290
+ },
291
+ },
292
+ };
293
+ ```
294
+
295
+ ### Nested Objects
296
+
297
+ ```typescript
298
+ const schema: FormSchema<MyForm> = {
299
+ address: {
300
+ street: { value: '', component: Input, componentProps: { label: 'Street' } },
301
+ city: { value: '', component: Input, componentProps: { label: 'City' } },
302
+ zip: { value: '', component: Input, componentProps: { label: 'ZIP' } },
303
+ },
304
+ };
305
+ ```
306
+
307
+ ### Arrays (Tuple Format)
308
+
309
+ ```typescript
310
+ const itemSchema = {
311
+ id: { value: '', component: Input, componentProps: { label: 'ID' } },
312
+ name: { value: '', component: Input, componentProps: { label: 'Name' } },
313
+ };
314
+
315
+ const schema: FormSchema<MyForm> = {
316
+ items: [itemSchema], // Array with ONE template item
317
+ };
318
+ ```
319
+
320
+ ### ❌ WRONG - This will NOT compile
321
+
322
+ ```typescript
323
+ // Missing value and component - TypeScript will error!
324
+ const schema = {
325
+ name: '', // ❌ Wrong
326
+ email: '', // ❌ Wrong
327
+ };
328
+ ```
329
+
330
+ ### createForm API
188
331
 
189
332
  ```typescript
190
333
  // Full config with behavior and validation
191
334
  const form = createForm<MyForm>({
192
- form: formSchema, // Required: form schema
335
+ form: formSchema, // Required: form schema with FieldConfig
193
336
  behavior: behaviorSchema, // Optional: behavior rules
194
337
  validation: validationSchema, // Optional: validation rules
195
338
  });
196
339
 
197
- // Legacy format (schema only)
198
- const form = createForm<MyForm>(formSchema);
199
-
200
- // Form schema example
201
- const formSchema: FormSchema<MyForm> = {
202
- name: '',
203
- email: '',
204
- address: {
205
- street: '',
206
- city: '',
207
- },
208
- // Arrays use tuple format
209
- items: [{ id: '', name: '' }] as [{ id: string; name: string }],
210
- };
340
+ // Access form controls
341
+ form.name.setValue('John');
342
+ form.address.city.value.value; // Get current value
343
+ form.items.push({ id: '1', name: 'Item' }); // Array operations
211
344
  ```
212
345
 
213
346
  ## 9. ARRAY SCHEMA FORMAT
214
347
 
348
+ **Array items are sub-forms!** Each array element is a complete sub-form with its own fields, validation, and behavior.
349
+
215
350
  ```typescript
216
351
  // ✅ CORRECT - use tuple format for arrays
217
- const schema = {
218
- items: [itemSchema] as [typeof itemSchema],
219
- properties: [propertySchema] as [typeof propertySchema],
352
+ // The template item defines the sub-form schema for each array element
353
+ const itemSchema = {
354
+ id: { value: '', component: Input },
355
+ name: { value: '', component: Input },
356
+ price: { value: 0, component: Input, componentProps: { type: 'number' } },
357
+ };
358
+
359
+ const schema: FormSchema<MyForm> = {
360
+ items: [itemSchema], // Array of sub-forms
220
361
  };
221
362
 
363
+ // Each array item is a GroupNode (sub-form) with its own controls:
364
+ form.items.map((item) => {
365
+ // item is a sub-form (GroupNode) - access fields like nested form
366
+ item.name.setValue('New Name');
367
+ item.price.value.value; // Get current value
368
+ });
369
+ ```
370
+
371
+ ```typescript
222
372
  // ❌ WRONG - object format is NOT supported
223
373
  const schema = {
224
374
  items: { schema: itemSchema, initialItems: [] }, // This will NOT work
225
375
  };
226
376
  ```
227
377
 
378
+ ### Array Item as Sub-Form
379
+
380
+ ```typescript
381
+ // Validation for array items (each item is a sub-form)
382
+ validateItems(path.items, (itemPath) => {
383
+ // itemPath provides paths to sub-form fields
384
+ required(itemPath.name);
385
+ min(itemPath.price, 0);
386
+ });
387
+
388
+ // Render array items - each item is a sub-form
389
+ {form.items.map((item, index) => (
390
+ <div key={item.id}>
391
+ {/* item is a sub-form - use FormField for each field */}
392
+ <FormField control={item.name} />
393
+ <FormField control={item.price} />
394
+ <button onClick={() => form.items.removeAt(index)}>Remove</button>
395
+ </div>
396
+ ))}
397
+ ```
398
+
228
399
  ## 10. ASYNC WATCHFIELD (CRITICALLY IMPORTANT)
229
400
 
230
401
  ```typescript
@@ -421,3 +592,53 @@ export const creditApplicationValidation: ValidationSchemaFn<Form> = (path) => {
421
592
  | Simple | Single file: `ContactForm.tsx` |
422
593
  | Medium | Separate files: `type.ts`, `schema.ts`, `validators.ts`, `Form.tsx` |
423
594
  | Complex | Full colocation with `steps/` and `sub-forms/` |
595
+
596
+ ## 15. NON-EXISTENT API (DO NOT USE)
597
+
598
+ ⚠️ **The following APIs do NOT exist in @reformer/core:**
599
+
600
+ | ❌ Wrong | ✅ Correct | Notes |
601
+ |----------|-----------|-------|
602
+ | `useForm` | `createForm` | There is no useForm hook |
603
+ | `FieldSchema` | `FieldConfig<T>` | Type for individual field config |
604
+ | `when()` | `applyWhen()` | Conditional validation function |
605
+ | `FormFields` | `FieldNode<T>` | Type for field nodes |
606
+
607
+ ### Common Import Errors
608
+
609
+ ```typescript
610
+ // ❌ WRONG - These do NOT exist
611
+ import { useForm } from '@reformer/core'; // NO!
612
+ import { when } from '@reformer/core/validators'; // NO!
613
+ import type { FieldSchema } from '@reformer/core'; // NO!
614
+ import type { FormFields } from '@reformer/core'; // NO!
615
+
616
+ // ✅ CORRECT
617
+ import { createForm, useFormControl } from '@reformer/core';
618
+ import { applyWhen } from '@reformer/core/validators';
619
+ import type { FieldConfig, FieldNode } from '@reformer/core';
620
+ ```
621
+
622
+ ### FormSchema Common Mistakes
623
+
624
+ ```typescript
625
+ // ❌ WRONG - Simple values don't work
626
+ const schema = {
627
+ name: '', // Missing { value, component }
628
+ email: '', // Missing { value, component }
629
+ };
630
+
631
+ // ✅ CORRECT - Every field needs value and component
632
+ const schema: FormSchema<MyForm> = {
633
+ name: {
634
+ value: '',
635
+ component: Input,
636
+ componentProps: { label: 'Name' },
637
+ },
638
+ email: {
639
+ value: '',
640
+ component: Input,
641
+ componentProps: { label: 'Email', type: 'email' },
642
+ },
643
+ };
644
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reformer/core",
3
- "version": "1.1.0-beta.1",
3
+ "version": "1.1.0-beta.3",
4
4
  "description": "Reactive form state management library for React with signals-based architecture",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",