@reckona/mreact-forms 0.0.181 → 0.0.183

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/README.md CHANGED
@@ -108,7 +108,9 @@ await inviteForm.submit((values) => {
108
108
 
109
109
  - `createForm()` creates reactive form state.
110
110
  - `form.field(name).bind()` returns `{ value, onInput, onChange, onBlur }` and handles string values and boolean checkbox-style values. Pass `{ event: "change" }` to update from `onChange`; the default updates from `onInput`.
111
+ - `form.fieldArray(name)` returns stable keyed rows and `append`, `insert`, `move`, `remove`, and `swap` helpers for array-valued form fields.
111
112
  - Field state includes `validating`, which is true while the latest async field validator is pending. Slower stale validator results are ignored.
113
+ - Field validators can use `{ validate, deps }` when one field should be revalidated after another field changes or blurs.
112
114
  - `setServerErrors()` applies errors returned by a route handler or server action.
113
115
  - `form.submit()` dedupes concurrent submissions and keeps `submitting` true until the active validation and handler finish.
114
116
  - `form.reset()` restores the initial values.
package/dist/index.d.ts CHANGED
@@ -8,6 +8,13 @@ export type FieldName<TValues extends FormValues> = Extract<keyof TValues, strin
8
8
  export type FormErrors<TValues extends FormValues> = Partial<Record<FieldName<TValues> | "root", string[]>>;
9
9
  /** Validates one field value against the full form values object. */
10
10
  export type FieldValidator<TValue, TValues extends FormValues> = (value: TValue, values: TValues) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;
11
+ /** Configures one field validator and which other fields should re-trigger it. */
12
+ export interface FieldValidationConfig<TValue, TValues extends FormValues> {
13
+ deps?: readonly FieldName<TValues>[] | undefined;
14
+ validate: FieldValidator<TValue, TValues>;
15
+ }
16
+ /** Provides either a validator function or a validator descriptor with dependencies. */
17
+ export type FieldValidationEntry<TValue, TValues extends FormValues> = FieldValidator<TValue, TValues> | FieldValidationConfig<TValue, TValues>;
11
18
  /** Names the form events that can trigger validation. */
12
19
  export type FormValidateMode = "change" | "blur" | "submit";
13
20
  /** Describes the complete reactive state tracked by a form instance. */
@@ -48,10 +55,27 @@ export interface FieldApi<TValues extends FormValues, Name extends FieldName<TVa
48
55
  blur(): Promise<void>;
49
56
  setValue(value: TValues[Name]): Promise<void>;
50
57
  }
58
+ /** Extracts an array field's item type. */
59
+ export type ArrayFieldValue<TValues extends FormValues, Name extends FieldName<TValues>> = TValues[Name] extends readonly (infer Item)[] ? Item : never;
60
+ /** Describes one rendered row in a form array field. */
61
+ export interface FieldArrayRow<TValue> {
62
+ index: number;
63
+ key: string;
64
+ value: TValue;
65
+ }
66
+ /** Exposes stable keyed row state and mutation helpers for an array field. */
67
+ export interface FieldArrayApi<TValues extends FormValues, Name extends FieldName<TValues>> {
68
+ readonly fields: ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;
69
+ append(value: ArrayFieldValue<TValues, Name>): Promise<void>;
70
+ insert(index: number, value: ArrayFieldValue<TValues, Name>): Promise<void>;
71
+ move(from: number, to: number): Promise<void>;
72
+ remove(index: number): Promise<void>;
73
+ swap(first: number, second: number): Promise<void>;
74
+ }
51
75
  interface BaseCreateFormOptions<TValues extends FormValues> {
52
76
  initialValues: TValues;
53
77
  validate?: Partial<{
54
- [Name in FieldName<TValues>]: FieldValidator<TValues[Name], TValues>;
78
+ [Name in FieldName<TValues>]: FieldValidationEntry<TValues[Name], TValues>;
55
79
  }> | undefined;
56
80
  validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;
57
81
  }
@@ -94,6 +118,7 @@ export interface ServerActionErrors<TValues extends FormValues> {
94
118
  /** Provides reactive form state, field access, validation, submit, reset, and error controls. */
95
119
  export interface FormApi<TValues extends FormValues, TSubmitValues> {
96
120
  readonly state: ReadonlyCell<FormState<TValues>>;
121
+ fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name>;
97
122
  field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;
98
123
  getValues(): TValues;
99
124
  reset(values?: TValues): void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { cell } from "@reckona/mreact-reactive-core";
1
+ import { cell, computed } from "@reckona/mreact-reactive-core";
2
2
  import { validateStandardSchema } from "./standard-schema.js";
3
3
  export function createForm(options) {
4
4
  const validateOn = new Set(Array.isArray(options.validateOn) ? options.validateOn : [options.validateOn ?? "submit"]);
@@ -16,6 +16,11 @@ export function createForm(options) {
16
16
  });
17
17
  const validationGenerations = new Map();
18
18
  const dirtyFields = new Set();
19
+ const dependentFields = buildDependentFields(options.validate);
20
+ const fieldArrayKeys = new Map();
21
+ const fieldStateCells = new Map();
22
+ const fieldArrayCells = new Map();
23
+ let nextFieldArrayKey = 0;
19
24
  let activeSubmit;
20
25
  function commit(patch, dirty = dirtyFields.size > 0) {
21
26
  const previous = state.get();
@@ -30,8 +35,19 @@ export function createForm(options) {
30
35
  function setErrors(errors) {
31
36
  commit({ errors: normalizeErrors(errors) });
32
37
  }
38
+ function invalidateFieldValidations(names) {
39
+ if (names.length === 0) {
40
+ return;
41
+ }
42
+ const validating = { ...state.get().validating };
43
+ for (const name of names) {
44
+ validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);
45
+ validating[name] = false;
46
+ }
47
+ commit({ validating });
48
+ }
33
49
  async function validateField(name) {
34
- const validator = options.validate?.[name];
50
+ const validator = validatorForField(options.validate?.[name]);
35
51
  const generation = (validationGenerations.get(name) ?? 0) + 1;
36
52
  validationGenerations.set(name, generation);
37
53
  if (validator === undefined) {
@@ -74,7 +90,7 @@ export function createForm(options) {
74
90
  },
75
91
  });
76
92
  if (validateOn.has("change")) {
77
- await validateField(name);
93
+ await validateFields([name, ...dependentFieldsFor(name)]);
78
94
  }
79
95
  }
80
96
  async function blurField(name) {
@@ -85,18 +101,21 @@ export function createForm(options) {
85
101
  },
86
102
  });
87
103
  if (validateOn.has("blur")) {
88
- await validateField(name);
104
+ await validateFields([name, ...dependentFieldsFor(name)]);
89
105
  }
90
106
  }
91
107
  async function validateForm() {
92
108
  const values = state.get().values;
93
109
  const errors = {};
94
- for (const name of Object.keys(options.validate ?? {})) {
110
+ const fieldNames = Object.keys(options.validate ?? {});
111
+ invalidateFieldValidations(fieldNames);
112
+ for (const name of fieldNames) {
95
113
  const validator = options.validate?.[name];
96
- if (validator === undefined) {
114
+ const validate = validatorForField(validator);
115
+ if (validate === undefined) {
97
116
  continue;
98
117
  }
99
- const fieldErrors = normalizeFieldErrors(await validator(values[name], values));
118
+ const fieldErrors = normalizeFieldErrors(await validate(values[name], values));
100
119
  if (fieldErrors.length > 0) {
101
120
  errors[name] = fieldErrors;
102
121
  }
@@ -127,13 +146,132 @@ export function createForm(options) {
127
146
  value: values,
128
147
  };
129
148
  }
149
+ async function validateFields(names) {
150
+ const uniqueNames = [...new Set(names)];
151
+ await Promise.all(uniqueNames.map((fieldName) => validateField(fieldName)));
152
+ }
153
+ function dependentFieldsFor(name) {
154
+ return dependentFields.get(name) ?? [];
155
+ }
156
+ function arrayValues(name) {
157
+ const value = state.get().values[name];
158
+ return Array.isArray(value) ? [...value] : [];
159
+ }
160
+ function createFieldArrayKey(name) {
161
+ const key = `${name}:${nextFieldArrayKey}`;
162
+ nextFieldArrayKey += 1;
163
+ return key;
164
+ }
165
+ function ensureFieldArrayKeys(name, length) {
166
+ const keys = fieldArrayKeys.get(name) ?? [];
167
+ if (keys.length > length) {
168
+ keys.length = length;
169
+ }
170
+ while (keys.length < length) {
171
+ keys.push(createFieldArrayKey(name));
172
+ }
173
+ fieldArrayKeys.set(name, keys);
174
+ return keys;
175
+ }
176
+ function fieldArrayRows(name) {
177
+ const values = arrayValues(name);
178
+ const keys = ensureFieldArrayKeys(name, values.length);
179
+ return values.map((value, index) => ({
180
+ index,
181
+ key: keys[index] ?? createFieldArrayKey(name),
182
+ value,
183
+ }));
184
+ }
185
+ function fieldArrayCell(name) {
186
+ const existing = fieldArrayCells.get(name);
187
+ if (existing !== undefined) {
188
+ return existing;
189
+ }
190
+ const next = computed(() => fieldArrayRows(name), { equals: fieldArrayRowsEqual });
191
+ fieldArrayCells.set(name, next);
192
+ return next;
193
+ }
194
+ function fieldStateCell(name) {
195
+ const existing = fieldStateCells.get(name);
196
+ if (existing !== undefined) {
197
+ return existing;
198
+ }
199
+ const next = computed(() => fieldState(state.get(), name), { equals: fieldStateEquals });
200
+ fieldStateCells.set(name, next);
201
+ return next;
202
+ }
203
+ async function setArrayValue(name, values, keys) {
204
+ fieldArrayKeys.set(name, keys);
205
+ await setValue(name, values);
206
+ }
207
+ async function validateArrayField(name) {
208
+ if (validateOn.has("change")) {
209
+ await validateFields([name, ...dependentFieldsFor(name)]);
210
+ }
211
+ }
130
212
  return {
131
213
  state,
132
- field(name) {
214
+ fieldArray(name) {
133
215
  return {
134
- state: {
135
- get: () => fieldState(state.get(), name),
216
+ fields: fieldArrayCell(name),
217
+ async append(value) {
218
+ const values = arrayValues(name);
219
+ const keys = [...ensureFieldArrayKeys(name, values.length), createFieldArrayKey(name)];
220
+ values.push(value);
221
+ await setArrayValue(name, values, keys);
222
+ },
223
+ async insert(index, value) {
224
+ const values = arrayValues(name);
225
+ const insertIndex = clampIndex(index, 0, values.length);
226
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
227
+ values.splice(insertIndex, 0, value);
228
+ keys.splice(insertIndex, 0, createFieldArrayKey(name));
229
+ await setArrayValue(name, values, keys);
230
+ },
231
+ async move(from, to) {
232
+ const values = arrayValues(name);
233
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
234
+ if (!isArrayIndex(from, values.length)) {
235
+ await validateArrayField(name);
236
+ return;
237
+ }
238
+ const [value] = values.splice(from, 1);
239
+ const [key] = keys.splice(from, 1);
240
+ const insertIndex = clampIndex(to, 0, values.length);
241
+ values.splice(insertIndex, 0, value);
242
+ keys.splice(insertIndex, 0, key);
243
+ await setArrayValue(name, values, keys);
244
+ },
245
+ async remove(index) {
246
+ const values = arrayValues(name);
247
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
248
+ if (!isArrayIndex(index, values.length)) {
249
+ await validateArrayField(name);
250
+ return;
251
+ }
252
+ values.splice(index, 1);
253
+ keys.splice(index, 1);
254
+ await setArrayValue(name, values, keys);
255
+ },
256
+ async swap(first, second) {
257
+ const values = arrayValues(name);
258
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
259
+ if (!isArrayIndex(first, values.length) || !isArrayIndex(second, values.length)) {
260
+ await validateArrayField(name);
261
+ return;
262
+ }
263
+ [values[first], values[second]] = [
264
+ values[second],
265
+ values[first],
266
+ ];
267
+ [keys[first], keys[second]] = [keys[second], keys[first]];
268
+ await setArrayValue(name, values, keys);
136
269
  },
270
+ };
271
+ },
272
+ field(name) {
273
+ return {
274
+ state: fieldStateCell(name),
137
275
  bind(bindingOptions = {}) {
138
276
  const bindingEvent = bindingOptions.event ?? "input";
139
277
  const updateValue = async (inputEvent) => {
@@ -173,6 +311,7 @@ export function createForm(options) {
173
311
  }
174
312
  initialValues = cloneValues(values);
175
313
  dirtyFields.clear();
314
+ fieldArrayKeys.clear();
176
315
  commit({
177
316
  errors: {},
178
317
  initialValues,
@@ -258,6 +397,41 @@ function fieldState(formState, name) {
258
397
  value: formState.values[name],
259
398
  };
260
399
  }
400
+ function fieldStateEquals(previous, next) {
401
+ return (previous.dirty === next.dirty &&
402
+ previous.touched === next.touched &&
403
+ previous.validating === next.validating &&
404
+ Object.is(previous.value, next.value) &&
405
+ stringArraysEqual(previous.errors, next.errors));
406
+ }
407
+ function fieldArrayRowsEqual(previous, next) {
408
+ if (previous.length !== next.length) {
409
+ return false;
410
+ }
411
+ for (let index = 0; index < previous.length; index += 1) {
412
+ const previousRow = previous[index];
413
+ const nextRow = next[index];
414
+ if (previousRow === undefined ||
415
+ nextRow === undefined ||
416
+ previousRow.index !== nextRow.index ||
417
+ previousRow.key !== nextRow.key ||
418
+ !Object.is(previousRow.value, nextRow.value)) {
419
+ return false;
420
+ }
421
+ }
422
+ return true;
423
+ }
424
+ function stringArraysEqual(previous, next) {
425
+ if (previous.length !== next.length) {
426
+ return false;
427
+ }
428
+ for (let index = 0; index < previous.length; index += 1) {
429
+ if (previous[index] !== next[index]) {
430
+ return false;
431
+ }
432
+ }
433
+ return true;
434
+ }
261
435
  function eventValue(event, currentValue) {
262
436
  const target = event.currentTarget ?? event.target;
263
437
  if (target !== null && typeof target === "object") {
@@ -303,6 +477,32 @@ function normalizeFieldErrors(errors) {
303
477
  }
304
478
  return typeof errors === "string" ? [errors] : [...errors];
305
479
  }
480
+ function clampIndex(index, min, max) {
481
+ return Math.min(Math.max(Math.trunc(index), min), max);
482
+ }
483
+ function isArrayIndex(index, length) {
484
+ return Number.isInteger(index) && index >= 0 && index < length;
485
+ }
486
+ function validatorForField(entry) {
487
+ return typeof entry === "function" ? entry : entry?.validate;
488
+ }
489
+ function buildDependentFields(validate) {
490
+ const dependents = new Map();
491
+ for (const [fieldName, entry] of Object.entries(validate ?? {})) {
492
+ if (entry === undefined || typeof entry === "function") {
493
+ continue;
494
+ }
495
+ for (const dep of entry.deps ?? []) {
496
+ const fields = dependents.get(dep);
497
+ if (fields === undefined) {
498
+ dependents.set(dep, [fieldName]);
499
+ continue;
500
+ }
501
+ fields.push(fieldName);
502
+ }
503
+ }
504
+ return dependents;
505
+ }
306
506
  function cloneValues(values) {
307
507
  return { ...values };
308
508
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAqB,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAyB,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAqJrF,MAAM,UAAU,UAAU,CACxB,OAAkD;IAElD,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,QAAQ,CAAC,CAC1F,CAAC;IACF,IAAI,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAqB;QACrC,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,EAAE;QACV,aAAa;QACb,WAAW,EAAE,CAAC;QACd,UAAU,EAAE,KAAK;QACjB,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,EAAE;QACd,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC;KAC3C,CAAC,CAAC;IACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAA8B,CAAC;IACpE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;IAClD,IAAI,YAAqE,CAAC;IAE1E,SAAS,MAAM,CAAC,KAAkC,EAAE,KAAK,GAAG,WAAW,CAAC,IAAI,GAAG,CAAC;QAC9E,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG;YACX,GAAG,QAAQ;YACX,GAAG,KAAK;SACT,CAAC;QACF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IAED,SAAS,SAAS,CAAC,MAA2B;QAC5C,MAAM,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,UAAU,aAAa,CAAkC,IAAU;QACtE,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,CAAC,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC9D,qBAAqB,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAE5C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACzB,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;QAClC,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAErD,IAAI,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;YACnD,OAAO;QACT,CAAC;QAED,cAAc,CAAC,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC;QACnD,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,SAAS,cAAc,CACrB,IAAU,EACV,MAAyB;QAEzB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;QACnC,SAAS,CAAC;YACR,GAAG,OAAO;YACV,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;SACG,CAAC,CAAC;IAC5B,CAAC;IAED,SAAS,kBAAkB,CACzB,IAAU,EACV,UAAmB;QAEnB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC;QACvC,MAAM,CAAC;YACL,UAAU,EAAE;gBACV,GAAG,OAAO;gBACV,CAAC,IAAI,CAAC,EAAE,UAAU;aACnB;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,UAAU,QAAQ,CACrB,IAAU,EACV,KAAoB;QAEpB,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC;YACL,MAAM,EAAE;gBACN,GAAG,QAAQ,CAAC,MAAM;gBAClB,CAAC,IAAI,CAAC,EAAE,KAAK;aACd;SACF,CAAC,CAAC;QAEH,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,UAAU,SAAS,CAAkC,IAAU;QAClE,MAAM,CAAC;YACL,OAAO,EAAE;gBACP,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO;gBACtB,CAAC,IAAI,CAAC,EAAE,IAAI;aACb;SACF,CAAC,CAAC;QAEH,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,UAAU,YAAY;QACzB,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;QAClC,MAAM,MAAM,GAAwB,EAAE,CAAC;QAEvC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAA8B,EAAE,CAAC;YACpF,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,SAAS;YACX,CAAC;YACD,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAChF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAEpE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC1C,CAAC;iBAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5C,SAAS,CAAC,EAAE,CAAC,CAAC;gBACd,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE,MAAM,CAAC,KAAsB;iBACrC,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,SAAS,CAAC,MAAM,CAAC,CAAC;YAClB,OAAO;gBACL,MAAM;gBACN,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;QAED,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,OAAO;YACL,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,MAAiC;SACzC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK;QACL,KAAK,CAAkC,IAAU;YAC/C,OAAO;gBACL,KAAK,EAAE;oBACL,GAAG,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC;iBACzC;gBACD,IAAI,CAAC,cAAc,GAAG,EAAE;oBACtB,MAAM,YAAY,GAAG,cAAc,CAAC,KAAK,IAAI,OAAO,CAAC;oBACrD,MAAM,WAAW,GAAG,KAAK,EAAE,UAAiB,EAAE,EAAE;wBAC9C,MAAM,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAkB,CAAC,CAAC;oBAC1F,CAAC,CAAC;oBAEF,OAAO;wBACL,MAAM,EAAE,KAAK,IAAI,EAAE;4BACjB,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;wBACxB,CAAC;wBACD,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;4BACxB,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;gCAC9B,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;4BAC3B,CAAC;wBACH,CAAC;wBACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;4BACvB,IAAI,YAAY,KAAK,OAAO,EAAE,CAAC;gCAC7B,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;4BAC3B,CAAC;wBACH,CAAC;wBACD,IAAI,KAAK;4BACP,OAAO,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;wBAClC,CAAC;qBACF,CAAC;gBACJ,CAAC;gBACD,KAAK,CAAC,IAAI;oBACR,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;gBACxB,CAAC;gBACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;aAC3C,CAAC;QACJ,CAAC;QACD,SAAS;YACP,OAAO,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,MAAM,GAAG,aAAa;YAC1B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAA8B,EAAE,CAAC;gBACpF,qBAAqB,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9E,CAAC;YAED,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;YACpC,WAAW,CAAC,KAAK,EAAE,CAAC;YACpB,MAAM,CAAC;gBACL,MAAM,EAAE,EAAE;gBACV,aAAa;gBACb,WAAW,EAAE,CAAC;gBACd,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,EAAE;gBACX,UAAU,EAAE,EAAE;gBACd,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;aAC5B,CAAC,CAAC;QACL,CAAC;QACD,SAAS;QACT,eAAe,CAAC,MAAM;YACpB,MAAM,IAAI,GAAwB,EAAE,CAAC;YAErC,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,CAAC;gBACxE,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC/B,SAAS;gBACX,CAAC;gBAED,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClD,IAAI,CAAC,IAA0B,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;YAED,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpE,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YACrC,CAAC;YAED,SAAS,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,QAAQ;QACR,KAAK,CAAC,MAAM,CACV,OAA8D;YAE9D,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC/B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YACjC,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,KAAK,IAAiD,EAAE;gBACpE,MAAM,CAAC;oBACL,WAAW,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,GAAG,CAAC;oBACxC,UAAU,EAAE,IAAI;iBACjB,CAAC,CAAC;gBAEH,IAAI,CAAC;oBACH,MAAM,UAAU,GAAG,MAAM,YAAY,EAAE,CAAC;oBAExC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;wBACxB,OAAO;4BACL,MAAM,EAAE,UAAU,CAAC,MAAM;4BACzB,MAAM,EAAE,SAAS;yBAClB,CAAC;oBACJ,CAAC;oBAED,IAAI,CAAC;wBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;wBAC7C,OAAO;4BACL,IAAI;4BACJ,MAAM,EAAE,SAAS;yBAClB,CAAC;oBACJ,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,OAAO;4BACL,KAAK;4BACL,MAAM,EAAE,OAAO;yBAChB,CAAC;oBACJ,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,YAAY,GAAG,SAAS,CAAC;oBACzB,MAAM,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC,CAAC,EAAE,CAAC;YAEL,YAAY,GAAG,IAAmD,CAAC;YACnE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,QAAQ,EAAE,YAAY;KACvB,CAAC;IAEF,SAAS,gBAAgB,CACvB,IAAU,EACV,KAAoB;QAEpB,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC1C,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO;QACT,CAAC;QAED,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CACjB,SAA6B,EAC7B,IAAU;IAEV,OAAO;QACL,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACxE,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3C,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI;QACzC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI;QAC/C,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,KAAY,EAAE,YAAqB;IACrD,MAAM,MAAM,GAAG,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,MAAM,CAAC;IAEnD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,IAAI,OAAO,YAAY,KAAK,SAAS,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;YAC7D,OAAO,OAAO,CAAE,MAA+B,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;YACtB,OAAQ,MAA6B,CAAC,KAAK,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,gBAAgB,CACvB,MAA2B,EAC3B,MAA6C;IAE7C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAEjE,CAAC;QACX,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CACnB,IAA2E;IAE3E,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAExB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK;QAClE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;QACnB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACpB,CAAC;AAED,SAAS,eAAe,CACtB,MAA2B;IAE3B,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAClD,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAC/E,CACqB,CAAC;AAC3B,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAgB;IAC5C,OAAO,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,WAAW,CAAC;AAC7E,CAAC;AAED,SAAS,WAAW,CAA6B,MAA2B;IAC1E,OAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,CAAiC,CAAC,KAAK,CACjE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,CAC9D,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,MAA8C;IAC1E,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,WAAW,CAA6B,MAAe;IAC9D,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC;AACvB,CAAC;AAQD,yFAAyF;AACzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["import { cell, type ReadonlyCell } from \"@reckona/mreact-reactive-core\";\nimport { type StandardSchemaV1, validateStandardSchema } from \"./standard-schema.js\";\n\n/** Represents the object shape managed by a form instance. */\nexport type FormValues = Record<string, unknown>;\n\n/** Extracts string field names from a form value object. */\nexport type FieldName<TValues extends FormValues> = Extract<keyof TValues, string>;\n\n/** Maps field names and the root form key to validation error messages. */\nexport type FormErrors<TValues extends FormValues> = Partial<\n Record<FieldName<TValues> | \"root\", string[]>\n>;\n\n/** Validates one field value against the full form values object. */\nexport type FieldValidator<TValue, TValues extends FormValues> = (\n value: TValue,\n values: TValues,\n) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;\n\n/** Names the form events that can trigger validation. */\nexport type FormValidateMode = \"change\" | \"blur\" | \"submit\";\n\n/** Describes the complete reactive state tracked by a form instance. */\nexport interface FormState<TValues extends FormValues> {\n dirty: boolean;\n errors: FormErrors<TValues>;\n initialValues: TValues;\n submitCount: number;\n submitting: boolean;\n touched: Partial<Record<FieldName<TValues>, boolean>>;\n valid: boolean;\n validating: Partial<Record<FieldName<TValues>, boolean>>;\n values: TValues;\n}\n\n/** Describes the derived state for one form field. */\nexport interface FieldState<TValue> {\n dirty: boolean;\n errors: string[];\n touched: boolean;\n validating: boolean;\n value: TValue;\n}\n\n/** Provides DOM event handlers and current value for binding a field to an input. */\nexport interface FieldBinding<TValue> {\n onBlur(event: Event): Promise<void>;\n onChange(event: Event): Promise<void>;\n onInput(event: Event): Promise<void>;\n value: TValue;\n}\n\n/** Configures which DOM event updates a field binding. */\nexport interface FieldBindingOptions {\n event?: \"change\" | \"input\" | undefined;\n}\n\n/** Exposes state, binding, blur, and value update controls for one field. */\nexport interface FieldApi<TValues extends FormValues, Name extends FieldName<TValues>> {\n readonly state: ReadonlyCell<FieldState<TValues[Name]>>;\n bind(options?: FieldBindingOptions): FieldBinding<TValues[Name]>;\n blur(): Promise<void>;\n setValue(value: TValues[Name]): Promise<void>;\n}\n\ninterface BaseCreateFormOptions<TValues extends FormValues> {\n initialValues: TValues;\n validate?:\n | Partial<{\n [Name in FieldName<TValues>]: FieldValidator<TValues[Name], TValues>;\n }>\n | undefined;\n validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;\n}\n\n/** Configures form creation without schema-level submit value transformation. */\nexport interface CreateFormOptionsWithoutSchema<TValues extends FormValues>\n extends BaseCreateFormOptions<TValues> {\n schema?: undefined;\n}\n\n/** Configures form creation with a Standard Schema validator that may transform submit values. */\nexport interface CreateFormOptionsWithSchema<TValues extends FormValues, TSubmitValues>\n extends BaseCreateFormOptions<TValues> {\n schema: StandardSchemaV1<TValues, TSubmitValues>;\n}\n\n/** Configures form creation with optional field validators and optional Standard Schema validation. */\nexport type CreateFormOptions<TValues extends FormValues, TSubmitValues = TValues> =\n | CreateFormOptionsWithoutSchema<TValues>\n | CreateFormOptionsWithSchema<TValues, TSubmitValues>;\n\n/** Reports either successful normalized submit values or form validation errors. */\nexport type FormValidationResult<TValues extends FormValues, TSubmitValues> =\n | {\n success: true;\n value: TSubmitValues;\n }\n | {\n errors: FormErrors<TValues>;\n success: false;\n };\n\n/** Reports the result of a form submit handler after validation and error capture. */\nexport type FormSubmitResult<TValues extends FormValues, TResult> =\n | {\n data: TResult;\n status: \"success\";\n }\n | {\n status: \"duplicate\";\n }\n | {\n errors: FormErrors<TValues>;\n status: \"invalid\";\n }\n | {\n error: unknown;\n status: \"error\";\n };\n\n/** Represents field and form errors returned by a server action. */\nexport interface ServerActionErrors<TValues extends FormValues> {\n fieldErrors?: Partial<Record<FieldName<TValues>, readonly string[]>> | undefined;\n formErrors?: readonly string[] | undefined;\n}\n\n/** Provides reactive form state, field access, validation, submit, reset, and error controls. */\nexport interface FormApi<TValues extends FormValues, TSubmitValues> {\n readonly state: ReadonlyCell<FormState<TValues>>;\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;\n getValues(): TValues;\n reset(values?: TValues): void;\n setErrors(errors: FormErrors<TValues>): void;\n setServerErrors(errors: ServerActionErrors<TValues>): void;\n setValue<Name extends FieldName<TValues>>(name: Name, value: TValues[Name]): Promise<void>;\n submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>>;\n validate(): Promise<FormValidationResult<TValues, TSubmitValues>>;\n}\n\n/** Creates a reactive form API from initial values, validators, and optional schema validation. */\nexport function createForm<TValues extends FormValues>(\n options: CreateFormOptionsWithoutSchema<TValues>,\n): FormApi<TValues, TValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues>(\n options: CreateFormOptionsWithSchema<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues = TValues>(\n options: CreateFormOptions<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues> {\n const validateOn = new Set(\n Array.isArray(options.validateOn) ? options.validateOn : [options.validateOn ?? \"submit\"],\n );\n let initialValues = cloneValues(options.initialValues);\n const state = cell<FormState<TValues>>({\n dirty: false,\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n valid: true,\n validating: {},\n values: cloneValues(options.initialValues),\n });\n const validationGenerations = new Map<FieldName<TValues>, number>();\n const dirtyFields = new Set<FieldName<TValues>>();\n let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;\n\n function commit(patch: Partial<FormState<TValues>>, dirty = dirtyFields.size > 0): void {\n const previous = state.get();\n const next = {\n ...previous,\n ...patch,\n };\n next.dirty = dirty;\n next.valid = hasNoErrors(next.errors);\n state.set(next);\n }\n\n function setErrors(errors: FormErrors<TValues>): void {\n commit({ errors: normalizeErrors(errors) });\n }\n\n async function validateField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n const validator = options.validate?.[name];\n const generation = (validationGenerations.get(name) ?? 0) + 1;\n validationGenerations.set(name, generation);\n\n if (validator === undefined) {\n setFieldErrors(name, []);\n setFieldValidating(name, false);\n return;\n }\n\n const values = state.get().values;\n setFieldValidating(name, true);\n const errors = await validator(values[name], values);\n\n if (validationGenerations.get(name) !== generation) {\n return;\n }\n\n setFieldErrors(name, normalizeFieldErrors(errors));\n setFieldValidating(name, false);\n }\n\n function setFieldErrors<Name extends FieldName<TValues>>(\n name: Name,\n errors: readonly string[],\n ): void {\n const current = state.get().errors;\n setErrors({\n ...current,\n [name]: [...errors],\n } as FormErrors<TValues>);\n }\n\n function setFieldValidating<Name extends FieldName<TValues>>(\n name: Name,\n validating: boolean,\n ): void {\n const current = state.get().validating;\n commit({\n validating: {\n ...current,\n [name]: validating,\n },\n });\n }\n\n async function setValue<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): Promise<void> {\n const previous = state.get();\n updateDirtyField(name, value);\n commit({\n values: {\n ...previous.values,\n [name]: value,\n },\n });\n\n if (validateOn.has(\"change\")) {\n await validateField(name);\n }\n }\n\n async function blurField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n commit({\n touched: {\n ...state.get().touched,\n [name]: true,\n },\n });\n\n if (validateOn.has(\"blur\")) {\n await validateField(name);\n }\n }\n\n async function validateForm(): Promise<FormValidationResult<TValues, TSubmitValues>> {\n const values = state.get().values;\n const errors: FormErrors<TValues> = {};\n\n for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {\n const validator = options.validate?.[name];\n if (validator === undefined) {\n continue;\n }\n const fieldErrors = normalizeFieldErrors(await validator(values[name], values));\n if (fieldErrors.length > 0) {\n errors[name] = fieldErrors;\n }\n }\n\n if (options.schema !== undefined) {\n const result = await validateStandardSchema(options.schema, values);\n\n if (!result.success) {\n mergeIssueErrors(errors, result.issues);\n } else if (Object.keys(errors).length === 0) {\n setErrors({});\n return {\n success: true,\n value: result.value as TSubmitValues,\n };\n }\n }\n\n if (Object.keys(errors).length > 0) {\n setErrors(errors);\n return {\n errors,\n success: false,\n };\n }\n\n setErrors({});\n return {\n success: true,\n value: values as TValues & TSubmitValues,\n };\n }\n\n return {\n state,\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {\n return {\n state: {\n get: () => fieldState(state.get(), name),\n },\n bind(bindingOptions = {}) {\n const bindingEvent = bindingOptions.event ?? \"input\";\n const updateValue = async (inputEvent: Event) => {\n await setValue(name, eventValue(inputEvent, state.get().values[name]) as TValues[Name]);\n };\n\n return {\n onBlur: async () => {\n await blurField(name);\n },\n onChange: async (event) => {\n if (bindingEvent === \"change\") {\n await updateValue(event);\n }\n },\n onInput: async (event) => {\n if (bindingEvent === \"input\") {\n await updateValue(event);\n }\n },\n get value() {\n return state.get().values[name];\n },\n };\n },\n async blur() {\n await blurField(name);\n },\n setValue: (value) => setValue(name, value),\n };\n },\n getValues() {\n return state.get().values;\n },\n reset(values = initialValues): void {\n for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {\n validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);\n }\n\n initialValues = cloneValues(values);\n dirtyFields.clear();\n commit({\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n validating: {},\n values: cloneValues(values),\n });\n },\n setErrors,\n setServerErrors(errors): void {\n const next: FormErrors<TValues> = {};\n\n for (const [name, messages] of Object.entries(errors.fieldErrors ?? {})) {\n if (isDangerousObjectKey(name)) {\n continue;\n }\n\n if (messages !== undefined && messages.length > 0) {\n next[name as FieldName<TValues>] = [...messages];\n }\n }\n\n if (errors.formErrors !== undefined && errors.formErrors.length > 0) {\n next.root = [...errors.formErrors];\n }\n\n setErrors(next);\n },\n setValue,\n async submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>> {\n if (activeSubmit !== undefined) {\n return { status: \"duplicate\" };\n }\n\n const task = (async (): Promise<FormSubmitResult<TValues, TResult>> => {\n commit({\n submitCount: state.get().submitCount + 1,\n submitting: true,\n });\n\n try {\n const validation = await validateForm();\n\n if (!validation.success) {\n return {\n errors: validation.errors,\n status: \"invalid\",\n };\n }\n\n try {\n const data = await handler(validation.value);\n return {\n data,\n status: \"success\",\n };\n } catch (error) {\n return {\n error,\n status: \"error\",\n };\n }\n } finally {\n activeSubmit = undefined;\n commit({ submitting: false });\n }\n })();\n\n activeSubmit = task as Promise<FormSubmitResult<TValues, unknown>>;\n return task;\n },\n validate: validateForm,\n };\n\n function updateDirtyField<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): void {\n if (Object.is(value, initialValues[name])) {\n dirtyFields.delete(name);\n return;\n }\n\n dirtyFields.add(name);\n }\n}\n\nfunction fieldState<TValues extends FormValues, Name extends FieldName<TValues>>(\n formState: FormState<TValues>,\n name: Name,\n): FieldState<TValues[Name]> {\n return {\n dirty: !Object.is(formState.values[name], formState.initialValues[name]),\n errors: [...(formState.errors[name] ?? [])],\n touched: formState.touched[name] === true,\n validating: formState.validating[name] === true,\n value: formState.values[name],\n };\n}\n\nfunction eventValue(event: Event, currentValue: unknown): unknown {\n const target = event.currentTarget ?? event.target;\n\n if (target !== null && typeof target === \"object\") {\n if (typeof currentValue === \"boolean\" && \"checked\" in target) {\n return Boolean((target as { checked: unknown }).checked);\n }\n\n if (\"value\" in target) {\n return (target as { value: unknown }).value;\n }\n }\n\n return currentValue;\n}\n\nfunction mergeIssueErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): void {\n for (const issue of issues) {\n const key = issuePathKey(issue.path);\n const name = (key === undefined || isDangerousObjectKey(key) ? \"root\" : key) as\n | FieldName<TValues>\n | \"root\";\n const messages = errors[name] ?? [];\n messages.push(issue.message);\n errors[name] = messages;\n }\n}\n\nfunction issuePathKey(\n path: ReadonlyArray<PropertyKey | StandardSchemaV1.PathSegment> | undefined,\n): string | undefined {\n const first = path?.[0];\n\n if (first === undefined) {\n return undefined;\n }\n\n return typeof first === \"object\" && first !== null && \"key\" in first\n ? String(first.key)\n : String(first);\n}\n\nfunction normalizeErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n): FormErrors<TValues> {\n return Object.fromEntries(\n Object.entries(errors).flatMap(([name, messages]) =>\n messages === undefined || messages.length === 0 ? [] : [[name, [...messages]]],\n ),\n ) as FormErrors<TValues>;\n}\n\nfunction isDangerousObjectKey(key: PropertyKey): boolean {\n return key === \"__proto__\" || key === \"constructor\" || key === \"prototype\";\n}\n\nfunction hasNoErrors<TValues extends FormValues>(errors: FormErrors<TValues>): boolean {\n return (Object.values(errors) as Array<string[] | undefined>).every(\n (messages) => messages === undefined || messages.length === 0,\n );\n}\n\nfunction normalizeFieldErrors(errors: readonly string[] | string | undefined): string[] {\n if (errors === undefined) {\n return [];\n }\n\n return typeof errors === \"string\" ? [errors] : [...errors];\n}\n\nfunction cloneValues<TValues extends FormValues>(values: TValues): TValues {\n return { ...values };\n}\n\n/** Re-exports Standard Schema types used by form schema options. */\nexport type {\n InferStandardSchemaInput,\n InferStandardSchemaOutput,\n StandardSchemaV1,\n} from \"./standard-schema.js\";\n/** Validates an unknown value with a Standard Schema and normalizes the result shape. */\nexport { validateStandardSchema } from \"./standard-schema.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { cell, computed, type ReadonlyCell } from \"@reckona/mreact-reactive-core\";\nimport { type StandardSchemaV1, validateStandardSchema } from \"./standard-schema.js\";\n\n/** Represents the object shape managed by a form instance. */\nexport type FormValues = Record<string, unknown>;\n\n/** Extracts string field names from a form value object. */\nexport type FieldName<TValues extends FormValues> = Extract<keyof TValues, string>;\n\n/** Maps field names and the root form key to validation error messages. */\nexport type FormErrors<TValues extends FormValues> = Partial<\n Record<FieldName<TValues> | \"root\", string[]>\n>;\n\n/** Validates one field value against the full form values object. */\nexport type FieldValidator<TValue, TValues extends FormValues> = (\n value: TValue,\n values: TValues,\n) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;\n\n/** Configures one field validator and which other fields should re-trigger it. */\nexport interface FieldValidationConfig<TValue, TValues extends FormValues> {\n deps?: readonly FieldName<TValues>[] | undefined;\n validate: FieldValidator<TValue, TValues>;\n}\n\n/** Provides either a validator function or a validator descriptor with dependencies. */\nexport type FieldValidationEntry<TValue, TValues extends FormValues> =\n | FieldValidator<TValue, TValues>\n | FieldValidationConfig<TValue, TValues>;\n\n/** Names the form events that can trigger validation. */\nexport type FormValidateMode = \"change\" | \"blur\" | \"submit\";\n\n/** Describes the complete reactive state tracked by a form instance. */\nexport interface FormState<TValues extends FormValues> {\n dirty: boolean;\n errors: FormErrors<TValues>;\n initialValues: TValues;\n submitCount: number;\n submitting: boolean;\n touched: Partial<Record<FieldName<TValues>, boolean>>;\n valid: boolean;\n validating: Partial<Record<FieldName<TValues>, boolean>>;\n values: TValues;\n}\n\n/** Describes the derived state for one form field. */\nexport interface FieldState<TValue> {\n dirty: boolean;\n errors: string[];\n touched: boolean;\n validating: boolean;\n value: TValue;\n}\n\n/** Provides DOM event handlers and current value for binding a field to an input. */\nexport interface FieldBinding<TValue> {\n onBlur(event: Event): Promise<void>;\n onChange(event: Event): Promise<void>;\n onInput(event: Event): Promise<void>;\n value: TValue;\n}\n\n/** Configures which DOM event updates a field binding. */\nexport interface FieldBindingOptions {\n event?: \"change\" | \"input\" | undefined;\n}\n\n/** Exposes state, binding, blur, and value update controls for one field. */\nexport interface FieldApi<TValues extends FormValues, Name extends FieldName<TValues>> {\n readonly state: ReadonlyCell<FieldState<TValues[Name]>>;\n bind(options?: FieldBindingOptions): FieldBinding<TValues[Name]>;\n blur(): Promise<void>;\n setValue(value: TValues[Name]): Promise<void>;\n}\n\n/** Extracts an array field's item type. */\nexport type ArrayFieldValue<\n TValues extends FormValues,\n Name extends FieldName<TValues>,\n> = TValues[Name] extends readonly (infer Item)[] ? Item : never;\n\n/** Describes one rendered row in a form array field. */\nexport interface FieldArrayRow<TValue> {\n index: number;\n key: string;\n value: TValue;\n}\n\n/** Exposes stable keyed row state and mutation helpers for an array field. */\nexport interface FieldArrayApi<TValues extends FormValues, Name extends FieldName<TValues>> {\n readonly fields: ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;\n append(value: ArrayFieldValue<TValues, Name>): Promise<void>;\n insert(index: number, value: ArrayFieldValue<TValues, Name>): Promise<void>;\n move(from: number, to: number): Promise<void>;\n remove(index: number): Promise<void>;\n swap(first: number, second: number): Promise<void>;\n}\n\ninterface BaseCreateFormOptions<TValues extends FormValues> {\n initialValues: TValues;\n validate?:\n | Partial<{\n [Name in FieldName<TValues>]: FieldValidationEntry<TValues[Name], TValues>;\n }>\n | undefined;\n validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;\n}\n\n/** Configures form creation without schema-level submit value transformation. */\nexport interface CreateFormOptionsWithoutSchema<\n TValues extends FormValues,\n> extends BaseCreateFormOptions<TValues> {\n schema?: undefined;\n}\n\n/** Configures form creation with a Standard Schema validator that may transform submit values. */\nexport interface CreateFormOptionsWithSchema<\n TValues extends FormValues,\n TSubmitValues,\n> extends BaseCreateFormOptions<TValues> {\n schema: StandardSchemaV1<TValues, TSubmitValues>;\n}\n\n/** Configures form creation with optional field validators and optional Standard Schema validation. */\nexport type CreateFormOptions<TValues extends FormValues, TSubmitValues = TValues> =\n | CreateFormOptionsWithoutSchema<TValues>\n | CreateFormOptionsWithSchema<TValues, TSubmitValues>;\n\n/** Reports either successful normalized submit values or form validation errors. */\nexport type FormValidationResult<TValues extends FormValues, TSubmitValues> =\n | {\n success: true;\n value: TSubmitValues;\n }\n | {\n errors: FormErrors<TValues>;\n success: false;\n };\n\n/** Reports the result of a form submit handler after validation and error capture. */\nexport type FormSubmitResult<TValues extends FormValues, TResult> =\n | {\n data: TResult;\n status: \"success\";\n }\n | {\n status: \"duplicate\";\n }\n | {\n errors: FormErrors<TValues>;\n status: \"invalid\";\n }\n | {\n error: unknown;\n status: \"error\";\n };\n\n/** Represents field and form errors returned by a server action. */\nexport interface ServerActionErrors<TValues extends FormValues> {\n fieldErrors?: Partial<Record<FieldName<TValues>, readonly string[]>> | undefined;\n formErrors?: readonly string[] | undefined;\n}\n\n/** Provides reactive form state, field access, validation, submit, reset, and error controls. */\nexport interface FormApi<TValues extends FormValues, TSubmitValues> {\n readonly state: ReadonlyCell<FormState<TValues>>;\n fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name>;\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;\n getValues(): TValues;\n reset(values?: TValues): void;\n setErrors(errors: FormErrors<TValues>): void;\n setServerErrors(errors: ServerActionErrors<TValues>): void;\n setValue<Name extends FieldName<TValues>>(name: Name, value: TValues[Name]): Promise<void>;\n submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>>;\n validate(): Promise<FormValidationResult<TValues, TSubmitValues>>;\n}\n\n/** Creates a reactive form API from initial values, validators, and optional schema validation. */\nexport function createForm<TValues extends FormValues>(\n options: CreateFormOptionsWithoutSchema<TValues>,\n): FormApi<TValues, TValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues>(\n options: CreateFormOptionsWithSchema<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues = TValues>(\n options: CreateFormOptions<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues> {\n const validateOn = new Set(\n Array.isArray(options.validateOn) ? options.validateOn : [options.validateOn ?? \"submit\"],\n );\n let initialValues = cloneValues(options.initialValues);\n const state = cell<FormState<TValues>>({\n dirty: false,\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n valid: true,\n validating: {},\n values: cloneValues(options.initialValues),\n });\n const validationGenerations = new Map<FieldName<TValues>, number>();\n const dirtyFields = new Set<FieldName<TValues>>();\n const dependentFields = buildDependentFields(options.validate);\n const fieldArrayKeys = new Map<FieldName<TValues>, string[]>();\n const fieldStateCells = new Map<\n FieldName<TValues>,\n ReadonlyCell<FieldState<TValues[FieldName<TValues>]>>\n >();\n const fieldArrayCells = new Map<\n FieldName<TValues>,\n ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, FieldName<TValues>>>>>\n >();\n let nextFieldArrayKey = 0;\n let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;\n\n function commit(patch: Partial<FormState<TValues>>, dirty = dirtyFields.size > 0): void {\n const previous = state.get();\n const next = {\n ...previous,\n ...patch,\n };\n next.dirty = dirty;\n next.valid = hasNoErrors(next.errors);\n state.set(next);\n }\n\n function setErrors(errors: FormErrors<TValues>): void {\n commit({ errors: normalizeErrors(errors) });\n }\n\n function invalidateFieldValidations(names: readonly FieldName<TValues>[]): void {\n if (names.length === 0) {\n return;\n }\n\n const validating = { ...state.get().validating };\n for (const name of names) {\n validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);\n validating[name] = false;\n }\n commit({ validating });\n }\n\n async function validateField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n const validator = validatorForField(options.validate?.[name]);\n const generation = (validationGenerations.get(name) ?? 0) + 1;\n validationGenerations.set(name, generation);\n\n if (validator === undefined) {\n setFieldErrors(name, []);\n setFieldValidating(name, false);\n return;\n }\n\n const values = state.get().values;\n setFieldValidating(name, true);\n const errors = await validator(values[name], values);\n\n if (validationGenerations.get(name) !== generation) {\n return;\n }\n\n setFieldErrors(name, normalizeFieldErrors(errors));\n setFieldValidating(name, false);\n }\n\n function setFieldErrors<Name extends FieldName<TValues>>(\n name: Name,\n errors: readonly string[],\n ): void {\n const current = state.get().errors;\n setErrors({\n ...current,\n [name]: [...errors],\n } as FormErrors<TValues>);\n }\n\n function setFieldValidating<Name extends FieldName<TValues>>(\n name: Name,\n validating: boolean,\n ): void {\n const current = state.get().validating;\n commit({\n validating: {\n ...current,\n [name]: validating,\n },\n });\n }\n\n async function setValue<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): Promise<void> {\n const previous = state.get();\n updateDirtyField(name, value);\n commit({\n values: {\n ...previous.values,\n [name]: value,\n },\n });\n\n if (validateOn.has(\"change\")) {\n await validateFields([name, ...dependentFieldsFor(name)]);\n }\n }\n\n async function blurField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n commit({\n touched: {\n ...state.get().touched,\n [name]: true,\n },\n });\n\n if (validateOn.has(\"blur\")) {\n await validateFields([name, ...dependentFieldsFor(name)]);\n }\n }\n\n async function validateForm(): Promise<FormValidationResult<TValues, TSubmitValues>> {\n const values = state.get().values;\n const errors: FormErrors<TValues> = {};\n const fieldNames = Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>;\n invalidateFieldValidations(fieldNames);\n\n for (const name of fieldNames) {\n const validator = options.validate?.[name];\n const validate = validatorForField(validator);\n if (validate === undefined) {\n continue;\n }\n const fieldErrors = normalizeFieldErrors(await validate(values[name], values));\n if (fieldErrors.length > 0) {\n errors[name] = fieldErrors;\n }\n }\n\n if (options.schema !== undefined) {\n const result = await validateStandardSchema(options.schema, values);\n\n if (!result.success) {\n mergeIssueErrors(errors, result.issues);\n } else if (Object.keys(errors).length === 0) {\n setErrors({});\n return {\n success: true,\n value: result.value as TSubmitValues,\n };\n }\n }\n\n if (Object.keys(errors).length > 0) {\n setErrors(errors);\n return {\n errors,\n success: false,\n };\n }\n\n setErrors({});\n return {\n success: true,\n value: values as TValues & TSubmitValues,\n };\n }\n\n async function validateFields(names: readonly FieldName<TValues>[]): Promise<void> {\n const uniqueNames = [...new Set(names)];\n await Promise.all(uniqueNames.map((fieldName) => validateField(fieldName)));\n }\n\n function dependentFieldsFor(name: FieldName<TValues>): readonly FieldName<TValues>[] {\n return dependentFields.get(name) ?? [];\n }\n\n function arrayValues<Name extends FieldName<TValues>>(\n name: Name,\n ): Array<ArrayFieldValue<TValues, Name>> {\n const value = state.get().values[name];\n return Array.isArray(value) ? ([...value] as Array<ArrayFieldValue<TValues, Name>>) : [];\n }\n\n function createFieldArrayKey(name: FieldName<TValues>): string {\n const key = `${name}:${nextFieldArrayKey}`;\n nextFieldArrayKey += 1;\n return key;\n }\n\n function ensureFieldArrayKeys(name: FieldName<TValues>, length: number): string[] {\n const keys = fieldArrayKeys.get(name) ?? [];\n if (keys.length > length) {\n keys.length = length;\n }\n\n while (keys.length < length) {\n keys.push(createFieldArrayKey(name));\n }\n\n fieldArrayKeys.set(name, keys);\n return keys;\n }\n\n function fieldArrayRows<Name extends FieldName<TValues>>(\n name: Name,\n ): Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>> {\n const values = arrayValues(name);\n const keys = ensureFieldArrayKeys(name, values.length);\n return values.map((value, index) => ({\n index,\n key: keys[index] ?? createFieldArrayKey(name),\n value,\n }));\n }\n\n function fieldArrayCell<Name extends FieldName<TValues>>(\n name: Name,\n ): ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>> {\n const existing = fieldArrayCells.get(name);\n if (existing !== undefined) {\n return existing as ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;\n }\n\n const next = computed(() => fieldArrayRows(name), { equals: fieldArrayRowsEqual });\n fieldArrayCells.set(\n name,\n next as ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, FieldName<TValues>>>>>,\n );\n return next;\n }\n\n function fieldStateCell<Name extends FieldName<TValues>>(\n name: Name,\n ): ReadonlyCell<FieldState<TValues[Name]>> {\n const existing = fieldStateCells.get(name);\n if (existing !== undefined) {\n return existing as ReadonlyCell<FieldState<TValues[Name]>>;\n }\n\n const next = computed(() => fieldState(state.get(), name), { equals: fieldStateEquals });\n fieldStateCells.set(name, next as ReadonlyCell<FieldState<TValues[FieldName<TValues>]>>);\n return next;\n }\n\n async function setArrayValue<Name extends FieldName<TValues>>(\n name: Name,\n values: Array<ArrayFieldValue<TValues, Name>>,\n keys: string[],\n ): Promise<void> {\n fieldArrayKeys.set(name, keys);\n await setValue(name, values as TValues[Name]);\n }\n\n async function validateArrayField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n if (validateOn.has(\"change\")) {\n await validateFields([name, ...dependentFieldsFor(name)]);\n }\n }\n\n return {\n state,\n fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name> {\n return {\n fields: fieldArrayCell(name),\n async append(value) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length), createFieldArrayKey(name)];\n values.push(value);\n await setArrayValue(name, values, keys);\n },\n async insert(index, value) {\n const values = arrayValues(name);\n const insertIndex = clampIndex(index, 0, values.length);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n values.splice(insertIndex, 0, value);\n keys.splice(insertIndex, 0, createFieldArrayKey(name));\n await setArrayValue(name, values, keys);\n },\n async move(from, to) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n if (!isArrayIndex(from, values.length)) {\n await validateArrayField(name);\n return;\n }\n\n const [value] = values.splice(from, 1);\n const [key] = keys.splice(from, 1);\n const insertIndex = clampIndex(to, 0, values.length);\n values.splice(insertIndex, 0, value as ArrayFieldValue<TValues, Name>);\n keys.splice(insertIndex, 0, key as string);\n await setArrayValue(name, values, keys);\n },\n async remove(index) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n if (!isArrayIndex(index, values.length)) {\n await validateArrayField(name);\n return;\n }\n\n values.splice(index, 1);\n keys.splice(index, 1);\n await setArrayValue(name, values, keys);\n },\n async swap(first, second) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n if (!isArrayIndex(first, values.length) || !isArrayIndex(second, values.length)) {\n await validateArrayField(name);\n return;\n }\n\n [values[first], values[second]] = [\n values[second] as ArrayFieldValue<TValues, Name>,\n values[first] as ArrayFieldValue<TValues, Name>,\n ];\n [keys[first], keys[second]] = [keys[second] as string, keys[first] as string];\n await setArrayValue(name, values, keys);\n },\n };\n },\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {\n return {\n state: fieldStateCell(name),\n bind(bindingOptions = {}) {\n const bindingEvent = bindingOptions.event ?? \"input\";\n const updateValue = async (inputEvent: Event) => {\n await setValue(name, eventValue(inputEvent, state.get().values[name]) as TValues[Name]);\n };\n\n return {\n onBlur: async () => {\n await blurField(name);\n },\n onChange: async (event) => {\n if (bindingEvent === \"change\") {\n await updateValue(event);\n }\n },\n onInput: async (event) => {\n if (bindingEvent === \"input\") {\n await updateValue(event);\n }\n },\n get value() {\n return state.get().values[name];\n },\n };\n },\n async blur() {\n await blurField(name);\n },\n setValue: (value) => setValue(name, value),\n };\n },\n getValues() {\n return state.get().values;\n },\n reset(values = initialValues): void {\n for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {\n validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);\n }\n\n initialValues = cloneValues(values);\n dirtyFields.clear();\n fieldArrayKeys.clear();\n commit({\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n validating: {},\n values: cloneValues(values),\n });\n },\n setErrors,\n setServerErrors(errors): void {\n const next: FormErrors<TValues> = {};\n\n for (const [name, messages] of Object.entries(errors.fieldErrors ?? {})) {\n if (isDangerousObjectKey(name)) {\n continue;\n }\n\n if (messages !== undefined && messages.length > 0) {\n next[name as FieldName<TValues>] = [...messages];\n }\n }\n\n if (errors.formErrors !== undefined && errors.formErrors.length > 0) {\n next.root = [...errors.formErrors];\n }\n\n setErrors(next);\n },\n setValue,\n async submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>> {\n if (activeSubmit !== undefined) {\n return { status: \"duplicate\" };\n }\n\n const task = (async (): Promise<FormSubmitResult<TValues, TResult>> => {\n commit({\n submitCount: state.get().submitCount + 1,\n submitting: true,\n });\n\n try {\n const validation = await validateForm();\n\n if (!validation.success) {\n return {\n errors: validation.errors,\n status: \"invalid\",\n };\n }\n\n try {\n const data = await handler(validation.value);\n return {\n data,\n status: \"success\",\n };\n } catch (error) {\n return {\n error,\n status: \"error\",\n };\n }\n } finally {\n activeSubmit = undefined;\n commit({ submitting: false });\n }\n })();\n\n activeSubmit = task as Promise<FormSubmitResult<TValues, unknown>>;\n return task;\n },\n validate: validateForm,\n };\n\n function updateDirtyField<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): void {\n if (Object.is(value, initialValues[name])) {\n dirtyFields.delete(name);\n return;\n }\n\n dirtyFields.add(name);\n }\n}\n\nfunction fieldState<TValues extends FormValues, Name extends FieldName<TValues>>(\n formState: FormState<TValues>,\n name: Name,\n): FieldState<TValues[Name]> {\n return {\n dirty: !Object.is(formState.values[name], formState.initialValues[name]),\n errors: [...(formState.errors[name] ?? [])],\n touched: formState.touched[name] === true,\n validating: formState.validating[name] === true,\n value: formState.values[name],\n };\n}\n\nfunction fieldStateEquals<TValue>(previous: FieldState<TValue>, next: FieldState<TValue>): boolean {\n return (\n previous.dirty === next.dirty &&\n previous.touched === next.touched &&\n previous.validating === next.validating &&\n Object.is(previous.value, next.value) &&\n stringArraysEqual(previous.errors, next.errors)\n );\n}\n\nfunction fieldArrayRowsEqual<TValue>(\n previous: Array<FieldArrayRow<TValue>>,\n next: Array<FieldArrayRow<TValue>>,\n): boolean {\n if (previous.length !== next.length) {\n return false;\n }\n\n for (let index = 0; index < previous.length; index += 1) {\n const previousRow = previous[index];\n const nextRow = next[index];\n if (\n previousRow === undefined ||\n nextRow === undefined ||\n previousRow.index !== nextRow.index ||\n previousRow.key !== nextRow.key ||\n !Object.is(previousRow.value, nextRow.value)\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction stringArraysEqual(previous: readonly string[], next: readonly string[]): boolean {\n if (previous.length !== next.length) {\n return false;\n }\n\n for (let index = 0; index < previous.length; index += 1) {\n if (previous[index] !== next[index]) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction eventValue(event: Event, currentValue: unknown): unknown {\n const target = event.currentTarget ?? event.target;\n\n if (target !== null && typeof target === \"object\") {\n if (typeof currentValue === \"boolean\" && \"checked\" in target) {\n return Boolean((target as { checked: unknown }).checked);\n }\n\n if (\"value\" in target) {\n return (target as { value: unknown }).value;\n }\n }\n\n return currentValue;\n}\n\nfunction mergeIssueErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): void {\n for (const issue of issues) {\n const key = issuePathKey(issue.path);\n const name = (key === undefined || isDangerousObjectKey(key) ? \"root\" : key) as\n | FieldName<TValues>\n | \"root\";\n const messages = errors[name] ?? [];\n messages.push(issue.message);\n errors[name] = messages;\n }\n}\n\nfunction issuePathKey(\n path: ReadonlyArray<PropertyKey | StandardSchemaV1.PathSegment> | undefined,\n): string | undefined {\n const first = path?.[0];\n\n if (first === undefined) {\n return undefined;\n }\n\n return typeof first === \"object\" && first !== null && \"key\" in first\n ? String(first.key)\n : String(first);\n}\n\nfunction normalizeErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n): FormErrors<TValues> {\n return Object.fromEntries(\n Object.entries(errors).flatMap(([name, messages]) =>\n messages === undefined || messages.length === 0 ? [] : [[name, [...messages]]],\n ),\n ) as FormErrors<TValues>;\n}\n\nfunction isDangerousObjectKey(key: PropertyKey): boolean {\n return key === \"__proto__\" || key === \"constructor\" || key === \"prototype\";\n}\n\nfunction hasNoErrors<TValues extends FormValues>(errors: FormErrors<TValues>): boolean {\n return (Object.values(errors) as Array<string[] | undefined>).every(\n (messages) => messages === undefined || messages.length === 0,\n );\n}\n\nfunction normalizeFieldErrors(errors: readonly string[] | string | undefined): string[] {\n if (errors === undefined) {\n return [];\n }\n\n return typeof errors === \"string\" ? [errors] : [...errors];\n}\n\nfunction clampIndex(index: number, min: number, max: number): number {\n return Math.min(Math.max(Math.trunc(index), min), max);\n}\n\nfunction isArrayIndex(index: number, length: number): boolean {\n return Number.isInteger(index) && index >= 0 && index < length;\n}\n\nfunction validatorForField<TValue, TValues extends FormValues>(\n entry: FieldValidationEntry<TValue, TValues> | undefined,\n): FieldValidator<TValue, TValues> | undefined {\n return typeof entry === \"function\" ? entry : entry?.validate;\n}\n\nfunction buildDependentFields<TValues extends FormValues>(\n validate: BaseCreateFormOptions<TValues>[\"validate\"],\n): Map<FieldName<TValues>, FieldName<TValues>[]> {\n const dependents = new Map<FieldName<TValues>, FieldName<TValues>[]>();\n\n for (const [fieldName, entry] of Object.entries(validate ?? {}) as Array<\n [FieldName<TValues>, FieldValidationEntry<TValues[FieldName<TValues>], TValues> | undefined]\n >) {\n if (entry === undefined || typeof entry === \"function\") {\n continue;\n }\n\n for (const dep of entry.deps ?? []) {\n const fields = dependents.get(dep);\n if (fields === undefined) {\n dependents.set(dep, [fieldName]);\n continue;\n }\n fields.push(fieldName);\n }\n }\n\n return dependents;\n}\n\nfunction cloneValues<TValues extends FormValues>(values: TValues): TValues {\n return { ...values };\n}\n\n/** Re-exports Standard Schema types used by form schema options. */\nexport type {\n InferStandardSchemaInput,\n InferStandardSchemaOutput,\n StandardSchemaV1,\n} from \"./standard-schema.js\";\n/** Validates an unknown value with a Standard Schema and normalizes the result shape. */\nexport { validateStandardSchema } from \"./standard-schema.js\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-forms",
3
- "version": "0.0.181",
3
+ "version": "0.0.183",
4
4
  "description": "Reactive form state and validation utilities for mreact.",
5
5
  "keywords": [
6
6
  "forms",
@@ -44,6 +44,6 @@
44
44
  "access": "public"
45
45
  },
46
46
  "dependencies": {
47
- "@reckona/mreact-reactive-core": "0.0.181"
47
+ "@reckona/mreact-reactive-core": "0.0.183"
48
48
  }
49
49
  }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { cell, type ReadonlyCell } from "@reckona/mreact-reactive-core";
1
+ import { cell, computed, type ReadonlyCell } from "@reckona/mreact-reactive-core";
2
2
  import { type StandardSchemaV1, validateStandardSchema } from "./standard-schema.js";
3
3
 
4
4
  /** Represents the object shape managed by a form instance. */
@@ -18,6 +18,17 @@ export type FieldValidator<TValue, TValues extends FormValues> = (
18
18
  values: TValues,
19
19
  ) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;
20
20
 
21
+ /** Configures one field validator and which other fields should re-trigger it. */
22
+ export interface FieldValidationConfig<TValue, TValues extends FormValues> {
23
+ deps?: readonly FieldName<TValues>[] | undefined;
24
+ validate: FieldValidator<TValue, TValues>;
25
+ }
26
+
27
+ /** Provides either a validator function or a validator descriptor with dependencies. */
28
+ export type FieldValidationEntry<TValue, TValues extends FormValues> =
29
+ | FieldValidator<TValue, TValues>
30
+ | FieldValidationConfig<TValue, TValues>;
31
+
21
32
  /** Names the form events that can trigger validation. */
22
33
  export type FormValidateMode = "change" | "blur" | "submit";
23
34
 
@@ -64,25 +75,51 @@ export interface FieldApi<TValues extends FormValues, Name extends FieldName<TVa
64
75
  setValue(value: TValues[Name]): Promise<void>;
65
76
  }
66
77
 
78
+ /** Extracts an array field's item type. */
79
+ export type ArrayFieldValue<
80
+ TValues extends FormValues,
81
+ Name extends FieldName<TValues>,
82
+ > = TValues[Name] extends readonly (infer Item)[] ? Item : never;
83
+
84
+ /** Describes one rendered row in a form array field. */
85
+ export interface FieldArrayRow<TValue> {
86
+ index: number;
87
+ key: string;
88
+ value: TValue;
89
+ }
90
+
91
+ /** Exposes stable keyed row state and mutation helpers for an array field. */
92
+ export interface FieldArrayApi<TValues extends FormValues, Name extends FieldName<TValues>> {
93
+ readonly fields: ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;
94
+ append(value: ArrayFieldValue<TValues, Name>): Promise<void>;
95
+ insert(index: number, value: ArrayFieldValue<TValues, Name>): Promise<void>;
96
+ move(from: number, to: number): Promise<void>;
97
+ remove(index: number): Promise<void>;
98
+ swap(first: number, second: number): Promise<void>;
99
+ }
100
+
67
101
  interface BaseCreateFormOptions<TValues extends FormValues> {
68
102
  initialValues: TValues;
69
103
  validate?:
70
104
  | Partial<{
71
- [Name in FieldName<TValues>]: FieldValidator<TValues[Name], TValues>;
105
+ [Name in FieldName<TValues>]: FieldValidationEntry<TValues[Name], TValues>;
72
106
  }>
73
107
  | undefined;
74
108
  validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;
75
109
  }
76
110
 
77
111
  /** Configures form creation without schema-level submit value transformation. */
78
- export interface CreateFormOptionsWithoutSchema<TValues extends FormValues>
79
- extends BaseCreateFormOptions<TValues> {
112
+ export interface CreateFormOptionsWithoutSchema<
113
+ TValues extends FormValues,
114
+ > extends BaseCreateFormOptions<TValues> {
80
115
  schema?: undefined;
81
116
  }
82
117
 
83
118
  /** Configures form creation with a Standard Schema validator that may transform submit values. */
84
- export interface CreateFormOptionsWithSchema<TValues extends FormValues, TSubmitValues>
85
- extends BaseCreateFormOptions<TValues> {
119
+ export interface CreateFormOptionsWithSchema<
120
+ TValues extends FormValues,
121
+ TSubmitValues,
122
+ > extends BaseCreateFormOptions<TValues> {
86
123
  schema: StandardSchemaV1<TValues, TSubmitValues>;
87
124
  }
88
125
 
@@ -129,6 +166,7 @@ export interface ServerActionErrors<TValues extends FormValues> {
129
166
  /** Provides reactive form state, field access, validation, submit, reset, and error controls. */
130
167
  export interface FormApi<TValues extends FormValues, TSubmitValues> {
131
168
  readonly state: ReadonlyCell<FormState<TValues>>;
169
+ fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name>;
132
170
  field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;
133
171
  getValues(): TValues;
134
172
  reset(values?: TValues): void;
@@ -168,6 +206,17 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
168
206
  });
169
207
  const validationGenerations = new Map<FieldName<TValues>, number>();
170
208
  const dirtyFields = new Set<FieldName<TValues>>();
209
+ const dependentFields = buildDependentFields(options.validate);
210
+ const fieldArrayKeys = new Map<FieldName<TValues>, string[]>();
211
+ const fieldStateCells = new Map<
212
+ FieldName<TValues>,
213
+ ReadonlyCell<FieldState<TValues[FieldName<TValues>]>>
214
+ >();
215
+ const fieldArrayCells = new Map<
216
+ FieldName<TValues>,
217
+ ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, FieldName<TValues>>>>>
218
+ >();
219
+ let nextFieldArrayKey = 0;
171
220
  let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;
172
221
 
173
222
  function commit(patch: Partial<FormState<TValues>>, dirty = dirtyFields.size > 0): void {
@@ -185,8 +234,21 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
185
234
  commit({ errors: normalizeErrors(errors) });
186
235
  }
187
236
 
237
+ function invalidateFieldValidations(names: readonly FieldName<TValues>[]): void {
238
+ if (names.length === 0) {
239
+ return;
240
+ }
241
+
242
+ const validating = { ...state.get().validating };
243
+ for (const name of names) {
244
+ validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);
245
+ validating[name] = false;
246
+ }
247
+ commit({ validating });
248
+ }
249
+
188
250
  async function validateField<Name extends FieldName<TValues>>(name: Name): Promise<void> {
189
- const validator = options.validate?.[name];
251
+ const validator = validatorForField(options.validate?.[name]);
190
252
  const generation = (validationGenerations.get(name) ?? 0) + 1;
191
253
  validationGenerations.set(name, generation);
192
254
 
@@ -246,7 +308,7 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
246
308
  });
247
309
 
248
310
  if (validateOn.has("change")) {
249
- await validateField(name);
311
+ await validateFields([name, ...dependentFieldsFor(name)]);
250
312
  }
251
313
  }
252
314
 
@@ -259,20 +321,23 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
259
321
  });
260
322
 
261
323
  if (validateOn.has("blur")) {
262
- await validateField(name);
324
+ await validateFields([name, ...dependentFieldsFor(name)]);
263
325
  }
264
326
  }
265
327
 
266
328
  async function validateForm(): Promise<FormValidationResult<TValues, TSubmitValues>> {
267
329
  const values = state.get().values;
268
330
  const errors: FormErrors<TValues> = {};
331
+ const fieldNames = Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>;
332
+ invalidateFieldValidations(fieldNames);
269
333
 
270
- for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {
334
+ for (const name of fieldNames) {
271
335
  const validator = options.validate?.[name];
272
- if (validator === undefined) {
336
+ const validate = validatorForField(validator);
337
+ if (validate === undefined) {
273
338
  continue;
274
339
  }
275
- const fieldErrors = normalizeFieldErrors(await validator(values[name], values));
340
+ const fieldErrors = normalizeFieldErrors(await validate(values[name], values));
276
341
  if (fieldErrors.length > 0) {
277
342
  errors[name] = fieldErrors;
278
343
  }
@@ -307,13 +372,164 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
307
372
  };
308
373
  }
309
374
 
375
+ async function validateFields(names: readonly FieldName<TValues>[]): Promise<void> {
376
+ const uniqueNames = [...new Set(names)];
377
+ await Promise.all(uniqueNames.map((fieldName) => validateField(fieldName)));
378
+ }
379
+
380
+ function dependentFieldsFor(name: FieldName<TValues>): readonly FieldName<TValues>[] {
381
+ return dependentFields.get(name) ?? [];
382
+ }
383
+
384
+ function arrayValues<Name extends FieldName<TValues>>(
385
+ name: Name,
386
+ ): Array<ArrayFieldValue<TValues, Name>> {
387
+ const value = state.get().values[name];
388
+ return Array.isArray(value) ? ([...value] as Array<ArrayFieldValue<TValues, Name>>) : [];
389
+ }
390
+
391
+ function createFieldArrayKey(name: FieldName<TValues>): string {
392
+ const key = `${name}:${nextFieldArrayKey}`;
393
+ nextFieldArrayKey += 1;
394
+ return key;
395
+ }
396
+
397
+ function ensureFieldArrayKeys(name: FieldName<TValues>, length: number): string[] {
398
+ const keys = fieldArrayKeys.get(name) ?? [];
399
+ if (keys.length > length) {
400
+ keys.length = length;
401
+ }
402
+
403
+ while (keys.length < length) {
404
+ keys.push(createFieldArrayKey(name));
405
+ }
406
+
407
+ fieldArrayKeys.set(name, keys);
408
+ return keys;
409
+ }
410
+
411
+ function fieldArrayRows<Name extends FieldName<TValues>>(
412
+ name: Name,
413
+ ): Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>> {
414
+ const values = arrayValues(name);
415
+ const keys = ensureFieldArrayKeys(name, values.length);
416
+ return values.map((value, index) => ({
417
+ index,
418
+ key: keys[index] ?? createFieldArrayKey(name),
419
+ value,
420
+ }));
421
+ }
422
+
423
+ function fieldArrayCell<Name extends FieldName<TValues>>(
424
+ name: Name,
425
+ ): ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>> {
426
+ const existing = fieldArrayCells.get(name);
427
+ if (existing !== undefined) {
428
+ return existing as ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;
429
+ }
430
+
431
+ const next = computed(() => fieldArrayRows(name), { equals: fieldArrayRowsEqual });
432
+ fieldArrayCells.set(
433
+ name,
434
+ next as ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, FieldName<TValues>>>>>,
435
+ );
436
+ return next;
437
+ }
438
+
439
+ function fieldStateCell<Name extends FieldName<TValues>>(
440
+ name: Name,
441
+ ): ReadonlyCell<FieldState<TValues[Name]>> {
442
+ const existing = fieldStateCells.get(name);
443
+ if (existing !== undefined) {
444
+ return existing as ReadonlyCell<FieldState<TValues[Name]>>;
445
+ }
446
+
447
+ const next = computed(() => fieldState(state.get(), name), { equals: fieldStateEquals });
448
+ fieldStateCells.set(name, next as ReadonlyCell<FieldState<TValues[FieldName<TValues>]>>);
449
+ return next;
450
+ }
451
+
452
+ async function setArrayValue<Name extends FieldName<TValues>>(
453
+ name: Name,
454
+ values: Array<ArrayFieldValue<TValues, Name>>,
455
+ keys: string[],
456
+ ): Promise<void> {
457
+ fieldArrayKeys.set(name, keys);
458
+ await setValue(name, values as TValues[Name]);
459
+ }
460
+
461
+ async function validateArrayField<Name extends FieldName<TValues>>(name: Name): Promise<void> {
462
+ if (validateOn.has("change")) {
463
+ await validateFields([name, ...dependentFieldsFor(name)]);
464
+ }
465
+ }
466
+
310
467
  return {
311
468
  state,
312
- field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {
469
+ fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name> {
313
470
  return {
314
- state: {
315
- get: () => fieldState(state.get(), name),
471
+ fields: fieldArrayCell(name),
472
+ async append(value) {
473
+ const values = arrayValues(name);
474
+ const keys = [...ensureFieldArrayKeys(name, values.length), createFieldArrayKey(name)];
475
+ values.push(value);
476
+ await setArrayValue(name, values, keys);
316
477
  },
478
+ async insert(index, value) {
479
+ const values = arrayValues(name);
480
+ const insertIndex = clampIndex(index, 0, values.length);
481
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
482
+ values.splice(insertIndex, 0, value);
483
+ keys.splice(insertIndex, 0, createFieldArrayKey(name));
484
+ await setArrayValue(name, values, keys);
485
+ },
486
+ async move(from, to) {
487
+ const values = arrayValues(name);
488
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
489
+ if (!isArrayIndex(from, values.length)) {
490
+ await validateArrayField(name);
491
+ return;
492
+ }
493
+
494
+ const [value] = values.splice(from, 1);
495
+ const [key] = keys.splice(from, 1);
496
+ const insertIndex = clampIndex(to, 0, values.length);
497
+ values.splice(insertIndex, 0, value as ArrayFieldValue<TValues, Name>);
498
+ keys.splice(insertIndex, 0, key as string);
499
+ await setArrayValue(name, values, keys);
500
+ },
501
+ async remove(index) {
502
+ const values = arrayValues(name);
503
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
504
+ if (!isArrayIndex(index, values.length)) {
505
+ await validateArrayField(name);
506
+ return;
507
+ }
508
+
509
+ values.splice(index, 1);
510
+ keys.splice(index, 1);
511
+ await setArrayValue(name, values, keys);
512
+ },
513
+ async swap(first, second) {
514
+ const values = arrayValues(name);
515
+ const keys = [...ensureFieldArrayKeys(name, values.length)];
516
+ if (!isArrayIndex(first, values.length) || !isArrayIndex(second, values.length)) {
517
+ await validateArrayField(name);
518
+ return;
519
+ }
520
+
521
+ [values[first], values[second]] = [
522
+ values[second] as ArrayFieldValue<TValues, Name>,
523
+ values[first] as ArrayFieldValue<TValues, Name>,
524
+ ];
525
+ [keys[first], keys[second]] = [keys[second] as string, keys[first] as string];
526
+ await setArrayValue(name, values, keys);
527
+ },
528
+ };
529
+ },
530
+ field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {
531
+ return {
532
+ state: fieldStateCell(name),
317
533
  bind(bindingOptions = {}) {
318
534
  const bindingEvent = bindingOptions.event ?? "input";
319
535
  const updateValue = async (inputEvent: Event) => {
@@ -355,6 +571,7 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
355
571
 
356
572
  initialValues = cloneValues(values);
357
573
  dirtyFields.clear();
574
+ fieldArrayKeys.clear();
358
575
  commit({
359
576
  errors: {},
360
577
  initialValues,
@@ -459,6 +676,55 @@ function fieldState<TValues extends FormValues, Name extends FieldName<TValues>>
459
676
  };
460
677
  }
461
678
 
679
+ function fieldStateEquals<TValue>(previous: FieldState<TValue>, next: FieldState<TValue>): boolean {
680
+ return (
681
+ previous.dirty === next.dirty &&
682
+ previous.touched === next.touched &&
683
+ previous.validating === next.validating &&
684
+ Object.is(previous.value, next.value) &&
685
+ stringArraysEqual(previous.errors, next.errors)
686
+ );
687
+ }
688
+
689
+ function fieldArrayRowsEqual<TValue>(
690
+ previous: Array<FieldArrayRow<TValue>>,
691
+ next: Array<FieldArrayRow<TValue>>,
692
+ ): boolean {
693
+ if (previous.length !== next.length) {
694
+ return false;
695
+ }
696
+
697
+ for (let index = 0; index < previous.length; index += 1) {
698
+ const previousRow = previous[index];
699
+ const nextRow = next[index];
700
+ if (
701
+ previousRow === undefined ||
702
+ nextRow === undefined ||
703
+ previousRow.index !== nextRow.index ||
704
+ previousRow.key !== nextRow.key ||
705
+ !Object.is(previousRow.value, nextRow.value)
706
+ ) {
707
+ return false;
708
+ }
709
+ }
710
+
711
+ return true;
712
+ }
713
+
714
+ function stringArraysEqual(previous: readonly string[], next: readonly string[]): boolean {
715
+ if (previous.length !== next.length) {
716
+ return false;
717
+ }
718
+
719
+ for (let index = 0; index < previous.length; index += 1) {
720
+ if (previous[index] !== next[index]) {
721
+ return false;
722
+ }
723
+ }
724
+
725
+ return true;
726
+ }
727
+
462
728
  function eventValue(event: Event, currentValue: unknown): unknown {
463
729
  const target = event.currentTarget ?? event.target;
464
730
 
@@ -532,6 +798,45 @@ function normalizeFieldErrors(errors: readonly string[] | string | undefined): s
532
798
  return typeof errors === "string" ? [errors] : [...errors];
533
799
  }
534
800
 
801
+ function clampIndex(index: number, min: number, max: number): number {
802
+ return Math.min(Math.max(Math.trunc(index), min), max);
803
+ }
804
+
805
+ function isArrayIndex(index: number, length: number): boolean {
806
+ return Number.isInteger(index) && index >= 0 && index < length;
807
+ }
808
+
809
+ function validatorForField<TValue, TValues extends FormValues>(
810
+ entry: FieldValidationEntry<TValue, TValues> | undefined,
811
+ ): FieldValidator<TValue, TValues> | undefined {
812
+ return typeof entry === "function" ? entry : entry?.validate;
813
+ }
814
+
815
+ function buildDependentFields<TValues extends FormValues>(
816
+ validate: BaseCreateFormOptions<TValues>["validate"],
817
+ ): Map<FieldName<TValues>, FieldName<TValues>[]> {
818
+ const dependents = new Map<FieldName<TValues>, FieldName<TValues>[]>();
819
+
820
+ for (const [fieldName, entry] of Object.entries(validate ?? {}) as Array<
821
+ [FieldName<TValues>, FieldValidationEntry<TValues[FieldName<TValues>], TValues> | undefined]
822
+ >) {
823
+ if (entry === undefined || typeof entry === "function") {
824
+ continue;
825
+ }
826
+
827
+ for (const dep of entry.deps ?? []) {
828
+ const fields = dependents.get(dep);
829
+ if (fields === undefined) {
830
+ dependents.set(dep, [fieldName]);
831
+ continue;
832
+ }
833
+ fields.push(fieldName);
834
+ }
835
+ }
836
+
837
+ return dependents;
838
+ }
839
+
535
840
  function cloneValues<TValues extends FormValues>(values: TValues): TValues {
536
841
  return { ...values };
537
842
  }