@conform-to/dom 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/dom.js +2 -2
- package/dist/dom.mjs +2 -2
- package/dist/formdata.d.ts +66 -44
- package/dist/formdata.js +96 -53
- package/dist/formdata.mjs +94 -53
- package/dist/future/index.d.ts +2 -2
- package/dist/future/index.js +2 -1
- package/dist/future/index.mjs +1 -1
- package/dist/standard-schema.d.ts +2 -14
- package/dist/standard-schema.js +3 -8
- package/dist/standard-schema.mjs +3 -8
- package/dist/types.d.ts +40 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Version 1.
|
|
10
|
+
Version 1.19.0 / License MIT / Copyright (c) 2026 Edmund Hung
|
|
11
11
|
|
|
12
12
|
Progressively enhance HTML forms with React. Build resilient, type-safe forms with no hassle using web standards.
|
|
13
13
|
|
package/dist/dom.js
CHANGED
|
@@ -570,8 +570,8 @@ function updateField(element, options) {
|
|
|
570
570
|
/**
|
|
571
571
|
* Triggering react custom change event
|
|
572
572
|
* Solution based on dom-testing-library
|
|
573
|
-
*
|
|
574
|
-
*
|
|
573
|
+
* See https://github.com/facebook/react/issues/10135#issuecomment-401496776
|
|
574
|
+
* See https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
|
|
575
575
|
*/
|
|
576
576
|
var {
|
|
577
577
|
set: valueSetter
|
package/dist/dom.mjs
CHANGED
|
@@ -566,8 +566,8 @@ function updateField(element, options) {
|
|
|
566
566
|
/**
|
|
567
567
|
* Triggering react custom change event
|
|
568
568
|
* Solution based on dom-testing-library
|
|
569
|
-
*
|
|
570
|
-
*
|
|
569
|
+
* See https://github.com/facebook/react/issues/10135#issuecomment-401496776
|
|
570
|
+
* See https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
|
|
571
571
|
*/
|
|
572
572
|
var {
|
|
573
573
|
set: valueSetter
|
package/dist/formdata.d.ts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
|
-
import type { FormValue, FieldName, JsonPrimitive,
|
|
2
|
-
import type { StandardSchemaIssue } from './standard-schema';
|
|
1
|
+
import type { FormError, FormValue, FieldName, JsonPrimitive, CustomSerialize, Submission, SubmissionResult, UnknownObject, Serialize, StandardSchemaError, CustomError } from './types';
|
|
3
2
|
export declare const DEFAULT_INTENT_NAME = "__INTENT__";
|
|
3
|
+
/**
|
|
4
|
+
* Returns whether an error payload contains a meaningful value.
|
|
5
|
+
* Empty strings and empty arrays are treated as no error.
|
|
6
|
+
*/
|
|
7
|
+
export declare function hasError<ErrorShape>(error: ErrorShape | null | undefined): error is ErrorShape;
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes a form error object by removing empty error payloads such as
|
|
10
|
+
* empty strings and empty arrays.
|
|
11
|
+
*
|
|
12
|
+
* Returns `null` when no form-level or field-level errors remain.
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeFormError<ErrorShape>(error: CustomError<ErrorShape> | null): FormError<ErrorShape> | null;
|
|
4
15
|
/**
|
|
5
16
|
* Construct a form data with the submitter value.
|
|
6
17
|
* It utilizes the submitter argument on the FormData constructor from modern browsers
|
|
7
18
|
* with fallback to append the submitter value in case it is not unsupported.
|
|
8
19
|
*
|
|
9
|
-
*
|
|
20
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
|
|
10
21
|
*/
|
|
11
22
|
export declare function getFormData(form: HTMLFormElement, submitter?: HTMLElement | null): FormData;
|
|
12
23
|
/**
|
|
13
24
|
* Convert a string path into an array of segments.
|
|
14
25
|
*
|
|
15
|
-
*
|
|
26
|
+
* **Example:**
|
|
16
27
|
* ```js
|
|
17
28
|
* parsePath("object.key"); // → ['object', 'key']
|
|
18
29
|
* parsePath("array[0].content"); // → ['array', 0, 'content']
|
|
@@ -23,7 +34,7 @@ export declare function parsePath(path: string | undefined): Array<string | numb
|
|
|
23
34
|
/**
|
|
24
35
|
* Returns a formatted name from the path segments based on the dot and bracket notation.
|
|
25
36
|
*
|
|
26
|
-
*
|
|
37
|
+
* **Example:**
|
|
27
38
|
* ```js
|
|
28
39
|
* formatPath(['object', 'key']); // → "object.key"
|
|
29
40
|
* formatPath(['array', 0, 'content']); // → "array[0].content"
|
|
@@ -43,7 +54,7 @@ export declare function appendPath(path: string | undefined, segment: string | n
|
|
|
43
54
|
/**
|
|
44
55
|
* Returns true if `prefix` is a valid leading path of `name`.
|
|
45
56
|
*
|
|
46
|
-
*
|
|
57
|
+
* **Example:**
|
|
47
58
|
* ```js
|
|
48
59
|
* isPathPrefix("foo.bar.baz", "foo.bar") // → true
|
|
49
60
|
* isPathPrefix("foo.bar[3].baz", "foo.bar[3]") // → true
|
|
@@ -60,7 +71,7 @@ export declare function isPathPrefix(name: string, prefix: string): boolean;
|
|
|
60
71
|
* @param basePath Base path as a dot/bracket string or array of segments
|
|
61
72
|
* @returns The “tail” segments, or `null` if `fullPath` isn’t nested under `basePath`
|
|
62
73
|
*
|
|
63
|
-
*
|
|
74
|
+
* **Example:**
|
|
64
75
|
* ```js
|
|
65
76
|
* getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"]
|
|
66
77
|
* getRelativePath("a.b.c.d", ["a","b"]) // → ["c","d"]
|
|
@@ -84,8 +95,9 @@ export declare function getPathValue(target: unknown, pathOrSegments: string | A
|
|
|
84
95
|
* This function structures the form values based on the naming convention.
|
|
85
96
|
* It also includes all the field names and extracts the intent from the submission.
|
|
86
97
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
98
|
+
* See https://conform.guide/api/react/future/parseSubmission
|
|
99
|
+
*
|
|
100
|
+
* **Example:**
|
|
89
101
|
* ```ts
|
|
90
102
|
* const formData = new FormData();
|
|
91
103
|
*
|
|
@@ -136,8 +148,9 @@ export declare function parseSubmission(formData: FormData | URLSearchParams, op
|
|
|
136
148
|
* file inputs cannot be initialized with files.
|
|
137
149
|
* You can specify `keepFiles: true` to keep the files if needed.
|
|
138
150
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
151
|
+
* See https://conform.guide/api/react/future/report
|
|
152
|
+
*
|
|
153
|
+
* **Example:**
|
|
141
154
|
* ```ts
|
|
142
155
|
* // Report the submission with the field errors
|
|
143
156
|
* report(submission, {
|
|
@@ -161,55 +174,61 @@ export declare function parseSubmission(formData: FormData | URLSearchParams, op
|
|
|
161
174
|
* })
|
|
162
175
|
* ```
|
|
163
176
|
*/
|
|
164
|
-
export declare function report<ErrorShape
|
|
177
|
+
export declare function report<ErrorShape>(submission: Submission, options: {
|
|
165
178
|
keepFiles?: false;
|
|
166
|
-
error
|
|
167
|
-
issues?: undefined;
|
|
168
|
-
formErrors?: ErrorShape[];
|
|
169
|
-
fieldErrors?: Record<string, ErrorShape[]>;
|
|
170
|
-
} | null;
|
|
179
|
+
error: CustomError<ErrorShape>;
|
|
171
180
|
value?: Record<string, FormValue> | null;
|
|
172
181
|
hideFields?: string[];
|
|
173
182
|
reset?: boolean;
|
|
174
183
|
}): SubmissionResult<ErrorShape, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
|
|
175
|
-
export declare function report<ErrorShape
|
|
184
|
+
export declare function report<ErrorShape>(submission: Submission, options: {
|
|
176
185
|
keepFiles: true;
|
|
177
|
-
error
|
|
178
|
-
issues?: undefined;
|
|
179
|
-
formErrors?: ErrorShape[];
|
|
180
|
-
fieldErrors?: Record<string, ErrorShape[]>;
|
|
181
|
-
} | null;
|
|
186
|
+
error: CustomError<ErrorShape>;
|
|
182
187
|
value?: Record<string, FormValue> | null;
|
|
183
188
|
hideFields?: string[];
|
|
184
189
|
reset?: boolean;
|
|
185
190
|
}): SubmissionResult<ErrorShape>;
|
|
186
|
-
export declare function report(submission: Submission, options
|
|
191
|
+
export declare function report(submission: Submission, options: {
|
|
187
192
|
keepFiles?: false;
|
|
188
|
-
error
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
+
error: StandardSchemaError;
|
|
194
|
+
value?: Record<string, FormValue> | null;
|
|
195
|
+
hideFields?: string[];
|
|
196
|
+
reset?: boolean;
|
|
197
|
+
}): SubmissionResult<string[], Exclude<JsonPrimitive | FormDataEntryValue, File>>;
|
|
198
|
+
export declare function report(submission: Submission, options: {
|
|
199
|
+
keepFiles: true;
|
|
200
|
+
error: StandardSchemaError;
|
|
193
201
|
value?: Record<string, FormValue> | null;
|
|
194
202
|
hideFields?: string[];
|
|
195
203
|
reset?: boolean;
|
|
196
|
-
}): SubmissionResult<string
|
|
204
|
+
}): SubmissionResult<string[]>;
|
|
197
205
|
export declare function report(submission: Submission, options?: {
|
|
206
|
+
keepFiles?: false;
|
|
207
|
+
error?: null;
|
|
208
|
+
value?: Record<string, FormValue> | null;
|
|
209
|
+
hideFields?: string[];
|
|
210
|
+
reset?: boolean;
|
|
211
|
+
}): SubmissionResult<never, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
|
|
212
|
+
export declare function report(submission: Submission, options: {
|
|
198
213
|
keepFiles: true;
|
|
199
|
-
error?:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
214
|
+
error?: null;
|
|
215
|
+
value?: Record<string, FormValue> | null;
|
|
216
|
+
hideFields?: string[];
|
|
217
|
+
reset?: boolean;
|
|
218
|
+
}): SubmissionResult<never>;
|
|
219
|
+
export declare function report<ErrorShape>(submission: Submission, options?: {
|
|
220
|
+
keepFiles?: boolean;
|
|
221
|
+
error?: CustomError<ErrorShape> | null;
|
|
204
222
|
value?: Record<string, FormValue> | null;
|
|
205
223
|
hideFields?: string[];
|
|
206
224
|
reset?: boolean;
|
|
207
|
-
}): SubmissionResult<
|
|
225
|
+
}): SubmissionResult<ErrorShape>;
|
|
208
226
|
/**
|
|
209
227
|
* A utility function that checks whether the current form data differs from the default values.
|
|
210
228
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
229
|
+
* See https://conform.guide/api/react/future/isDirty
|
|
230
|
+
*
|
|
231
|
+
* **Example: Enable a submit button only if the form is dirty**
|
|
213
232
|
*
|
|
214
233
|
* ```tsx
|
|
215
234
|
* const dirty = useFormData(
|
|
@@ -251,19 +270,19 @@ formData: FormData | URLSearchParams | FormValue | null, options?: {
|
|
|
251
270
|
* - Returned as-is
|
|
252
271
|
* - boolean:
|
|
253
272
|
* - true → 'on'
|
|
254
|
-
* - false →
|
|
273
|
+
* - false → null
|
|
255
274
|
* - number / bigint:
|
|
256
275
|
* - Converted to string using `.toString()`
|
|
257
276
|
* - Date:
|
|
258
277
|
* - Converted to UTC datetime string without trailing `Z` (e.g. `2026-01-01T12:00:00.000`)
|
|
259
278
|
*/
|
|
260
|
-
serialize?:
|
|
279
|
+
serialize?: CustomSerialize;
|
|
261
280
|
/**
|
|
262
281
|
* A function to exclude specific fields from the comparison.
|
|
263
282
|
* Useful for ignoring hidden inputs like CSRF tokens or internal fields added by frameworks
|
|
264
283
|
* (e.g. Next.js uses hidden inputs to support server actions).
|
|
265
284
|
*
|
|
266
|
-
*
|
|
285
|
+
* **Example:**
|
|
267
286
|
* ```ts
|
|
268
287
|
* isDirty(formData, {
|
|
269
288
|
* skipEntry: (name) => name === 'csrf-token',
|
|
@@ -287,7 +306,7 @@ formData: FormData | URLSearchParams | FormValue | null, options?: {
|
|
|
287
306
|
* - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined
|
|
288
307
|
* - anything else -> undefined
|
|
289
308
|
*/
|
|
290
|
-
export declare function
|
|
309
|
+
export declare function defaultSerialize(value: unknown): ReturnType<Serialize>;
|
|
291
310
|
/**
|
|
292
311
|
* Recursively serializes a value using the provided serialize function,
|
|
293
312
|
* collapsing empty leaves (`null`, `''`, empty files) to `undefined`
|
|
@@ -299,11 +318,13 @@ export declare function serialize(value: unknown): SerializedValue | null | unde
|
|
|
299
318
|
* Single-element arrays where the element is a string or undefined are unwrapped
|
|
300
319
|
* to handle the case where a multi-value field (e.g. checkboxes) has only one value.
|
|
301
320
|
*/
|
|
302
|
-
export declare function normalize(value: unknown,
|
|
321
|
+
export declare function normalize(value: unknown, serialize?: Serialize, name?: string): unknown;
|
|
303
322
|
/**
|
|
304
323
|
* Retrieve a field value from FormData with optional type guards.
|
|
305
324
|
*
|
|
306
|
-
*
|
|
325
|
+
* **Example:**
|
|
326
|
+
*
|
|
327
|
+
* ```ts
|
|
307
328
|
* // Basic field access: return `unknown`
|
|
308
329
|
* const email = getFieldValue(formData, 'email');
|
|
309
330
|
* // String type: returns `string`
|
|
@@ -318,6 +339,7 @@ export declare function normalize(value: unknown, serializeValue?: Serialize): u
|
|
|
318
339
|
* const items = getFieldValue<Item[]>(formData, 'items', { type: 'object', array: true });
|
|
319
340
|
* // Optional string type: returns `string | undefined`
|
|
320
341
|
* const bio = getFieldValue(formData, 'bio', { type: 'string', optional: true });
|
|
342
|
+
* ```
|
|
321
343
|
*/
|
|
322
344
|
export declare function getFieldValue<FieldShape extends Array<Record<string, unknown>>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
|
|
323
345
|
type: 'object';
|
package/dist/formdata.js
CHANGED
|
@@ -9,12 +9,48 @@ var standardSchema = require('./standard-schema.js');
|
|
|
9
9
|
|
|
10
10
|
var DEFAULT_INTENT_NAME = '__INTENT__';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Returns whether an error payload contains a meaningful value.
|
|
14
|
+
* Empty strings and empty arrays are treated as no error.
|
|
15
|
+
*/
|
|
16
|
+
function hasError(error) {
|
|
17
|
+
return error != null && error !== '' && (!Array.isArray(error) || error.length > 0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Normalizes a form error object by removing empty error payloads such as
|
|
22
|
+
* empty strings and empty arrays.
|
|
23
|
+
*
|
|
24
|
+
* Returns `null` when no form-level or field-level errors remain.
|
|
25
|
+
*/
|
|
26
|
+
function normalizeFormError(error) {
|
|
27
|
+
var _error$fieldErrors;
|
|
28
|
+
if (error === null) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
var formErrors = hasError(error.formErrors) ? error.formErrors : null;
|
|
32
|
+
var fieldErrors = Object.entries((_error$fieldErrors = error.fieldErrors) !== null && _error$fieldErrors !== void 0 ? _error$fieldErrors : {}).reduce((result, _ref) => {
|
|
33
|
+
var [name, value] = _ref;
|
|
34
|
+
if (hasError(value)) {
|
|
35
|
+
result[name] = value;
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}, {});
|
|
39
|
+
if (formErrors === null && Object.keys(fieldErrors).length === 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
formErrors,
|
|
44
|
+
fieldErrors
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
12
48
|
/**
|
|
13
49
|
* Construct a form data with the submitter value.
|
|
14
50
|
* It utilizes the submitter argument on the FormData constructor from modern browsers
|
|
15
51
|
* with fallback to append the submitter value in case it is not unsupported.
|
|
16
52
|
*
|
|
17
|
-
*
|
|
53
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
|
|
18
54
|
*/
|
|
19
55
|
function getFormData(form, submitter) {
|
|
20
56
|
var payload = new FormData(form, submitter);
|
|
@@ -37,7 +73,7 @@ function getFormData(form, submitter) {
|
|
|
37
73
|
/**
|
|
38
74
|
* Convert a string path into an array of segments.
|
|
39
75
|
*
|
|
40
|
-
*
|
|
76
|
+
* **Example:**
|
|
41
77
|
* ```js
|
|
42
78
|
* parsePath("object.key"); // → ['object', 'key']
|
|
43
79
|
* parsePath("array[0].content"); // → ['array', 0, 'content']
|
|
@@ -83,7 +119,7 @@ function parsePath(path) {
|
|
|
83
119
|
/**
|
|
84
120
|
* Returns a formatted name from the path segments based on the dot and bracket notation.
|
|
85
121
|
*
|
|
86
|
-
*
|
|
122
|
+
* **Example:**
|
|
87
123
|
* ```js
|
|
88
124
|
* formatPath(['object', 'key']); // → "object.key"
|
|
89
125
|
* formatPath(['array', 0, 'content']); // → "array[0].content"
|
|
@@ -103,30 +139,32 @@ function formatPath(segments) {
|
|
|
103
139
|
* - segment = `string` ⇒ dot-notation ".prop"
|
|
104
140
|
*/
|
|
105
141
|
function appendPath(path, segment) {
|
|
142
|
+
var base = path !== null && path !== void 0 ? path : '';
|
|
143
|
+
|
|
106
144
|
// 1) nothing to append
|
|
107
145
|
if (typeof segment === 'undefined') {
|
|
108
|
-
return
|
|
146
|
+
return base;
|
|
109
147
|
}
|
|
110
148
|
|
|
111
149
|
// 2) explicit empty-segment => empty bracket
|
|
112
150
|
if (segment === '') {
|
|
113
151
|
// even as first segment, "[]" is valid
|
|
114
|
-
return "".concat(
|
|
152
|
+
return "".concat(base, "[]");
|
|
115
153
|
}
|
|
116
154
|
|
|
117
155
|
// 3) numeric index => [n]
|
|
118
156
|
if (typeof segment === 'number') {
|
|
119
|
-
return "".concat(
|
|
157
|
+
return "".concat(base, "[").concat(segment, "]");
|
|
120
158
|
}
|
|
121
159
|
|
|
122
160
|
// 4) non-empty string => .prop (no leading dot if no base)
|
|
123
|
-
return
|
|
161
|
+
return base ? "".concat(base, ".").concat(segment) : segment;
|
|
124
162
|
}
|
|
125
163
|
|
|
126
164
|
/**
|
|
127
165
|
* Returns true if `prefix` is a valid leading path of `name`.
|
|
128
166
|
*
|
|
129
|
-
*
|
|
167
|
+
* **Example:**
|
|
130
168
|
* ```js
|
|
131
169
|
* isPathPrefix("foo.bar.baz", "foo.bar") // → true
|
|
132
170
|
* isPathPrefix("foo.bar[3].baz", "foo.bar[3]") // → true
|
|
@@ -146,7 +184,7 @@ function isPathPrefix(name, prefix) {
|
|
|
146
184
|
* @param basePath Base path as a dot/bracket string or array of segments
|
|
147
185
|
* @returns The “tail” segments, or `null` if `fullPath` isn’t nested under `basePath`
|
|
148
186
|
*
|
|
149
|
-
*
|
|
187
|
+
* **Example:**
|
|
150
188
|
* ```js
|
|
151
189
|
* getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"]
|
|
152
190
|
* getRelativePath("a.b.c.d", ["a","b"]) // → ["c","d"]
|
|
@@ -265,8 +303,9 @@ function isEmptyValue(value) {
|
|
|
265
303
|
* This function structures the form values based on the naming convention.
|
|
266
304
|
* It also includes all the field names and extracts the intent from the submission.
|
|
267
305
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
306
|
+
* See https://conform.guide/api/react/future/parseSubmission
|
|
307
|
+
*
|
|
308
|
+
* **Example:**
|
|
270
309
|
* ```ts
|
|
271
310
|
* const formData = new FormData();
|
|
272
311
|
*
|
|
@@ -302,7 +341,7 @@ function parseSubmission(formData, options) {
|
|
|
302
341
|
var _options$skipEntry;
|
|
303
342
|
if (_name !== intentName && !(options !== null && options !== void 0 && (_options$skipEntry = options.skipEntry) !== null && _options$skipEntry !== void 0 && _options$skipEntry.call(options, _name))) {
|
|
304
343
|
var _options$stripEmptyVa;
|
|
305
|
-
var
|
|
344
|
+
var value = formData.getAll(_name);
|
|
306
345
|
var segments = parsePath(_name);
|
|
307
346
|
|
|
308
347
|
// If the name ends with [], remove the empty segment and keep the full array
|
|
@@ -310,19 +349,19 @@ function parseSubmission(formData, options) {
|
|
|
310
349
|
if (segments.length > 0 && segments[segments.length - 1] === '') {
|
|
311
350
|
segments.pop();
|
|
312
351
|
} else {
|
|
313
|
-
|
|
352
|
+
value = value.length > 1 ? value : value[0];
|
|
314
353
|
}
|
|
315
354
|
var stripEmptyValues = (_options$stripEmptyVa = options === null || options === void 0 ? void 0 : options.stripEmptyValues) !== null && _options$stripEmptyVa !== void 0 ? _options$stripEmptyVa : false;
|
|
316
355
|
if (stripEmptyValues) {
|
|
317
356
|
// For arrays, filter out individual empty items
|
|
318
|
-
if (Array.isArray(
|
|
319
|
-
|
|
357
|
+
if (Array.isArray(value)) {
|
|
358
|
+
value = value.filter(item => !isEmptyValue(item));
|
|
320
359
|
}
|
|
321
|
-
if (isEmptyValue(
|
|
322
|
-
|
|
360
|
+
if (isEmptyValue(value)) {
|
|
361
|
+
value = undefined;
|
|
323
362
|
}
|
|
324
363
|
}
|
|
325
|
-
setPathValue(submission.payload, segments,
|
|
364
|
+
setPathValue(submission.payload, segments, value, {
|
|
326
365
|
silent: true // Avoid errors if the path is invalid
|
|
327
366
|
});
|
|
328
367
|
submission.fields.push(_name);
|
|
@@ -344,8 +383,9 @@ function parseSubmission(formData, options) {
|
|
|
344
383
|
* file inputs cannot be initialized with files.
|
|
345
384
|
* You can specify `keepFiles: true` to keep the files if needed.
|
|
346
385
|
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
386
|
+
* See https://conform.guide/api/react/future/report
|
|
387
|
+
*
|
|
388
|
+
* **Example:**
|
|
349
389
|
* ```ts
|
|
350
390
|
* // Report the submission with the field errors
|
|
351
391
|
* report(submission, {
|
|
@@ -376,29 +416,16 @@ function report(submission) {
|
|
|
376
416
|
var error;
|
|
377
417
|
if (options.error == null) {
|
|
378
418
|
error = options.error;
|
|
379
|
-
} else {
|
|
419
|
+
} else if ('issues' in options.error) {
|
|
380
420
|
var _options$error$issues;
|
|
381
421
|
error = standardSchema.formatIssues((_options$error$issues = options.error.issues) !== null && _options$error$issues !== void 0 ? _options$error$issues : []);
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
if (options.error.fieldErrors) {
|
|
386
|
-
for (var [_name2, messages] of Object.entries(options.error.fieldErrors)) {
|
|
387
|
-
if (messages.length === 0) {
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
if (!error.fieldErrors[_name2]) {
|
|
391
|
-
error.fieldErrors[_name2] = messages;
|
|
392
|
-
} else {
|
|
393
|
-
error.fieldErrors[_name2].push(...messages);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
422
|
+
} else {
|
|
423
|
+
error = normalizeFormError(options.error);
|
|
397
424
|
}
|
|
398
425
|
var targetValue = typeof options.value === 'undefined' || submission.payload === options.value && !options.reset ? undefined : options.value && !options.keepFiles ? util.stripFiles(options.value) : (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : {};
|
|
399
426
|
if (options.hideFields) {
|
|
400
|
-
for (var
|
|
401
|
-
var path = parsePath(
|
|
427
|
+
for (var _name2 of options.hideFields) {
|
|
428
|
+
var path = parsePath(_name2);
|
|
402
429
|
setPathValue(submission.payload, path, undefined);
|
|
403
430
|
if (targetValue) {
|
|
404
431
|
setPathValue(targetValue, path, undefined);
|
|
@@ -418,8 +445,9 @@ function report(submission) {
|
|
|
418
445
|
/**
|
|
419
446
|
* A utility function that checks whether the current form data differs from the default values.
|
|
420
447
|
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
448
|
+
* See https://conform.guide/api/react/future/isDirty
|
|
449
|
+
*
|
|
450
|
+
* **Example: Enable a submit button only if the form is dirty**
|
|
423
451
|
*
|
|
424
452
|
* ```tsx
|
|
425
453
|
* const dirty = useFormData(
|
|
@@ -450,9 +478,16 @@ formData, options) {
|
|
|
450
478
|
intentName: options === null || options === void 0 ? void 0 : options.intentName,
|
|
451
479
|
skipEntry: options === null || options === void 0 ? void 0 : options.skipEntry
|
|
452
480
|
}).payload : formData;
|
|
453
|
-
var
|
|
454
|
-
|
|
455
|
-
|
|
481
|
+
var serialize = (value, context) => {
|
|
482
|
+
if (options !== null && options !== void 0 && options.serialize) {
|
|
483
|
+
return options.serialize(value, {
|
|
484
|
+
name: context.name,
|
|
485
|
+
defaultSerialize
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
return defaultSerialize(value);
|
|
489
|
+
};
|
|
490
|
+
return !util.deepEqual(normalize(formValue, serialize), normalize(options === null || options === void 0 ? void 0 : options.defaultValue, serialize));
|
|
456
491
|
}
|
|
457
492
|
|
|
458
493
|
/**
|
|
@@ -470,13 +505,13 @@ formData, options) {
|
|
|
470
505
|
* - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined
|
|
471
506
|
* - anything else -> undefined
|
|
472
507
|
*/
|
|
473
|
-
function
|
|
508
|
+
function defaultSerialize(value) {
|
|
474
509
|
function serializePrimitive(value) {
|
|
475
510
|
if (typeof value === 'string' || value === null) {
|
|
476
511
|
return value;
|
|
477
512
|
}
|
|
478
513
|
if (typeof value === 'boolean') {
|
|
479
|
-
return value ? 'on' :
|
|
514
|
+
return value ? 'on' : null;
|
|
480
515
|
}
|
|
481
516
|
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
482
517
|
return value.toString();
|
|
@@ -537,8 +572,11 @@ function serialize(value) {
|
|
|
537
572
|
* to handle the case where a multi-value field (e.g. checkboxes) has only one value.
|
|
538
573
|
*/
|
|
539
574
|
function normalize(value) {
|
|
540
|
-
var
|
|
541
|
-
var
|
|
575
|
+
var serialize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultSerialize;
|
|
576
|
+
var name = arguments.length > 2 ? arguments[2] : undefined;
|
|
577
|
+
var data = serialize(value, {
|
|
578
|
+
name
|
|
579
|
+
});
|
|
542
580
|
if (typeof data === 'undefined') {
|
|
543
581
|
data = value;
|
|
544
582
|
}
|
|
@@ -555,16 +593,16 @@ function normalize(value) {
|
|
|
555
593
|
if (data.length === 0) {
|
|
556
594
|
return undefined;
|
|
557
595
|
}
|
|
558
|
-
var array = data.map(item => normalize(item,
|
|
596
|
+
var array = data.map((item, index) => normalize(item, serialize, appendPath(name, index)));
|
|
559
597
|
if (array.length === 1 && (typeof array[0] === 'string' || array[0] === undefined)) {
|
|
560
598
|
return array[0];
|
|
561
599
|
}
|
|
562
600
|
return array;
|
|
563
601
|
}
|
|
564
602
|
if (util.isPlainObject(data)) {
|
|
565
|
-
var entries = Object.entries(data).reduce((list,
|
|
566
|
-
var [key, value] =
|
|
567
|
-
var normalizedValue = normalize(value,
|
|
603
|
+
var entries = Object.entries(data).reduce((list, _ref2) => {
|
|
604
|
+
var [key, value] = _ref2;
|
|
605
|
+
var normalizedValue = normalize(value, serialize, appendPath(name, key));
|
|
568
606
|
if (typeof normalizedValue !== 'undefined') {
|
|
569
607
|
list.push([key, normalizedValue]);
|
|
570
608
|
}
|
|
@@ -581,7 +619,9 @@ function normalize(value) {
|
|
|
581
619
|
/**
|
|
582
620
|
* Retrieve a field value from FormData with optional type guards.
|
|
583
621
|
*
|
|
584
|
-
*
|
|
622
|
+
* **Example:**
|
|
623
|
+
*
|
|
624
|
+
* ```ts
|
|
585
625
|
* // Basic field access: return `unknown`
|
|
586
626
|
* const email = getFieldValue(formData, 'email');
|
|
587
627
|
* // String type: returns `string`
|
|
@@ -596,6 +636,7 @@ function normalize(value) {
|
|
|
596
636
|
* const items = getFieldValue<Item[]>(formData, 'items', { type: 'object', array: true });
|
|
597
637
|
* // Optional string type: returns `string | undefined`
|
|
598
638
|
* const bio = getFieldValue(formData, 'bio', { type: 'string', optional: true });
|
|
639
|
+
* ```
|
|
599
640
|
*/
|
|
600
641
|
|
|
601
642
|
function getFieldValue(formData, name, options) {
|
|
@@ -649,16 +690,18 @@ function getFieldValue(formData, name, options) {
|
|
|
649
690
|
|
|
650
691
|
exports.DEFAULT_INTENT_NAME = DEFAULT_INTENT_NAME;
|
|
651
692
|
exports.appendPath = appendPath;
|
|
693
|
+
exports.defaultSerialize = defaultSerialize;
|
|
652
694
|
exports.formatPath = formatPath;
|
|
653
695
|
exports.getFieldValue = getFieldValue;
|
|
654
696
|
exports.getFormData = getFormData;
|
|
655
697
|
exports.getPathValue = getPathValue;
|
|
656
698
|
exports.getRelativePath = getRelativePath;
|
|
699
|
+
exports.hasError = hasError;
|
|
657
700
|
exports.isDirty = isDirty;
|
|
658
701
|
exports.isPathPrefix = isPathPrefix;
|
|
659
702
|
exports.normalize = normalize;
|
|
703
|
+
exports.normalizeFormError = normalizeFormError;
|
|
660
704
|
exports.parsePath = parsePath;
|
|
661
705
|
exports.parseSubmission = parseSubmission;
|
|
662
706
|
exports.report = report;
|
|
663
|
-
exports.serialize = serialize;
|
|
664
707
|
exports.setPathValue = setPathValue;
|
package/dist/formdata.mjs
CHANGED
|
@@ -5,12 +5,48 @@ import { formatIssues } from './standard-schema.mjs';
|
|
|
5
5
|
|
|
6
6
|
var DEFAULT_INTENT_NAME = '__INTENT__';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Returns whether an error payload contains a meaningful value.
|
|
10
|
+
* Empty strings and empty arrays are treated as no error.
|
|
11
|
+
*/
|
|
12
|
+
function hasError(error) {
|
|
13
|
+
return error != null && error !== '' && (!Array.isArray(error) || error.length > 0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Normalizes a form error object by removing empty error payloads such as
|
|
18
|
+
* empty strings and empty arrays.
|
|
19
|
+
*
|
|
20
|
+
* Returns `null` when no form-level or field-level errors remain.
|
|
21
|
+
*/
|
|
22
|
+
function normalizeFormError(error) {
|
|
23
|
+
var _error$fieldErrors;
|
|
24
|
+
if (error === null) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
var formErrors = hasError(error.formErrors) ? error.formErrors : null;
|
|
28
|
+
var fieldErrors = Object.entries((_error$fieldErrors = error.fieldErrors) !== null && _error$fieldErrors !== void 0 ? _error$fieldErrors : {}).reduce((result, _ref) => {
|
|
29
|
+
var [name, value] = _ref;
|
|
30
|
+
if (hasError(value)) {
|
|
31
|
+
result[name] = value;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}, {});
|
|
35
|
+
if (formErrors === null && Object.keys(fieldErrors).length === 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
formErrors,
|
|
40
|
+
fieldErrors
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
8
44
|
/**
|
|
9
45
|
* Construct a form data with the submitter value.
|
|
10
46
|
* It utilizes the submitter argument on the FormData constructor from modern browsers
|
|
11
47
|
* with fallback to append the submitter value in case it is not unsupported.
|
|
12
48
|
*
|
|
13
|
-
*
|
|
49
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
|
|
14
50
|
*/
|
|
15
51
|
function getFormData(form, submitter) {
|
|
16
52
|
var payload = new FormData(form, submitter);
|
|
@@ -33,7 +69,7 @@ function getFormData(form, submitter) {
|
|
|
33
69
|
/**
|
|
34
70
|
* Convert a string path into an array of segments.
|
|
35
71
|
*
|
|
36
|
-
*
|
|
72
|
+
* **Example:**
|
|
37
73
|
* ```js
|
|
38
74
|
* parsePath("object.key"); // → ['object', 'key']
|
|
39
75
|
* parsePath("array[0].content"); // → ['array', 0, 'content']
|
|
@@ -79,7 +115,7 @@ function parsePath(path) {
|
|
|
79
115
|
/**
|
|
80
116
|
* Returns a formatted name from the path segments based on the dot and bracket notation.
|
|
81
117
|
*
|
|
82
|
-
*
|
|
118
|
+
* **Example:**
|
|
83
119
|
* ```js
|
|
84
120
|
* formatPath(['object', 'key']); // → "object.key"
|
|
85
121
|
* formatPath(['array', 0, 'content']); // → "array[0].content"
|
|
@@ -99,30 +135,32 @@ function formatPath(segments) {
|
|
|
99
135
|
* - segment = `string` ⇒ dot-notation ".prop"
|
|
100
136
|
*/
|
|
101
137
|
function appendPath(path, segment) {
|
|
138
|
+
var base = path !== null && path !== void 0 ? path : '';
|
|
139
|
+
|
|
102
140
|
// 1) nothing to append
|
|
103
141
|
if (typeof segment === 'undefined') {
|
|
104
|
-
return
|
|
142
|
+
return base;
|
|
105
143
|
}
|
|
106
144
|
|
|
107
145
|
// 2) explicit empty-segment => empty bracket
|
|
108
146
|
if (segment === '') {
|
|
109
147
|
// even as first segment, "[]" is valid
|
|
110
|
-
return "".concat(
|
|
148
|
+
return "".concat(base, "[]");
|
|
111
149
|
}
|
|
112
150
|
|
|
113
151
|
// 3) numeric index => [n]
|
|
114
152
|
if (typeof segment === 'number') {
|
|
115
|
-
return "".concat(
|
|
153
|
+
return "".concat(base, "[").concat(segment, "]");
|
|
116
154
|
}
|
|
117
155
|
|
|
118
156
|
// 4) non-empty string => .prop (no leading dot if no base)
|
|
119
|
-
return
|
|
157
|
+
return base ? "".concat(base, ".").concat(segment) : segment;
|
|
120
158
|
}
|
|
121
159
|
|
|
122
160
|
/**
|
|
123
161
|
* Returns true if `prefix` is a valid leading path of `name`.
|
|
124
162
|
*
|
|
125
|
-
*
|
|
163
|
+
* **Example:**
|
|
126
164
|
* ```js
|
|
127
165
|
* isPathPrefix("foo.bar.baz", "foo.bar") // → true
|
|
128
166
|
* isPathPrefix("foo.bar[3].baz", "foo.bar[3]") // → true
|
|
@@ -142,7 +180,7 @@ function isPathPrefix(name, prefix) {
|
|
|
142
180
|
* @param basePath Base path as a dot/bracket string or array of segments
|
|
143
181
|
* @returns The “tail” segments, or `null` if `fullPath` isn’t nested under `basePath`
|
|
144
182
|
*
|
|
145
|
-
*
|
|
183
|
+
* **Example:**
|
|
146
184
|
* ```js
|
|
147
185
|
* getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"]
|
|
148
186
|
* getRelativePath("a.b.c.d", ["a","b"]) // → ["c","d"]
|
|
@@ -261,8 +299,9 @@ function isEmptyValue(value) {
|
|
|
261
299
|
* This function structures the form values based on the naming convention.
|
|
262
300
|
* It also includes all the field names and extracts the intent from the submission.
|
|
263
301
|
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
302
|
+
* See https://conform.guide/api/react/future/parseSubmission
|
|
303
|
+
*
|
|
304
|
+
* **Example:**
|
|
266
305
|
* ```ts
|
|
267
306
|
* const formData = new FormData();
|
|
268
307
|
*
|
|
@@ -298,7 +337,7 @@ function parseSubmission(formData, options) {
|
|
|
298
337
|
var _options$skipEntry;
|
|
299
338
|
if (_name !== intentName && !(options !== null && options !== void 0 && (_options$skipEntry = options.skipEntry) !== null && _options$skipEntry !== void 0 && _options$skipEntry.call(options, _name))) {
|
|
300
339
|
var _options$stripEmptyVa;
|
|
301
|
-
var
|
|
340
|
+
var value = formData.getAll(_name);
|
|
302
341
|
var segments = parsePath(_name);
|
|
303
342
|
|
|
304
343
|
// If the name ends with [], remove the empty segment and keep the full array
|
|
@@ -306,19 +345,19 @@ function parseSubmission(formData, options) {
|
|
|
306
345
|
if (segments.length > 0 && segments[segments.length - 1] === '') {
|
|
307
346
|
segments.pop();
|
|
308
347
|
} else {
|
|
309
|
-
|
|
348
|
+
value = value.length > 1 ? value : value[0];
|
|
310
349
|
}
|
|
311
350
|
var stripEmptyValues = (_options$stripEmptyVa = options === null || options === void 0 ? void 0 : options.stripEmptyValues) !== null && _options$stripEmptyVa !== void 0 ? _options$stripEmptyVa : false;
|
|
312
351
|
if (stripEmptyValues) {
|
|
313
352
|
// For arrays, filter out individual empty items
|
|
314
|
-
if (Array.isArray(
|
|
315
|
-
|
|
353
|
+
if (Array.isArray(value)) {
|
|
354
|
+
value = value.filter(item => !isEmptyValue(item));
|
|
316
355
|
}
|
|
317
|
-
if (isEmptyValue(
|
|
318
|
-
|
|
356
|
+
if (isEmptyValue(value)) {
|
|
357
|
+
value = undefined;
|
|
319
358
|
}
|
|
320
359
|
}
|
|
321
|
-
setPathValue(submission.payload, segments,
|
|
360
|
+
setPathValue(submission.payload, segments, value, {
|
|
322
361
|
silent: true // Avoid errors if the path is invalid
|
|
323
362
|
});
|
|
324
363
|
submission.fields.push(_name);
|
|
@@ -340,8 +379,9 @@ function parseSubmission(formData, options) {
|
|
|
340
379
|
* file inputs cannot be initialized with files.
|
|
341
380
|
* You can specify `keepFiles: true` to keep the files if needed.
|
|
342
381
|
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
382
|
+
* See https://conform.guide/api/react/future/report
|
|
383
|
+
*
|
|
384
|
+
* **Example:**
|
|
345
385
|
* ```ts
|
|
346
386
|
* // Report the submission with the field errors
|
|
347
387
|
* report(submission, {
|
|
@@ -372,29 +412,16 @@ function report(submission) {
|
|
|
372
412
|
var error;
|
|
373
413
|
if (options.error == null) {
|
|
374
414
|
error = options.error;
|
|
375
|
-
} else {
|
|
415
|
+
} else if ('issues' in options.error) {
|
|
376
416
|
var _options$error$issues;
|
|
377
417
|
error = formatIssues((_options$error$issues = options.error.issues) !== null && _options$error$issues !== void 0 ? _options$error$issues : []);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
if (options.error.fieldErrors) {
|
|
382
|
-
for (var [_name2, messages] of Object.entries(options.error.fieldErrors)) {
|
|
383
|
-
if (messages.length === 0) {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (!error.fieldErrors[_name2]) {
|
|
387
|
-
error.fieldErrors[_name2] = messages;
|
|
388
|
-
} else {
|
|
389
|
-
error.fieldErrors[_name2].push(...messages);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
418
|
+
} else {
|
|
419
|
+
error = normalizeFormError(options.error);
|
|
393
420
|
}
|
|
394
421
|
var targetValue = typeof options.value === 'undefined' || submission.payload === options.value && !options.reset ? undefined : options.value && !options.keepFiles ? stripFiles(options.value) : (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : {};
|
|
395
422
|
if (options.hideFields) {
|
|
396
|
-
for (var
|
|
397
|
-
var path = parsePath(
|
|
423
|
+
for (var _name2 of options.hideFields) {
|
|
424
|
+
var path = parsePath(_name2);
|
|
398
425
|
setPathValue(submission.payload, path, undefined);
|
|
399
426
|
if (targetValue) {
|
|
400
427
|
setPathValue(targetValue, path, undefined);
|
|
@@ -414,8 +441,9 @@ function report(submission) {
|
|
|
414
441
|
/**
|
|
415
442
|
* A utility function that checks whether the current form data differs from the default values.
|
|
416
443
|
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
444
|
+
* See https://conform.guide/api/react/future/isDirty
|
|
445
|
+
*
|
|
446
|
+
* **Example: Enable a submit button only if the form is dirty**
|
|
419
447
|
*
|
|
420
448
|
* ```tsx
|
|
421
449
|
* const dirty = useFormData(
|
|
@@ -446,9 +474,16 @@ formData, options) {
|
|
|
446
474
|
intentName: options === null || options === void 0 ? void 0 : options.intentName,
|
|
447
475
|
skipEntry: options === null || options === void 0 ? void 0 : options.skipEntry
|
|
448
476
|
}).payload : formData;
|
|
449
|
-
var
|
|
450
|
-
|
|
451
|
-
|
|
477
|
+
var serialize = (value, context) => {
|
|
478
|
+
if (options !== null && options !== void 0 && options.serialize) {
|
|
479
|
+
return options.serialize(value, {
|
|
480
|
+
name: context.name,
|
|
481
|
+
defaultSerialize
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
return defaultSerialize(value);
|
|
485
|
+
};
|
|
486
|
+
return !deepEqual(normalize(formValue, serialize), normalize(options === null || options === void 0 ? void 0 : options.defaultValue, serialize));
|
|
452
487
|
}
|
|
453
488
|
|
|
454
489
|
/**
|
|
@@ -466,13 +501,13 @@ formData, options) {
|
|
|
466
501
|
* - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined
|
|
467
502
|
* - anything else -> undefined
|
|
468
503
|
*/
|
|
469
|
-
function
|
|
504
|
+
function defaultSerialize(value) {
|
|
470
505
|
function serializePrimitive(value) {
|
|
471
506
|
if (typeof value === 'string' || value === null) {
|
|
472
507
|
return value;
|
|
473
508
|
}
|
|
474
509
|
if (typeof value === 'boolean') {
|
|
475
|
-
return value ? 'on' :
|
|
510
|
+
return value ? 'on' : null;
|
|
476
511
|
}
|
|
477
512
|
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
478
513
|
return value.toString();
|
|
@@ -533,8 +568,11 @@ function serialize(value) {
|
|
|
533
568
|
* to handle the case where a multi-value field (e.g. checkboxes) has only one value.
|
|
534
569
|
*/
|
|
535
570
|
function normalize(value) {
|
|
536
|
-
var
|
|
537
|
-
var
|
|
571
|
+
var serialize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultSerialize;
|
|
572
|
+
var name = arguments.length > 2 ? arguments[2] : undefined;
|
|
573
|
+
var data = serialize(value, {
|
|
574
|
+
name
|
|
575
|
+
});
|
|
538
576
|
if (typeof data === 'undefined') {
|
|
539
577
|
data = value;
|
|
540
578
|
}
|
|
@@ -551,16 +589,16 @@ function normalize(value) {
|
|
|
551
589
|
if (data.length === 0) {
|
|
552
590
|
return undefined;
|
|
553
591
|
}
|
|
554
|
-
var array = data.map(item => normalize(item,
|
|
592
|
+
var array = data.map((item, index) => normalize(item, serialize, appendPath(name, index)));
|
|
555
593
|
if (array.length === 1 && (typeof array[0] === 'string' || array[0] === undefined)) {
|
|
556
594
|
return array[0];
|
|
557
595
|
}
|
|
558
596
|
return array;
|
|
559
597
|
}
|
|
560
598
|
if (isPlainObject(data)) {
|
|
561
|
-
var entries = Object.entries(data).reduce((list,
|
|
562
|
-
var [key, value] =
|
|
563
|
-
var normalizedValue = normalize(value,
|
|
599
|
+
var entries = Object.entries(data).reduce((list, _ref2) => {
|
|
600
|
+
var [key, value] = _ref2;
|
|
601
|
+
var normalizedValue = normalize(value, serialize, appendPath(name, key));
|
|
564
602
|
if (typeof normalizedValue !== 'undefined') {
|
|
565
603
|
list.push([key, normalizedValue]);
|
|
566
604
|
}
|
|
@@ -577,7 +615,9 @@ function normalize(value) {
|
|
|
577
615
|
/**
|
|
578
616
|
* Retrieve a field value from FormData with optional type guards.
|
|
579
617
|
*
|
|
580
|
-
*
|
|
618
|
+
* **Example:**
|
|
619
|
+
*
|
|
620
|
+
* ```ts
|
|
581
621
|
* // Basic field access: return `unknown`
|
|
582
622
|
* const email = getFieldValue(formData, 'email');
|
|
583
623
|
* // String type: returns `string`
|
|
@@ -592,6 +632,7 @@ function normalize(value) {
|
|
|
592
632
|
* const items = getFieldValue<Item[]>(formData, 'items', { type: 'object', array: true });
|
|
593
633
|
* // Optional string type: returns `string | undefined`
|
|
594
634
|
* const bio = getFieldValue(formData, 'bio', { type: 'string', optional: true });
|
|
635
|
+
* ```
|
|
595
636
|
*/
|
|
596
637
|
|
|
597
638
|
function getFieldValue(formData, name, options) {
|
|
@@ -643,4 +684,4 @@ function getFieldValue(formData, name, options) {
|
|
|
643
684
|
return value;
|
|
644
685
|
}
|
|
645
686
|
|
|
646
|
-
export { DEFAULT_INTENT_NAME, appendPath, formatPath, getFieldValue, getFormData, getPathValue, getRelativePath, isDirty, isPathPrefix, normalize, parsePath, parseSubmission, report,
|
|
687
|
+
export { DEFAULT_INTENT_NAME, appendPath, defaultSerialize, formatPath, getFieldValue, getFormData, getPathValue, getRelativePath, hasError, isDirty, isPathPrefix, normalize, normalizeFormError, parsePath, parseSubmission, report, setPathValue };
|
package/dist/future/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { Serialize, FieldName, FormValue, FormError, Submission, SubmissionResult, ValidationAttributes, } from '../types';
|
|
2
|
-
export { DEFAULT_INTENT_NAME, getFormData, isDirty, normalize, parseSubmission, parsePath, formatPath, appendPath, getRelativePath, getPathValue, setPathValue, report,
|
|
1
|
+
export type { Serialize, CustomSerialize, FieldName, FormValue, FormError, Submission, SubmissionResult, ValidationAttributes, } from '../types';
|
|
2
|
+
export { DEFAULT_INTENT_NAME, getFormData, isDirty, normalize, parseSubmission, parsePath, formatPath, appendPath, getRelativePath, getPathValue, setPathValue, report, defaultSerialize, getFieldValue, normalizeFormError, } from '../formdata';
|
|
3
3
|
export { isPlainObject, deepEqual } from '../util';
|
|
4
4
|
export { isFieldElement, isGlobalInstance, updateField, createFileList, createGlobalFormsObserver, dispatchInternalUpdateEvent, focus, change, blur, getFormAction, getFormEncType, getFormMethod, requestSubmit, requestIntent, } from '../dom';
|
|
5
5
|
export { formatIssues } from '../standard-schema';
|
package/dist/future/index.js
CHANGED
|
@@ -11,6 +11,7 @@ var standardSchema = require('../standard-schema.js');
|
|
|
11
11
|
|
|
12
12
|
exports.DEFAULT_INTENT_NAME = formdata.DEFAULT_INTENT_NAME;
|
|
13
13
|
exports.appendPath = formdata.appendPath;
|
|
14
|
+
exports.defaultSerialize = formdata.defaultSerialize;
|
|
14
15
|
exports.formatPath = formdata.formatPath;
|
|
15
16
|
exports.getFieldValue = formdata.getFieldValue;
|
|
16
17
|
exports.getFormData = formdata.getFormData;
|
|
@@ -18,10 +19,10 @@ exports.getPathValue = formdata.getPathValue;
|
|
|
18
19
|
exports.getRelativePath = formdata.getRelativePath;
|
|
19
20
|
exports.isDirty = formdata.isDirty;
|
|
20
21
|
exports.normalize = formdata.normalize;
|
|
22
|
+
exports.normalizeFormError = formdata.normalizeFormError;
|
|
21
23
|
exports.parsePath = formdata.parsePath;
|
|
22
24
|
exports.parseSubmission = formdata.parseSubmission;
|
|
23
25
|
exports.report = formdata.report;
|
|
24
|
-
exports.serialize = formdata.serialize;
|
|
25
26
|
exports.setPathValue = formdata.setPathValue;
|
|
26
27
|
exports.deepEqual = util.deepEqual;
|
|
27
28
|
exports.isPlainObject = util.isPlainObject;
|
package/dist/future/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_INTENT_NAME, appendPath, formatPath, getFieldValue, getFormData, getPathValue, getRelativePath, isDirty, normalize, parsePath, parseSubmission, report,
|
|
1
|
+
export { DEFAULT_INTENT_NAME, appendPath, defaultSerialize, formatPath, getFieldValue, getFormData, getPathValue, getRelativePath, isDirty, normalize, normalizeFormError, parsePath, parseSubmission, report, setPathValue } from '../formdata.mjs';
|
|
2
2
|
export { deepEqual, isPlainObject } from '../util.mjs';
|
|
3
3
|
export { blur, change, createFileList, createGlobalFormsObserver, dispatchInternalUpdateEvent, focus, getFormAction, getFormEncType, getFormMethod, isFieldElement, isGlobalInstance, requestIntent, requestSubmit, updateField } from '../dom.mjs';
|
|
4
4
|
export { formatIssues } from '../standard-schema.mjs';
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
import type { FormError } from './types';
|
|
2
|
-
|
|
3
|
-
* A widened version of `StandardSchemaV1.Issue`.
|
|
4
|
-
*
|
|
5
|
-
* The `path` elements and `PropertyKey` fields are loosened to `unknown`
|
|
6
|
-
* to stay compatible with Valibot's native issue type.
|
|
7
|
-
*/
|
|
8
|
-
export type StandardSchemaIssue = {
|
|
9
|
-
readonly message: string;
|
|
10
|
-
readonly path?: ReadonlyArray<unknown | {
|
|
11
|
-
key: unknown;
|
|
12
|
-
}> | undefined;
|
|
13
|
-
};
|
|
14
|
-
export declare function formatIssues(issues: Readonly<StandardSchemaIssue[]>): FormError<string>;
|
|
1
|
+
import type { FormError, StandardSchemaIssue } from './types';
|
|
2
|
+
export declare function formatIssues(issues: Readonly<StandardSchemaIssue[]>): FormError<string[]>;
|
|
15
3
|
//# sourceMappingURL=standard-schema.d.ts.map
|
package/dist/standard-schema.js
CHANGED
|
@@ -5,16 +5,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var formdata = require('./formdata.js');
|
|
6
6
|
var util = require('./util.js');
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* A widened version of `StandardSchemaV1.Issue`.
|
|
10
|
-
*
|
|
11
|
-
* The `path` elements and `PropertyKey` fields are loosened to `unknown`
|
|
12
|
-
* to stay compatible with Valibot's native issue type.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
8
|
function formatIssues(issues) {
|
|
16
9
|
var error = {
|
|
17
|
-
formErrors:
|
|
10
|
+
formErrors: null,
|
|
18
11
|
fieldErrors: {}
|
|
19
12
|
};
|
|
20
13
|
for (var issue of issues) {
|
|
@@ -28,6 +21,8 @@ function formatIssues(issues) {
|
|
|
28
21
|
})) !== null && _issue$path$map !== void 0 ? _issue$path$map : [];
|
|
29
22
|
var name = formdata.formatPath(segments !== null && segments !== void 0 ? segments : []);
|
|
30
23
|
if (!name) {
|
|
24
|
+
var _error$formErrors;
|
|
25
|
+
(_error$formErrors = error.formErrors) !== null && _error$formErrors !== void 0 ? _error$formErrors : error.formErrors = [];
|
|
31
26
|
error.formErrors.push(issue.message);
|
|
32
27
|
} else {
|
|
33
28
|
var _error$fieldErrors, _error$fieldErrors$na;
|
package/dist/standard-schema.mjs
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { formatPath } from './formdata.mjs';
|
|
2
2
|
import { isPlainObject } from './util.mjs';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* A widened version of `StandardSchemaV1.Issue`.
|
|
6
|
-
*
|
|
7
|
-
* The `path` elements and `PropertyKey` fields are loosened to `unknown`
|
|
8
|
-
* to stay compatible with Valibot's native issue type.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
4
|
function formatIssues(issues) {
|
|
12
5
|
var error = {
|
|
13
|
-
formErrors:
|
|
6
|
+
formErrors: null,
|
|
14
7
|
fieldErrors: {}
|
|
15
8
|
};
|
|
16
9
|
for (var issue of issues) {
|
|
@@ -24,6 +17,8 @@ function formatIssues(issues) {
|
|
|
24
17
|
})) !== null && _issue$path$map !== void 0 ? _issue$path$map : [];
|
|
25
18
|
var name = formatPath(segments !== null && segments !== void 0 ? segments : []);
|
|
26
19
|
if (!name) {
|
|
20
|
+
var _error$formErrors;
|
|
21
|
+
(_error$formErrors = error.formErrors) !== null && _error$formErrors !== void 0 ? _error$formErrors : error.formErrors = [];
|
|
27
22
|
error.formErrors.push(issue.message);
|
|
28
23
|
} else {
|
|
29
24
|
var _error$fieldErrors, _error$fieldErrors$na;
|
package/dist/types.d.ts
CHANGED
|
@@ -12,15 +12,35 @@ export type FormValue<Type extends JsonPrimitive | FormDataEntryValue = JsonPrim
|
|
|
12
12
|
/**
|
|
13
13
|
* Form error object that contains both form errors and field errors.
|
|
14
14
|
*/
|
|
15
|
-
export type FormError<ErrorShape = string> = {
|
|
15
|
+
export type FormError<ErrorShape = string[]> = {
|
|
16
16
|
/**
|
|
17
|
-
* The error
|
|
17
|
+
* The form-level error payload.
|
|
18
|
+
* Set to `null` when there is no form-level error.
|
|
18
19
|
*/
|
|
19
|
-
formErrors: ErrorShape
|
|
20
|
+
formErrors: ErrorShape | null;
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
+
* Field-level error payloads mapped by field name.
|
|
23
|
+
* Use `null` to explicitly clear a field error.
|
|
22
24
|
*/
|
|
23
|
-
fieldErrors: Record<string, ErrorShape
|
|
25
|
+
fieldErrors: Record<string, ErrorShape | null>;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A widened version of `StandardSchemaV1.Issue`.
|
|
29
|
+
*
|
|
30
|
+
* The `path` elements and `PropertyKey` fields are loosened to `unknown`
|
|
31
|
+
* to stay compatible with Valibot's native issue type.
|
|
32
|
+
*/
|
|
33
|
+
export type StandardSchemaIssue = {
|
|
34
|
+
readonly message: string;
|
|
35
|
+
readonly path?: ReadonlyArray<unknown | {
|
|
36
|
+
key: unknown;
|
|
37
|
+
}> | undefined;
|
|
38
|
+
};
|
|
39
|
+
export type StandardSchemaError = Partial<Record<keyof FormError, never>> & {
|
|
40
|
+
issues: ReadonlyArray<StandardSchemaIssue>;
|
|
41
|
+
};
|
|
42
|
+
export type CustomError<ErrorShape> = Partial<FormError<ErrorShape>> & {
|
|
43
|
+
issues?: never;
|
|
24
44
|
};
|
|
25
45
|
/**
|
|
26
46
|
* Structured data parsed from a form submission.
|
|
@@ -30,7 +50,7 @@ export type Submission<ValueType extends JsonPrimitive | FormDataEntryValue = Js
|
|
|
30
50
|
* The submitted values mapped by field name.
|
|
31
51
|
* Supports nested names like `user.email` or indexed names like `items[0].id`.
|
|
32
52
|
*
|
|
33
|
-
*
|
|
53
|
+
* **Example:**
|
|
34
54
|
* ```json
|
|
35
55
|
* {
|
|
36
56
|
* "username": "johndoe",
|
|
@@ -58,7 +78,7 @@ export type Submission<ValueType extends JsonPrimitive | FormDataEntryValue = Js
|
|
|
58
78
|
/**
|
|
59
79
|
* The result of a submission.
|
|
60
80
|
*/
|
|
61
|
-
export type SubmissionResult<ErrorShape = string, ValueType extends JsonPrimitive | FormDataEntryValue = JsonPrimitive | FormDataEntryValue> = {
|
|
81
|
+
export type SubmissionResult<ErrorShape = string[], ValueType extends JsonPrimitive | FormDataEntryValue = JsonPrimitive | FormDataEntryValue> = {
|
|
62
82
|
/**
|
|
63
83
|
* The original submission data.
|
|
64
84
|
*/
|
|
@@ -81,8 +101,9 @@ export type FieldName<FieldShape> = string & {
|
|
|
81
101
|
'~shape'?: FieldShape;
|
|
82
102
|
};
|
|
83
103
|
/**
|
|
84
|
-
* The input attributes related to form field constraints
|
|
85
|
-
*
|
|
104
|
+
* The input attributes related to form field constraints.
|
|
105
|
+
*
|
|
106
|
+
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation
|
|
86
107
|
*/
|
|
87
108
|
export type ValidationAttributes = {
|
|
88
109
|
required?: boolean | undefined;
|
|
@@ -103,23 +124,22 @@ export type Serializable<T> = T extends File ? undefined : T extends Array<infer
|
|
|
103
124
|
[K in keyof T]: Serializable<T[K]>;
|
|
104
125
|
} : T;
|
|
105
126
|
/**
|
|
106
|
-
* Converts an arbitrary value into a
|
|
127
|
+
* Converts an arbitrary value into a form value.
|
|
107
128
|
*
|
|
108
129
|
* This function is used to prepare field values for submission,
|
|
109
130
|
* ensuring they are compatible with the browser's `FormData` API.
|
|
110
|
-
*
|
|
111
|
-
* @param value - The original value to serialize.
|
|
112
|
-
* @returns A `SerializedValue` if the input can be represented in `FormData`,
|
|
113
|
-
* or `undefined` if it cannot be serialized.
|
|
114
131
|
*/
|
|
115
|
-
export type Serialize = (value: unknown
|
|
132
|
+
export type Serialize = (value: unknown, ctx: {
|
|
133
|
+
name: string | undefined;
|
|
134
|
+
}) => FormValue<FormDataEntryValue> | null | undefined;
|
|
116
135
|
/**
|
|
117
|
-
* A
|
|
118
|
-
*
|
|
119
|
-
* - `string` and `File` are supported natively by `FormData`.
|
|
120
|
-
* - Arrays allow representing multi-value fields.
|
|
136
|
+
* A custom serializer that can override specific values and delegate everything
|
|
137
|
+
* else back to the default serializer.
|
|
121
138
|
*/
|
|
122
|
-
export type
|
|
139
|
+
export type CustomSerialize = (value: unknown, ctx: {
|
|
140
|
+
name: string | undefined;
|
|
141
|
+
defaultSerialize: (value: unknown) => ReturnType<Serialize>;
|
|
142
|
+
}) => FormValue<FormDataEntryValue> | null | undefined;
|
|
123
143
|
/**
|
|
124
144
|
* Flatten a discriminated union into a single type with all properties.
|
|
125
145
|
*/
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "A set of opinionated helpers built on top of the Constraint Validation API",
|
|
4
4
|
"homepage": "https://conform.guide",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.19.0",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"module": "./dist/index.mjs",
|
|
9
9
|
"types": "./dist/index.d.ts",
|