@conform-to/dom 1.8.1 → 1.9.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.
@@ -0,0 +1,5 @@
1
+ export type { Serialize, FormValue, FormError, Submission, SubmissionResult, ValidationAttributes, } from '../types';
2
+ export { DEFAULT_INTENT_NAME, getFormData, isDirty, parseSubmission, getPathSegments, formatPathSegments, appendPathSegment, getRelativePath, getValueAtPath, setValueAtPath, report, serialize, } from '../formdata';
3
+ export { isPlainObject, deepEqual } from '../util';
4
+ export { isFieldElement, isGlobalInstance, updateField, createFileList, createSubmitEvent, createGlobalFormsObserver, focus, change, blur, getFormAction, getFormEncType, getFormMethod, requestSubmit, requestIntent, } from '../dom';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var formdata = require('../formdata.js');
6
+ var util = require('../util.js');
7
+ var dom = require('../dom.js');
8
+
9
+
10
+
11
+ exports.DEFAULT_INTENT_NAME = formdata.DEFAULT_INTENT_NAME;
12
+ exports.appendPathSegment = formdata.appendPathSegment;
13
+ exports.formatPathSegments = formdata.formatPathSegments;
14
+ exports.getFormData = formdata.getFormData;
15
+ exports.getPathSegments = formdata.getPathSegments;
16
+ exports.getRelativePath = formdata.getRelativePath;
17
+ exports.getValueAtPath = formdata.getValueAtPath;
18
+ exports.isDirty = formdata.isDirty;
19
+ exports.parseSubmission = formdata.parseSubmission;
20
+ exports.report = formdata.report;
21
+ exports.serialize = formdata.serialize;
22
+ exports.setValueAtPath = formdata.setValueAtPath;
23
+ exports.deepEqual = util.deepEqual;
24
+ exports.isPlainObject = util.isPlainObject;
25
+ exports.blur = dom.blur;
26
+ exports.change = dom.change;
27
+ exports.createFileList = dom.createFileList;
28
+ exports.createGlobalFormsObserver = dom.createGlobalFormsObserver;
29
+ exports.createSubmitEvent = dom.createSubmitEvent;
30
+ exports.focus = dom.focus;
31
+ exports.getFormAction = dom.getFormAction;
32
+ exports.getFormEncType = dom.getFormEncType;
33
+ exports.getFormMethod = dom.getFormMethod;
34
+ exports.isFieldElement = dom.isFieldElement;
35
+ exports.isGlobalInstance = dom.isGlobalInstance;
36
+ exports.requestIntent = dom.requestIntent;
37
+ exports.requestSubmit = dom.requestSubmit;
38
+ exports.updateField = dom.updateField;
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_INTENT_NAME, appendPathSegment, formatPathSegments, getFormData, getPathSegments, getRelativePath, getValueAtPath, isDirty, parseSubmission, report, serialize, setValueAtPath } from '../formdata.mjs';
2
+ export { deepEqual, isPlainObject } from '../util.mjs';
3
+ export { blur, change, createFileList, createGlobalFormsObserver, createSubmitEvent, focus, getFormAction, getFormEncType, getFormMethod, isFieldElement, isGlobalInstance, requestIntent, requestSubmit, updateField } from '../dom.mjs';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { type Combine, type Constraint, type ControlButtonProps, type FormId, type FieldName, type DefaultValue, type FormValue, type FormOptions, type FormState, type FormContext, type SubscriptionSubject, type SubscriptionScope, createFormContext as unstable_createFormContext, } from './form';
2
- export { type FieldElement, isFieldElement, updateField as unstable_updateField, createFileList, createGlobalFormsObserver as unstable_createGlobalFormsObserver, focus as unstable_focus, change as unstable_change, blur as unstable_blur, } from './dom';
2
+ export { type FieldElement, isFieldElement, isGlobalInstance, updateField as unstable_updateField, createFileList, } from './dom';
3
3
  export { type Submission, type SubmissionResult, type Intent, INTENT, STATE, serializeIntent, parse, } from './submission';
