@blueprint-ts/core 4.1.0-beta.6 → 5.0.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/bulkRequests/BulkRequestSender.js +81 -102
- package/dist/bulkRequests/BulkRequestWrapper.js +16 -26
- package/dist/laravel/pagination/dataDrivers/RequestDriver.js +1 -0
- package/dist/pagination/BasePaginator.js +4 -4
- package/dist/pagination/PageAwarePaginator.js +4 -1
- package/dist/pagination/StatePaginator.js +6 -3
- package/dist/pagination/dataDrivers/ArrayDriver.js +1 -0
- package/dist/pagination/dtos/PaginationDataDto.js +2 -0
- package/dist/pagination/dtos/StatePaginationDataDto.js +1 -0
- package/dist/pagination/frontendDrivers/VueBaseViewDriver.js +2 -0
- package/dist/pagination/frontendDrivers/VuePaginationDriver.js +6 -0
- package/dist/persistenceDrivers/LocalStorageDriver.js +1 -0
- package/dist/persistenceDrivers/MemoryPersistenceDriver.js +2 -1
- package/dist/persistenceDrivers/SessionStorageDriver.js +1 -0
- package/dist/requests/BaseRequest.js +93 -100
- package/dist/requests/ErrorHandler.js +21 -31
- package/dist/requests/RequestErrorRouter.js +12 -25
- package/dist/requests/bodies/BinaryBody.js +2 -0
- package/dist/requests/bodies/FormDataBody.js +1 -0
- package/dist/requests/bodies/JsonBody.js +1 -0
- package/dist/requests/drivers/fetch/FetchDriver.js +26 -26
- package/dist/requests/drivers/fetch/FetchResponse.js +7 -21
- package/dist/requests/drivers/mock/MockRequestDriver.js +190 -169
- package/dist/requests/drivers/mock/MockRequestTestHelpers.js +10 -4
- package/dist/requests/drivers/mock/MockResponseHandler.js +12 -23
- package/dist/requests/drivers/xhr/XMLHttpRequestDriver.js +68 -74
- package/dist/requests/drivers/xhr/XMLHttpRequestResponse.js +9 -21
- package/dist/requests/exceptions/InvalidJsonException.js +1 -0
- package/dist/requests/exceptions/ResponseBodyException.js +1 -0
- package/dist/requests/exceptions/ResponseException.js +1 -0
- package/dist/requests/exceptions/StaleResponseException.js +1 -0
- package/dist/requests/factories/BinaryBodyFactory.js +1 -0
- package/dist/requests/responses/BaseResponse.js +9 -21
- package/dist/requests/responses/BlobResponse.js +1 -0
- package/dist/support/DeferredPromise.js +5 -4
- package/dist/vue/composables/useConfirmDialog.js +7 -18
- package/dist/vue/composables/useGlobalCheckbox.js +55 -66
- package/dist/vue/forms/BaseForm.d.ts +11 -8
- package/dist/vue/forms/BaseForm.js +153 -149
- package/dist/vue/forms/PropertyAwareArray.js +0 -1
- package/dist/vue/forms/PropertyAwareObject.js +4 -2
- package/dist/vue/forms/index.d.ts +2 -2
- package/dist/vue/forms/persistence/types.d.ts +2 -0
- package/dist/vue/forms/validation/rules/BaseRule.js +3 -16
- package/dist/vue/forms/validation/rules/ConfirmedRule.js +2 -0
- package/dist/vue/forms/validation/rules/EmailRule.js +1 -0
- package/dist/vue/forms/validation/rules/JsonRule.js +2 -1
- package/dist/vue/forms/validation/rules/MinRule.js +2 -0
- package/dist/vue/forms/validation/rules/PrecognitiveRule.js +21 -31
- package/dist/vue/forms/validation/rules/RequiredRule.js +1 -0
- package/dist/vue/forms/validation/rules/UrlRule.js +2 -1
- package/dist/vue/requests/loaders/VueRequestBatchLoader.js +3 -3
- package/dist/vue/requests/loaders/VueRequestLoader.js +1 -0
- package/dist/vue/router/routeResourceBinding/RouteResourceBoundView.js +8 -18
- package/dist/vue/router/routeResourceBinding/RouteResourceRequestResolver.js +4 -14
- package/dist/vue/router/routeResourceBinding/defineRoute.js +8 -7
- package/dist/vue/router/routeResourceBinding/installRouteInjection.js +12 -21
- package/dist/vue/router/routeResourceBinding/useRouteResource.js +5 -17
- package/dist/vue/state/State.d.ts +2 -0
- package/dist/vue/state/State.js +24 -11
- package/dist/vue/state/index.d.ts +2 -1
- package/package.json +27 -27
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { reactive, computed, toRaw, watch } from 'vue';
|
|
11
2
|
import { camelCase, upperFirst, cloneDeep, debounce, isEqual } from 'lodash-es';
|
|
12
3
|
import { NonPersistentDriver } from '../../persistenceDrivers/NonPersistentDriver';
|
|
@@ -57,7 +48,6 @@ function restoreSerializedPropertyAwareValue(value) {
|
|
|
57
48
|
return value;
|
|
58
49
|
}
|
|
59
50
|
export function propertyAwareToRaw(propertyAwareObject) {
|
|
60
|
-
var _a;
|
|
61
51
|
if (Array.isArray(propertyAwareObject)) {
|
|
62
52
|
return propertyAwareObject.map((item) => propertyAwareToRaw(item));
|
|
63
53
|
}
|
|
@@ -71,7 +61,7 @@ export function propertyAwareToRaw(propertyAwareObject) {
|
|
|
71
61
|
continue;
|
|
72
62
|
}
|
|
73
63
|
const value = record[key];
|
|
74
|
-
const modelValue = isRecord(value) ?
|
|
64
|
+
const modelValue = isRecord(value) ? value.model?.value : undefined;
|
|
75
65
|
if (modelValue !== undefined) {
|
|
76
66
|
result[key] = modelValue;
|
|
77
67
|
continue;
|
|
@@ -100,7 +90,7 @@ function deepMergeArrays(target, source) {
|
|
|
100
90
|
return t;
|
|
101
91
|
}
|
|
102
92
|
if (t && typeof t === 'object' && s && typeof s === 'object') {
|
|
103
|
-
return shallowMerge(
|
|
93
|
+
return shallowMerge({ ...t }, s);
|
|
104
94
|
}
|
|
105
95
|
return s;
|
|
106
96
|
});
|
|
@@ -150,6 +140,27 @@ function restorePropertyAwareStructure(defaults, value) {
|
|
|
150
140
|
* (We assume that for every key in RequestBody there is a corresponding key in FormBody.)
|
|
151
141
|
*/
|
|
152
142
|
export class BaseForm {
|
|
143
|
+
options;
|
|
144
|
+
state;
|
|
145
|
+
dirty;
|
|
146
|
+
touched;
|
|
147
|
+
original;
|
|
148
|
+
_model;
|
|
149
|
+
_errors = reactive({});
|
|
150
|
+
_asyncErrors = reactive({});
|
|
151
|
+
_hasErrors;
|
|
152
|
+
append = [];
|
|
153
|
+
ignore = [];
|
|
154
|
+
errorMap = {};
|
|
155
|
+
rules = {};
|
|
156
|
+
validationGroups = {};
|
|
157
|
+
fieldDependencies = new Map();
|
|
158
|
+
arrayWrapperCache = new Map();
|
|
159
|
+
arrayItemWrapperCache = new Map();
|
|
160
|
+
asyncValidationDebouncers = new Map();
|
|
161
|
+
pendingAsyncValidationContexts = new Map();
|
|
162
|
+
asyncValidationTokens = reactive({});
|
|
163
|
+
persistKey;
|
|
153
164
|
/**
|
|
154
165
|
* Returns the persistence driver to use.
|
|
155
166
|
* The default is a NonPersistentDriver.
|
|
@@ -159,6 +170,12 @@ export class BaseForm {
|
|
|
159
170
|
getPersistenceDriver(_suffix) {
|
|
160
171
|
return new NonPersistentDriver();
|
|
161
172
|
}
|
|
173
|
+
getActivePersistenceDriver() {
|
|
174
|
+
if (this.options?.persist !== true) {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
return this.getPersistenceDriver(this.options.persistSuffix);
|
|
178
|
+
}
|
|
162
179
|
getPersistenceRestorePolicy() {
|
|
163
180
|
return new StrictPersistenceRestorePolicy();
|
|
164
181
|
}
|
|
@@ -169,9 +186,9 @@ export class BaseForm {
|
|
|
169
186
|
if (!this.shouldLogPersistenceDebug()) {
|
|
170
187
|
return;
|
|
171
188
|
}
|
|
172
|
-
const
|
|
189
|
+
const context = event.persistSuffix ? `${event.formName}, ${event.persistSuffix}` : event.formName;
|
|
173
190
|
const details = event.details ? ` ${JSON.stringify(event.details)}` : '';
|
|
174
|
-
console.debug(`[BaseForm persistence] ${event.
|
|
191
|
+
console.debug(`[BaseForm persistence] ${event.persistKey} (${context}): ${event.action} (${event.reason})${details}`);
|
|
175
192
|
}
|
|
176
193
|
/**
|
|
177
194
|
* Helper: recursively computes the dirty state for a value based on the original.
|
|
@@ -231,12 +248,14 @@ export class BaseForm {
|
|
|
231
248
|
this.state[key] = new PropertyAwareArray(values);
|
|
232
249
|
}
|
|
233
250
|
persistState(driver) {
|
|
234
|
-
|
|
235
|
-
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.persist) === false) {
|
|
251
|
+
if (this.options?.persist !== true) {
|
|
236
252
|
return;
|
|
237
253
|
}
|
|
238
|
-
const persistDriver = driver
|
|
239
|
-
persistDriver
|
|
254
|
+
const persistDriver = driver ?? this.getActivePersistenceDriver();
|
|
255
|
+
if (!persistDriver) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
persistDriver.set(this.resolvePersistKey(), {
|
|
240
259
|
state: toRaw(this.state),
|
|
241
260
|
original: toRaw(this.original),
|
|
242
261
|
dirty: toRaw(this.dirty),
|
|
@@ -259,24 +278,23 @@ export class BaseForm {
|
|
|
259
278
|
* This identifies which fields need to be revalidated when another field changes
|
|
260
279
|
*/
|
|
261
280
|
buildFieldDependencies() {
|
|
262
|
-
var _a, _b, _c, _d;
|
|
263
281
|
for (const field in this.rules) {
|
|
264
282
|
if (Object.prototype.hasOwnProperty.call(this.rules, field)) {
|
|
265
|
-
const fieldRules =
|
|
283
|
+
const fieldRules = this.rules[field]?.rules || [];
|
|
266
284
|
for (const rule of fieldRules) {
|
|
267
285
|
for (const dependencyField of rule.dependsOn) {
|
|
268
286
|
if (!this.fieldDependencies.has(dependencyField)) {
|
|
269
287
|
this.fieldDependencies.set(dependencyField, new Set());
|
|
270
288
|
}
|
|
271
|
-
|
|
289
|
+
this.fieldDependencies.get(dependencyField)?.add(field);
|
|
272
290
|
}
|
|
273
|
-
const bidirectionalFields =
|
|
291
|
+
const bidirectionalFields = rule.getBidirectionalFields?.();
|
|
274
292
|
if (bidirectionalFields && bidirectionalFields.length > 0) {
|
|
275
293
|
for (const bidirectionalField of bidirectionalFields) {
|
|
276
294
|
if (!this.fieldDependencies.has(field)) {
|
|
277
295
|
this.fieldDependencies.set(field, new Set());
|
|
278
296
|
}
|
|
279
|
-
|
|
297
|
+
this.fieldDependencies.get(field)?.add(bidirectionalField);
|
|
280
298
|
}
|
|
281
299
|
}
|
|
282
300
|
}
|
|
@@ -306,35 +324,24 @@ export class BaseForm {
|
|
|
306
324
|
}
|
|
307
325
|
}
|
|
308
326
|
constructor(defaults, options) {
|
|
309
|
-
var _a;
|
|
310
327
|
this.options = options;
|
|
311
|
-
|
|
312
|
-
this.
|
|
313
|
-
this.append = [];
|
|
314
|
-
this.ignore = [];
|
|
315
|
-
this.errorMap = {};
|
|
316
|
-
this.rules = {};
|
|
317
|
-
this.validationGroups = {};
|
|
318
|
-
this.fieldDependencies = new Map();
|
|
319
|
-
this.arrayWrapperCache = new Map();
|
|
320
|
-
this.arrayItemWrapperCache = new Map();
|
|
321
|
-
this.asyncValidationDebouncers = new Map();
|
|
322
|
-
this.pendingAsyncValidationContexts = new Map();
|
|
323
|
-
this.asyncValidationTokens = reactive({});
|
|
324
|
-
const persist = (options === null || options === void 0 ? void 0 : options.persist) !== false;
|
|
328
|
+
const persist = options?.persist === true;
|
|
329
|
+
this.persistKey = persist ? this.requirePersistKey(options) : options?.persistKey;
|
|
325
330
|
let initialData;
|
|
326
|
-
const driver = this.getPersistenceDriver(options
|
|
327
|
-
if (persist) {
|
|
328
|
-
const persisted =
|
|
331
|
+
const driver = persist ? this.getPersistenceDriver(options?.persistSuffix) : undefined;
|
|
332
|
+
if (persist && driver) {
|
|
333
|
+
const persisted = driver.get(this.resolvePersistKey()) ?? null;
|
|
329
334
|
const restoreDecision = this.getPersistenceRestorePolicy().resolve({
|
|
330
335
|
formName: this.constructor.name,
|
|
331
|
-
|
|
336
|
+
persistKey: this.resolvePersistKey(),
|
|
337
|
+
persistSuffix: options?.persistSuffix,
|
|
332
338
|
defaults,
|
|
333
339
|
persisted
|
|
334
340
|
});
|
|
335
341
|
this.logPersistenceDebug({
|
|
336
342
|
formName: this.constructor.name,
|
|
337
|
-
|
|
343
|
+
persistKey: this.resolvePersistKey(),
|
|
344
|
+
persistSuffix: options?.persistSuffix,
|
|
338
345
|
action: restoreDecision.action,
|
|
339
346
|
reason: restoreDecision.reason,
|
|
340
347
|
details: restoreDecision.details
|
|
@@ -352,7 +359,7 @@ export class BaseForm {
|
|
|
352
359
|
this.dirty = init.dirty;
|
|
353
360
|
this.touched = init.touched;
|
|
354
361
|
if (restoreDecision.action === 'discard') {
|
|
355
|
-
driver.remove(this.
|
|
362
|
+
driver.remove(this.resolvePersistKey());
|
|
356
363
|
}
|
|
357
364
|
}
|
|
358
365
|
}
|
|
@@ -400,11 +407,23 @@ export class BaseForm {
|
|
|
400
407
|
}
|
|
401
408
|
}
|
|
402
409
|
this._hasErrors = computed(() => Object.keys(this.flattenErrors()).length > 0);
|
|
403
|
-
if (persist) {
|
|
410
|
+
if (persist && driver) {
|
|
404
411
|
watch(() => this.state, () => this.persistState(driver), { deep: true, immediate: true });
|
|
405
412
|
}
|
|
406
413
|
this.validate();
|
|
407
414
|
}
|
|
415
|
+
requirePersistKey(options) {
|
|
416
|
+
if (!options?.persistKey) {
|
|
417
|
+
throw new Error('BaseForm persistence requires a stable persistKey option.');
|
|
418
|
+
}
|
|
419
|
+
return options.persistKey;
|
|
420
|
+
}
|
|
421
|
+
resolvePersistKey() {
|
|
422
|
+
if (!this.persistKey) {
|
|
423
|
+
throw new Error('BaseForm persistence requires a stable persistKey option.');
|
|
424
|
+
}
|
|
425
|
+
return this.persistKey;
|
|
426
|
+
}
|
|
408
427
|
defineRules() {
|
|
409
428
|
return {};
|
|
410
429
|
}
|
|
@@ -429,35 +448,32 @@ export class BaseForm {
|
|
|
429
448
|
}
|
|
430
449
|
hasAsyncRules(field) {
|
|
431
450
|
const fieldConfig = this.rules[field];
|
|
432
|
-
if (!
|
|
451
|
+
if (!fieldConfig?.rules || fieldConfig.rules.length === 0) {
|
|
433
452
|
return false;
|
|
434
453
|
}
|
|
435
454
|
return fieldConfig.rules.some((rule) => rule.validateAsync !== BaseRule.prototype.validateAsync);
|
|
436
455
|
}
|
|
437
456
|
bumpAsyncValidationToken(field) {
|
|
438
|
-
var _a;
|
|
439
457
|
const fieldKey = String(field);
|
|
440
|
-
const nextToken = (
|
|
458
|
+
const nextToken = (this.asyncValidationTokens[fieldKey] ?? 0) + 1;
|
|
441
459
|
this.asyncValidationTokens[fieldKey] = nextToken;
|
|
442
460
|
return nextToken;
|
|
443
461
|
}
|
|
444
462
|
cancelPendingAsyncValidations() {
|
|
445
|
-
var _a;
|
|
446
463
|
for (const debouncer of this.asyncValidationDebouncers.values()) {
|
|
447
464
|
debouncer.cancel();
|
|
448
465
|
}
|
|
449
466
|
this.pendingAsyncValidationContexts.clear();
|
|
450
467
|
for (const fieldKey of Object.keys(this.asyncValidationTokens)) {
|
|
451
|
-
this.asyncValidationTokens[fieldKey] = (
|
|
468
|
+
this.asyncValidationTokens[fieldKey] = (this.asyncValidationTokens[fieldKey] ?? 0) + 1;
|
|
452
469
|
}
|
|
453
470
|
}
|
|
454
471
|
scheduleAsyncValidation(field, context) {
|
|
455
|
-
var _a, _b, _c;
|
|
456
472
|
if (!this.hasAsyncRules(field)) {
|
|
457
473
|
return;
|
|
458
474
|
}
|
|
459
475
|
const token = this.bumpAsyncValidationToken(field);
|
|
460
|
-
const debounceMs =
|
|
476
|
+
const debounceMs = this.rules[field]?.options?.asyncDebounceMs ?? 0;
|
|
461
477
|
this.pendingAsyncValidationContexts.set(field, { token, context });
|
|
462
478
|
if (debounceMs <= 0) {
|
|
463
479
|
void this.executeScheduledAsyncValidation(field).catch(() => undefined);
|
|
@@ -472,14 +488,16 @@ export class BaseForm {
|
|
|
472
488
|
}
|
|
473
489
|
debouncer();
|
|
474
490
|
}
|
|
475
|
-
executeScheduledAsyncValidation(field) {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
491
|
+
async executeScheduledAsyncValidation(field) {
|
|
492
|
+
const pending = this.pendingAsyncValidationContexts.get(field);
|
|
493
|
+
if (pending === undefined) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
await this.runFieldAsyncValidation(field, {
|
|
497
|
+
...pending.context,
|
|
498
|
+
skipSyncValidation: true,
|
|
499
|
+
skipAsyncValidation: true
|
|
500
|
+
}, pending.token);
|
|
483
501
|
}
|
|
484
502
|
flattenErrorValue(value, path, flattened) {
|
|
485
503
|
if (value === undefined) {
|
|
@@ -538,15 +556,13 @@ export class BaseForm {
|
|
|
538
556
|
return merged;
|
|
539
557
|
}
|
|
540
558
|
flattenErrors() {
|
|
541
|
-
var _a;
|
|
542
559
|
const flattened = this.flattenErrorsFromBag(this._errors);
|
|
543
560
|
for (const [key, messages] of Object.entries(this.flattenErrorsFromBag(this._asyncErrors))) {
|
|
544
|
-
flattened[key] = this.mergeErrorMessages(
|
|
561
|
+
flattened[key] = this.mergeErrorMessages(flattened[key] ?? [], messages);
|
|
545
562
|
}
|
|
546
563
|
return flattened;
|
|
547
564
|
}
|
|
548
565
|
applyErrors(errorsData, useErrorMap, targetBag = this._errors) {
|
|
549
|
-
var _a, _b, _c;
|
|
550
566
|
for (const serverKey in errorsData) {
|
|
551
567
|
if (!Object.prototype.hasOwnProperty.call(errorsData, serverKey)) {
|
|
552
568
|
continue;
|
|
@@ -557,7 +573,7 @@ export class BaseForm {
|
|
|
557
573
|
}
|
|
558
574
|
let targetKeys = [serverKey];
|
|
559
575
|
if (useErrorMap) {
|
|
560
|
-
const mapping =
|
|
576
|
+
const mapping = this.errorMap?.[serverKey];
|
|
561
577
|
if (mapping) {
|
|
562
578
|
targetKeys = Array.isArray(mapping) ? mapping : [mapping];
|
|
563
579
|
}
|
|
@@ -565,8 +581,8 @@ export class BaseForm {
|
|
|
565
581
|
for (const targetKey of targetKeys) {
|
|
566
582
|
const parts = targetKey.split('.');
|
|
567
583
|
if (parts.length > 1) {
|
|
568
|
-
const topKey =
|
|
569
|
-
const indexPart =
|
|
584
|
+
const topKey = parts[0] ?? '';
|
|
585
|
+
const indexPart = parts[1] ?? '';
|
|
570
586
|
const index = Number.parseInt(indexPart, 10);
|
|
571
587
|
if (!topKey) {
|
|
572
588
|
targetBag[targetKey] = errorMessage;
|
|
@@ -957,11 +973,10 @@ export class BaseForm {
|
|
|
957
973
|
const index = this.resolveArrayItemIndex(field, item);
|
|
958
974
|
return index < 0 ? [] : this.getArrayItemFieldErrors(String(field), index, [innerKey, ...path].join('.'));
|
|
959
975
|
}, (path) => {
|
|
960
|
-
var _a;
|
|
961
976
|
const index = this.resolveArrayItemIndex(field, item);
|
|
962
977
|
return index < 0
|
|
963
978
|
? false
|
|
964
|
-
: this.getNestedDirtyValue(
|
|
979
|
+
: this.getNestedDirtyValue(this.dirty[field]?.[index], [innerKey, ...path]);
|
|
965
980
|
}, () => this.touched[field] || false);
|
|
966
981
|
continue;
|
|
967
982
|
}
|
|
@@ -1011,11 +1026,10 @@ export class BaseForm {
|
|
|
1011
1026
|
* @param field The field to mark as touched
|
|
1012
1027
|
*/
|
|
1013
1028
|
touch(field) {
|
|
1014
|
-
var _a, _b, _c;
|
|
1015
1029
|
this.touched[field] = true;
|
|
1016
1030
|
const fieldConfig = this.rules[field];
|
|
1017
1031
|
if (fieldConfig) {
|
|
1018
|
-
const mode =
|
|
1032
|
+
const mode = fieldConfig.options?.mode ?? ValidationMode.DEFAULT;
|
|
1019
1033
|
if (mode & ValidationMode.ON_TOUCH) {
|
|
1020
1034
|
this.validateField(field, {
|
|
1021
1035
|
isSubmitting: false,
|
|
@@ -1023,7 +1037,7 @@ export class BaseForm {
|
|
|
1023
1037
|
});
|
|
1024
1038
|
}
|
|
1025
1039
|
}
|
|
1026
|
-
if (
|
|
1040
|
+
if (this.options?.persist === true) {
|
|
1027
1041
|
this.persistState();
|
|
1028
1042
|
}
|
|
1029
1043
|
}
|
|
@@ -1036,16 +1050,15 @@ export class BaseForm {
|
|
|
1036
1050
|
return !!this.touched[field];
|
|
1037
1051
|
}
|
|
1038
1052
|
validateField(field, context = {}) {
|
|
1039
|
-
var _a, _b;
|
|
1040
1053
|
const emptyErrors = [];
|
|
1041
1054
|
const errorKey = String(field);
|
|
1042
1055
|
this._errors[errorKey] = emptyErrors;
|
|
1043
1056
|
const value = this.state[field];
|
|
1044
1057
|
const fieldConfig = this.rules[field];
|
|
1045
|
-
if (!
|
|
1058
|
+
if (!fieldConfig?.rules || fieldConfig.rules.length === 0) {
|
|
1046
1059
|
return; // No rules to validate
|
|
1047
1060
|
}
|
|
1048
|
-
const mode =
|
|
1061
|
+
const mode = fieldConfig.options?.mode ?? ValidationMode.DEFAULT;
|
|
1049
1062
|
const isDirty = context.isDirty !== undefined ? context.isDirty : this.isDirty(field);
|
|
1050
1063
|
const isTouched = context.isTouched !== undefined ? context.isTouched : this.isTouched(field);
|
|
1051
1064
|
const shouldValidate = (context.isSubmitting && mode & ValidationMode.ON_SUBMIT) ||
|
|
@@ -1066,7 +1079,6 @@ export class BaseForm {
|
|
|
1066
1079
|
}
|
|
1067
1080
|
}
|
|
1068
1081
|
validate(isSubmitting = false, options = {}) {
|
|
1069
|
-
var _a;
|
|
1070
1082
|
let isValid = true;
|
|
1071
1083
|
this.clearSyncErrors();
|
|
1072
1084
|
for (const field in this.rules) {
|
|
@@ -1075,7 +1087,7 @@ export class BaseForm {
|
|
|
1075
1087
|
isSubmitting,
|
|
1076
1088
|
isDependentChange: false,
|
|
1077
1089
|
isTouched: this.isTouched(field),
|
|
1078
|
-
skipAsyncValidation:
|
|
1090
|
+
skipAsyncValidation: options.skipAsyncValidation ?? false
|
|
1079
1091
|
});
|
|
1080
1092
|
const fieldErrors = this._errors[String(field)];
|
|
1081
1093
|
if (isErrorMessages(fieldErrors) && fieldErrors.length > 0) {
|
|
@@ -1086,7 +1098,6 @@ export class BaseForm {
|
|
|
1086
1098
|
return isValid;
|
|
1087
1099
|
}
|
|
1088
1100
|
validateGroup(group, isSubmitting = false, options = {}) {
|
|
1089
|
-
var _a;
|
|
1090
1101
|
const fields = this.getValidationGroupFields(group);
|
|
1091
1102
|
if (fields.length === 0) {
|
|
1092
1103
|
return true;
|
|
@@ -1097,81 +1108,78 @@ export class BaseForm {
|
|
|
1097
1108
|
isSubmitting,
|
|
1098
1109
|
isDependentChange: false,
|
|
1099
1110
|
isTouched: this.isTouched(field),
|
|
1100
|
-
skipAsyncValidation:
|
|
1111
|
+
skipAsyncValidation: options.skipAsyncValidation ?? false
|
|
1101
1112
|
});
|
|
1102
1113
|
}
|
|
1103
1114
|
return !this.hasErrorsInGroup(group);
|
|
1104
1115
|
}
|
|
1105
|
-
runFieldAsyncValidation(
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
}
|
|
1128
|
-
if (expectedToken !== undefined && this.asyncValidationTokens[String(field)] !== expectedToken) {
|
|
1129
|
-
return !Object.keys(this.flattenErrors()).some((errorKey) => this.matchesValidationGroupPath(errorKey, String(field)));
|
|
1130
|
-
}
|
|
1131
|
-
this.clearErrorBagPaths(this._asyncErrors, asyncPaths);
|
|
1132
|
-
for (const [key, messages] of Object.entries(this.flattenErrorsFromBag(nextAsyncErrors))) {
|
|
1133
|
-
this.applyErrors({ [key]: messages }, false, this._asyncErrors);
|
|
1116
|
+
async runFieldAsyncValidation(field, context = {}, expectedToken) {
|
|
1117
|
+
if (!context.skipSyncValidation) {
|
|
1118
|
+
this.validateField(field, {
|
|
1119
|
+
...context,
|
|
1120
|
+
skipAsyncValidation: true
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
const fieldConfig = this.rules[field];
|
|
1124
|
+
if (!fieldConfig?.rules || fieldConfig.rules.length === 0) {
|
|
1125
|
+
return !Object.keys(this.flattenErrors()).some((errorKey) => this.matchesValidationGroupPath(errorKey, String(field)));
|
|
1126
|
+
}
|
|
1127
|
+
const asyncPaths = fieldConfig.rules.flatMap((rule) => rule.getAsyncValidationPaths(field, this.state));
|
|
1128
|
+
const payload = this.buildPayload();
|
|
1129
|
+
const nextAsyncErrors = {};
|
|
1130
|
+
for (const rule of fieldConfig.rules) {
|
|
1131
|
+
const errors = await rule.validateAsync(this.state[field], this.state, {
|
|
1132
|
+
field: String(field),
|
|
1133
|
+
payload,
|
|
1134
|
+
isSubmitting: context.isSubmitting ?? false
|
|
1135
|
+
});
|
|
1136
|
+
if (errors && Object.keys(errors).length > 0) {
|
|
1137
|
+
this.applyErrors(errors, false, nextAsyncErrors);
|
|
1134
1138
|
}
|
|
1139
|
+
}
|
|
1140
|
+
if (expectedToken !== undefined && this.asyncValidationTokens[String(field)] !== expectedToken) {
|
|
1135
1141
|
return !Object.keys(this.flattenErrors()).some((errorKey) => this.matchesValidationGroupPath(errorKey, String(field)));
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
});
|
|
1142
|
+
}
|
|
1143
|
+
this.clearErrorBagPaths(this._asyncErrors, asyncPaths);
|
|
1144
|
+
for (const [key, messages] of Object.entries(this.flattenErrorsFromBag(nextAsyncErrors))) {
|
|
1145
|
+
this.applyErrors({ [key]: messages }, false, this._asyncErrors);
|
|
1146
|
+
}
|
|
1147
|
+
return !Object.keys(this.flattenErrors()).some((errorKey) => this.matchesValidationGroupPath(errorKey, String(field)));
|
|
1143
1148
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
isSubmitting,
|
|
1151
|
-
isTouched: this.isTouched(field),
|
|
1152
|
-
skipSyncValidation: true
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
return isSyncValid && !this.hasErrors();
|
|
1157
|
-
});
|
|
1149
|
+
async validateFieldAsync(field, context = {}) {
|
|
1150
|
+
const token = this.bumpAsyncValidationToken(field);
|
|
1151
|
+
return await this.runFieldAsyncValidation(field, {
|
|
1152
|
+
...context,
|
|
1153
|
+
skipAsyncValidation: true
|
|
1154
|
+
}, token);
|
|
1158
1155
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
if (
|
|
1163
|
-
|
|
1164
|
-
}
|
|
1165
|
-
const isSyncValid = this.validateGroup(group, isSubmitting, { skipAsyncValidation: true });
|
|
1166
|
-
for (const field of fields) {
|
|
1167
|
-
yield this.validateFieldAsync(field, {
|
|
1156
|
+
async validateAsync(isSubmitting = false) {
|
|
1157
|
+
const isSyncValid = this.validate(isSubmitting, { skipAsyncValidation: true });
|
|
1158
|
+
for (const field in this.rules) {
|
|
1159
|
+
if (Object.prototype.hasOwnProperty.call(this.rules, field)) {
|
|
1160
|
+
await this.validateFieldAsync(field, {
|
|
1168
1161
|
isSubmitting,
|
|
1169
1162
|
isTouched: this.isTouched(field),
|
|
1170
1163
|
skipSyncValidation: true
|
|
1171
1164
|
});
|
|
1172
1165
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1166
|
+
}
|
|
1167
|
+
return isSyncValid && !this.hasErrors();
|
|
1168
|
+
}
|
|
1169
|
+
async validateGroupAsync(group, isSubmitting = false) {
|
|
1170
|
+
const fields = this.getValidationGroupFields(group);
|
|
1171
|
+
if (fields.length === 0) {
|
|
1172
|
+
return true;
|
|
1173
|
+
}
|
|
1174
|
+
const isSyncValid = this.validateGroup(group, isSubmitting, { skipAsyncValidation: true });
|
|
1175
|
+
for (const field of fields) {
|
|
1176
|
+
await this.validateFieldAsync(field, {
|
|
1177
|
+
isSubmitting,
|
|
1178
|
+
isTouched: this.isTouched(field),
|
|
1179
|
+
skipSyncValidation: true
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
return isSyncValid && !this.hasErrorsInGroup(group);
|
|
1175
1183
|
}
|
|
1176
1184
|
touchGroup(group) {
|
|
1177
1185
|
for (const field of this.getValidationGroupFields(group)) {
|
|
@@ -1179,8 +1187,7 @@ export class BaseForm {
|
|
|
1179
1187
|
}
|
|
1180
1188
|
}
|
|
1181
1189
|
fillState(data) {
|
|
1182
|
-
|
|
1183
|
-
const driver = this.getPersistenceDriver((_a = this.options) === null || _a === void 0 ? void 0 : _a.persistSuffix);
|
|
1190
|
+
const driver = this.getActivePersistenceDriver();
|
|
1184
1191
|
for (const key of Object.keys(data)) {
|
|
1185
1192
|
if (!Object.prototype.hasOwnProperty.call(data, key) || !(key in this.state)) {
|
|
1186
1193
|
continue;
|
|
@@ -1208,7 +1215,7 @@ export class BaseForm {
|
|
|
1208
1215
|
continue;
|
|
1209
1216
|
}
|
|
1210
1217
|
if (isRecord(newVal) && isRecord(currentVal)) {
|
|
1211
|
-
this.state[key] = shallowMerge(
|
|
1218
|
+
this.state[key] = shallowMerge({ ...currentVal }, newVal);
|
|
1212
1219
|
this.dirty[key] = this.computeDirtyState(this.state[key], this.original[key]);
|
|
1213
1220
|
this.touched[key] = true;
|
|
1214
1221
|
continue;
|
|
@@ -1332,8 +1339,7 @@ export class BaseForm {
|
|
|
1332
1339
|
return payload;
|
|
1333
1340
|
}
|
|
1334
1341
|
reset() {
|
|
1335
|
-
|
|
1336
|
-
const driver = this.getPersistenceDriver((_a = this.options) === null || _a === void 0 ? void 0 : _a.persistSuffix);
|
|
1342
|
+
const driver = this.getActivePersistenceDriver();
|
|
1337
1343
|
for (const key in this.state) {
|
|
1338
1344
|
if (this.state[key] instanceof PropertyAwareArray) {
|
|
1339
1345
|
const originalValue = this.original[key];
|
|
@@ -1364,8 +1370,7 @@ export class BaseForm {
|
|
|
1364
1370
|
this.validate();
|
|
1365
1371
|
}
|
|
1366
1372
|
addToArrayProperty(property, newElement) {
|
|
1367
|
-
|
|
1368
|
-
const driver = this.getPersistenceDriver((_a = this.options) === null || _a === void 0 ? void 0 : _a.persistSuffix);
|
|
1373
|
+
const driver = this.getActivePersistenceDriver();
|
|
1369
1374
|
const arr = this.state[property];
|
|
1370
1375
|
if (arr instanceof PropertyAwareArray) {
|
|
1371
1376
|
arr.push(newElement);
|
|
@@ -1515,8 +1520,7 @@ export class BaseForm {
|
|
|
1515
1520
|
* @param value The new value to set
|
|
1516
1521
|
*/
|
|
1517
1522
|
syncValue(key, value) {
|
|
1518
|
-
|
|
1519
|
-
const driver = this.getPersistenceDriver((_a = this.options) === null || _a === void 0 ? void 0 : _a.persistSuffix);
|
|
1523
|
+
const driver = this.getActivePersistenceDriver();
|
|
1520
1524
|
const currentVal = this.state[key];
|
|
1521
1525
|
if (currentVal instanceof PropertyAwareArray) {
|
|
1522
1526
|
const arr = this.state[key];
|
|
@@ -1557,7 +1561,7 @@ export class BaseForm {
|
|
|
1557
1561
|
this.dirty[key] = false;
|
|
1558
1562
|
this.touched[key] = true;
|
|
1559
1563
|
}
|
|
1560
|
-
if (
|
|
1564
|
+
if (this.options?.persist === true) {
|
|
1561
1565
|
this.persistState(driver);
|
|
1562
1566
|
}
|
|
1563
1567
|
this.validateField(key);
|
|
@@ -11,7 +11,6 @@ export class PropertyAwareArray extends Array {
|
|
|
11
11
|
constructor(items = []) {
|
|
12
12
|
// Call Array constructor with array length
|
|
13
13
|
super();
|
|
14
|
-
void this.__propertyAwareArrayBrand;
|
|
15
14
|
// Add items to the array
|
|
16
15
|
if (items && items.length) {
|
|
17
16
|
items.forEach((item) => this.push(item));
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
export const PROPERTY_AWARE_OBJECT_MARKER = '__propertyAwareObject';
|
|
7
7
|
export class PropertyAwareObject {
|
|
8
8
|
constructor(values) {
|
|
9
|
-
void this.__propertyAwareObjectBrand;
|
|
10
9
|
Object.assign(this, values);
|
|
11
10
|
}
|
|
12
11
|
static from(values) {
|
|
13
12
|
return new PropertyAwareObject(values);
|
|
14
13
|
}
|
|
15
14
|
toJSON() {
|
|
16
|
-
return
|
|
15
|
+
return {
|
|
16
|
+
[PROPERTY_AWARE_OBJECT_MARKER]: true,
|
|
17
|
+
...this
|
|
18
|
+
};
|
|
17
19
|
}
|
|
18
20
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseForm, propertyAwareToRaw } from './BaseForm';
|
|
1
|
+
import { BaseForm, propertyAwareToRaw, type BaseFormOptions } from './BaseForm';
|
|
2
2
|
import { type PersistedForm } from './types/PersistedForm';
|
|
3
3
|
import { LocalStorageDriver } from '../../persistenceDrivers/LocalStorageDriver';
|
|
4
4
|
import { MemoryPersistenceDriver } from '../../persistenceDrivers/MemoryPersistenceDriver';
|
|
@@ -10,4 +10,4 @@ import { PropertyAwareObject } from './PropertyAwareObject';
|
|
|
10
10
|
import { StrictPersistenceRestorePolicy } from './persistence';
|
|
11
11
|
import type { PersistenceDebugEvent, PersistenceRestoreContext, PersistenceRestorePolicy, PersistenceRestoreResult } from './persistence';
|
|
12
12
|
export { BaseForm, propertyAwareToRaw, PropertyAwareArray, PropertyAwareObject, NonPersistentDriver, SessionStorageDriver, LocalStorageDriver, MemoryPersistenceDriver, StrictPersistenceRestorePolicy };
|
|
13
|
-
export type { PersistedForm, PersistenceDriver, PropertyAwareField, PropertyAware, PersistenceDebugEvent, PersistenceRestoreContext, PersistenceRestorePolicy, PersistenceRestoreResult };
|
|
13
|
+
export type { PersistedForm, PersistenceDriver, PropertyAwareField, PropertyAware, PersistenceDebugEvent, PersistenceRestoreContext, PersistenceRestorePolicy, PersistenceRestoreResult, BaseFormOptions };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PersistedForm } from '../types/PersistedForm';
|
|
2
2
|
export interface PersistenceRestoreContext<FormBody extends object> {
|
|
3
3
|
formName: string;
|
|
4
|
+
persistKey: string;
|
|
4
5
|
persistSuffix?: string | undefined;
|
|
5
6
|
defaults: FormBody;
|
|
6
7
|
persisted: PersistedForm<FormBody> | null;
|
|
@@ -13,6 +14,7 @@ export interface PersistenceRestoreResult<FormBody extends object> {
|
|
|
13
14
|
}
|
|
14
15
|
export interface PersistenceDebugEvent<FormBody extends object> {
|
|
15
16
|
formName: string;
|
|
17
|
+
persistKey: string;
|
|
16
18
|
persistSuffix?: string | undefined;
|
|
17
19
|
action: PersistenceRestoreResult<FormBody>['action'];
|
|
18
20
|
reason: string;
|