@conform-to/react 1.19.4 → 1.20.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.
@@ -1,20 +1,37 @@
1
- import { type FieldName, FormError } from '@conform-to/dom/future';
2
- import type { FieldMetadata, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, IntentHandler, BaseFieldMetadata, BaseFormMetadata, DefineConditionalField } from './types';
3
- export declare function initializeState<ErrorShape>(options?: {
1
+ import { type FieldName, FormError, type SubmissionResult } from '@conform-to/dom/future';
2
+ import type { FieldMetadata, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, IntentHandler, BaseFieldMetadata, BaseFormMetadata, DefineConditionalField, ApplyStatus, CustomStateHandler, FormCustomState } from './types';
3
+ export declare function initializeState<ErrorShape = any, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(options?: {
4
4
  defaultValue?: Record<string, unknown> | null | undefined;
5
5
  resetKey?: string | undefined;
6
- }): FormState<ErrorShape>;
6
+ customStateHandlers?: CustomStateHandlers | undefined;
7
+ lastCustomState?: FormCustomState<CustomStateHandlers> | undefined;
8
+ result?: SubmissionResult<ErrorShape> | undefined;
9
+ }): FormState<ErrorShape, FormCustomState<CustomStateHandlers>>;
7
10
  /**
8
11
  * Updates form state based on action type:
9
12
  * - Client actions: update target value and client errors
10
13
  * - Server actions: update server errors and clear client errors, with optional target value
11
14
  * - Initialize: set initial server value
12
15
  */
13
- export declare function updateState<ErrorShape>(state: FormState<ErrorShape>, action: FormAction<ErrorShape, UnknownIntent | null, {
14
- handlers: Record<string, IntentHandler>;
15
- cancelled: boolean;
16
- reset: (defaultValue?: Record<string, unknown> | null | undefined) => FormState<ErrorShape>;
17
- }>): FormState<ErrorShape>;
16
+ export declare function updateState<ErrorShape, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(state: FormState<ErrorShape, FormCustomState<CustomStateHandlers>>, action: FormAction<ErrorShape, UnknownIntent, {
17
+ intentHandlers: Record<string, IntentHandler<any, any>>;
18
+ customStateHandlers: CustomStateHandlers | undefined;
19
+ status: ApplyStatus;
20
+ reset: () => FormState<ErrorShape, FormCustomState<CustomStateHandlers>>;
21
+ }>): FormState<ErrorShape, FormCustomState<CustomStateHandlers>>;
22
+ export declare function getFields<ErrorShape>(result: SubmissionResult<ErrorShape>): string[];
23
+ export declare function getApplyStatus(baseTargetValue: Record<string, unknown> | undefined, finalTargetValue: Record<string, unknown> | undefined): ApplyStatus;
24
+ /**
25
+ * Fallback state transition for intents that change the payload shape without
26
+ * providing a `move()` mapping. It drops list keys and touched fields under
27
+ * changed paths so stale client state does not point at the wrong fields.
28
+ */
29
+ export declare function invalidateState<ErrorShape, CustomState extends Record<string, unknown> = Record<string, unknown>>(state: FormState<ErrorShape, CustomState>, previousValue: Record<string, unknown>, nextValue: Record<string, unknown>): FormState<ErrorShape, CustomState>;
30
+ /**
31
+ * Preserves list keys and touched fields for intents that can map old field
32
+ * paths to new ones, such as insert, remove, and reorder.
33
+ */
34
+ export declare function moveState<ErrorShape, CustomState extends Record<string, unknown> = Record<string, unknown>>(state: FormState<ErrorShape, CustomState>, previousValue: Record<string, unknown>, nextValue: Record<string, unknown>, move: (name: string) => string | null): FormState<ErrorShape, CustomState>;
18
35
  /**
19
36
  * Removes list keys where array length has changed to force regeneration.
20
37
  * Minimizes UI state loss by only invalidating keys when necessary.
@@ -40,43 +57,53 @@ export declare function getFieldErrors<ErrorShape>(state: FormState<ErrorShape>,
40
57
  */
41
58
  export declare function hasFieldError<ErrorShape>(error: FormError<ErrorShape>, name: string): boolean;
42
59
  export declare function isValid(state: FormState<any>, name?: string): boolean;