4
- export { getFormData, getPaths, formatPaths, isPrefix, isGlobalInstance, deepEqual as unstable_deepEqual, isDirty as unstable_isDirty, } from './formdata';
4
+ export { getFormData, getPathSegments as getPaths, formatPathSegments as formatPaths, isPrefix, } from './formdata';
5
5
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -12,19 +12,13 @@ var formdata = require('./formdata.js');
12
12
  exports.unstable_createFormContext = form.createFormContext;
13
13
  exports.createFileList = dom.createFileList;
14
14
  exports.isFieldElement = dom.isFieldElement;
15
- exports.unstable_blur = dom.blur;
16
- exports.unstable_change = dom.change;
17
- exports.unstable_createGlobalFormsObserver = dom.createGlobalFormsObserver;
18
- exports.unstable_focus = dom.focus;
15
+ exports.isGlobalInstance = dom.isGlobalInstance;
19
16
  exports.unstable_updateField = dom.updateField;
20
17
  exports.INTENT = submission.INTENT;
21
18
  exports.STATE = submission.STATE;
22
19
  exports.parse = submission.parse;
23
20
  exports.serializeIntent = submission.serializeIntent;
24
- exports.formatPaths = formdata.formatPaths;
21
+ exports.formatPaths = formdata.formatPathSegments;
25
22
  exports.getFormData = formdata.getFormData;
26
- exports.getPaths = formdata.getPaths;
27
- exports.isGlobalInstance = formdata.isGlobalInstance;
23
+ exports.getPaths = formdata.getPathSegments;
28
24
  exports.isPrefix = formdata.isPrefix;
29
- exports.unstable_deepEqual = formdata.deepEqual;
30
- exports.unstable_isDirty = formdata.isDirty;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  export { createFormContext as unstable_createFormContext } from './form.mjs';
2
- export { createFileList, isFieldElement, blur as unstable_blur, change as unstable_change, createGlobalFormsObserver as unstable_createGlobalFormsObserver, focus as unstable_focus, updateField as unstable_updateField } from './dom.mjs';
2
+ export { createFileList, isFieldElement, isGlobalInstance, updateField as unstable_updateField } from './dom.mjs';
3
3
  export { INTENT, STATE, parse, serializeIntent } from './submission.mjs';
4
- export { formatPaths, getFormData, getPaths, isGlobalInstance, isPrefix, deepEqual as unstable_deepEqual, isDirty as unstable_isDirty } from './formdata.mjs';
4
+ export { formatPathSegments as formatPaths, getFormData, getPathSegments as getPaths, isPrefix } from './formdata.mjs';
@@ -136,4 +136,17 @@ export declare const root: unique symbol;
136
136
  export declare function setState(state: Record<string, unknown>, name: string, valueFn: (value: unknown) => unknown): void;
137
137
  export declare function setListState(state: Record<string, unknown>, intent: InsertIntent | RemoveIntent | ReorderIntent, getDefaultValue?: (defaultValue: any) => any): void;
138
138
  export declare function serialize<Schema>(defaultValue: Schema): FormValue<Schema>;
139
+ /**
140
+ * Normalize value by removing empty object or array, empty string and null values
141
+ */
142
+ export declare function normalize<Type extends Record<string, unknown>>(value: Type, acceptFile?: boolean): Type | undefined;
143
+ export declare function normalize<Type extends Array<unknown>>(value: Type, acceptFile?: boolean): Type | undefined;
144
+ export declare function normalize(value: unknown, acceptFile?: boolean): unknown | undefined;
145
+ /**
146
+ * Flatten a tree into a dictionary
147
+ */
148
+ export declare function flatten(data: unknown, options?: {
149
+ resolve?: (data: unknown) => unknown;
150
+ prefix?: string;
151
+ }): Record<string, unknown>;
139
152
  //# sourceMappingURL=submission.d.ts.map
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
6
+ var dom = require('./dom.js');
6
7
  var formdata = require('./formdata.js');
