fieldwise 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Artem Kuzko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,474 @@
1
+ # Fieldwise
2
+
3
+ **Type-safe, reactive form management for React with fine-grained field subscriptions.**
4
+
5
+ Fieldwise is a lightweight, event-driven form library that provides precise control over component re-renders through field-level subscriptions. No more unnecessary re-renders from unrelated field changes.
6
+
7
+ ## Features
8
+
9
+ - ✨ **Fine-grained reactivity** - Subscribe to specific fields, not entire form state
10
+ - 🎯 **Type-safe** - Full TypeScript support with type inference
11
+ - 🪶 **Lightweight** - Event-driven architecture with no state in React components
12
+ - 🔌 **Plugin system** - Extensible with custom validation and behavior
13
+ - ⚡ **Performance** - Automatic microtask batching for synchronous updates
14
+ - 🛡️ **Zod validation** - Built-in Zod schema validation
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install fieldwise zod
20
+ # or
21
+ yarn add fieldwise zod
22
+ # or
23
+ pnpm add fieldwise zod
24
+ ```
25
+
26
+ **Peer dependencies:**
27
+
28
+ - React 18+ or React 19+
29
+ - Zod 3.x (optional, only if using validation)
30
+
31
+ ## Quick Start
32
+
33
+ ```typescript
34
+ import { fieldwise, validateZodSchema } from 'fieldwise';
35
+ import { z } from 'zod';
36
+
37
+ // Define your schema
38
+ const userSchema = z.object({
39
+ name: z.string().min(1, 'Name is required'),
40
+ email: z.string().email('Invalid email address')
41
+ });
42
+
43
+ // Infer Form values type
44
+ type UserFormValues = z.infer<typeof schema>;
45
+
46
+ // Define initial values
47
+ const emptyUser: UserFormValues = { name: '', email: '' };
48
+
49
+ // Create form hooks
50
+ const { useForm, useSlice } = fieldwise(emptyUser)
51
+ .use(validateZodSchema(userSchema))
52
+ .hooks();
53
+
54
+ // Export for use in components
55
+ export { useForm as useUserForm, useSlice as useUserSlice };
56
+ ```
57
+
58
+ ```typescript
59
+ // In your component
60
+ import { useUserForm } from './userForm';
61
+
62
+ function UserForm() {
63
+ const { fields, emit, once, i } = useUserForm();
64
+
65
+ const handleSubmit = (e) => {
66
+ e.preventDefault();
67
+
68
+ emit.later('validate'); // Defer validation to microtask
69
+
70
+ once('validated', ({ values, errors }) => {
71
+ if (errors) return; // Validation failed
72
+
73
+ // Submit the form
74
+ console.log('Submitting:', values);
75
+ });
76
+ };
77
+
78
+ return (
79
+ <form onSubmit={handleSubmit}>
80
+ <input {...i('name')} placeholder="Name" />
81
+ {fields.name.error && <span>{fields.name.error}</span>}
82
+
83
+ <input {...i('email')} type="email" placeholder="Email" />
84
+ {fields.email.error && <span>{fields.email.error}</span>}
85
+
86
+ <button type="submit">Submit</button>
87
+ </form>
88
+ );
89
+ }
90
+ ```
91
+
92
+ ## Core Concepts
93
+
94
+ ### Fine-Grained Subscriptions
95
+
96
+ Unlike traditional form libraries, Fieldwise allows you to subscribe to specific fields:
97
+
98
+ ```typescript
99
+ // Subscribe to ALL fields (re-renders on any change)
100
+ const { fields } = useUserForm();
101
+
102
+ // Subscribe to SPECIFIC fields only (re-renders only when email changes)
103
+ const { fields } = useUserSlice(['email']);
104
+ ```
105
+
106
+ ### Event-Driven Architecture
107
+
108
+ Fieldwise uses an event system for all state changes:
109
+
110
+ ```typescript
111
+ const { emit, once, fields } = useUserForm();
112
+
113
+ // Update a field
114
+ emit('change', 'name', 'John Doe');
115
+
116
+ // Trigger validation
117
+ emit.later('validate');
118
+
119
+ // Listen for validation results (one-time)
120
+ once('validated', ({ values, errors }) => {
121
+ // Handle result
122
+ });
123
+
124
+ // Reset form
125
+ emit('reset'); // to initial values
126
+ emit('reset', newValues); // to specific values
127
+ ```
128
+
129
+ ### Input Helper
130
+
131
+ The `i()` function generates all necessary props for controlled inputs:
132
+
133
+ ```typescript
134
+ <input {...i('email')} />
135
+
136
+ // Expands to:
137
+ {
138
+ name: 'email',
139
+ value: fields.email.value,
140
+ onChange: (value) => emit('change', 'email', value),
141
+ error: fields.email.error
142
+ }
143
+ ```
144
+
145
+ ## API Reference
146
+
147
+ ### `fieldwise(initialValues)`
148
+
149
+ Creates a form builder with the specified initial values.
150
+
151
+ ```typescript
152
+ const builder = fieldwise({ name: '', email: '' });
153
+ ```
154
+
155
+ ### `.use(plugin)`
156
+
157
+ Applies a plugin to the form. Plugins can add validation, logging, or custom behavior.
158
+
159
+ ```typescript
160
+ builder.use(validateZodSchema(schema)).use(myEventHandler); // Chain multiple plugins
161
+ ```
162
+
163
+ ### `.hooks()`
164
+
165
+ Generates React hooks for the form.
166
+
167
+ ```typescript
168
+ const { useForm, useSlice } = builder.hooks();
169
+ ```
170
+
171
+ ### `useForm()`
172
+
173
+ Hook that subscribes to all form fields.
174
+
175
+ **Returns:**
176
+
177
+ - `fields: FieldSet<T>` - Object containing all fields with `{ value, error, isTouched }`
178
+ - `emit: EmitFn` - Function to trigger events
179
+ - `once: OneTimeFn` - Function to listen to events once
180
+ - `isTouched: boolean` - Whether any field has been modified
181
+ - `i: InputHelper` - Function to generate input props
182
+
183
+ ### `useSlice(keys)`
184
+
185
+ Hook that subscribes to specific form fields.
186
+
187
+ ```typescript
188
+ const { fields, emit, i } = useUserSlice(['email', 'name']);
189
+ // Only re-renders when email or name changes
190
+ ```
191
+
192
+ ### Events
193
+
194
+ Available events:
195
+
196
+ - `change` - Field value changed: `emit('change', key, value)`
197
+ - `changeSome` - Multiple fields changed: `emit('changeSome', { field1: value1, field2: value2 })`
198
+ - `touch` - Mark field as touched: `emit('touch', key)`
199
+ - `touchSome` - Mark multiple fields as touched: `emit('touchSome', [key1, key2])`
200
+ - `validate` - Validation requested: `emit('validate')`
201
+ - `validated` - Validation completed: `once('validated', ({ values, errors }) => {})`
202
+ - `reset` - Form reset: `emit('reset', snapshot?)`
203
+
204
+ ## Validation
205
+
206
+ ### Zod Schema Validation
207
+
208
+ ```typescript
209
+ import { validateZodSchema } from 'fieldwise';
210
+ import { z } from 'zod';
211
+
212
+ const schema = z
213
+ .object({
214
+ email: z.email(),
215
+ password: z.string().min(8, 'Must be at least 8 characters'),
216
+ confirmPassword: z.string()
217
+ })
218
+ .refine((data) => data.password === data.confirmPassword, {
219
+ message: 'Passwords must match',
220
+ path: ['confirmPassword']
221
+ });
222
+ type UserValues = z.infer<typeof schema>;
223
+
224
+ const emptyUser: UserValues = { email: '', password: '', confirmPassword: '' };
225
+ const { useForm } = fieldwise(emptyUser).use(validateZodSchema(schema)).hooks();
226
+ ```
227
+
228
+ The validation plugin:
229
+
230
+ - Handles schema refinements with custom paths
231
+ - Returns errors as strings (can be integrated with i18n libraries if needed)
232
+ - Supports `z.coerce` for HTML input type coercion
233
+ - Error format: `{ field: 'error message' }` as `Record<keyof T, string | null>`
234
+
235
+ ### Custom Validation Plugin
236
+
237
+ ```typescript
238
+ const customValidation = (form) => {
239
+ form.on('validate', async () => {
240
+ const values = form.getValues();
241
+
242
+ // Your validation logic
243
+ const errors = await validateAsync(values);
244
+
245
+ form.emit('validated', { values, errors });
246
+ });
247
+ };
248
+
249
+ fieldwise(initialValues).use(customValidation).hooks();
250
+ ```
251
+
252
+ ## Advanced Usage
253
+
254
+ ### Conditional Fields
255
+
256
+ ```typescript
257
+ function RegistrationForm() {
258
+ const { fields, emit, i } = useForm();
259
+
260
+ // Show/hide based on field value
261
+ return (
262
+ <form>
263
+ <select {...i('accountType')}>
264
+ <option value="personal">Personal</option>
265
+ <option value="business">Business</option>
266
+ </select>
267
+
268
+ {fields.accountType.value === 'business' && (
269
+ <input {...i('companyName')} placeholder="Company Name" />
270
+ )}
271
+ </form>
272
+ );
273
+ }
274
+ ```
275
+
276
+ ### Async Validation
277
+
278
+ ```typescript
279
+ const asyncValidation = (form) => {
280
+ form.on('validate', async () => {
281
+ const values = form.getValues();
282
+
283
+ // Async check (e.g., username availability)
284
+ const isAvailable = await checkUsernameAvailability(values.username);
285
+
286
+ const errors = isAvailable
287
+ ? null
288
+ : { username: 'Username is already taken' };
289
+
290
+ form.emit('validated', { values, errors });
291
+ });
292
+ };
293
+ ```
294
+
295
+ ### Debug Mode
296
+
297
+ Enable debug logging by setting `Form.debugMode`:
298
+
299
+ ```typescript
300
+ import { Form } from 'fieldwise';
301
+
302
+ // Log all events
303
+ Form.debugMode = true;
304
+
305
+ // Log only specific events
306
+ Form.debugMode = { only: ['reset', 'validate', 'validated'] };
307
+ ```
308
+
309
+ Debug plugin is attached automatically when debug mode is enabled.
310
+
311
+ ### Material-UI Integration
312
+
313
+ **Note**: Material-UI inputs require custom wrappers since their API differs from standard HTML inputs. You'll need to create wrapper components that adapt the `i()` helper props to Material-UI's expected props.
314
+
315
+ ```typescript
316
+ import TextField from '@mui/material/TextField';
317
+
318
+ const TextFieldWrapper = ({ name, value, onChange, error }) => (
319
+ <TextField
320
+ name={name}
321
+ value={value}
322
+ onChange={(e) => onChange(e.target.value)}
323
+ label={name}
324
+ helperText={error}
325
+ error={!!error}
326
+ />
327
+ );
328
+
329
+ function MyForm() {
330
+ const { i } = useMyForm();
331
+
332
+ return <TextFieldWrapper {...i('email')} />;
333
+ }
334
+ ```
335
+
336
+ ## Performance Optimization
337
+
338
+ ### Prevent Unnecessary Re-renders
339
+
340
+ ```typescript
341
+ // ❌ Bad: Re-renders on ANY field change
342
+ const { fields } = useUserForm();
343
+
344
+ // ✅ Good: Only re-renders when email or password changes
345
+ const { fields } = useUserSlice(['email', 'password']);
346
+ ```
347
+
348
+ ### Microtask Batching
349
+
350
+ Fieldwise automatically batches synchronous updates:
351
+
352
+ ```typescript
353
+ emit('change', 'name', 'John');
354
+ emit('change', 'email', 'john@example.com');
355
+ // Both updates trigger only ONE re-render
356
+ ```
357
+
358
+ ### Validation Deferral
359
+
360
+ Use `emit.later()` to defer validation to the microtask queue:
361
+
362
+ ```typescript
363
+ const handleSubmit = () => {
364
+ emit.later('validate'); // Defers to microtask
365
+
366
+ once('validated', ({ values, errors }) => {
367
+ // Runs after all synchronous updates complete
368
+ });
369
+ };
370
+ ```
371
+
372
+ ## TypeScript Support
373
+
374
+ Fieldwise is written in TypeScript and provides full type inference:
375
+
376
+ ```typescript
377
+ type User = {
378
+ name: string;
379
+ email: string;
380
+ age: number;
381
+ };
382
+
383
+ const { useForm } = fieldwise<User>({
384
+ name: '',
385
+ email: '',
386
+ age: 0
387
+ }).hooks();
388
+
389
+ const { fields, emit, i } = useForm();
390
+
391
+ // ✅ Type-safe
392
+ emit('change', 'name', 'John');
393
+ fields.name.value; // string
394
+
395
+ // ❌ Type errors
396
+ emit('change', 'invalid', 'value'); // Error: 'invalid' is not a valid key
397
+ emit('change', 'age', 'not a number'); // Error: expected number
398
+ ```
399
+
400
+ ## Migration Guide
401
+
402
+ ### From Formik
403
+
404
+ ```typescript
405
+ // Formik
406
+ const formik = useFormik({
407
+ initialValues: { email: '' },
408
+ validationSchema: schema,
409
+ onSubmit: (values) => { ... }
410
+ });
411
+
412
+ // Fieldwise
413
+ const { fields, emit, once, i } = useForm();
414
+ const handleSubmit = () => {
415
+ emit.later('validate');
416
+ once('validated', ({ values, errors }) => {
417
+ if (!errors) onSubmit(values);
418
+ });
419
+ };
420
+ ```
421
+
422
+ ### From React Hook Form
423
+
424
+ ```typescript
425
+ // React Hook Form
426
+ const {
427
+ register,
428
+ handleSubmit,
429
+ formState: { errors }
430
+ } = useForm();
431
+
432
+ // Fieldwise
433
+ const { i, emit, once, fields } = useForm();
434
+ // Errors available at fields.fieldName.error
435
+ ```
436
+
437
+ ## Plugin Development
438
+
439
+ Create custom plugins to extend Fieldwise:
440
+
441
+ ```typescript
442
+ const myPlugin = (form) => {
443
+ // Listen to events
444
+ form.on('change', (key, value) => {
445
+ console.log(`${key} changed to ${value}`);
446
+ });
447
+
448
+ // Emit events
449
+ form.on('validate', () => {
450
+ const values = form.getValues();
451
+ // Custom validation logic
452
+ form.emit('validated', { values, errors: null });
453
+ });
454
+ };
455
+
456
+ fieldwise(initialValues).use(myPlugin).hooks();
457
+ ```
458
+
459
+ ## Contributing
460
+
461
+ Contributions are welcome! Please follow these guidelines:
462
+
463
+ - Maintain zero React state in Form class
464
+ - Keep plugins composable and single-responsibility
465
+ - Add tests for new features
466
+ - Document all public API changes
467
+
468
+ ## License
469
+
470
+ MIT
471
+
472
+ ## Credits
473
+
474
+ Extracted from a production application managing 15+ complex forms with dynamic validation, conditional fields, and multi-step flows.
package/dist/Form.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ export type Field<T> = {
2
+ value: T;
3
+ error: string | null;
4
+ isTouched: boolean;
5
+ };
6
+ export type FieldSet<T extends Values> = {
7
+ [K in keyof T]: Field<T[K]>;
8
+ };
9
+ export type FieldSubscriber<T> = (field: Field<T>) => void;
10
+ export type FieldUnsubscribeFn = () => void;
11
+ export type EventHandler<TArgs extends unknown[] = []> = (...args: TArgs) => void;
12
+ export type EventUnsubscribeFn = () => void;
13
+ export type Values = Record<string, unknown>;
14
+ export type Errors<T extends Values> = Partial<Record<keyof T, string>>;
15
+ export type EventMap<T extends Values> = {
16
+ change: [key: keyof T, value: T[keyof T]];
17
+ changeSome: [payload: Partial<T>];
18
+ touch: [key: keyof T];
19
+ touchSome: [keys: (keyof T)[]];
20
+ reset: [snapshot?: T];
21
+ errors: [errors: Errors<T>];
22
+ validate: [];
23
+ validated: [
24
+ payload: {
25
+ values: T;
26
+ errors: Errors<T> | null;
27
+ }
28
+ ];
29
+ };
30
+ export type EmitFn<T extends Values> = <K extends keyof EventMap<T>>(event: K, ...args: EventMap<T>[K]) => void;
31
+ export type DebugMode = boolean | DebugModeConfig;
32
+ export type DebugModeConfig = {
33
+ only: (keyof EventMap<Values>)[];
34
+ };
35
+ export declare class Form<T extends Values> {
36
+ static debugMode: DebugMode;
37
+ initialValues: T;
38
+ private fields;
39
+ private fieldSubscribers;
40
+ private eventHandlers;
41
+ private eventQueue;
42
+ constructor(initialValues: T);
43
+ getValue<K extends keyof T>(key: K): T[K];
44
+ getValues(): T;
45
+ get<K extends keyof T>(key: K): Field<T[K]>;
46
+ setValue<K extends keyof T>(key: K, value: T[K]): void;
47
+ setValues(newValues: Partial<T>): void;
48
+ touch<K extends keyof T>(key: K): void;
49
+ setError<K extends keyof T>(key: K, error: string | null): void;
50
+ setErrors(newErrors: Partial<Record<keyof T, string>>): void;
51
+ reset(snapshot: T): void;
52
+ getSlice<K extends keyof T>(keys: readonly K[]): Pick<FieldSet<T>, K>;
53
+ subscribeField<K extends keyof T>(key: K, callback: FieldSubscriber<T[K]>): FieldUnsubscribeFn;
54
+ on<E extends keyof EventMap<T>>(event: E, handler: EventHandler<EventMap<T>[E]>): EventUnsubscribeFn;
55
+ once<E extends keyof EventMap<T>>(event: E, handler: EventHandler<EventMap<T>[E]>): void;
56
+ emit: EmitFn<T>;
57
+ emitLater: EmitFn<T>;
58
+ private doEmit;
59
+ private processQueuedEvents;
60
+ private notify;
61
+ private valuesToFields;
62
+ }
63
+ //# sourceMappingURL=Form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../src/Form.ts"],"names":[],"mappings":"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"}