43
- export declare function getFormMetadata<ErrorShape, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options?: {
44
- extendFormMetadata?: ((metadata: BaseFormMetadata<ErrorShape>) => CustomFormMetadata) | undefined;
60
+ export declare function getFormMetadata<ErrorShape, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}, CustomState extends Record<string, unknown> = {}>(context: FormContext<ErrorShape, CustomState>, options?: {
61
+ extendFormMetadata?: ((metadata: BaseFormMetadata<ErrorShape, CustomState>) => CustomFormMetadata) | undefined;
45
62
  extendFieldMetadata?: (<FieldShape>(metadata: BaseFieldMetadata<FieldShape, ErrorShape>, ctx: {
46
- form: BaseFormMetadata<ErrorShape>;
63
+ form: BaseFormMetadata<ErrorShape, CustomState>;
47
64
  when: DefineConditionalField;
48
65
  }) => CustomFieldMetadata) | undefined;
49
- }): FormMetadata<ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
50
- export declare function getField<FieldShape, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options: {
66
+ }): FormMetadata<ErrorShape, CustomFormMetadata, CustomFieldMetadata, CustomState>;
67
+ export declare function getField<FieldShape, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape, any>, options: {
51
68
  name: FieldName<FieldShape>;
52
69
  extendFieldMetadata?: (<F>(metadata: BaseFieldMetadata<F, ErrorShape>, ctx: {
53
- form: BaseFormMetadata<ErrorShape>;
70
+ form: BaseFormMetadata<ErrorShape, any>;
54
71
  when: DefineConditionalField;
55
72
  }) => CustomFieldMetadata) | undefined;
56
- form?: BaseFormMetadata<ErrorShape, CustomFieldMetadata> | undefined;
73
+ form?: BaseFormMetadata<ErrorShape, any> | undefined;
57
74
  key?: string | undefined;
58
75
  }): FieldMetadata<FieldShape, ErrorShape, CustomFieldMetadata>;
59
76
  /**
60
77
  * Creates a proxy that dynamically generates field objects when properties are accessed.
61
78
  */
62
- export declare function getFieldset<FieldShape = Record<string, any>, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options: {
79
+ export declare function getFieldset<FieldShape = Record<string, any>, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape, any>, options: {
63
80
  name?: FieldName<FieldShape> | undefined;
64
81
  extendFieldMetadata?: (<F>(metadata: BaseFieldMetadata<F, ErrorShape>, ctx: {
65
- form: BaseFormMetadata<ErrorShape>;
82
+ form: BaseFormMetadata<ErrorShape, any>;
66
83
  when: DefineConditionalField;
67
84
  }) => CustomFieldMetadata) | undefined;
68
- form?: BaseFormMetadata<ErrorShape, CustomFieldMetadata> | undefined;
85
+ form?: BaseFormMetadata<ErrorShape, any> | undefined;
69
86
  }): Fieldset<FieldShape, ErrorShape, CustomFieldMetadata>;
70
87
  /**
71
88
  * Creates an array of field objects for list/array inputs
72
89
  */
73
- export declare function getFieldList<FieldShape = Array<any>, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options: {
90
+ export declare function getFieldList<FieldShape = Array<any>, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape, any>, options: {
74
91
  name: FieldName<FieldShape>;
75
92
  extendFieldMetadata?: (<F>(metadata: BaseFieldMetadata<F, ErrorShape>, ctx: {
76
- form: BaseFormMetadata<ErrorShape>;
93
+ form: BaseFormMetadata<ErrorShape, any>;
77
94
  when: DefineConditionalField;
78
95
  }) => CustomFieldMetadata) | undefined;
79
96
  }): FieldMetadata<[
80
97
  FieldShape
81
98
  ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, ErrorShape, CustomFieldMetadata>[];
99
+ export declare function defineCustomState<State, CustomIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, ErrorShape = any>(definition: CustomStateHandler<State, CustomIntentHandlers, ErrorShape>): CustomStateHandler<State, CustomIntentHandlers, ErrorShape>;
100
+ export declare function mergeCustomStateHandlers<GlobalCustomState extends Record<string, CustomStateHandler<any, any, any>> | undefined, InlineCustomState extends Record<string, CustomStateHandler<any, any, any>> | undefined>(globalCustomState: GlobalCustomState, inlineCustomState: InlineCustomState): GlobalCustomState & InlineCustomState;
101
+ export declare function initializeCustomState<ErrorShape, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>>>(options: {
102
+ handlers: CustomStateHandlers | undefined;
103
+ currentState?: FormCustomState<CustomStateHandlers> | undefined;
104
+ result?: SubmissionResult<ErrorShape> | undefined;
105
+ }): FormCustomState<CustomStateHandlers>;
106
+ export declare function updateCustomState<ErrorShape, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>>>(state: FormCustomState<CustomStateHandlers>, action: FormAction<ErrorShape, UnknownIntent>, options: {
107
+ handlers: CustomStateHandlers | undefined;
108
+ }): FormCustomState<CustomStateHandlers>;
82
109
  //# sourceMappingURL=state.d.ts.map
@@ -16,7 +16,12 @@ function initializeState(options) {
16
16
  serverValue: null,
17
17
  serverError: null,
18
18
  clientError: null,
19
- touchedFields: []
19
+ touchedFields: [],
20
+ customState: initializeCustomState({
21
+ handlers: options === null || options === void 0 ? void 0 : options.customStateHandlers,
22
+ currentState: options === null || options === void 0 ? void 0 : options.lastCustomState,
23
+ result: options === null || options === void 0 ? void 0 : options.result
24
+ })
20
25
  };
21
26
  }