7
8
  var util = require('./util.js');
8
9
 
@@ -32,7 +33,7 @@ function getSubmissionContext(body) {
32
33
  return 1; // continue
33
34
  }
34
35
  context.fields.add(name);
35
- formdata.setValue(context.payload, name, prev => {
36
+ formdata.setValueAtPath(context.payload, name, prev => {
36
37
  if (!prev) {
37
38
  return next;
38
39
  } else if (Array.isArray(prev)) {
@@ -40,6 +41,8 @@ function getSubmissionContext(body) {
40
41
  } else {
41
42
  return [prev, next];
42
43
  }
44
+ }, {
45
+ silent: true
43
46
  });
44
47
  };
45
48
  for (var [name, next] of body.entries()) {
@@ -54,11 +57,11 @@ function parse(payload, options) {
54
57
  switch (intent.type) {
55
58
  case 'update':
56
59
  {
57
- var name = formdata.formatName(intent.payload.name, intent.payload.index);
60
+ var name = formdata.appendPathSegment(intent.payload.name, intent.payload.index);
58
61
  var _value = intent.payload.value;
59
62
  if (typeof intent.payload.value !== 'undefined') {
60
63
  if (name) {
61
- formdata.setValue(context.payload, name, () => _value);
64
+ formdata.setValueAtPath(context.payload, name, () => _value);
62
65
  } else {
63
66
  context.payload = _value;
64
67
  }
@@ -67,9 +70,9 @@ function parse(payload, options) {
67
70
  }
68
71
  case 'reset':
69
72
  {
70
- var _name = formdata.formatName(intent.payload.name, intent.payload.index);
73
+ var _name = formdata.appendPathSegment(intent.payload.name, intent.payload.index);
71
74
  if (_name) {
72
- formdata.setValue(context.payload, _name, () => undefined);
75
+ formdata.setValueAtPath(context.payload, _name, () => undefined);
73
76
  } else {
74
77
  context.payload = {};
75
78
  }
@@ -124,17 +127,17 @@ function replySubmission(context) {
124
127
  }
125
128
  if ('hideFields' in options && options.hideFields) {
126
129
  for (var name of options.hideFields) {
127
- var _value2 = formdata.getValue(context.payload, name);
130
+ var _value2 = formdata.getValueAtPath(context.payload, name);
128
131
  if (typeof _value2 !== 'undefined') {
129
- formdata.setValue(context.payload, name, () => undefined);
132
+ formdata.setValueAtPath(context.payload, name, () => undefined);
130
133
  }
131
134
  }
132
135
  }
133
- var extraError = 'formErrors' in options || 'fieldErrors' in options ? formdata.normalize(_rollupPluginBabelHelpers.objectSpread2({
136
+ var extraError = 'formErrors' in options || 'fieldErrors' in options ? normalize(_rollupPluginBabelHelpers.objectSpread2({
134
137
  '': (_options$formErrors = options.formErrors) !== null && _options$formErrors !== void 0 ? _options$formErrors : null
135
138
  }, options.fieldErrors)) : null;
136
139
  var error = context.error || extraError ? _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, context.error), extraError) : undefined;
137
- var initialValue = (_ref = formdata.normalize(context.payload,
140
+ var initialValue = (_ref = normalize(context.payload,
138
141
  // We can't serialize the file and send it back from the server, but we can preserve it in the client
139
142
  typeof document !== 'undefined'
140
143
  // We need the file on the client because it's treated as the form value
@@ -198,7 +201,7 @@ function updateList(list, intent) {
198
201
  }
199
202
  }
200
203
  function setListValue(data, intent) {
201
- formdata.setValue(data, intent.payload.name, value => {
204
+ formdata.setValueAtPath(data, intent.payload.name, value => {
202
205
  var list = value !== null && value !== void 0 ? value : [];
203
206
  updateList(list, intent);
204
207
  return list;
@@ -216,7 +219,7 @@ function setState(state, name, valueFn) {
216
219
  var _loop2 = function _loop2() {
217
220
  var value = state[_key];
218
221
  if (formdata.isPrefix(_key, name) && _key !== name) {
219
- formdata.setValue(target, _key, currentValue => {
222
+ formdata.setValueAtPath(target, _key, currentValue => {
220
223
  if (typeof currentValue === 'undefined') {
221
224
  return value;
222
225
  }
@@ -236,10 +239,10 @@ function setState(state, name, valueFn) {
236
239
  for (var _key of keys) {
237
240
  _loop2();
238
241
  }
239
- var result = valueFn(formdata.getValue(target, name));
240
- Object.assign(state, formdata.flatten(result, {
242
+ var result = valueFn(formdata.getValueAtPath(target, name));
243
+ Object.assign(state, flatten(result, {
241
244
  resolve(data) {
242
- if (formdata.isPlainObject(data) || Array.isArray(data)) {
245
+ if (util.isPlainObject(data) || Array.isArray(data)) {
243
246
  var _data$root;
244
247
  // @ts-expect-error
245
248
  return (_data$root = data[root]) !== null && _data$root !== void 0 ? _data$root : null;
@@ -269,7 +272,7 @@ function setListState(state, intent, getDefaultValue) {
269
272
  });
270
273
  }
271
274
  function serialize(defaultValue) {
272
- if (formdata.isPlainObject(defaultValue)) {
275
+ if (util.isPlainObject(defaultValue)) {
273
276
  // @ts-expect-error FIXME
274
277
  return Object.entries(defaultValue).reduce((result, _ref2) => {
275
278
  var [key, value] = _ref2;
@@ -294,11 +297,74 @@ function serialize(defaultValue) {
294
297
  }
295
298
  }
296
299
 
300
+ /**
301
+ * Normalize value by removing empty object or array, empty string and null values
302
+ */
303
+
304
+ function normalize(value) {
305
+ var acceptFile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
306
+ if (util.isPlainObject(value)) {
307
+ var obj = Object.keys(value).sort().reduce((result, key) => {
308
+ var data = normalize(value[key], acceptFile);
309
+ if (typeof data !== 'undefined') {
310
+ result[key] = data;
311
+ }
312
+ return result;
313
+ }, {});
314
+ if (Object.keys(obj).length === 0) {
315
+ return;
316
+ }
317
+ return obj;
318
+ }
319
+ if (Array.isArray(value)) {
320
+ if (value.length === 0) {
321
+ return undefined;
322
+ }
323
+ return value.map(item => normalize(item, acceptFile));
324
+ }
325
+ if (typeof value === 'string' && value === '' || value === null || dom.isGlobalInstance(value, 'File') && (!acceptFile || value.size === 0)) {
326
+ return;
327
+ }
328
+ return value;
329
+ }
330
+
331
+ /**
332
+ * Flatten a tree into a dictionary
333
+ */
334
+ function flatten(data) {
335
+ var _options$resolve;
336
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
337
+ var result = {};
338
+ var resolve = (_options$resolve = options.resolve) !== null && _options$resolve !== void 0 ? _options$resolve : data => data;
339
+ function process(data, prefix) {
340
+ var value = normalize(resolve(data));
341
+ if (typeof value !== 'undefined') {
342
+ result[prefix] = value;
343
+ }
344
+ if (Array.isArray(data)) {
345
+ for (var i = 0; i < data.length; i++) {
346
+ process(data[i], "".concat(prefix, "[").concat(i, "]"));
347
+ }
348
+ } else if (util.isPlainObject(data)) {
349
+ for (var [_key2, _value3] of Object.entries(data)) {
350
+ process(_value3, prefix ? "".concat(prefix, ".").concat(_key2) : _key2);
351
+ }
352
+ }
353
+ }
354
+ if (data) {
355
+ var _options$prefix;
356
+ process(data, (_options$prefix = options.prefix) !== null && _options$prefix !== void 0 ? _options$prefix : '');
357
+ }
358
+ return result;
359
+ }
360
+
297
361
  exports.INTENT = INTENT;
298
362
  exports.STATE = STATE;
299
363
  exports.createSubmission = createSubmission;
364
+ exports.flatten = flatten;
300
365
  exports.getIntent = getIntent;
301
366
  exports.getSubmissionContext = getSubmissionContext;
367
+ exports.normalize = normalize;
302
368
  exports.parse = parse;
303
369
  exports.replySubmission = replySubmission;
304
370
  exports.root = root;
@@ -1,6 +1,7 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
2
- import { formatName, setValue, isPlainObject, getValue, normalize, flatten, isPrefix } from './formdata.mjs';
3
- import { invariant } from './util.mjs';
2
+ import { isGlobalInstance } from './dom.mjs';
3
+ import { appendPathSegment, setValueAtPath, getValueAtPath, isPrefix } from './formdata.mjs';
4
+ import { isPlainObject, invariant } from './util.mjs';
4
5
 
5
6
  /**
6
7
  * The name to be used when submitting a form control
@@ -28,7 +29,7 @@ function getSubmissionContext(body) {
28
29
  return 1; // continue
29
30
  }
30
31
  context.fields.add(name);
31
- setValue(context.payload, name, prev => {
32
+ setValueAtPath(context.payload, name, prev => {
32
33
  if (!prev) {
33
34
  return next;
34
35
  } else if (Array.isArray(prev)) {
@@ -36,6 +37,8 @@ function getSubmissionContext(body) {
36
37
  } else {
37
38
  return [prev, next];
38
39
  }
40
+ }, {
41
+ silent: true
39
42
  });
40
43
  };
41
44
  for (var [name, next] of body.entries()) {
@@ -50,11 +53,11 @@ function parse(payload, options) {
50
53
  switch (intent.type) {
51
54
  case 'update':
52
55
  {
53
- var name = formatName(intent.payload.name, intent.payload.index);
56
+ var name = appendPathSegment(intent.payload.name, intent.payload.index);
54
57
  var _value = intent.payload.value;
55
58
  if (typeof intent.payload.value !== 'undefined') {
56
59
  if (name) {
57
- setValue(context.payload, name, () => _value);
60
+ setValueAtPath(context.payload, name, () => _value);
58
61
  } else {
59
62
  context.payload = _value;
60
63
  }
@@ -63,9 +66,9 @@ function parse(payload, options) {
63
66
  }
64
67
  case 'reset':
65
68
  {
66
- var _name = formatName(intent.payload.name, intent.payload.index);
69
+ var _name = appendPathSegment(intent.payload.name, intent.payload.index);
67
70
  if (_name) {
68
- setValue(context.payload, _name, () => undefined);
71
+ setValueAtPath(context.payload, _name, () => undefined);
69
72
  } else {
70
73
  context.payload = {};
71
74
  }
@@ -120,9 +123,9 @@ function replySubmission(context) {
120
123
  }
121
124
  if ('hideFields' in options && options.hideFields) {
122
125
  for (var name of options.hideFields) {
123
- var _value2 = getValue(context.payload, name);
126
+ var _value2 = getValueAtPath(context.payload, name);
124
127
  if (typeof _value2 !== 'undefined') {
125
- setValue(context.payload, name, () => undefined);
128
+ setValueAtPath(context.payload, name, () => undefined);
126
129
  }
127
130
  }
128
131
  }
@@ -194,7 +197,7 @@ function updateList(list, intent) {
194
197
  }
195
198
  }
196
199
  function setListValue(data, intent) {
197
- setValue(data, intent.payload.name, value => {
200
+ setValueAtPath(data, intent.payload.name, value => {
198
201
  var list = value !== null && value !== void 0 ? value : [];
199
202
  updateList(list, intent);
200
203
  return list;
@@ -212,7 +215,7 @@ function setState(state, name, valueFn) {
212
215
  var _loop2 = function _loop2() {
213
216
  var value = state[_key];
214
217
  if (isPrefix(_key, name) && _key !== name) {
215
- setValue(target, _key, currentValue => {
218
+ setValueAtPath(target, _key, currentValue => {
216
219
  if (typeof currentValue === 'undefined') {
217
220
  return value;
218
221
  }
@@ -232,7 +235,7 @@ function setState(state, name, valueFn) {
232
235
  for (var _key of keys) {
233
236
  _loop2();
234
237
  }
235
- var result = valueFn(getValue(target, name));
238
+ var result = valueFn(getValueAtPath(target, name));
236
239
  Object.assign(state, flatten(result, {
237
240
  resolve(data) {
238
241
  if (isPlainObject(data) || Array.isArray(data)) {
@@ -290,4 +293,65 @@ function serialize(defaultValue) {
290
293
  }
291
294
  }
292
295
 
293
- export { INTENT, STATE, createSubmission, getIntent, getSubmissionContext, parse, replySubmission, root, serialize, serializeIntent, setListState, setListValue, setState, updateList };
296
+ /**
297
+ * Normalize value by removing empty object or array, empty string and null values
298
+ */
299
+
300
+ function normalize(value) {
301
+ var acceptFile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
302
+ if (isPlainObject(value)) {
303
+ var obj = Object.keys(value).sort().reduce((result, key) => {
304
+ var data = normalize(value[key], acceptFile);
305
+ if (typeof data !== 'undefined') {
306
+ result[key] = data;
307
+ }
308
+ return result;
309
+ }, {});
310
+ if (Object.keys(obj).length === 0) {
311
+ return;
312
+ }
313
+ return obj;
314
+ }
315
+ if (Array.isArray(value)) {
316
+ if (value.length === 0) {
317
+ return undefined;
318
+ }
319
+ return value.map(item => normalize(item, acceptFile));
320
+ }
321
+ if (typeof value === 'string' && value === '' || value === null || isGlobalInstance(value, 'File') && (!acceptFile || value.size === 0)) {
322
+ return;
323
+ }
324
+ return value;
325
+ }
326
+
327
+ /**
328
+ * Flatten a tree into a dictionary
329
+ */
330
+ function flatten(data) {
331
+ var _options$resolve;
332
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
333
+ var result = {};
334
+ var resolve = (_options$resolve = options.resolve) !== null && _options$resolve !== void 0 ? _options$resolve : data => data;
335
+ function process(data, prefix) {
336
+ var value = normalize(resolve(data));
337
+ if (typeof value !== 'undefined') {
338
+ result[prefix] = value;
339
+ }
340
+ if (Array.isArray(data)) {
341
+ for (var i = 0; i < data.length; i++) {
342
+ process(data[i], "".concat(prefix, "[").concat(i, "]"));
343
+ }
344
+ } else if (isPlainObject(data)) {
345
+ for (var [_key2, _value3] of Object.entries(data)) {
346
+ process(_value3, prefix ? "".concat(prefix, ".").concat(_key2) : _key2);
347
+ }
348
+ }
349
+ }
350
+ if (data) {
351
+ var _options$prefix;
352
+ process(data, (_options$prefix = options.prefix) !== null && _options$prefix !== void 0 ? _options$prefix : '');
353
+ }
354
+ return result;
355
+ }
356
+
357
+ export { INTENT, STATE, createSubmission, flatten, getIntent, getSubmissionContext, normalize, parse, replySubmission, root, serialize, serializeIntent, setListState, setListValue, setState, updateList };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Valid JSON primitive types.
3
+ */
4
+ export type JsonPrimitive = string | number | boolean | null;
5
+ /**
6
+ * The form value of a submission. This is usually constructed from a FormData or URLSearchParams.
7
+ * It may contains JSON primitives if the value is updated based on a form intent.
8
+ */
9
+ export type FormValue<Type extends JsonPrimitive | FormDataEntryValue = JsonPrimitive | FormDataEntryValue> = Type | FormValue<Type | null>[] | {
10
+ [key: string]: FormValue<Type>;
11
+ };
12
+ /**
13
+ * Form error object that contains both form errors and field errors.
14
+ */
15
+ export type FormError<ErrorShape = string> = {
16
+ /**
17
+ * The error of the form.
18
+ */
19
+ formErrors: ErrorShape[];
20
+ /**
21
+ * The field errors based on the field name.
22
+ */
23
+ fieldErrors: Record<string, ErrorShape[]>;
24
+ };
25
+ /**
26
+ * Structured data parsed from a form submission.
27
+ */
28
+ export type Submission<ValueType extends JsonPrimitive | FormDataEntryValue = JsonPrimitive | FormDataEntryValue> = {
29
+ /**
30
+ * The submitted values mapped by field name.
31
+ * Supports nested names like `user.email` or indexed names like `items[0].id`.
32
+ */
33
+ payload: Record<string, FormValue<ValueType>>;
34
+ /**
35
+ * The list of field names present in the FormData or URLSearchParams.
36
+ */
37
+ fields: string[];
38
+ /**
39
+ * The submission intent, usually set by the name/value of the button that triggered the submission.
40
+ */
41
+ intent: string | null;
42
+ };
43
+ /**
44
+ * The result of a submission.
45
+ */
46
+ export type SubmissionResult<ErrorShape = string, ValueType extends JsonPrimitive | FormDataEntryValue = JsonPrimitive | FormDataEntryValue> = {
47
+ /**
48
+ * The original submission data.
49
+ */
50
+ submission: Submission<ValueType>;
51
+ /**
52
+ * The intended value of the submission. Defined only when the intended value is different from the submitted value.
53
+ */
54
+ intendedValue?: Record<string, FormValue<ValueType>> | null;
55
+ /**
56
+ * Validation errors for `intendedValue` when present, otherwise for the original payload.
57
+ */
58
+ error?: FormError<ErrorShape> | null;
59
+ };
60
+ /**
61
+ * The input attributes with related to the Constraint Validation API
62
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation
63
+ */
64
+ export type ValidationAttributes = {
65
+ required?: boolean;
66
+ minLength?: number;
67
+ maxLength?: number;
68
+ min?: string | number;
69
+ max?: string | number;
70
+ step?: string | number;
71
+ multiple?: boolean;
72
+ pattern?: string;
73
+ };
74
+ /**
75
+ * A type helper that makes sure the FormError type is serializable.
76
+ * Used only to strip `File` type from the Form Shape at the moment.
77
+ */
78
+ export type Serializable<T> = T extends File ? undefined : T extends Array<infer U> ? Serializable<U>[] : T extends object ? {
79
+ [K in keyof T]: Serializable<T[K]>;
80
+ } : T;
81
+ /**
82
+ * Converts an arbitrary value into a {@link SerializedValue}.
83
+ *
84
+ * This function is used to prepare field values for submission,
85
+ * ensuring they are compatible with the browser's `FormData` API.
86
+ *
87
+ * @param value - The original value to serialize.
88
+ * @returns A `SerializedValue` if the input can be represented in `FormData`,
89
+ * or `undefined` if it cannot be serialized.
90
+ */
91
+ export type Serialize = (value: unknown) => SerializedValue | undefined;
92
+ /**
93
+ * A value that can be serialized into `FormData`.
94
+ *
95
+ * - `string` and `File` are supported natively by `FormData`.
96
+ * - Arrays allow representing multi-value fields.
97
+ */
98
+ export type SerializedValue = string | string[] | File | File[];
99
+ //# sourceMappingURL=types.d.ts.map
package/dist/util.d.ts CHANGED
@@ -1,4 +1,15 @@
1
+ import type { FormValue } from './types';
1
2
  export declare function invariant(expectedCondition: boolean, message: string): asserts expectedCondition;
2
3
  export declare function generateId(): string;
3
4
  export declare function clone<Data>(data: Data): Data;
5
+ /**
6
+ * Check if the value is a plain object
7
+ */
8
+ export declare function isPlainObject(obj: unknown): obj is Record<string | number | symbol, unknown>;
9
+ /**
10
+ * A utility function that performs a deep equality check between two values.
11
+ * It handles plain objects, arrays, and primitive values only.
12
+ */
13
+ export declare function deepEqual(left: unknown, right: unknown): boolean;
14
+ export declare function stripFiles<Type extends string | number | boolean | File | null>(value: Record<string, FormValue<Type>>): Record<string, FormValue<Exclude<Type, File>>>;
4
15
  //# sourceMappingURL=util.d.ts.map
package/dist/util.js CHANGED
@@ -14,6 +14,73 @@ function clone(data) {
14
14
  return JSON.parse(JSON.stringify(data));
15
15
  }
16
16
 
17
+ /**
18
+ * Check if the value is a plain object
19
+ */
20
+ function isPlainObject(obj) {
21
+ return !!obj && obj.constructor === Object && Object.getPrototypeOf(obj) === Object.prototype;
22
+ }
23
+
24
+ /**
25
+ * A utility function that performs a deep equality check between two values.
26
+ * It handles plain objects, arrays, and primitive values only.
27
+ */
28
+ function deepEqual(left, right) {
29
+ if (Object.is(left, right)) {
30
+ return true;
31
+ }
32
+ if (left == null || right == null) {
33
+ return false;
34
+ }
35
+
36
+ // Compare plain objects
37
+ if (isPlainObject(left) && isPlainObject(right)) {
38
+ var prevKeys = Object.keys(left);
39
+ var nextKeys = Object.keys(right);
40
+ if (prevKeys.length !== nextKeys.length) {
41
+ return false;
42
+ }
43
+ for (var key of prevKeys) {
44
+ if (!Object.prototype.hasOwnProperty.call(right, key) || !deepEqual(left[key], right[key])) {
45
+ return false;
46
+ }
47
+ }
48
+ return true;
49
+ }
50
+
51
+ // Compare arrays
52
+ if (Array.isArray(left) && Array.isArray(right)) {
53
+ if (left.length !== right.length) {
54
+ return false;
55
+ }
56
+ for (var i = 0; i < left.length; i++) {
57
+ if (!deepEqual(left[i], right[i])) {
58
+ return false;
59
+ }
60
+ }
61
+ return true;
62
+ }
63
+ return false;
64
+ }
65
+
66
+ /*
67
+ * Removes File object from the FormValue.
68
+ * Used to avoid serialzing/sending File object back to the client.
69
+ */
70
+ function stripFiles(value) {
71
+ var json = JSON.stringify(value, (_, value) => {
72
+ // If the current value is a File, return undefined to omit it
73
+ if (typeof File !== 'undefined' && value instanceof File) {
74
+ return undefined;
75
+ }
76
+ return value;
77
+ });
78
+ return JSON.parse(json);
79
+ }
80
+
17
81
  exports.clone = clone;
82
+ exports.deepEqual = deepEqual;
18
83
  exports.generateId = generateId;
19
84
  exports.invariant = invariant;
85
+ exports.isPlainObject = isPlainObject;
86
+ exports.stripFiles = stripFiles;