@holo-js/forms 0.2.5 → 0.3.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/dist/internal/client.d.ts +2 -1
- package/dist/internal/client.mjs +157 -171
- package/package.json +3 -3
|
@@ -27,6 +27,7 @@ interface UseFormOptions<TData, TSuccess = unknown> {
|
|
|
27
27
|
readonly initialState?: SerializedFormSubmission<TData> | FormFailurePayload<TData> | null;
|
|
28
28
|
readonly submitter?: (context: ClientSubmitContext<TData>) => Promise<ClientSubmitResult<TData, TSuccess> | void> | ClientSubmitResult<TData, TSuccess> | void;
|
|
29
29
|
}
|
|
30
|
+
declare function markClientSubmitControlFlowError(error: unknown): unknown;
|
|
30
31
|
interface FormFieldState<TValue> {
|
|
31
32
|
value: TValue;
|
|
32
33
|
readonly errors: readonly string[];
|
|
@@ -65,4 +66,4 @@ declare function runWithBrowserFormElement<TData, TSuccess>(formClient: Pick<Use
|
|
|
65
66
|
*/
|
|
66
67
|
declare function createFormClient<TSchema extends FormSchema, TSuccess = unknown>(schemaDefinition: TSchema, options?: UseFormOptions<InferFormData<TSchema>, TSuccess>): UseFormResult<InferFormData<TSchema>, TSuccess, InferFormFieldTree<TSchema>>;
|
|
67
68
|
|
|
68
|
-
export { type ClientSubmitContext, type ClientSubmitResult, type FormFieldState, type FormFieldTree, type InferFormFieldTree, type UseFormOptions, type UseFormResult, type ValidateOnMode, createFormClient, runWithBrowserFormElement };
|
|
69
|
+
export { type ClientSubmitContext, type ClientSubmitResult, type FormFieldState, type FormFieldTree, type InferFormFieldTree, type UseFormOptions, type UseFormResult, type ValidateOnMode, createFormClient, markClientSubmitControlFlowError, runWithBrowserFormElement };
|
package/dist/internal/client.mjs
CHANGED
|
@@ -133,11 +133,139 @@ function validationExceptionToFailure(error, fallbackValues = {}) {
|
|
|
133
133
|
return void 0;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// src/internal/formValues.ts
|
|
137
|
+
function isPlainFormObject(value) {
|
|
138
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Blob);
|
|
139
|
+
}
|
|
140
|
+
function isSchemaFieldLike(value) {
|
|
141
|
+
return !!value && typeof value === "object" && value.kind === "field" && !!value.definition && typeof value.definition === "object";
|
|
142
|
+
}
|
|
143
|
+
function areFormValuesEqual(left, right) {
|
|
144
|
+
if (Object.is(left, right)) return true;
|
|
145
|
+
if (left instanceof Date && right instanceof Date) return left.getTime() === right.getTime();
|
|
146
|
+
if (left instanceof Blob || right instanceof Blob) return false;
|
|
147
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
148
|
+
return left.length === right.length && left.every((value, index) => areFormValuesEqual(value, right[index]));
|
|
149
|
+
}
|
|
150
|
+
if (isPlainFormObject(left) && isPlainFormObject(right)) {
|
|
151
|
+
const leftKeys = Object.keys(left);
|
|
152
|
+
const rightKeys = Object.keys(right);
|
|
153
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key) => key in right && areFormValuesEqual(left[key], right[key]));
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
function cloneFormValue(value) {
|
|
158
|
+
if (Array.isArray(value)) return value.map((item) => cloneFormValue(item));
|
|
159
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
160
|
+
if (isPlainFormObject(value)) {
|
|
161
|
+
return Object.fromEntries(
|
|
162
|
+
Object.entries(value).map(([key, entry]) => [key, cloneFormValue(entry)])
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return value;
|
|
166
|
+
}
|
|
167
|
+
function isFormLeafValue(value) {
|
|
168
|
+
return Array.isArray(value) || value instanceof Date || value instanceof Blob || isSchemaFieldLike(value) || !isPlainFormObject(value);
|
|
169
|
+
}
|
|
170
|
+
function normalizeFormObject(value) {
|
|
171
|
+
return isPlainFormObject(value) ? cloneFormValue(value) : {};
|
|
172
|
+
}
|
|
173
|
+
function mergeFormValues(base, override) {
|
|
174
|
+
if (!override || !isPlainFormObject(override)) return cloneFormValue(base);
|
|
175
|
+
const output = cloneFormValue(base);
|
|
176
|
+
for (const [key, value] of Object.entries(override)) {
|
|
177
|
+
if (typeof value === "undefined" && key in output) continue;
|
|
178
|
+
if (isPlainFormObject(value) && isPlainFormObject(output[key])) {
|
|
179
|
+
output[key] = mergeFormValues(output[key], value);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
output[key] = cloneFormValue(value);
|
|
183
|
+
}
|
|
184
|
+
return output;
|
|
185
|
+
}
|
|
186
|
+
function splitFormPath(path) {
|
|
187
|
+
return path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
188
|
+
}
|
|
189
|
+
function getFormValueAtPath(root, path) {
|
|
190
|
+
let cursor = root;
|
|
191
|
+
for (const part of splitFormPath(path)) {
|
|
192
|
+
if (!isPlainFormObject(cursor) && !Array.isArray(cursor)) return void 0;
|
|
193
|
+
cursor = cursor[part];
|
|
194
|
+
}
|
|
195
|
+
return cursor;
|
|
196
|
+
}
|
|
197
|
+
function setFormValueAtPath(root, path, value) {
|
|
198
|
+
const parts = splitFormPath(path);
|
|
199
|
+
let cursor = root;
|
|
200
|
+
for (const [index, part] of parts.entries()) {
|
|
201
|
+
const last = index === parts.length - 1;
|
|
202
|
+
const nextPart = parts[index + 1];
|
|
203
|
+
if (!nextPart && !last) return;
|
|
204
|
+
if (Array.isArray(cursor)) {
|
|
205
|
+
const offset = Number(part);
|
|
206
|
+
if (!Number.isInteger(offset) || offset < 0) return;
|
|
207
|
+
if (last) {
|
|
208
|
+
cursor[offset] = value;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const existing2 = cursor[offset];
|
|
212
|
+
if (!isPlainFormObject(existing2) && !Array.isArray(existing2)) cursor[offset] = /^\d+$/.test(nextPart) ? [] : {};
|
|
213
|
+
cursor = cursor[offset];
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (last) {
|
|
217
|
+
cursor[part] = value;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const existing = cursor[part];
|
|
221
|
+
if (!isPlainFormObject(existing) && !Array.isArray(existing)) cursor[part] = /^\d+$/.test(nextPart) ? [] : {};
|
|
222
|
+
cursor = cursor[part];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function flattenFormLeafPaths(value, prefix = "") {
|
|
226
|
+
if (isFormLeafValue(value)) return [prefix].filter(Boolean);
|
|
227
|
+
return Object.entries(value).flatMap(([key, nested]) => {
|
|
228
|
+
return flattenFormLeafPaths(nested, prefix ? `${prefix}.${key}` : key);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function buildFormData(value, path = "", formData = new FormData()) {
|
|
232
|
+
if (typeof value === "undefined") return formData;
|
|
233
|
+
if (value instanceof Date) {
|
|
234
|
+
formData.append(path, value.toISOString());
|
|
235
|
+
return formData;
|
|
236
|
+
}
|
|
237
|
+
if (value instanceof Blob) {
|
|
238
|
+
formData.append(path, value);
|
|
239
|
+
return formData;
|
|
240
|
+
}
|
|
241
|
+
if (Array.isArray(value)) {
|
|
242
|
+
for (const item of value) buildFormData(item, `${path}[]`, formData);
|
|
243
|
+
return formData;
|
|
244
|
+
}
|
|
245
|
+
if (isPlainFormObject(value)) {
|
|
246
|
+
for (const [key, nested] of Object.entries(value)) buildFormData(nested, path ? `${path}.${key}` : key, formData);
|
|
247
|
+
return formData;
|
|
248
|
+
}
|
|
249
|
+
formData.append(path, String(value));
|
|
250
|
+
return formData;
|
|
251
|
+
}
|
|
252
|
+
|
|
136
253
|
// src/internal/client.ts
|
|
137
254
|
var browserFormSubmitSymbol = /* @__PURE__ */ Symbol("browserFormSubmit");
|
|
255
|
+
var clientSubmitControlFlowErrors = /* @__PURE__ */ new WeakSet();
|
|
138
256
|
function isBrowserFormSubmitter(value) {
|
|
139
257
|
return browserFormSubmitSymbol in value;
|
|
140
258
|
}
|
|
259
|
+
function markClientSubmitControlFlowError(error) {
|
|
260
|
+
if (!error || typeof error !== "object" && typeof error !== "function") {
|
|
261
|
+
return error;
|
|
262
|
+
}
|
|
263
|
+
clientSubmitControlFlowErrors.add(error);
|
|
264
|
+
return error;
|
|
265
|
+
}
|
|
266
|
+
function isClientSubmitControlFlowError(error) {
|
|
267
|
+
return !!error && (typeof error === "object" || typeof error === "function") && clientSubmitControlFlowErrors.has(error);
|
|
268
|
+
}
|
|
141
269
|
var transportFailureMessage = "Unable to submit the form right now. Please try again.";
|
|
142
270
|
function serializeSubmissionState(valid, values, errors, schemaDefinition) {
|
|
143
271
|
return Object.freeze({
|
|
@@ -232,152 +360,6 @@ async function validateClientValues(values, schemaDefinition) {
|
|
|
232
360
|
result.errors.flatten()
|
|
233
361
|
);
|
|
234
362
|
}
|
|
235
|
-
function isPlainObject2(value) {
|
|
236
|
-
return !!value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Blob);
|
|
237
|
-
}
|
|
238
|
-
function isSchemaFieldLike(value) {
|
|
239
|
-
return !!value && typeof value === "object" && value.kind === "field" && !!value.definition && typeof value.definition === "object";
|
|
240
|
-
}
|
|
241
|
-
function areEqual(left, right) {
|
|
242
|
-
if (Object.is(left, right)) {
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
if (left instanceof Date && right instanceof Date) {
|
|
246
|
-
return left.getTime() === right.getTime();
|
|
247
|
-
}
|
|
248
|
-
if (left instanceof Blob || right instanceof Blob) {
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
if (Array.isArray(left) && Array.isArray(right)) {
|
|
252
|
-
return left.length === right.length && left.every((value, index) => areEqual(value, right[index]));
|
|
253
|
-
}
|
|
254
|
-
if (isPlainObject2(left) && isPlainObject2(right)) {
|
|
255
|
-
const leftKeys = Object.keys(left);
|
|
256
|
-
const rightKeys = Object.keys(right);
|
|
257
|
-
return leftKeys.length === rightKeys.length && leftKeys.every((key) => key in right && areEqual(left[key], right[key]));
|
|
258
|
-
}
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
function cloneValue(value) {
|
|
262
|
-
if (Array.isArray(value)) {
|
|
263
|
-
return value.map((item) => cloneValue(item));
|
|
264
|
-
}
|
|
265
|
-
if (value instanceof Date) {
|
|
266
|
-
return new Date(value.getTime());
|
|
267
|
-
}
|
|
268
|
-
if (isPlainObject2(value)) {
|
|
269
|
-
return Object.fromEntries(
|
|
270
|
-
Object.entries(value).map(([key, entry]) => [key, cloneValue(entry)])
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
return value;
|
|
274
|
-
}
|
|
275
|
-
function isLeafValue(value) {
|
|
276
|
-
return Array.isArray(value) || value instanceof Date || value instanceof Blob || isSchemaFieldLike(value) || !isPlainObject2(value);
|
|
277
|
-
}
|
|
278
|
-
function normalizeObject(value) {
|
|
279
|
-
return isPlainObject2(value) ? cloneValue(value) : {};
|
|
280
|
-
}
|
|
281
|
-
function mergeValues(base, override) {
|
|
282
|
-
if (!override || !isPlainObject2(override)) {
|
|
283
|
-
return cloneValue(base);
|
|
284
|
-
}
|
|
285
|
-
const output = cloneValue(base);
|
|
286
|
-
for (const [key, value] of Object.entries(override)) {
|
|
287
|
-
if (typeof value === "undefined" && key in output) {
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
if (isPlainObject2(value) && isPlainObject2(output[key])) {
|
|
291
|
-
output[key] = mergeValues(output[key], value);
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
output[key] = cloneValue(value);
|
|
295
|
-
}
|
|
296
|
-
return output;
|
|
297
|
-
}
|
|
298
|
-
function splitPath(path) {
|
|
299
|
-
return path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
300
|
-
}
|
|
301
|
-
function getValueAtPath(root, path) {
|
|
302
|
-
let cursor = root;
|
|
303
|
-
for (const part of splitPath(path)) {
|
|
304
|
-
if (!isPlainObject2(cursor) && !Array.isArray(cursor)) {
|
|
305
|
-
return void 0;
|
|
306
|
-
}
|
|
307
|
-
cursor = cursor[part];
|
|
308
|
-
}
|
|
309
|
-
return cursor;
|
|
310
|
-
}
|
|
311
|
-
function setValueAtPath(root, path, value) {
|
|
312
|
-
const parts = splitPath(path);
|
|
313
|
-
let cursor = root;
|
|
314
|
-
for (const [index, part] of parts.entries()) {
|
|
315
|
-
const last = index === parts.length - 1;
|
|
316
|
-
const nextPart = parts[index + 1];
|
|
317
|
-
if (Array.isArray(cursor)) {
|
|
318
|
-
const offset = Number(part);
|
|
319
|
-
if (!Number.isInteger(offset) || offset < 0) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
if (last) {
|
|
323
|
-
cursor[offset] = value;
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
const existing2 = cursor[offset];
|
|
327
|
-
if (!isPlainObject2(existing2) && !Array.isArray(existing2)) {
|
|
328
|
-
cursor[offset] = /^\d+$/.test(nextPart) ? [] : {};
|
|
329
|
-
}
|
|
330
|
-
cursor = cursor[offset];
|
|
331
|
-
continue;
|
|
332
|
-
}
|
|
333
|
-
if (last) {
|
|
334
|
-
cursor[part] = value;
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
const existing = cursor[part];
|
|
338
|
-
if (!isPlainObject2(existing) && !Array.isArray(existing)) {
|
|
339
|
-
cursor[part] = /^\d+$/.test(nextPart) ? [] : {};
|
|
340
|
-
}
|
|
341
|
-
cursor = cursor[part];
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
function flattenLeafPaths(value, prefix = "") {
|
|
345
|
-
if (isLeafValue(value)) {
|
|
346
|
-
return [prefix].filter(Boolean);
|
|
347
|
-
}
|
|
348
|
-
return Object.entries(value).flatMap(([key, nested]) => {
|
|
349
|
-
const next = prefix ? `${prefix}.${key}` : key;
|
|
350
|
-
return flattenLeafPaths(nested, next);
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
function buildFormData(value, path = "", formData = new FormData()) {
|
|
354
|
-
if (typeof value === "undefined") {
|
|
355
|
-
return formData;
|
|
356
|
-
}
|
|
357
|
-
if (value instanceof Date) {
|
|
358
|
-
formData.append(path, value.toISOString());
|
|
359
|
-
return formData;
|
|
360
|
-
}
|
|
361
|
-
if (value instanceof Blob) {
|
|
362
|
-
formData.append(path, value);
|
|
363
|
-
return formData;
|
|
364
|
-
}
|
|
365
|
-
if (Array.isArray(value)) {
|
|
366
|
-
for (const item of value) {
|
|
367
|
-
buildFormData(item, `${path}[]`, formData);
|
|
368
|
-
}
|
|
369
|
-
return formData;
|
|
370
|
-
}
|
|
371
|
-
if (isPlainObject2(value)) {
|
|
372
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
373
|
-
const next = path ? `${path}.${key}` : key;
|
|
374
|
-
buildFormData(nested, next, formData);
|
|
375
|
-
}
|
|
376
|
-
return formData;
|
|
377
|
-
}
|
|
378
|
-
formData.append(path, String(value));
|
|
379
|
-
return formData;
|
|
380
|
-
}
|
|
381
363
|
function getActiveElementBrowserForm() {
|
|
382
364
|
const activeElement = globalThis.document?.activeElement;
|
|
383
365
|
return activeElement?.form ?? void 0;
|
|
@@ -432,11 +414,11 @@ function replaceErrorsForPath(flattenedErrors, path, nextErrors) {
|
|
|
432
414
|
return merged;
|
|
433
415
|
}
|
|
434
416
|
function buildFieldsTree(state, schemaDefinition, source, validateOn, prefix = "") {
|
|
435
|
-
if (
|
|
417
|
+
if (isFormLeafValue(source)) {
|
|
436
418
|
const path = prefix;
|
|
437
419
|
return Object.freeze({
|
|
438
420
|
get value() {
|
|
439
|
-
return
|
|
421
|
+
return getFormValueAtPath(state.values, path);
|
|
440
422
|
},
|
|
441
423
|
set value(next) {
|
|
442
424
|
void this.set(next);
|
|
@@ -451,9 +433,9 @@ function buildFieldsTree(state, schemaDefinition, source, validateOn, prefix = "
|
|
|
451
433
|
return state.dirty.has(path);
|
|
452
434
|
},
|
|
453
435
|
async set(value) {
|
|
454
|
-
|
|
436
|
+
setFormValueAtPath(state.values, path, value);
|
|
455
437
|
state.touched.add(path);
|
|
456
|
-
if (!
|
|
438
|
+
if (!areFormValuesEqual(getFormValueAtPath(state.initialValues, path), value)) {
|
|
457
439
|
state.dirty.add(path);
|
|
458
440
|
} else {
|
|
459
441
|
state.dirty.delete(path);
|
|
@@ -461,7 +443,7 @@ function buildFieldsTree(state, schemaDefinition, source, validateOn, prefix = "
|
|
|
461
443
|
if (validateOn === "change") {
|
|
462
444
|
const sequence = nextValidationSequence(state);
|
|
463
445
|
const submission = await validateClientValues(
|
|
464
|
-
|
|
446
|
+
cloneFormValue(state.values),
|
|
465
447
|
schemaDefinition
|
|
466
448
|
);
|
|
467
449
|
if (isLatestValidation(state, sequence)) {
|
|
@@ -478,7 +460,7 @@ function buildFieldsTree(state, schemaDefinition, source, validateOn, prefix = "
|
|
|
478
460
|
if (validateOn === "blur") {
|
|
479
461
|
const sequence = nextValidationSequence(state);
|
|
480
462
|
const submission = await validateClientValues(
|
|
481
|
-
|
|
463
|
+
cloneFormValue(state.values),
|
|
482
464
|
schemaDefinition
|
|
483
465
|
);
|
|
484
466
|
if (isLatestValidation(state, sequence)) {
|
|
@@ -493,7 +475,7 @@ function buildFieldsTree(state, schemaDefinition, source, validateOn, prefix = "
|
|
|
493
475
|
},
|
|
494
476
|
async validate() {
|
|
495
477
|
const sequence = nextValidationSequence(state);
|
|
496
|
-
const submission = await validateClientValues(
|
|
478
|
+
const submission = await validateClientValues(cloneFormValue(state.values), schemaDefinition);
|
|
497
479
|
if (isLatestValidation(state, sequence)) {
|
|
498
480
|
state.flattenedErrors = submission.errors.flatten();
|
|
499
481
|
notifyListeners(state);
|
|
@@ -515,13 +497,13 @@ function isSerializedSubmission(value) {
|
|
|
515
497
|
return "submitted" in value && "errors" in value && "values" in value && !("ok" in value);
|
|
516
498
|
}
|
|
517
499
|
function isFormFailurePayload2(value) {
|
|
518
|
-
return
|
|
500
|
+
return isPlainFormObject(value) && value.ok === false && typeof value.status === "number" && value.valid === false && isPlainFormObject(value.values) && isPlainFormObject(value.errors);
|
|
519
501
|
}
|
|
520
502
|
function isSvelteKitActionFailureResult(value) {
|
|
521
|
-
return
|
|
503
|
+
return isPlainFormObject(value) && value.type === "failure" && typeof value.status === "number" && typeof value.data === "string";
|
|
522
504
|
}
|
|
523
505
|
function isSvelteKitActionErrorResult(value) {
|
|
524
|
-
return
|
|
506
|
+
return isPlainFormObject(value) && value.type === "error" && isPlainFormObject(value.error);
|
|
525
507
|
}
|
|
526
508
|
function parseJsonObject(value) {
|
|
527
509
|
try {
|
|
@@ -648,13 +630,13 @@ function isSafeMethod(method) {
|
|
|
648
630
|
}
|
|
649
631
|
function createFormClient(schemaDefinition, options = {}) {
|
|
650
632
|
const initialState = options.initialState ?? void 0;
|
|
651
|
-
const initialValues =
|
|
652
|
-
|
|
653
|
-
|
|
633
|
+
const initialValues = mergeFormValues(
|
|
634
|
+
normalizeFormObject(options.initialValues),
|
|
635
|
+
normalizeFormObject(initialState?.values)
|
|
654
636
|
);
|
|
655
637
|
const state = {
|
|
656
|
-
values:
|
|
657
|
-
initialValues:
|
|
638
|
+
values: cloneFormValue(initialValues),
|
|
639
|
+
initialValues: cloneFormValue(initialValues),
|
|
658
640
|
flattenedErrors: { ...initialState?.errors ?? {} },
|
|
659
641
|
touched: /* @__PURE__ */ new Set(),
|
|
660
642
|
dirty: /* @__PURE__ */ new Set(),
|
|
@@ -664,7 +646,7 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
664
646
|
validationSequence: 0
|
|
665
647
|
};
|
|
666
648
|
const validateOn = options.validateOn ?? "submit";
|
|
667
|
-
const fieldPaths =
|
|
649
|
+
const fieldPaths = flattenFormLeafPaths(schemaDefinition.fields);
|
|
668
650
|
const fields = buildFieldsTree(
|
|
669
651
|
state,
|
|
670
652
|
schemaDefinition,
|
|
@@ -672,13 +654,13 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
672
654
|
validateOn
|
|
673
655
|
);
|
|
674
656
|
async function runValidation() {
|
|
675
|
-
return await runValidationForInput(
|
|
657
|
+
return await runValidationForInput(cloneFormValue(state.values));
|
|
676
658
|
}
|
|
677
659
|
async function runValidationForInput(input) {
|
|
678
660
|
const sequence = nextValidationSequence(state);
|
|
679
661
|
const submission = await validateClientValues(input, schemaDefinition);
|
|
680
662
|
if (isLatestValidation(state, sequence)) {
|
|
681
|
-
state.values =
|
|
663
|
+
state.values = mergeFormValues(
|
|
682
664
|
state.values,
|
|
683
665
|
submission.valid ? submission.data : submission.values
|
|
684
666
|
);
|
|
@@ -704,7 +686,7 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
704
686
|
...normalized,
|
|
705
687
|
values: sanitizeFlashedInput(normalized.values, schemaDefinition)
|
|
706
688
|
};
|
|
707
|
-
state.values =
|
|
689
|
+
state.values = mergeFormValues(state.values, sanitized.values);
|
|
708
690
|
clearSensitiveInputValues(state.values, schemaDefinition);
|
|
709
691
|
state.flattenedErrors = sanitized.errors;
|
|
710
692
|
state.lastSubmission = sanitized;
|
|
@@ -712,7 +694,7 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
712
694
|
return sanitized;
|
|
713
695
|
}
|
|
714
696
|
const normalizedSubmission = normalized;
|
|
715
|
-
state.values =
|
|
697
|
+
state.values = mergeFormValues(state.values, normalizedSubmission.values);
|
|
716
698
|
clearSensitiveInputValues(state.values, schemaDefinition);
|
|
717
699
|
state.flattenedErrors = normalizedSubmission.errors.flatten();
|
|
718
700
|
state.lastSubmission = normalizedSubmission.valid ? void 0 : normalizedSubmission.fail();
|
|
@@ -748,6 +730,9 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
748
730
|
formData
|
|
749
731
|
});
|
|
750
732
|
} catch (error) {
|
|
733
|
+
if (isClientSubmitControlFlowError(error)) {
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
751
736
|
return applyServerState(
|
|
752
737
|
validationExceptionToFailure(error, state.values) ?? createTransportFailure(state.values)
|
|
753
738
|
);
|
|
@@ -799,9 +784,9 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
799
784
|
return submit(form);
|
|
800
785
|
},
|
|
801
786
|
reset(values) {
|
|
802
|
-
const next =
|
|
803
|
-
state.values =
|
|
804
|
-
state.initialValues =
|
|
787
|
+
const next = mergeFormValues(state.initialValues, values);
|
|
788
|
+
state.values = cloneFormValue(next);
|
|
789
|
+
state.initialValues = cloneFormValue(next);
|
|
805
790
|
state.flattenedErrors = {};
|
|
806
791
|
state.touched.clear();
|
|
807
792
|
state.dirty.clear();
|
|
@@ -809,9 +794,9 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
809
794
|
notifyListeners(state);
|
|
810
795
|
},
|
|
811
796
|
async setValue(path, value) {
|
|
812
|
-
|
|
797
|
+
setFormValueAtPath(state.values, path, value);
|
|
813
798
|
state.touched.add(path);
|
|
814
|
-
if (!
|
|
799
|
+
if (!areFormValuesEqual(getFormValueAtPath(state.initialValues, path), value)) {
|
|
815
800
|
state.dirty.add(path);
|
|
816
801
|
} else {
|
|
817
802
|
state.dirty.delete(path);
|
|
@@ -829,5 +814,6 @@ function createFormClient(schemaDefinition, options = {}) {
|
|
|
829
814
|
}
|
|
830
815
|
export {
|
|
831
816
|
createFormClient,
|
|
817
|
+
markClientSubmitControlFlowError,
|
|
832
818
|
runWithBrowserFormElement
|
|
833
819
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/forms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Holo-JS Framework - forms-first validation and submission contracts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"test": "vitest --run"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@holo-js/validation": "^0.
|
|
36
|
+
"@holo-js/validation": "^0.3.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@holo-js/security": "^0.
|
|
39
|
+
"@holo-js/security": "^0.3.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@holo-js/security": {
|