22
27
 
@@ -27,28 +32,30 @@ function initializeState(options) {
27
32
  * - Initialize: set initial server value
28
33
  */
29
34
  function updateState(state, action) {
30
- var _action$targetValue, _action$targetValue2, _action$intent, _action$ctx$handlers;
31
- if (action.reset) {
32
- return action.ctx.reset(action.targetValue);
35
+ var _action$result$target, _action$result$target2, _action$intent, _action$ctx$intentHan;
36
+ if (action.result.reset) {
37
+ return action.ctx.reset();
33
38
  }
34
- var value = (_action$targetValue = action.targetValue) !== null && _action$targetValue !== void 0 ? _action$targetValue : action.submission.payload;
39
+ var value = (_action$result$target = action.result.targetValue) !== null && _action$result$target !== void 0 ? _action$result$target : action.result.submission.payload;
40
+ var isClientAction = action.type === 'client' || action.type === 'client:async';
41
+ var hasIntentEffects = action.type !== 'server' && action.type !== 'client:async';
35
42
 
36
43
  // Apply the form error and target value from the result first
37
- state = action.type === 'client' ? util.merge(state, {
38
- targetValue: (_action$targetValue2 = action.targetValue) !== null && _action$targetValue2 !== void 0 ? _action$targetValue2 : state.targetValue,
39
- serverValue: action.targetValue ? null : state.serverValue,
44
+ state = isClientAction ? util.merge(state, {
45
+ targetValue: action.type === 'client' ? (_action$result$target2 = action.result.targetValue) !== null && _action$result$target2 !== void 0 ? _action$result$target2 : state.targetValue : state.targetValue,
46
+ serverValue: action.type === 'client' && action.result.targetValue ? null : state.serverValue,
40
47
  // Update client error only if the error is different from the previous one to minimize unnecessary re-renders
41
- clientError: typeof action.error !== 'undefined' && !future.deepEqual(state.clientError, action.error) ? action.error : state.clientError,
48
+ clientError: typeof action.result.error !== 'undefined' && !future.deepEqual(state.clientError, action.result.error) ? action.result.error : state.clientError,
42
49
  // Reset server error if form value is changed
43
- serverError: typeof action.error !== 'undefined' && !future.deepEqual(state.serverValue, value) ? null : state.serverError
50
+ serverError: typeof action.result.error !== 'undefined' && !future.deepEqual(state.serverValue, value) ? null : state.serverError
44
51
  }) : util.merge(state, {
45
52
  // Clear client error to avoid showing stale errors
46
53
  clientError: null,
47
54
  // Update server error if the error is defined.
48
55
  // There is no need to check if the error is different as we are updating other states as well
49
- serverError: typeof action.error !== 'undefined' ? action.error : state.serverError,
50
- listKeys: action.type === 'server' && action.targetValue ? pruneListKeys(state.listKeys, action.targetValue) : state.listKeys,
51
- targetValue: action.type === 'server' && action.targetValue ? action.targetValue : state.targetValue,
56
+ serverError: typeof action.result.error !== 'undefined' ? action.result.error : state.serverError,
57
+ listKeys: action.type === 'server' && action.result.targetValue ? pruneListKeys(state.listKeys, action.result.targetValue) : state.listKeys,
58
+ targetValue: action.type === 'server' && action.result.targetValue ? action.result.targetValue : state.targetValue,
52
59
  // Keep track of the value that the serverError is based on
53
60
  serverValue: !future.deepEqual(state.serverValue, value) ? value : state.serverValue
54
61
  });
@@ -56,23 +63,250 @@ function updateState(state, action) {
56
63
  var intent = (_action$intent = action.intent) !== null && _action$intent !== void 0 ? _action$intent : {
57
64
  type: 'validate'
58
65
  };
59
- var handler = (_action$ctx$handlers = action.ctx.handlers) === null || _action$ctx$handlers === void 0 ? void 0 : _action$ctx$handlers[intent.type];
60
- if (typeof (handler === null || handler === void 0 ? void 0 : handler.update) === 'function') {
61
- var _handler$validate, _handler$validate2;
62
- if ((_handler$validate = (_handler$validate2 = handler.validate) === null || _handler$validate2 === void 0 ? void 0 : _handler$validate2.call(handler, intent.payload)) !== null && _handler$validate !== void 0 ? _handler$validate : true) {
63
- return handler.update(state, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, action), {}, {
64
- ctx: {
65
- reset: action.ctx.reset,
66
- cancelled: action.ctx.cancelled
67
- },
68
- intent: {
69
- type: intent.type,
70
- payload: intent.payload
71
- }
66
+ var handler = (_action$ctx$intentHan = action.ctx.intentHandlers) === null || _action$ctx$intentHan === void 0 ? void 0 : _action$ctx$intentHan[intent.type];
67
+ if (handler && action.type === 'client' && hasIntentEffects) {
68
+ if (typeof handler.move === 'function') {
69
+ var handleMove = handler.move;
70
+ state = moveState(state, action.result.submission.payload, value, name => handleMove({
71
+ name,
72
+ payload: intent.payload,
73
+ status: action.ctx.status,
74
+ targetValue: action.result.targetValue
72
75
  }));
76
+ } else if (typeof handler.resolve === 'function') {
77
+ state = invalidateState(state, action.result.submission.payload, value);
78
+ }
79
+ }
80
+ if (handler && action.type !== 'server' && hasIntentEffects && typeof handler.touch === 'function') {
81
+ var touchedFields = state.touchedFields;
82
+ for (var _name of getFields(action.result)) {
83
+ if (handler.touch({
84
+ name: _name,
85
+ payload: intent.payload
86
+ })) {
87
+ touchedFields = util.appendUniqueItem(touchedFields, _name);
88
+ }
89
+ }
90
+ state = util.merge(state, {
91
+ touchedFields
92
+ });
93
+ }
94
+ return util.merge(state, {
95
+ customState: updateCustomState(state.customState, action, {
96
+ handlers: action.ctx.customStateHandlers
97
+ })
98
+ });
99
+ }
100
+ function getFields(result) {
101
+ var fields = result.submission.fields;
102
+ if (result.error) {
103
+ fields = fields.concat(Object.keys(result.error.fieldErrors));
104
+ }
105
+ var fieldsSet = new Set(['']);
106
+ for (var field of fields) {
107
+ var paths = future.parsePath(field);
108
+ for (var index = 1; index <= paths.length; index++) {
109
+ fieldsSet.add(future.formatPath(paths.slice(0, index)));
110
+ }
111
+ }
112
+ return Array.from(fieldsSet);
113
+ }
114
+ function getApplyStatus(baseTargetValue, finalTargetValue) {
115
+ if (baseTargetValue === finalTargetValue) {
116
+ return 'applied';
117
+ }
118
+ return typeof finalTargetValue === 'undefined' ? 'reverted' : 'modified';
119
+ }
120
+
121
+ /**
122
+ * Fallback state transition for intents that change the payload shape without
123
+ * providing a `move()` mapping. It drops list keys and touched fields under
124
+ * changed paths so stale client state does not point at the wrong fields.
125
+ */
126
+ function invalidateState(state, previousValue, nextValue) {
127
+ if (previousValue === nextValue) {
128
+ return state;
129
+ }
130
+ var changedNames = [];
131
+ var stack = [{
132
+ previousValue,
133
+ nextValue,
134
+ name: ''
135
+ }];
136
+ var _loop = function _loop() {
137
+ var current = stack.pop();
138
+ if (!current) {
139
+ return 0; // break
140
+ }
141
+ if (Object.is(current.previousValue, current.nextValue)) {
142
+ return 1; // continue
143
+ }
144
+ if (Array.isArray(current.previousValue) && Array.isArray(current.nextValue)) {
145
+ changedNames = changedNames.concat(current.name);
146
+ return 1; // continue
147
+ }
148
+ if (future.isPlainObject(current.previousValue) && future.isPlainObject(current.nextValue)) {
149
+ for (var key of new Set([...Object.keys(current.previousValue), ...Object.keys(current.nextValue)])) {
150
+ stack.push({
151
+ previousValue: current.previousValue[key],
152
+ nextValue: current.nextValue[key],
153
+ name: future.appendPath(current.name, key)
154
+ });
155
+ }
156
+ return 1; // continue
157
+ }
158
+ var basePath = future.parsePath(current.name);
159
+ if (changedNames.some(existingName => future.getRelativePath(current.name, future.parsePath(existingName)) !== null)) {
160
+ return 1; // continue
161
+ }
162
+ changedNames = changedNames.filter(existingName => future.getRelativePath(existingName, basePath) === null).concat(current.name);
163
+ },
164
+ _ret;
165
+ while (stack.length > 0) {
166
+ _ret = _loop();
167
+ if (_ret === 0) break;
168
+ if (_ret === 1) continue;
169
+ }
170
+ var basePaths = changedNames.map(future.parsePath);
171
+ if (basePaths.length === 0) {
172
+ return state;
173
+ }
174
+ var listKeys = state.listKeys;
175
+ var touchedFields = state.touchedFields;
176
+ if (Object.keys(state.listKeys).length > 0) {
177
+ var changed = false;
178
+ var entries = Object.entries(state.listKeys).filter(_ref => {
179
+ var [name] = _ref;
180
+ var keep = !basePaths.some(basePath => future.getRelativePath(name, basePath) !== null);
181
+ changed || (changed = !keep);
182
+ return keep;
183
+ });
184
+ if (changed) {
185
+ listKeys = Object.fromEntries(entries);
186
+ }
187
+ }
188
+ if (state.touchedFields.length > 0) {
189
+ var _changed = false;
190
+ var nextTouchedFields = state.touchedFields.filter(name => {
191
+ var keep = !basePaths.some(basePath => future.getRelativePath(name, basePath) !== null);
192
+ _changed || (_changed = !keep);
193
+ return keep;
194
+ });
195
+ if (_changed) {
196
+ touchedFields = nextTouchedFields;
73
197
  }
74
198
  }
75
- return state;
199
+ return util.merge(state, {
200
+ listKeys,
201
+ touchedFields
202
+ });
203
+ }
204
+
205
+ /**
206
+ * Preserves list keys and touched fields for intents that can map old field
207
+ * paths to new ones, such as insert, remove, and reorder.
208
+ */
209
+ function moveState(state, previousValue, nextValue, move) {
210
+ if (previousValue === nextValue) {
211
+ return state;
212
+ }
213
+ function collectListEntries(value) {
214
+ var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
215
+ var entries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
216
+ if (Array.isArray(value)) {
217
+ entries.push(name);
218
+ for (var index = 0; index < value.length; index++) {
219
+ collectListEntries(value[index], future.appendPath(name, index), entries);
220
+ }
221
+ } else if (future.isPlainObject(value)) {
222
+ for (var [key, childValue] of Object.entries(value)) {
223
+ collectListEntries(childValue, future.appendPath(name, key), entries);
224
+ }
225
+ }
226
+ return entries;
227
+ }
228
+ function getListEntry(name) {
229
+ if (name === null) {
230
+ return null;
231
+ }
232
+ var paths = future.parsePath(name);
233
+ var index = paths[paths.length - 1];
234
+ if (typeof index !== 'number') {
235
+ return null;
236
+ }
237
+ return {
238
+ name: future.formatPath(paths.slice(0, -1)),
239
+ index
240
+ };
241
+ }
242
+ var targetSlots = {};
243
+ var getTargetSlots = listName => {
244
+ var _targetSlots$listName;
245
+ return (_targetSlots$listName = targetSlots[listName]) !== null && _targetSlots$listName !== void 0 ? _targetSlots$listName : targetSlots[listName] = Array.from({
246
+ length: util.getPathArray(nextValue, listName).length
247
+ }, () => undefined);
248
+ };
249
+ for (var listName of collectListEntries(previousValue)) {
250
+ var _state$listKeys$listN;
251
+ var sourceKeys = (_state$listKeys$listN = state.listKeys[listName]) !== null && _state$listKeys$listN !== void 0 ? _state$listKeys$listN : getDefaultListKey(state.resetKey, previousValue, listName);
252
+ for (var index = 0; index < sourceKeys.length; index++) {
253
+ var _entry$index, _target$_entry$index;
254
+ var entry = getListEntry(move(future.appendPath(listName, index)));
255
+ if (!entry) {
256
+ continue;
257
+ }
258
+ var target = getTargetSlots(entry.name);
259
+ if (entry.index >= target.length) {
260
+ continue;
261
+ }
262
+ (_target$_entry$index = target[_entry$index = entry.index]) !== null && _target$_entry$index !== void 0 ? _target$_entry$index : target[_entry$index] = sourceKeys[index];
263
+ }
264
+ }
265
+ var touchedFields = state.touchedFields;
266
+ for (var [_index, currentName] of state.touchedFields.entries()) {
267
+ var movedName = move(currentName);
268
+ if (movedName === currentName) {
269
+ continue;
270
+ }
271
+ touchedFields = state.touchedFields.slice(0, _index);
272
+ if (movedName !== null) {
273
+ touchedFields.push(movedName);
274
+ }
275
+ for (var nextName of state.touchedFields.slice(_index + 1)) {
276
+ var _movedName = move(nextName);
277
+ if (_movedName !== null) {
278
+ touchedFields.push(_movedName);
279
+ }
280
+ }
281
+ break;
282
+ }
283
+ var changed = false;
284
+ var result = {};
285
+ var listNames = new Set([...Object.keys(state.listKeys), ...Object.keys(targetSlots)]);
286
+ for (var _listName of listNames) {
287
+ var keys = targetSlots[_listName];
288
+ var currentKeys = state.listKeys[_listName];
289
+ if (!keys) {
290
+ changed = true;
291
+ continue;
292
+ }
293
+ var nextKeys = keys.map(key => key !== null && key !== void 0 ? key : util.generateUniqueKey());
294
+ var defaultKeys = getDefaultListKey(state.resetKey, nextValue, _listName);
295
+ if (future.deepEqual(nextKeys, defaultKeys)) {
296
+ changed || (changed = typeof currentKeys !== 'undefined');
297
+ continue;
298
+ }
299
+ if (currentKeys && future.deepEqual(currentKeys, nextKeys)) {
300
+ result[_listName] = currentKeys;
301
+ continue;
302
+ }
303
+ result[_listName] = nextKeys;
304
+ changed = true;
305
+ }
306
+ return util.merge(state, {
307
+ listKeys: changed ? result : state.listKeys,
308
+ touchedFields
309
+ });
76
310
  }
77
311
 
78
312
  /**
@@ -81,8 +315,8 @@ function updateState(state, action) {
81
315
  */
82
316
  function pruneListKeys(listKeys, targetValue) {
83
317
  var result = listKeys;
84
- for (var [name, keys] of Object.entries(listKeys)) {
85
- var list = util.getPathArray(targetValue, name);
318
+ for (var [_name2, keys] of Object.entries(listKeys)) {
319
+ var list = util.getPathArray(targetValue, _name2);
86
320
 
87
321
  // Reset list keys only if the length has changed
88
322
  // to minimize potential UI state loss due to key changes
@@ -93,22 +327,26 @@ function pruneListKeys(listKeys, targetValue) {
93
327
  }
94
328
 
95
329
  // Remove the list key to force regeneration
96
- delete result[name];
330
+ delete result[_name2];
97
331
  }
98
332
  }
99
333
  return result;
100
334
  }
101
335
  function getDefaultPayload(context, name) {
102
- var _ref, _context$state$server;
103
- var value = future.getPathValue((_ref = (_context$state$server = context.state.serverValue) !== null && _context$state$server !== void 0 ? _context$state$server : context.state.targetValue) !== null && _ref !== void 0 ? _ref : context.state.defaultValue, name);
336
+ var _ref2, _context$state$server;
337
+ var value = future.getPathValue((_ref2 = (_context$state$server = context.state.serverValue) !== null && _context$state$server !== void 0 ? _context$state$server : context.state.targetValue) !== null && _ref2 !== void 0 ? _ref2 : context.state.defaultValue, name);
104
338
  if (value === null) {
105
339
  return null;
106
340
  }
107
- return future.normalize(value, context.serialize, name);
341
+ return future.normalize(value, {
342
+ serialize: context.serialize,
343
+ stripEmptyValue: false,
344
+ name
345
+ });
108
346
  }
109
347
  function getDefaultValue(context, name) {
110
- var _ref2, _context$state$server2;
111
- var value = future.getPathValue((_ref2 = (_context$state$server2 = context.state.serverValue) !== null && _context$state$server2 !== void 0 ? _context$state$server2 : context.state.targetValue) !== null && _ref2 !== void 0 ? _ref2 : context.state.defaultValue, name);
348
+ var _ref3, _context$state$server2;
349
+ var value = future.getPathValue((_ref3 = (_context$state$server2 = context.state.serverValue) !== null && _context$state$server2 !== void 0 ? _context$state$server2 : context.state.targetValue) !== null && _ref3 !== void 0 ? _ref3 : context.state.defaultValue, name);
112
350
  var serializedValue = context.serialize(value, {
113
351
  name
114
352
  });
@@ -118,8 +356,8 @@ function getDefaultValue(context, name) {
118
356
  return '';
119
357
  }
120
358
  function getDefaultOptions(context, name) {
121
- var _ref3, _context$state$server3;
122
- var value = future.getPathValue((_ref3 = (_context$state$server3 = context.state.serverValue) !== null && _context$state$server3 !== void 0 ? _context$state$server3 : context.state.targetValue) !== null && _ref3 !== void 0 ? _ref3 : context.state.defaultValue, name);
359
+ var _ref4, _context$state$server3;
360
+ var value = future.getPathValue((_ref4 = (_context$state$server3 = context.state.serverValue) !== null && _context$state$server3 !== void 0 ? _context$state$server3 : context.state.targetValue) !== null && _ref4 !== void 0 ? _ref4 : context.state.defaultValue, name);
123
361
  var serializedValue = context.serialize(value, {
124
362
  name
125
363
  });
@@ -132,8 +370,8 @@ function getDefaultOptions(context, name) {
132
370
  return [];
133
371
  }
134
372
  function isDefaultChecked(context, name) {
135
- var _ref4, _context$state$server4;
136
- var value = future.getPathValue((_ref4 = (_context$state$server4 = context.state.serverValue) !== null && _context$state$server4 !== void 0 ? _context$state$server4 : context.state.targetValue) !== null && _ref4 !== void 0 ? _ref4 : context.state.defaultValue, name);
373
+ var _ref5, _context$state$server4;
374
+ var value = future.getPathValue((_ref5 = (_context$state$server4 = context.state.serverValue) !== null && _context$state$server4 !== void 0 ? _context$state$server4 : context.state.targetValue) !== null && _ref5 !== void 0 ? _ref5 : context.state.defaultValue, name);
137
375
  var serializedValue = context.serialize(value, {
138
376
  name
139
377
  });
@@ -163,8 +401,8 @@ function getDefaultListKey(prefix, initialValue, name) {
163
401
  return util.getPathArray(initialValue, name).map((_, index) => "".concat(prefix, "-").concat(future.appendPath(name, index)));
164
402
  }
165
403
  function getListKey(context, name) {
166
- var _context$state$listKe, _context$state$listKe2, _ref5, _context$state$server5;
167
- return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_ref5 = (_context$state$server5 = context.state.serverValue) !== null && _context$state$server5 !== void 0 ? _context$state$server5 : context.state.targetValue) !== null && _ref5 !== void 0 ? _ref5 : context.state.defaultValue, name);
404
+ var _context$state$listKe, _context$state$listKe2, _ref6, _context$state$server5;
405
+ return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_ref6 = (_context$state$server5 = context.state.serverValue) !== null && _context$state$server5 !== void 0 ? _context$state$server5 : context.state.targetValue) !== null && _ref6 !== void 0 ? _ref6 : context.state.defaultValue, name);
168
406
  }
169
407
  function getErrors(state, name) {
170
408
  var _state$serverError;
@@ -204,8 +442,8 @@ function getFieldErrors(state, name) {
204
442
  */
205
443
  function hasFieldError(error, name) {
206
444
  var basePath = future.parsePath(name);
207
- return Object.entries(error.fieldErrors).some(_ref6 => {
208
- var [field, fieldError] = _ref6;
445
+ return Object.entries(error.fieldErrors).some(_ref7 => {
446
+ var [field, fieldError] = _ref7;
209
447
  return future.getRelativePath(field, basePath) !== null && fieldError !== null;
210
448
  });
211
449
  }
@@ -245,6 +483,9 @@ function getFormMetadata(context, options) {
245
483
  errorId: "".concat(context.formId, "-form-error"),
246
484
  descriptionId: "".concat(context.formId, "-form-description"),
247
485
  defaultValue: context.state.defaultValue,
486
+ get customState() {
487
+ return context.state.customState;
488
+ },
248
489
  get errors() {
249
490
  return getErrors(context.state);
250
491
  },
@@ -414,7 +655,61 @@ function getFieldList(context, options) {
414
655
  });
415
656
  });
416
657
  }
658
+ function defineCustomState(definition) {
659
+ return definition;
660
+ }
661
+ function mergeCustomStateHandlers(globalCustomState, inlineCustomState) {
662
+ if (globalCustomState && inlineCustomState) {
663
+ for (var key of Object.keys(inlineCustomState)) {
664
+ if (Object.prototype.hasOwnProperty.call(globalCustomState, key)) {
665
+ throw new Error("Duplicate custom state key \"".concat(key, "\""));
666
+ }
667
+ }
668
+ }
669
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, globalCustomState), inlineCustomState);
670
+ }
671
+ function initializeCustomState(options) {
672
+ var _options$handlers;
673
+ return Object.fromEntries(Object.entries((_options$handlers = options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : {}).map(_ref8 => {
674
+ var [key, handler] = _ref8;
675
+ if (!options.currentState || !Object.prototype.hasOwnProperty.call(options.currentState, key) || handler.reset === undefined || handler.reset === true) {
676
+ return [key, handler.initialize()];
677
+ }
678
+ var currentState = options.currentState[key];
679
+ if (typeof handler.reset === 'function') {
680
+ return [key, handler.reset(currentState, {
681
+ result: options.result
682
+ })];
683
+ }
684
+ return [key, currentState];
685
+ }));
686
+ }
687
+ function updateCustomState(state, action, options) {
688
+ if (!options.handlers) {
689
+ return state;
690
+ }
691
+ return Object.fromEntries(Object.entries(options.handlers).map(_ref9 => {
692
+ var [key, handler] = _ref9;
693
+ var nextState = state[key];
694
+ if (action.type !== 'server' && action.type !== 'client:async' && handler.handleIntent) {
695
+ nextState = handler.handleIntent(nextState, {
696
+ intent: action.intent,
697
+ submission: action.result.submission
698
+ });
699
+ }
700
+ if (handler.handleResult && typeof action.result.error !== 'undefined') {
701
+ nextState = handler.handleResult(nextState, {
702
+ intent: action.intent,
703
+ result: action.result,
704
+ phase: action.type === 'client' || action.type === 'client:async' ? 'client' : 'server'
705
+ });
706
+ }
707
+ return [key, nextState];
708
+ }));
709
+ }
417
710
 
711
+ exports.defineCustomState = defineCustomState;
712
+ exports.getApplyStatus = getApplyStatus;
418
713
  exports.getDefaultListKey = getDefaultListKey;
419
714
  exports.getDefaultOptions = getDefaultOptions;
420
715
  exports.getDefaultPayload = getDefaultPayload;
@@ -423,13 +718,19 @@ exports.getErrors = getErrors;
423
718
  exports.getField = getField;
424
719
  exports.getFieldErrors = getFieldErrors;
425
720
  exports.getFieldList = getFieldList;
721
+ exports.getFields = getFields;
426
722
  exports.getFieldset = getFieldset;
427
723
  exports.getFormMetadata = getFormMetadata;
428
724
  exports.getListKey = getListKey;
429
725
  exports.hasFieldError = hasFieldError;
726
+ exports.initializeCustomState = initializeCustomState;
430
727
  exports.initializeState = initializeState;
728
+ exports.invalidateState = invalidateState;
431
729
  exports.isDefaultChecked = isDefaultChecked;
432
730
  exports.isTouched = isTouched;
433
731
  exports.isValid = isValid;
732
+ exports.mergeCustomStateHandlers = mergeCustomStateHandlers;
733
+ exports.moveState = moveState;
434
734
  exports.pruneListKeys = pruneListKeys;
735
+ exports.updateCustomState = updateCustomState;
435
736
  exports.updateState = updateState;