@regle/core 1.2.3 → 1.3.0-beta.1
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 +13 -11
- package/dist/regle-core.d.ts +1102 -1165
- package/dist/regle-core.js +2555 -0
- package/dist/regle-core.min.js +1 -0
- package/package.json +12 -12
- package/dist/regle-core.min.mjs +0 -2
- package/dist/regle-core.mjs +0 -2877
|
@@ -0,0 +1,2555 @@
|
|
|
1
|
+
import { computed, effectScope, getCurrentInstance, getCurrentScope, isRef, markRaw, nextTick, onMounted, onScopeDispose, reactive, ref, shallowRef, toRef, toValue, triggerRef, unref, version, watch, watchEffect } from "vue";
|
|
2
|
+
|
|
3
|
+
//#region ../shared/utils/isFile.ts
|
|
4
|
+
/**
|
|
5
|
+
* Server side friendly way of checking for a File
|
|
6
|
+
*/
|
|
7
|
+
function isFile(value) {
|
|
8
|
+
return value?.constructor.name == "File" || value?.constructor.name == "FileList";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region ../shared/utils/isEmpty.ts
|
|
13
|
+
/**
|
|
14
|
+
* This is the inverse of isFilled. It will check if the value is in any way empty (including arrays and objects)
|
|
15
|
+
*
|
|
16
|
+
* isEmpty also acts as a type guard.
|
|
17
|
+
*
|
|
18
|
+
* @param value - the target value
|
|
19
|
+
* @param [considerEmptyArrayInvalid=true] - will return false if set to `false`. (default: `true`)
|
|
20
|
+
*/
|
|
21
|
+
function isEmpty(value, considerEmptyArrayInvalid = true) {
|
|
22
|
+
if (value === void 0 || value === null) return true;
|
|
23
|
+
if (value instanceof Date) return isNaN(value.getTime());
|
|
24
|
+
else if (isFile(value)) return value.size <= 0;
|
|
25
|
+
else if (Array.isArray(value)) {
|
|
26
|
+
if (considerEmptyArrayInvalid) return value.length === 0;
|
|
27
|
+
return false;
|
|
28
|
+
} else if (typeof value === "object" && value != null) return Object.keys(value).length === 0;
|
|
29
|
+
return !String(value).length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region ../shared/utils/symbol.ts
|
|
34
|
+
const RegleRuleSymbol = Symbol("regle-rule");
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region ../shared/utils/cloneDeep.ts
|
|
38
|
+
function getRegExpFlags(regExp) {
|
|
39
|
+
if (typeof regExp.source.flags == "string") return regExp.source.flags;
|
|
40
|
+
else {
|
|
41
|
+
let flags = [];
|
|
42
|
+
regExp.global && flags.push("g");
|
|
43
|
+
regExp.ignoreCase && flags.push("i");
|
|
44
|
+
regExp.multiline && flags.push("m");
|
|
45
|
+
regExp.sticky && flags.push("y");
|
|
46
|
+
regExp.unicode && flags.push("u");
|
|
47
|
+
return flags.join("");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function cloneDeep(obj) {
|
|
51
|
+
let result = obj;
|
|
52
|
+
let type = {}.toString.call(obj).slice(8, -1);
|
|
53
|
+
if (type == "Set") result = new Set([...obj].map((value) => cloneDeep(value)));
|
|
54
|
+
if (type == "Map") result = new Map([...obj].map((kv) => [cloneDeep(kv[0]), cloneDeep(kv[1])]));
|
|
55
|
+
if (type == "Date") result = new Date(obj.getTime());
|
|
56
|
+
if (type == "RegExp") result = RegExp(obj.source, getRegExpFlags(obj));
|
|
57
|
+
if (type == "Array" || type == "Object") {
|
|
58
|
+
result = Array.isArray(obj) ? [] : {};
|
|
59
|
+
for (let key in obj) result[key] = cloneDeep(obj[key]);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region ../shared/utils/object.utils.ts
|
|
66
|
+
function isObject(obj) {
|
|
67
|
+
if (obj && (obj instanceof Date || obj.constructor.name == "File" || obj.constructor.name == "FileList")) return false;
|
|
68
|
+
return typeof obj === "object" && obj !== null && !Array.isArray(obj);
|
|
69
|
+
}
|
|
70
|
+
function merge(obj1, ...objs) {
|
|
71
|
+
var args = [].slice.call(arguments);
|
|
72
|
+
var arg;
|
|
73
|
+
var i = args.length;
|
|
74
|
+
while (arg = args[i - 1], i--) if (!arg || typeof arg != "object" && typeof arg != "function") throw new Error("expected object, got " + arg);
|
|
75
|
+
var result = args[0];
|
|
76
|
+
var extenders = args.slice(1);
|
|
77
|
+
var len = extenders.length;
|
|
78
|
+
for (var i = 0; i < len; i++) {
|
|
79
|
+
var extender = extenders[i];
|
|
80
|
+
for (var key in extender) result[key] = extender[key];
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region ../shared/utils/toDate.ts
|
|
87
|
+
/**
|
|
88
|
+
* This utility will coerce any string, number or Date value into a Date using the Date constructor.
|
|
89
|
+
*/
|
|
90
|
+
function toDate(argument) {
|
|
91
|
+
const argStr = Object.prototype.toString.call(argument);
|
|
92
|
+
if (argument == null) return new Date(NaN);
|
|
93
|
+
else if (argument instanceof Date || typeof argument === "object" && argStr === "[object Date]") return new Date(argument.getTime());
|
|
94
|
+
else if (typeof argument === "number" || argStr === "[object Number]") return new Date(argument);
|
|
95
|
+
else if (typeof argument === "string" || argStr === "[object String]") return new Date(argument);
|
|
96
|
+
else return new Date(NaN);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region ../shared/utils/debounce.ts
|
|
101
|
+
function debounce(func, wait, immediate) {
|
|
102
|
+
let timeout;
|
|
103
|
+
const debouncedFn = (...args) => new Promise((resolve) => {
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
timeout = setTimeout(() => {
|
|
106
|
+
timeout = void 0;
|
|
107
|
+
if (!immediate) Promise.resolve(func.apply(this, [...args])).then(resolve);
|
|
108
|
+
}, wait);
|
|
109
|
+
if (immediate && !timeout) Promise.resolve(func.apply(this, [...args])).then(resolve);
|
|
110
|
+
});
|
|
111
|
+
debouncedFn.cancel = () => {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
timeout = void 0;
|
|
114
|
+
};
|
|
115
|
+
return debouncedFn;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region ../shared/utils/isEqual.ts
|
|
120
|
+
function isEqual(a, b, deep = false, firstDeep = true) {
|
|
121
|
+
if (a === b) return true;
|
|
122
|
+
if (a && b && typeof a == "object" && typeof b == "object") {
|
|
123
|
+
if (a.constructor !== b.constructor) return false;
|
|
124
|
+
var length, i, keys;
|
|
125
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
126
|
+
length = a.length;
|
|
127
|
+
if (length != b.length) return false;
|
|
128
|
+
if (firstDeep || !firstDeep && deep) {
|
|
129
|
+
for (i = length; i-- !== 0;) if (!isEqual(a[i], b[i], deep, false)) return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
|
|
134
|
+
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
|
|
135
|
+
keys = Object.keys(a);
|
|
136
|
+
length = keys.length;
|
|
137
|
+
if (length !== Object.keys(b).length) return false;
|
|
138
|
+
for (i = length; i-- !== 0;) if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
|
|
139
|
+
for (i = length; i-- !== 0;) {
|
|
140
|
+
var key = keys[i];
|
|
141
|
+
if (isObject(a) && isObject(b)) {
|
|
142
|
+
if (firstDeep || !firstDeep && deep) {
|
|
143
|
+
if (!isEqual(a[key], b[key], deep, false)) return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
return a !== a && b !== b;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/types/rules/rule.internal.types.ts
|
|
155
|
+
const InternalRuleType = {
|
|
156
|
+
Inline: "__inline",
|
|
157
|
+
Async: "__async"
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/types/utils/groups.ts
|
|
162
|
+
function mergeBooleanGroupProperties(entries, property) {
|
|
163
|
+
return entries.some((entry) => {
|
|
164
|
+
return entry[property];
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function mergeArrayGroupProperties(entries, property) {
|
|
168
|
+
return entries.reduce((all, entry) => {
|
|
169
|
+
const fetchedProperty = entry[property] || [];
|
|
170
|
+
return all.concat(fetchedProperty);
|
|
171
|
+
}, []);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/core/createRule/unwrapRuleParameters.ts
|
|
176
|
+
/**
|
|
177
|
+
* Returns a clean list of parameters
|
|
178
|
+
* Removing Ref and executing function to return the unwrapped value
|
|
179
|
+
*/
|
|
180
|
+
function unwrapRuleParameters(params) {
|
|
181
|
+
try {
|
|
182
|
+
return params.map((param) => toValue(param));
|
|
183
|
+
} catch (e) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Returns a clean list of parameters
|
|
189
|
+
* Removing Ref and executing function to return the unwrapped value
|
|
190
|
+
*/
|
|
191
|
+
function createReactiveParams(params) {
|
|
192
|
+
return params.map((param) => {
|
|
193
|
+
if (param instanceof Function) return computed(param);
|
|
194
|
+
else if (isRef(param)) return param;
|
|
195
|
+
return toRef(() => param);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Due to `function.length` not returning default parameters, it needed to parse the func.toString()
|
|
200
|
+
*/
|
|
201
|
+
function getFunctionParametersLength(func) {
|
|
202
|
+
const funcStr = func.toString();
|
|
203
|
+
const cleanStr = funcStr.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
204
|
+
const paramsMatch = cleanStr.match(/^(?:async\s*)?(?:function\b.*?\(|\((.*?)\)|(\w+))\s*=>|\((.*?)\)\s*=>|function.*?\((.*?)\)|\((.*?)\)/);
|
|
205
|
+
if (!paramsMatch) return 0;
|
|
206
|
+
const paramsSection = paramsMatch[0] || paramsMatch[1] || paramsMatch[2] || paramsMatch[3] || paramsMatch[4] || "";
|
|
207
|
+
const paramList = paramsSection.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
208
|
+
return paramList.length;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/core/createRule/defineRuleProcessors.ts
|
|
213
|
+
function defineRuleProcessors(definition, ...params) {
|
|
214
|
+
const { validator, type } = definition;
|
|
215
|
+
const isAsync = type === InternalRuleType.Async || validator.constructor.name === "AsyncFunction";
|
|
216
|
+
const defaultProcessors = {
|
|
217
|
+
validator(value, ...args) {
|
|
218
|
+
return definition.validator(value, ...unwrapRuleParameters(args.length ? args : params));
|
|
219
|
+
},
|
|
220
|
+
message(metadata) {
|
|
221
|
+
if (typeof definition.message === "function") return definition.message({
|
|
222
|
+
...metadata,
|
|
223
|
+
$params: unwrapRuleParameters(metadata?.$params?.length ? metadata.$params : params)
|
|
224
|
+
});
|
|
225
|
+
else return definition.message;
|
|
226
|
+
},
|
|
227
|
+
active(metadata) {
|
|
228
|
+
if (typeof definition.active === "function") return definition.active({
|
|
229
|
+
...metadata,
|
|
230
|
+
$params: unwrapRuleParameters(metadata?.$params?.length ? metadata.$params : params)
|
|
231
|
+
});
|
|
232
|
+
else return definition.active ?? true;
|
|
233
|
+
},
|
|
234
|
+
tooltip(metadata) {
|
|
235
|
+
if (typeof definition.tooltip === "function") return definition.tooltip({
|
|
236
|
+
...metadata,
|
|
237
|
+
$params: unwrapRuleParameters(metadata?.$params?.length ? metadata.$params : params)
|
|
238
|
+
});
|
|
239
|
+
else return definition.tooltip ?? [];
|
|
240
|
+
},
|
|
241
|
+
exec(value) {
|
|
242
|
+
const validator$1 = definition.validator(value, ...unwrapRuleParameters(params));
|
|
243
|
+
let rawResult;
|
|
244
|
+
if (validator$1 instanceof Promise) return validator$1.then((result) => {
|
|
245
|
+
rawResult = result;
|
|
246
|
+
if (typeof rawResult === "object" && "$valid" in rawResult) return rawResult.$valid;
|
|
247
|
+
else if (typeof rawResult === "boolean") return rawResult;
|
|
248
|
+
return false;
|
|
249
|
+
});
|
|
250
|
+
else rawResult = validator$1;
|
|
251
|
+
if (typeof rawResult === "object" && "$valid" in rawResult) return rawResult.$valid;
|
|
252
|
+
else if (typeof rawResult === "boolean") return rawResult;
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const processors = markRaw({
|
|
257
|
+
...defaultProcessors,
|
|
258
|
+
_validator: definition.validator,
|
|
259
|
+
_message: definition.message,
|
|
260
|
+
_active: definition.active,
|
|
261
|
+
_tooltip: definition.tooltip,
|
|
262
|
+
_type: definition.type,
|
|
263
|
+
_message_patched: false,
|
|
264
|
+
_tooltip_patched: false,
|
|
265
|
+
_async: isAsync,
|
|
266
|
+
_params: createReactiveParams(params),
|
|
267
|
+
_brand: RegleRuleSymbol
|
|
268
|
+
});
|
|
269
|
+
return processors;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/core/createRule/createRule.ts
|
|
274
|
+
/**
|
|
275
|
+
* Create a typed custom rule that can be used like default rules.
|
|
276
|
+
* It can also be declared in the global options
|
|
277
|
+
*
|
|
278
|
+
* It will automatically detect if the rule is async
|
|
279
|
+
*
|
|
280
|
+
*
|
|
281
|
+
* @param definition - The rule processors object
|
|
282
|
+
*
|
|
283
|
+
* @returns A rule definition that can be callable depending on params presence
|
|
284
|
+
*
|
|
285
|
+
* @exemple
|
|
286
|
+
*
|
|
287
|
+
* ```ts
|
|
288
|
+
* // Create a simple rule with no params
|
|
289
|
+
* import {isFilled} from '@regle/rules';
|
|
290
|
+
*
|
|
291
|
+
* export const isFoo = createRule({
|
|
292
|
+
* validator(value: Maybe<string>) {
|
|
293
|
+
* if (isFilled(value)) {
|
|
294
|
+
* return value === 'foo';
|
|
295
|
+
* }
|
|
296
|
+
* return true
|
|
297
|
+
* },
|
|
298
|
+
* message: "The value should be 'foo'"
|
|
299
|
+
* })
|
|
300
|
+
*
|
|
301
|
+
* ```
|
|
302
|
+
*
|
|
303
|
+
* Docs: {@link https://reglejs.dev/core-concepts/rules/reusable-rules}
|
|
304
|
+
*/
|
|
305
|
+
function createRule(definition) {
|
|
306
|
+
if (typeof definition.validator === "function") {
|
|
307
|
+
let fakeParams = [];
|
|
308
|
+
const staticProcessors = defineRuleProcessors(definition, ...fakeParams);
|
|
309
|
+
const isAsync = definition.async ?? definition.validator.constructor.name === "AsyncFunction";
|
|
310
|
+
if (getFunctionParametersLength(definition.validator) > 1) {
|
|
311
|
+
const ruleFactory = function(...params) {
|
|
312
|
+
return defineRuleProcessors(definition, ...params);
|
|
313
|
+
};
|
|
314
|
+
ruleFactory.validator = staticProcessors.validator;
|
|
315
|
+
ruleFactory.message = staticProcessors.message;
|
|
316
|
+
ruleFactory.active = staticProcessors.active;
|
|
317
|
+
ruleFactory.tooltip = staticProcessors.tooltip;
|
|
318
|
+
ruleFactory.type = staticProcessors.type;
|
|
319
|
+
ruleFactory.exec = staticProcessors.exec;
|
|
320
|
+
ruleFactory._validator = staticProcessors.validator;
|
|
321
|
+
ruleFactory._message = staticProcessors.message;
|
|
322
|
+
ruleFactory._active = staticProcessors.active;
|
|
323
|
+
ruleFactory._tooltip = staticProcessors.tooltip;
|
|
324
|
+
ruleFactory._type = definition.type;
|
|
325
|
+
ruleFactory._message_pacthed = false;
|
|
326
|
+
ruleFactory._tooltip_pacthed = false;
|
|
327
|
+
ruleFactory._async = isAsync;
|
|
328
|
+
return ruleFactory;
|
|
329
|
+
} else return staticProcessors;
|
|
330
|
+
}
|
|
331
|
+
throw new Error("[createRule] validator must be a function");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
//#endregion
|
|
335
|
+
//#region src/core/useStorage/useStorage.ts
|
|
336
|
+
/**
|
|
337
|
+
* Inspired by Vuelidate storage
|
|
338
|
+
*/
|
|
339
|
+
function useStorage() {
|
|
340
|
+
const ruleDeclStorage = shallowRef(new Map());
|
|
341
|
+
const fieldsStorage = shallowRef(new Map());
|
|
342
|
+
const collectionsStorage = shallowRef(new Map());
|
|
343
|
+
const dirtyStorage = shallowRef(new Map());
|
|
344
|
+
const ruleStatusStorage = shallowRef(new Map());
|
|
345
|
+
const arrayStatusStorage = shallowRef(new Map());
|
|
346
|
+
function getFieldsEntry($path) {
|
|
347
|
+
const existingFields = fieldsStorage.value.get($path);
|
|
348
|
+
if (existingFields) return existingFields;
|
|
349
|
+
else {
|
|
350
|
+
const $fields = ref({});
|
|
351
|
+
fieldsStorage.value.set($path, $fields);
|
|
352
|
+
return $fields;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function getCollectionsEntry($path) {
|
|
356
|
+
const existingEach = collectionsStorage.value.get($path);
|
|
357
|
+
if (existingEach) return existingEach;
|
|
358
|
+
else {
|
|
359
|
+
const $each = ref([]);
|
|
360
|
+
collectionsStorage.value.set($path, $each);
|
|
361
|
+
return $each;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function addArrayStatus($arrayId, itemId, value) {
|
|
365
|
+
arrayStatusStorage.value.set(`${$arrayId}-${itemId}`, value);
|
|
366
|
+
}
|
|
367
|
+
function getArrayStatus($arrayId, itemId) {
|
|
368
|
+
return arrayStatusStorage.value.get(`${$arrayId}-${itemId}`);
|
|
369
|
+
}
|
|
370
|
+
function deleteArrayStatus($arrayId, itemId) {
|
|
371
|
+
if ($arrayId && itemId != null) arrayStatusStorage.value.delete(`${$arrayId}-${itemId}`);
|
|
372
|
+
}
|
|
373
|
+
function setDirtyEntry($path, dirty) {
|
|
374
|
+
dirtyStorage.value.set($path, dirty);
|
|
375
|
+
}
|
|
376
|
+
function getDirtyState(path) {
|
|
377
|
+
return dirtyStorage.value.get(path) ?? false;
|
|
378
|
+
}
|
|
379
|
+
function addRuleDeclEntry($path, options) {
|
|
380
|
+
ruleDeclStorage.value.set($path, options);
|
|
381
|
+
}
|
|
382
|
+
function checkRuleDeclEntry($path, newRules) {
|
|
383
|
+
const storedRulesDefs = ruleDeclStorage.value.get($path);
|
|
384
|
+
if (!storedRulesDefs) return void 0;
|
|
385
|
+
const storedRules = storedRulesDefs;
|
|
386
|
+
const isValidCache = areRulesChanged(newRules, storedRules);
|
|
387
|
+
if (!isValidCache) return { valid: false };
|
|
388
|
+
return { valid: true };
|
|
389
|
+
}
|
|
390
|
+
function areRulesChanged(newRules, storedRules) {
|
|
391
|
+
const storedRulesKeys = Object.keys(storedRules);
|
|
392
|
+
const newRulesKeys = Object.keys(newRules);
|
|
393
|
+
if (newRulesKeys.length !== storedRulesKeys.length) return false;
|
|
394
|
+
const hasAllValidators = newRulesKeys.every((ruleKey) => storedRulesKeys.includes(ruleKey));
|
|
395
|
+
if (!hasAllValidators) return false;
|
|
396
|
+
return newRulesKeys.every((ruleKey) => {
|
|
397
|
+
const newRuleElement = newRules[ruleKey];
|
|
398
|
+
const storedRuleElement = storedRules[ruleKey];
|
|
399
|
+
if (!storedRuleElement || !newRuleElement || typeof newRuleElement === "function" || typeof storedRuleElement === "function") return false;
|
|
400
|
+
if (typeof newRuleElement === "number") return false;
|
|
401
|
+
else if (typeof newRuleElement === "boolean") return false;
|
|
402
|
+
else if (!newRuleElement._params) return true;
|
|
403
|
+
else return newRuleElement._params?.every((paramKey, index) => {
|
|
404
|
+
if (typeof storedRuleElement === "number" || typeof storedRuleElement === "boolean") return true;
|
|
405
|
+
else {
|
|
406
|
+
const storedParams = unwrapRuleParameters(storedRuleElement._params);
|
|
407
|
+
const newParams = unwrapRuleParameters(newRuleElement._params);
|
|
408
|
+
return storedParams?.[index] === newParams?.[index];
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
function trySetRuleStatusRef(path) {
|
|
414
|
+
const ruleStatus = ruleStatusStorage.value.get(path);
|
|
415
|
+
if (ruleStatus) return ruleStatus;
|
|
416
|
+
else {
|
|
417
|
+
const $pending = ref(false);
|
|
418
|
+
const $valid = ref(true);
|
|
419
|
+
const $metadata = ref({});
|
|
420
|
+
const $validating = ref(false);
|
|
421
|
+
ruleStatusStorage.value.set(path, {
|
|
422
|
+
$pending,
|
|
423
|
+
$valid,
|
|
424
|
+
$metadata,
|
|
425
|
+
$validating
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
$pending,
|
|
429
|
+
$valid,
|
|
430
|
+
$metadata,
|
|
431
|
+
$validating
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (getCurrentScope()) onScopeDispose(() => {
|
|
436
|
+
ruleDeclStorage.value.clear();
|
|
437
|
+
fieldsStorage.value.clear();
|
|
438
|
+
collectionsStorage.value.clear();
|
|
439
|
+
dirtyStorage.value.clear();
|
|
440
|
+
ruleStatusStorage.value.clear();
|
|
441
|
+
arrayStatusStorage.value.clear();
|
|
442
|
+
});
|
|
443
|
+
return {
|
|
444
|
+
addRuleDeclEntry,
|
|
445
|
+
setDirtyEntry,
|
|
446
|
+
checkRuleDeclEntry,
|
|
447
|
+
getDirtyState,
|
|
448
|
+
trySetRuleStatusRef,
|
|
449
|
+
getFieldsEntry,
|
|
450
|
+
getCollectionsEntry,
|
|
451
|
+
getArrayStatus,
|
|
452
|
+
addArrayStatus,
|
|
453
|
+
deleteArrayStatus,
|
|
454
|
+
arrayStatusStorage
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
//#endregion
|
|
459
|
+
//#region src/utils/object.utils.ts
|
|
460
|
+
function isRefObject(obj) {
|
|
461
|
+
return isObject(obj.value);
|
|
462
|
+
}
|
|
463
|
+
function unwrapGetter(getter, value, index) {
|
|
464
|
+
const scope = effectScope();
|
|
465
|
+
let unwrapped;
|
|
466
|
+
if (getter instanceof Function) unwrapped = scope.run(() => getter(value, index ?? 0));
|
|
467
|
+
else unwrapped = getter;
|
|
468
|
+
return {
|
|
469
|
+
scope,
|
|
470
|
+
unwrapped
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/utils/version-compare.ts
|
|
476
|
+
const VersionIs = {
|
|
477
|
+
LessThan: -1,
|
|
478
|
+
EqualTo: 0,
|
|
479
|
+
GreaterThan: 1
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* Compare two versions quickly.
|
|
483
|
+
* @param current Is this version greater, equal to, or less than the other?
|
|
484
|
+
* @param other The version to compare against the current version
|
|
485
|
+
* @return 1 if current is greater than other, 0 if they are equal or equivalent, and -1 if current is less than other
|
|
486
|
+
*/
|
|
487
|
+
function versionCompare(current, other) {
|
|
488
|
+
const cp = String(current).split(".");
|
|
489
|
+
const op = String(other).split(".");
|
|
490
|
+
for (let depth = 0; depth < Math.min(cp.length, op.length); depth++) {
|
|
491
|
+
const cn = Number(cp[depth]);
|
|
492
|
+
const on = Number(op[depth]);
|
|
493
|
+
if (cn > on) return VersionIs.GreaterThan;
|
|
494
|
+
if (on > cn) return VersionIs.LessThan;
|
|
495
|
+
if (!isNaN(cn) && isNaN(on)) return VersionIs.GreaterThan;
|
|
496
|
+
if (isNaN(cn) && !isNaN(on)) return VersionIs.LessThan;
|
|
497
|
+
}
|
|
498
|
+
return VersionIs.EqualTo;
|
|
499
|
+
}
|
|
500
|
+
const isVueSuperiorOrEqualTo3dotFive = versionCompare(version, "3.5.0") === -1 ? false : true;
|
|
501
|
+
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region src/utils/randomId.ts
|
|
504
|
+
function uniqueIDNuxt() {
|
|
505
|
+
return Math.floor(Math.random() * Date.now()).toString();
|
|
506
|
+
}
|
|
507
|
+
function randomId() {
|
|
508
|
+
if (typeof window === "undefined") return uniqueIDNuxt();
|
|
509
|
+
else {
|
|
510
|
+
const uint32 = window.crypto.getRandomValues(new Uint32Array(1))[0];
|
|
511
|
+
return uint32.toString(10);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/utils/state.utils.ts
|
|
517
|
+
function tryOnScopeDispose(fn) {
|
|
518
|
+
if (getCurrentScope()) {
|
|
519
|
+
onScopeDispose(fn);
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
function createGlobalState(stateFactory) {
|
|
525
|
+
let initialized = false;
|
|
526
|
+
let state;
|
|
527
|
+
const scope = effectScope(true);
|
|
528
|
+
return (...args) => {
|
|
529
|
+
if (!initialized) {
|
|
530
|
+
state = scope.run(() => stateFactory(...args));
|
|
531
|
+
initialized = true;
|
|
532
|
+
}
|
|
533
|
+
return state;
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region src/core/useRegle/guards/ruleDef.guards.ts
|
|
539
|
+
function isNestedRulesDef(state, rules) {
|
|
540
|
+
return isRefObject(state) || isObject(rules.value) && !isEmpty(rules.value) && !Object.entries(rules.value).some(([key, rule]) => isRuleDef(rule) || typeof rule === "function");
|
|
541
|
+
}
|
|
542
|
+
function isCollectionRulesDef(rules, state, schemaMode = false) {
|
|
543
|
+
return !!rules.value && isObject(rules.value) && "$each" in rules.value || schemaMode && Array.isArray(state.value) && state.value.some(isObject) || Array.isArray(state.value) && state.value.some(isObject);
|
|
544
|
+
}
|
|
545
|
+
function isValidatorRulesDef(rules) {
|
|
546
|
+
return !!rules.value && isObject(rules.value);
|
|
547
|
+
}
|
|
548
|
+
function isRuleDef(rule) {
|
|
549
|
+
return isObject(rule) && "_validator" in rule;
|
|
550
|
+
}
|
|
551
|
+
function isFormRuleDefinition(rule) {
|
|
552
|
+
if (typeof rule.value === "function") {
|
|
553
|
+
if ("_validator" in rule.value) return true;
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/core/useRegle/guards/rule.status.guards.ts
|
|
561
|
+
function isNestedRulesStatus(rule) {
|
|
562
|
+
return isObject(rule) && "$fields" in rule;
|
|
563
|
+
}
|
|
564
|
+
function isFieldStatus(rule) {
|
|
565
|
+
return !!rule && "$rules" in rule;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
//#endregion
|
|
569
|
+
//#region src/core/useRegle/useErrors.ts
|
|
570
|
+
function extractRulesErrors({ field, silent = false }) {
|
|
571
|
+
return Object.entries(field.$rules ?? {}).map(([_, rule]) => {
|
|
572
|
+
if (silent && !rule.$valid) return rule.$message;
|
|
573
|
+
else if (!rule.$valid && field.$error && !rule.$validating) return rule.$message;
|
|
574
|
+
return null;
|
|
575
|
+
}).filter((msg) => !!msg).reduce((acc, value) => {
|
|
576
|
+
if (typeof value === "string") return acc?.concat([value]);
|
|
577
|
+
else return acc?.concat(value);
|
|
578
|
+
}, []).concat(field.$error ? field.$externalErrors ?? [] : []).concat(field.$error ? field.$schemaErrors ?? [] : []);
|
|
579
|
+
}
|
|
580
|
+
function extractRulesTooltips({ field }) {
|
|
581
|
+
return Object.entries(field.$rules ?? {}).map(([_, rule]) => rule.$tooltip).filter((tooltip) => !!tooltip).reduce((acc, value) => {
|
|
582
|
+
if (typeof value === "string") return acc?.concat([value]);
|
|
583
|
+
else return acc?.concat(value);
|
|
584
|
+
}, []);
|
|
585
|
+
}
|
|
586
|
+
function isCollectionError(errors) {
|
|
587
|
+
return isObject(errors) && "$each" in errors;
|
|
588
|
+
}
|
|
589
|
+
function flatErrors(errors, options) {
|
|
590
|
+
const { includePath = false } = options ?? {};
|
|
591
|
+
if (Array.isArray(errors) && errors.every((err) => !isObject(err))) return errors;
|
|
592
|
+
else if (isCollectionError(errors)) {
|
|
593
|
+
const selfErrors = includePath ? errors.$self?.map((err) => ({
|
|
594
|
+
error: err,
|
|
595
|
+
path: ""
|
|
596
|
+
})) ?? [] : errors.$self ?? [];
|
|
597
|
+
const eachErrors = errors.$each?.map((err) => iterateErrors(err, includePath)) ?? [];
|
|
598
|
+
return selfErrors?.concat(eachErrors.flat());
|
|
599
|
+
} else return Object.entries(errors).map(([key, value]) => iterateErrors(value, includePath, [key])).flat();
|
|
600
|
+
}
|
|
601
|
+
function iterateErrors(errors, includePath = false, _path) {
|
|
602
|
+
const path = includePath && !_path ? [] : _path;
|
|
603
|
+
if (Array.isArray(errors) && errors.every((err) => !isObject(err))) {
|
|
604
|
+
if (includePath) return errors.map((err) => ({
|
|
605
|
+
error: err,
|
|
606
|
+
path: path?.join(".") ?? ""
|
|
607
|
+
}));
|
|
608
|
+
return errors;
|
|
609
|
+
} else if (isCollectionError(errors)) {
|
|
610
|
+
const selfErrors = path?.length ? errors.$self?.map((err) => ({
|
|
611
|
+
error: err,
|
|
612
|
+
path: path.join(".")
|
|
613
|
+
})) ?? [] : errors.$self ?? [];
|
|
614
|
+
const eachErrors = errors.$each?.map((err, index) => iterateErrors(err, includePath, path?.concat(index.toString()))) ?? [];
|
|
615
|
+
return selfErrors?.concat(eachErrors.flat());
|
|
616
|
+
} else return Object.entries(errors).map(([key, value]) => iterateErrors(value, includePath, path?.concat(key))).flat();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/core/useRegle/root/createReactiveRuleStatus.ts
|
|
621
|
+
function createReactiveRuleStatus({ customMessages, rule, ruleKey, state, path, storage, $debounce, modifiers }) {
|
|
622
|
+
let scope = effectScope();
|
|
623
|
+
let scopeState = {};
|
|
624
|
+
let $unwatchState;
|
|
625
|
+
const $haveAsync = ref(false);
|
|
626
|
+
const $maybePending = ref(false);
|
|
627
|
+
const { $pending, $valid, $metadata, $validating } = storage.trySetRuleStatusRef(`${path}.${ruleKey}`);
|
|
628
|
+
function $watch() {
|
|
629
|
+
scope = effectScope();
|
|
630
|
+
scopeState = scope.run(() => {
|
|
631
|
+
const $fieldDirty = ref(false);
|
|
632
|
+
const $fieldError = ref(false);
|
|
633
|
+
const $fieldInvalid = ref(true);
|
|
634
|
+
const $fieldPending = ref(false);
|
|
635
|
+
const $fieldCorrect = ref(false);
|
|
636
|
+
const $defaultMetadata = computed(() => ({
|
|
637
|
+
$value: state.value,
|
|
638
|
+
$error: $fieldError.value,
|
|
639
|
+
$dirty: $fieldDirty.value,
|
|
640
|
+
$pending: $fieldPending.value,
|
|
641
|
+
$correct: $fieldCorrect.value,
|
|
642
|
+
$invalid: $fieldInvalid.value,
|
|
643
|
+
$rule: {
|
|
644
|
+
$valid: $valid.value,
|
|
645
|
+
$invalid: !$valid.value,
|
|
646
|
+
$pending: $pending.value
|
|
647
|
+
},
|
|
648
|
+
$params: $params.value,
|
|
649
|
+
...$metadata.value
|
|
650
|
+
}));
|
|
651
|
+
const $active = computed(() => {
|
|
652
|
+
if (isFormRuleDefinition(rule)) if (typeof rule.value.active === "function") return rule.value.active($defaultMetadata.value);
|
|
653
|
+
else return !!rule.value.active;
|
|
654
|
+
else return true;
|
|
655
|
+
});
|
|
656
|
+
function computeRuleProcessor(key) {
|
|
657
|
+
let result = "";
|
|
658
|
+
const customProcessor = customMessages ? customMessages[ruleKey]?.[key] : void 0;
|
|
659
|
+
if (customProcessor) if (typeof customProcessor === "function") result = customProcessor($defaultMetadata.value);
|
|
660
|
+
else result = customProcessor;
|
|
661
|
+
if (isFormRuleDefinition(rule)) {
|
|
662
|
+
const patchedKey = `_${key}_patched`;
|
|
663
|
+
if (!(customProcessor && !rule.value[patchedKey])) if (typeof rule.value[key] === "function") result = rule.value[key]($defaultMetadata.value);
|
|
664
|
+
else result = rule.value[key] ?? "";
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
const $message = computed(() => {
|
|
669
|
+
let message = computeRuleProcessor("message");
|
|
670
|
+
if (isEmpty(message)) message = "This field is not valid";
|
|
671
|
+
return message;
|
|
672
|
+
});
|
|
673
|
+
const $tooltip = computed(() => {
|
|
674
|
+
return computeRuleProcessor("tooltip");
|
|
675
|
+
});
|
|
676
|
+
const $type = computed(() => {
|
|
677
|
+
if (isFormRuleDefinition(rule) && rule.value.type) return rule.value.type;
|
|
678
|
+
else return ruleKey;
|
|
679
|
+
});
|
|
680
|
+
const $validator = computed(() => {
|
|
681
|
+
if (isFormRuleDefinition(rule)) return rule.value.validator;
|
|
682
|
+
else return rule.value;
|
|
683
|
+
});
|
|
684
|
+
const $params = computed(() => {
|
|
685
|
+
if (typeof rule.value === "function") return [];
|
|
686
|
+
return unwrapRuleParameters(rule.value._params ?? []);
|
|
687
|
+
});
|
|
688
|
+
const $path = computed(() => `${path}.${$type.value}`);
|
|
689
|
+
return {
|
|
690
|
+
$active,
|
|
691
|
+
$message,
|
|
692
|
+
$type,
|
|
693
|
+
$validator,
|
|
694
|
+
$params,
|
|
695
|
+
$path,
|
|
696
|
+
$tooltip,
|
|
697
|
+
$fieldCorrect,
|
|
698
|
+
$fieldError,
|
|
699
|
+
$fieldDirty,
|
|
700
|
+
$fieldPending,
|
|
701
|
+
$fieldInvalid
|
|
702
|
+
};
|
|
703
|
+
});
|
|
704
|
+
$unwatchState = watch(scopeState?.$params, () => {
|
|
705
|
+
if (!modifiers.$silent.value || modifiers.$rewardEarly.value && scopeState.$fieldError.value) $parse();
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
$watch();
|
|
709
|
+
function updatePendingState() {
|
|
710
|
+
$valid.value = true;
|
|
711
|
+
if (scopeState.$fieldDirty.value) $pending.value = true;
|
|
712
|
+
}
|
|
713
|
+
async function computeAsyncResult() {
|
|
714
|
+
let ruleResult = false;
|
|
715
|
+
try {
|
|
716
|
+
const validator = scopeState.$validator.value;
|
|
717
|
+
if (typeof validator !== "function") {
|
|
718
|
+
console.error(`${path}: Incorrect rule format, it needs to be either a function or created with "createRule".`);
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
const resultOrPromise = validator(state.value, ...scopeState.$params.value);
|
|
722
|
+
let cachedValue = state.value;
|
|
723
|
+
updatePendingState();
|
|
724
|
+
let validatorResult;
|
|
725
|
+
if (resultOrPromise instanceof Promise) validatorResult = await resultOrPromise;
|
|
726
|
+
else validatorResult = resultOrPromise;
|
|
727
|
+
if (state.value !== cachedValue) return true;
|
|
728
|
+
if (typeof validatorResult === "boolean") ruleResult = validatorResult;
|
|
729
|
+
else {
|
|
730
|
+
const { $valid: $valid$1,...rest } = validatorResult;
|
|
731
|
+
ruleResult = $valid$1;
|
|
732
|
+
$metadata.value = rest;
|
|
733
|
+
}
|
|
734
|
+
} catch (e) {
|
|
735
|
+
ruleResult = false;
|
|
736
|
+
} finally {
|
|
737
|
+
$pending.value = false;
|
|
738
|
+
}
|
|
739
|
+
return ruleResult;
|
|
740
|
+
}
|
|
741
|
+
const $computeAsyncDebounce = debounce(computeAsyncResult, $debounce ?? 200);
|
|
742
|
+
async function $parse() {
|
|
743
|
+
try {
|
|
744
|
+
$validating.value = true;
|
|
745
|
+
let ruleResult = false;
|
|
746
|
+
$maybePending.value = true;
|
|
747
|
+
if (isRuleDef(rule.value) && rule.value._async) ruleResult = await $computeAsyncDebounce();
|
|
748
|
+
else {
|
|
749
|
+
const validator = scopeState.$validator.value;
|
|
750
|
+
const resultOrPromise = validator(state.value, ...scopeState.$params.value);
|
|
751
|
+
if (resultOrPromise instanceof Promise) console.warn("You used a async validator function on a non-async rule, please use \"async await\" or the \"withAsync\" helper");
|
|
752
|
+
else if (resultOrPromise != null) if (typeof resultOrPromise === "boolean") ruleResult = resultOrPromise;
|
|
753
|
+
else {
|
|
754
|
+
const { $valid: $valid$1,...rest } = resultOrPromise;
|
|
755
|
+
ruleResult = $valid$1;
|
|
756
|
+
$metadata.value = rest;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
$valid.value = ruleResult;
|
|
760
|
+
return ruleResult;
|
|
761
|
+
} catch (e) {
|
|
762
|
+
return false;
|
|
763
|
+
} finally {
|
|
764
|
+
$validating.value = false;
|
|
765
|
+
$maybePending.value = false;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function $reset() {
|
|
769
|
+
$valid.value = true;
|
|
770
|
+
$metadata.value = {};
|
|
771
|
+
$pending.value = false;
|
|
772
|
+
$validating.value = false;
|
|
773
|
+
$watch();
|
|
774
|
+
}
|
|
775
|
+
function $unwatch() {
|
|
776
|
+
$unwatchState();
|
|
777
|
+
scope.stop();
|
|
778
|
+
scope = effectScope();
|
|
779
|
+
}
|
|
780
|
+
return reactive({
|
|
781
|
+
...scopeState,
|
|
782
|
+
$pending,
|
|
783
|
+
$valid,
|
|
784
|
+
$metadata,
|
|
785
|
+
$haveAsync,
|
|
786
|
+
$maybePending,
|
|
787
|
+
$validating,
|
|
788
|
+
$parse,
|
|
789
|
+
$unwatch,
|
|
790
|
+
$watch,
|
|
791
|
+
$reset
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
//#endregion
|
|
796
|
+
//#region src/core/useRegle/root/createReactiveFieldStatus.ts
|
|
797
|
+
function createReactiveFieldStatus({ state, rulesDef, customMessages, path, fieldName, storage, options, externalErrors, schemaErrors, schemaMode, onUnwatch, $isArray, initialState, shortcuts, onValidate }) {
|
|
798
|
+
let scope = effectScope();
|
|
799
|
+
let scopeState;
|
|
800
|
+
let fieldScopes = [];
|
|
801
|
+
let $unwatchState;
|
|
802
|
+
let $unwatchValid;
|
|
803
|
+
let $unwatchDirty;
|
|
804
|
+
let $unwatchAsync;
|
|
805
|
+
let $unwatchRuleFieldValues;
|
|
806
|
+
let $commit = () => {};
|
|
807
|
+
function createReactiveRulesResult() {
|
|
808
|
+
const declaredRules = rulesDef.value;
|
|
809
|
+
const storeResult = storage.checkRuleDeclEntry(path, declaredRules);
|
|
810
|
+
$localOptions.value = Object.fromEntries(Object.entries(declaredRules).filter(([ruleKey]) => ruleKey.startsWith("$")));
|
|
811
|
+
$watch();
|
|
812
|
+
$rules.value = Object.fromEntries(Object.entries(rulesDef.value).filter(([ruleKey]) => !ruleKey.startsWith("$")).map(([ruleKey, rule]) => {
|
|
813
|
+
if (rule) {
|
|
814
|
+
const ruleRef = toRef(() => rule);
|
|
815
|
+
return [ruleKey, createReactiveRuleStatus({
|
|
816
|
+
modifiers: {
|
|
817
|
+
$silent: scopeState.$silent,
|
|
818
|
+
$rewardEarly: scopeState.$rewardEarly
|
|
819
|
+
},
|
|
820
|
+
customMessages,
|
|
821
|
+
rule: ruleRef,
|
|
822
|
+
ruleKey,
|
|
823
|
+
state,
|
|
824
|
+
path,
|
|
825
|
+
storage,
|
|
826
|
+
$debounce: $localOptions.value.$debounce
|
|
827
|
+
})];
|
|
828
|
+
}
|
|
829
|
+
return [];
|
|
830
|
+
}).filter((ruleDef) => !!ruleDef.length));
|
|
831
|
+
scopeState.processShortcuts();
|
|
832
|
+
define$commit();
|
|
833
|
+
if (storeResult?.valid != null) {
|
|
834
|
+
scopeState.$dirty.value = storage.getDirtyState(path);
|
|
835
|
+
if (scopeState.$dirty.value && !scopeState.$silent.value || scopeState.$rewardEarly.value && scopeState.$error.value) $commit();
|
|
836
|
+
}
|
|
837
|
+
storage.addRuleDeclEntry(path, declaredRules);
|
|
838
|
+
}
|
|
839
|
+
function define$commit() {
|
|
840
|
+
$commit = scopeState.$debounce.value ? debounce($commitHandler, scopeState.$debounce.value ?? scopeState.$haveAnyAsyncRule ? 100 : 0) : $commitHandler;
|
|
841
|
+
}
|
|
842
|
+
function $unwatch() {
|
|
843
|
+
if ($rules.value) Object.entries($rules.value).forEach(([_, rule]) => {
|
|
844
|
+
rule.$unwatch();
|
|
845
|
+
});
|
|
846
|
+
$unwatchDirty();
|
|
847
|
+
$unwatchRuleFieldValues?.();
|
|
848
|
+
if (scopeState.$dirty.value) storage.setDirtyEntry(path, scopeState.$dirty.value);
|
|
849
|
+
$unwatchState?.();
|
|
850
|
+
scope.stop();
|
|
851
|
+
scope = effectScope();
|
|
852
|
+
fieldScopes.forEach((s) => s.stop());
|
|
853
|
+
fieldScopes = [];
|
|
854
|
+
onUnwatch?.();
|
|
855
|
+
$unwatchAsync?.();
|
|
856
|
+
}
|
|
857
|
+
function $watch() {
|
|
858
|
+
if ($rules.value) Object.entries($rules.value).forEach(([_, rule]) => {
|
|
859
|
+
rule.$watch();
|
|
860
|
+
});
|
|
861
|
+
scopeState = scope.run(() => {
|
|
862
|
+
const $dirty = ref(false);
|
|
863
|
+
const triggerPunishment = ref(false);
|
|
864
|
+
const $anyDirty = computed(() => $dirty.value);
|
|
865
|
+
const $debounce$1 = computed(() => {
|
|
866
|
+
return $localOptions.value.$debounce;
|
|
867
|
+
});
|
|
868
|
+
const $deepCompare = computed(() => {
|
|
869
|
+
if ($localOptions.value.$deepCompare != null) return $localOptions.value.$deepCompare;
|
|
870
|
+
return false;
|
|
871
|
+
});
|
|
872
|
+
const $lazy$1 = computed(() => {
|
|
873
|
+
if ($localOptions.value.$lazy != null) return $localOptions.value.$lazy;
|
|
874
|
+
else if (unref(options.lazy) != null) return unref(options.lazy);
|
|
875
|
+
return false;
|
|
876
|
+
});
|
|
877
|
+
const $rewardEarly$1 = computed(() => {
|
|
878
|
+
if ($localOptions.value.$rewardEarly != null) return $localOptions.value.$rewardEarly;
|
|
879
|
+
else if (unref(options.rewardEarly) != null) return unref(options.rewardEarly);
|
|
880
|
+
return false;
|
|
881
|
+
});
|
|
882
|
+
const $clearExternalErrorsOnChange$1 = computed(() => {
|
|
883
|
+
if ($localOptions.value.$clearExternalErrorsOnChange != null) return $localOptions.value.$clearExternalErrorsOnChange;
|
|
884
|
+
else if (unref(options.clearExternalErrorsOnChange) != null) return unref(options.clearExternalErrorsOnChange);
|
|
885
|
+
else if ($silent.value) return false;
|
|
886
|
+
return true;
|
|
887
|
+
});
|
|
888
|
+
const $silent = computed(() => {
|
|
889
|
+
if ($rewardEarly$1.value) return true;
|
|
890
|
+
else if ($localOptions.value.$silent != null) return $localOptions.value.$silent;
|
|
891
|
+
else if (unref(options.silent) != null) return unref(options.silent);
|
|
892
|
+
else return false;
|
|
893
|
+
});
|
|
894
|
+
const $autoDirty$1 = computed(() => {
|
|
895
|
+
if ($localOptions.value.$autoDirty != null) return $localOptions.value.$autoDirty;
|
|
896
|
+
else if (unref(options.autoDirty) != null) return unref(options.autoDirty);
|
|
897
|
+
return true;
|
|
898
|
+
});
|
|
899
|
+
const $validating$1 = computed(() => {
|
|
900
|
+
return Object.entries($rules.value).some(([key, ruleResult]) => {
|
|
901
|
+
return ruleResult.$validating;
|
|
902
|
+
});
|
|
903
|
+
});
|
|
904
|
+
const $silentValue = computed({
|
|
905
|
+
get: () => state.value,
|
|
906
|
+
set(value) {
|
|
907
|
+
$unwatchState();
|
|
908
|
+
state.value = value;
|
|
909
|
+
define$watchState();
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
const $error = computed(() => {
|
|
913
|
+
return $invalid.value && !$pending.value && $dirty.value;
|
|
914
|
+
});
|
|
915
|
+
const $errors = computed(() => {
|
|
916
|
+
return extractRulesErrors({ field: {
|
|
917
|
+
$rules: $rules.value,
|
|
918
|
+
$error: $error.value,
|
|
919
|
+
$externalErrors: externalErrors?.value,
|
|
920
|
+
$schemaErrors: schemaErrors?.value
|
|
921
|
+
} });
|
|
922
|
+
});
|
|
923
|
+
const $silentErrors = computed(() => {
|
|
924
|
+
return extractRulesErrors({
|
|
925
|
+
field: {
|
|
926
|
+
$rules: $rules.value,
|
|
927
|
+
$error: $error.value,
|
|
928
|
+
$externalErrors: externalErrors?.value,
|
|
929
|
+
$schemaErrors: schemaErrors?.value
|
|
930
|
+
},
|
|
931
|
+
silent: true
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
const $edited = computed(() => {
|
|
935
|
+
if ($dirty.value) {
|
|
936
|
+
if (initialState.value instanceof Date && state.value instanceof Date) return toDate(initialState.value).getDate() !== toDate(state.value).getDate();
|
|
937
|
+
else if (initialState.value == null) return !!state.value;
|
|
938
|
+
else if (Array.isArray(state.value) && Array.isArray(initialState.value)) return !isEqual(state.value, initialState.value, $localOptions.value.$deepCompare);
|
|
939
|
+
return initialState.value !== state.value;
|
|
940
|
+
}
|
|
941
|
+
return false;
|
|
942
|
+
});
|
|
943
|
+
const $anyEdited = computed(() => $edited.value);
|
|
944
|
+
const $tooltips = computed(() => {
|
|
945
|
+
return extractRulesTooltips({ field: { $rules: $rules.value } });
|
|
946
|
+
});
|
|
947
|
+
const $ready = computed(() => {
|
|
948
|
+
if ($silent.value) return !($invalid.value || $pending.value);
|
|
949
|
+
return $anyDirty.value && !($invalid.value || $pending.value);
|
|
950
|
+
});
|
|
951
|
+
const $pending = computed(() => {
|
|
952
|
+
if (triggerPunishment.value || !$rewardEarly$1.value) return Object.entries($rules.value).some(([key, ruleResult]) => {
|
|
953
|
+
return ruleResult.$pending;
|
|
954
|
+
});
|
|
955
|
+
return false;
|
|
956
|
+
});
|
|
957
|
+
const $invalid = computed(() => {
|
|
958
|
+
if (externalErrors?.value?.length || schemaErrors?.value?.length) return true;
|
|
959
|
+
else if ($inactive.value) return false;
|
|
960
|
+
else if (!$rewardEarly$1.value || $rewardEarly$1.value) {
|
|
961
|
+
const result = Object.entries($rules.value).some(([_, ruleResult]) => {
|
|
962
|
+
return !(ruleResult.$valid && !ruleResult.$maybePending);
|
|
963
|
+
});
|
|
964
|
+
return result;
|
|
965
|
+
}
|
|
966
|
+
return false;
|
|
967
|
+
});
|
|
968
|
+
const $name = computed(() => fieldName);
|
|
969
|
+
const $inactive = computed(() => {
|
|
970
|
+
if (Object.keys(rulesDef.value).filter(([ruleKey]) => !ruleKey.startsWith("$")).length === 0 && !schemaMode) return true;
|
|
971
|
+
return false;
|
|
972
|
+
});
|
|
973
|
+
const $correct = computed(() => {
|
|
974
|
+
if (externalErrors?.value?.length) return false;
|
|
975
|
+
else if ($inactive.value) return false;
|
|
976
|
+
else if ($dirty.value && !isEmpty(state.value) && !$validating$1.value && !$pending.value) if (schemaMode) return !schemaErrors?.value?.length;
|
|
977
|
+
else {
|
|
978
|
+
const atLeastOneActiveRule = Object.values($rules.value).some((ruleResult) => ruleResult.$active);
|
|
979
|
+
if (atLeastOneActiveRule) return Object.values($rules.value).filter((ruleResult) => ruleResult.$active).every((ruleResult) => ruleResult.$valid);
|
|
980
|
+
else return false;
|
|
981
|
+
}
|
|
982
|
+
return false;
|
|
983
|
+
});
|
|
984
|
+
const $haveAnyAsyncRule$1 = computed(() => {
|
|
985
|
+
return Object.entries($rules.value).some(([key, ruleResult]) => {
|
|
986
|
+
return ruleResult.$haveAsync;
|
|
987
|
+
});
|
|
988
|
+
});
|
|
989
|
+
function processShortcuts() {
|
|
990
|
+
if (shortcuts?.fields) Object.entries(shortcuts.fields).forEach(([key, value]) => {
|
|
991
|
+
const scope$1 = effectScope();
|
|
992
|
+
$shortcuts$1[key] = scope$1.run(() => {
|
|
993
|
+
const result = ref();
|
|
994
|
+
watchEffect(() => {
|
|
995
|
+
result.value = value(reactive({
|
|
996
|
+
$dirty,
|
|
997
|
+
$externalErrors: externalErrors?.value ?? [],
|
|
998
|
+
$value: state,
|
|
999
|
+
$silentValue,
|
|
1000
|
+
$rules,
|
|
1001
|
+
$error,
|
|
1002
|
+
$pending,
|
|
1003
|
+
$invalid,
|
|
1004
|
+
$correct,
|
|
1005
|
+
$errors,
|
|
1006
|
+
$ready,
|
|
1007
|
+
$silentErrors,
|
|
1008
|
+
$anyDirty,
|
|
1009
|
+
$tooltips,
|
|
1010
|
+
$name,
|
|
1011
|
+
$inactive,
|
|
1012
|
+
$edited,
|
|
1013
|
+
$anyEdited
|
|
1014
|
+
}));
|
|
1015
|
+
});
|
|
1016
|
+
return result;
|
|
1017
|
+
});
|
|
1018
|
+
fieldScopes.push(scope$1);
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
const $shortcuts$1 = {};
|
|
1022
|
+
return {
|
|
1023
|
+
$error,
|
|
1024
|
+
$pending,
|
|
1025
|
+
$invalid,
|
|
1026
|
+
$correct,
|
|
1027
|
+
$debounce: $debounce$1,
|
|
1028
|
+
$deepCompare,
|
|
1029
|
+
$lazy: $lazy$1,
|
|
1030
|
+
$errors,
|
|
1031
|
+
$ready,
|
|
1032
|
+
$silentErrors,
|
|
1033
|
+
$rewardEarly: $rewardEarly$1,
|
|
1034
|
+
$autoDirty: $autoDirty$1,
|
|
1035
|
+
$silent,
|
|
1036
|
+
$clearExternalErrorsOnChange: $clearExternalErrorsOnChange$1,
|
|
1037
|
+
$anyDirty,
|
|
1038
|
+
$edited,
|
|
1039
|
+
$anyEdited,
|
|
1040
|
+
$name,
|
|
1041
|
+
$haveAnyAsyncRule: $haveAnyAsyncRule$1,
|
|
1042
|
+
$shortcuts: $shortcuts$1,
|
|
1043
|
+
$validating: $validating$1,
|
|
1044
|
+
$tooltips,
|
|
1045
|
+
$dirty,
|
|
1046
|
+
processShortcuts,
|
|
1047
|
+
$silentValue,
|
|
1048
|
+
$inactive
|
|
1049
|
+
};
|
|
1050
|
+
});
|
|
1051
|
+
define$watchState();
|
|
1052
|
+
$unwatchDirty = watch(scopeState.$dirty, (newDirty) => {
|
|
1053
|
+
storage.setDirtyEntry(path, newDirty);
|
|
1054
|
+
Object.values($rules.value).forEach((rule) => {
|
|
1055
|
+
rule.$fieldDirty = newDirty;
|
|
1056
|
+
});
|
|
1057
|
+
});
|
|
1058
|
+
$unwatchRuleFieldValues = watch([
|
|
1059
|
+
scopeState.$error,
|
|
1060
|
+
scopeState.$correct,
|
|
1061
|
+
scopeState.$invalid,
|
|
1062
|
+
scopeState.$pending
|
|
1063
|
+
], () => {
|
|
1064
|
+
Object.values($rules.value).forEach((rule) => {
|
|
1065
|
+
rule.$fieldError = scopeState.$error.value;
|
|
1066
|
+
rule.$fieldInvalid = scopeState.$invalid.value;
|
|
1067
|
+
rule.$fieldPending = scopeState.$pending.value;
|
|
1068
|
+
rule.$fieldCorrect = scopeState.$correct.value;
|
|
1069
|
+
});
|
|
1070
|
+
});
|
|
1071
|
+
$unwatchAsync = watch(scopeState.$haveAnyAsyncRule, define$commit);
|
|
1072
|
+
}
|
|
1073
|
+
function define$watchState() {
|
|
1074
|
+
$unwatchState = watch(state, () => {
|
|
1075
|
+
if (scopeState.$autoDirty.value && !scopeState.$silent.value) {
|
|
1076
|
+
if (!scopeState.$dirty.value) scopeState.$dirty.value = true;
|
|
1077
|
+
}
|
|
1078
|
+
if (rulesDef.value instanceof Function) createReactiveRulesResult();
|
|
1079
|
+
if (!scopeState.$silent.value || scopeState.$rewardEarly.value && scopeState.$error.value) $commit();
|
|
1080
|
+
if (scopeState.$clearExternalErrorsOnChange.value) $clearExternalErrors();
|
|
1081
|
+
}, { deep: $isArray ? true : isVueSuperiorOrEqualTo3dotFive ? 1 : true });
|
|
1082
|
+
}
|
|
1083
|
+
function $commitHandler() {
|
|
1084
|
+
Object.values($rules.value).forEach((rule) => {
|
|
1085
|
+
rule.$parse();
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
const $rules = ref({});
|
|
1089
|
+
const $localOptions = ref({});
|
|
1090
|
+
createReactiveRulesResult();
|
|
1091
|
+
function $reset(options$1, fromParent) {
|
|
1092
|
+
$clearExternalErrors();
|
|
1093
|
+
scopeState.$dirty.value = false;
|
|
1094
|
+
storage.setDirtyEntry(path, false);
|
|
1095
|
+
if (!fromParent) if (options$1?.toInitialState) state.value = cloneDeep(initialState.value);
|
|
1096
|
+
else if (options$1?.toState) {
|
|
1097
|
+
let newInitialState;
|
|
1098
|
+
if (typeof options$1?.toState === "function") newInitialState = options$1?.toState();
|
|
1099
|
+
else newInitialState = options$1?.toState;
|
|
1100
|
+
initialState.value = cloneDeep(newInitialState);
|
|
1101
|
+
state.value = cloneDeep(newInitialState);
|
|
1102
|
+
} else initialState.value = isObject(state.value) ? cloneDeep(state.value) : Array.isArray(state.value) ? [...state.value] : state.value;
|
|
1103
|
+
if (options$1?.clearExternalErrors) $clearExternalErrors();
|
|
1104
|
+
if (!fromParent) Object.entries($rules.value).forEach(([_, rule]) => {
|
|
1105
|
+
rule.$reset();
|
|
1106
|
+
});
|
|
1107
|
+
if (!scopeState.$lazy.value && !scopeState.$silent.value && !fromParent) Object.values($rules.value).forEach((rule) => {
|
|
1108
|
+
return rule.$parse();
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
function $touch(runCommit = true, withConditions = false) {
|
|
1112
|
+
if (!scopeState.$dirty.value) scopeState.$dirty.value = true;
|
|
1113
|
+
if (withConditions && runCommit) {
|
|
1114
|
+
if (!scopeState.$silent.value || scopeState.$rewardEarly.value && scopeState.$error.value) $commit();
|
|
1115
|
+
} else if (runCommit) $commit();
|
|
1116
|
+
}
|
|
1117
|
+
async function $validate() {
|
|
1118
|
+
try {
|
|
1119
|
+
if (schemaMode) if (onValidate) {
|
|
1120
|
+
$touch(false);
|
|
1121
|
+
return onValidate();
|
|
1122
|
+
} else return {
|
|
1123
|
+
valid: false,
|
|
1124
|
+
data: state.value
|
|
1125
|
+
};
|
|
1126
|
+
const data = state.value;
|
|
1127
|
+
if (!scopeState.$dirty.value) scopeState.$dirty.value = true;
|
|
1128
|
+
else if (!scopeState.$silent.value && scopeState.$dirty.value && !scopeState.$pending.value) return {
|
|
1129
|
+
valid: !scopeState.$error.value,
|
|
1130
|
+
data
|
|
1131
|
+
};
|
|
1132
|
+
if (schemaMode) return {
|
|
1133
|
+
valid: !schemaErrors?.value?.length,
|
|
1134
|
+
data
|
|
1135
|
+
};
|
|
1136
|
+
else if (isEmpty($rules.value)) return {
|
|
1137
|
+
valid: true,
|
|
1138
|
+
data
|
|
1139
|
+
};
|
|
1140
|
+
const results = await Promise.allSettled(Object.entries($rules.value).map(([key, rule]) => {
|
|
1141
|
+
return rule.$parse();
|
|
1142
|
+
}));
|
|
1143
|
+
const validationResults = results.every((value) => {
|
|
1144
|
+
if (value.status === "fulfilled") return value.value === true;
|
|
1145
|
+
else return false;
|
|
1146
|
+
});
|
|
1147
|
+
return {
|
|
1148
|
+
valid: validationResults,
|
|
1149
|
+
data
|
|
1150
|
+
};
|
|
1151
|
+
} catch (e) {
|
|
1152
|
+
return {
|
|
1153
|
+
valid: false,
|
|
1154
|
+
data: state.value
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
function $extractDirtyFields(filterNullishValues = true) {
|
|
1159
|
+
if (scopeState.$dirty.value) return state.value;
|
|
1160
|
+
if (filterNullishValues) return { _null: true };
|
|
1161
|
+
return null;
|
|
1162
|
+
}
|
|
1163
|
+
function $clearExternalErrors() {
|
|
1164
|
+
if (externalErrors?.value?.length) externalErrors.value = [];
|
|
1165
|
+
}
|
|
1166
|
+
if (!scopeState.$lazy.value && !scopeState.$dirty.value && !scopeState.$silent.value) $commit();
|
|
1167
|
+
const { $shortcuts, $validating, $autoDirty, $rewardEarly, $clearExternalErrorsOnChange, $haveAnyAsyncRule, $debounce, $lazy,...restScope } = scopeState;
|
|
1168
|
+
return reactive({
|
|
1169
|
+
...restScope,
|
|
1170
|
+
$externalErrors: externalErrors,
|
|
1171
|
+
$value: state,
|
|
1172
|
+
$rules,
|
|
1173
|
+
...$shortcuts,
|
|
1174
|
+
$reset,
|
|
1175
|
+
$touch,
|
|
1176
|
+
$validate,
|
|
1177
|
+
$unwatch,
|
|
1178
|
+
$watch,
|
|
1179
|
+
$extractDirtyFields,
|
|
1180
|
+
$clearExternalErrors
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
//#endregion
|
|
1185
|
+
//#region src/core/useRegle/root/collections/createReactiveCollectionElement.ts
|
|
1186
|
+
function createCollectionElement({ $id, path, index, options, storage, stateValue, customMessages, rules, externalErrors, schemaErrors, initialState, shortcuts, fieldName, schemaMode }) {
|
|
1187
|
+
const $fieldId = rules.$key ? rules.$key : randomId();
|
|
1188
|
+
let $path = `${path}.${String($fieldId)}`;
|
|
1189
|
+
if (typeof stateValue.value === "object" && stateValue.value != null) if (!stateValue.value.$id) Object.defineProperties(stateValue.value, { $id: {
|
|
1190
|
+
value: $fieldId,
|
|
1191
|
+
enumerable: false,
|
|
1192
|
+
configurable: false,
|
|
1193
|
+
writable: false
|
|
1194
|
+
} });
|
|
1195
|
+
else $path = `${path}.${stateValue.value.$id}`;
|
|
1196
|
+
const $externalErrors = toRef(externalErrors?.value ?? [], index);
|
|
1197
|
+
const $schemaErrors = computed(() => schemaErrors?.value?.[index]);
|
|
1198
|
+
const $status = createReactiveChildrenStatus({
|
|
1199
|
+
state: stateValue,
|
|
1200
|
+
rulesDef: toRef(() => rules),
|
|
1201
|
+
customMessages,
|
|
1202
|
+
path: $path,
|
|
1203
|
+
storage,
|
|
1204
|
+
options,
|
|
1205
|
+
externalErrors: $externalErrors,
|
|
1206
|
+
schemaErrors: $schemaErrors,
|
|
1207
|
+
initialState,
|
|
1208
|
+
shortcuts,
|
|
1209
|
+
fieldName,
|
|
1210
|
+
schemaMode
|
|
1211
|
+
});
|
|
1212
|
+
if ($status) {
|
|
1213
|
+
const valueId = stateValue.value?.$id;
|
|
1214
|
+
$status.$id = valueId ?? String($fieldId);
|
|
1215
|
+
storage.addArrayStatus($id, $status.$id, $status);
|
|
1216
|
+
}
|
|
1217
|
+
return $status;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
//#endregion
|
|
1221
|
+
//#region src/core/useRegle/root/collections/createReactiveCollectionRoot.ts
|
|
1222
|
+
function createReactiveCollectionStatus({ state, rulesDef, customMessages, path, storage, options, externalErrors, schemaErrors, schemaMode, initialState, shortcuts, fieldName }) {
|
|
1223
|
+
let scope = effectScope();
|
|
1224
|
+
let scopeState;
|
|
1225
|
+
let immediateScope = effectScope();
|
|
1226
|
+
let immediateScopeState;
|
|
1227
|
+
let collectionScopes = [];
|
|
1228
|
+
if (!Array.isArray(state.value) && !rulesDef.value.$each) return void 0;
|
|
1229
|
+
const $id = ref();
|
|
1230
|
+
const $value = ref(state.value);
|
|
1231
|
+
const $localOptions = ref({});
|
|
1232
|
+
let $unwatchState;
|
|
1233
|
+
let $unwatchDirty;
|
|
1234
|
+
const $selfStatus = ref({});
|
|
1235
|
+
const $eachStatus = storage.getCollectionsEntry(path);
|
|
1236
|
+
immediateScopeState = immediateScope.run(() => {
|
|
1237
|
+
const isPrimitiveArray = computed(() => {
|
|
1238
|
+
if (!state.value?.length) return false;
|
|
1239
|
+
if (Array.isArray(state.value) && state.value.length) return state.value.every((s) => typeof s !== "object");
|
|
1240
|
+
else if (rulesDef.value.$each && !(rulesDef.value.$each instanceof Function)) return Object.values(rulesDef.value.$each).every((rule) => isRuleDef(rule) || typeof rule === "function");
|
|
1241
|
+
return false;
|
|
1242
|
+
});
|
|
1243
|
+
return { isPrimitiveArray };
|
|
1244
|
+
});
|
|
1245
|
+
createStatus();
|
|
1246
|
+
$watch();
|
|
1247
|
+
function createStatus() {
|
|
1248
|
+
$localOptions.value = Object.fromEntries(Object.entries(rulesDef.value).filter(([ruleKey]) => ruleKey.startsWith("$")));
|
|
1249
|
+
if (typeof state.value === "object") {
|
|
1250
|
+
if (state.value != null && !state.value?.$id && state.value !== null) {
|
|
1251
|
+
$id.value = randomId();
|
|
1252
|
+
Object.defineProperties(state.value, { $id: {
|
|
1253
|
+
value: $id.value,
|
|
1254
|
+
enumerable: false,
|
|
1255
|
+
configurable: false,
|
|
1256
|
+
writable: false
|
|
1257
|
+
} });
|
|
1258
|
+
} else if (state.value?.$id) $id.value = state.value.$id;
|
|
1259
|
+
}
|
|
1260
|
+
$value.value = $selfStatus.value.$value;
|
|
1261
|
+
if (Array.isArray(state.value) && !immediateScopeState.isPrimitiveArray.value) $eachStatus.value = state.value.filter((value) => typeof value === "object").map((value, index) => {
|
|
1262
|
+
const { scope: scope$1, unwrapped } = unwrapGetter(rulesDef.value.$each, toRef(() => value), index);
|
|
1263
|
+
if (scope$1) collectionScopes.push(scope$1);
|
|
1264
|
+
const initialStateRef = toRef(initialState.value ?? [], index);
|
|
1265
|
+
const $externalErrors = toRef(externalErrors?.value ?? {}, `$each`);
|
|
1266
|
+
const $schemaErrors = computed(() => schemaErrors?.value?.$each);
|
|
1267
|
+
const element = createCollectionElement({
|
|
1268
|
+
$id: $id.value,
|
|
1269
|
+
path,
|
|
1270
|
+
customMessages,
|
|
1271
|
+
rules: unwrapped ?? {},
|
|
1272
|
+
stateValue: toRef(() => value),
|
|
1273
|
+
index,
|
|
1274
|
+
options,
|
|
1275
|
+
storage,
|
|
1276
|
+
externalErrors: $externalErrors,
|
|
1277
|
+
schemaErrors: $schemaErrors,
|
|
1278
|
+
initialState: initialStateRef,
|
|
1279
|
+
shortcuts,
|
|
1280
|
+
fieldName,
|
|
1281
|
+
schemaMode
|
|
1282
|
+
});
|
|
1283
|
+
if (element) return element;
|
|
1284
|
+
return null;
|
|
1285
|
+
}).filter((each) => !!each);
|
|
1286
|
+
else $eachStatus.value = [];
|
|
1287
|
+
$selfStatus.value = createReactiveFieldStatus({
|
|
1288
|
+
state,
|
|
1289
|
+
rulesDef,
|
|
1290
|
+
customMessages,
|
|
1291
|
+
path,
|
|
1292
|
+
storage,
|
|
1293
|
+
options,
|
|
1294
|
+
externalErrors: toRef(externalErrors?.value ?? {}, `$self`),
|
|
1295
|
+
schemaErrors: computed(() => schemaErrors?.value?.$self),
|
|
1296
|
+
$isArray: true,
|
|
1297
|
+
initialState,
|
|
1298
|
+
shortcuts,
|
|
1299
|
+
fieldName,
|
|
1300
|
+
schemaMode
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
function updateStatus() {
|
|
1304
|
+
if (Array.isArray(state.value) && !immediateScopeState.isPrimitiveArray.value) {
|
|
1305
|
+
const previousStatus = cloneDeep($eachStatus.value);
|
|
1306
|
+
$eachStatus.value = state.value.filter((value) => typeof value === "object").map((value, index) => {
|
|
1307
|
+
const currentValue = toRef(() => value);
|
|
1308
|
+
if (value.$id && $eachStatus.value.find((each) => each.$id === value.$id)) {
|
|
1309
|
+
const existingStatus = storage.getArrayStatus($id.value, value.$id);
|
|
1310
|
+
if (existingStatus) {
|
|
1311
|
+
existingStatus.$value = currentValue;
|
|
1312
|
+
return existingStatus;
|
|
1313
|
+
}
|
|
1314
|
+
return null;
|
|
1315
|
+
} else {
|
|
1316
|
+
const { scope: scope$1, unwrapped } = unwrapGetter(rulesDef.value.$each, currentValue, index);
|
|
1317
|
+
if (scope$1) collectionScopes.push(scope$1);
|
|
1318
|
+
const $externalErrors = toRef(externalErrors?.value ?? {}, `$each`);
|
|
1319
|
+
const $schemaErrors = computed(() => schemaErrors?.value?.$each ?? []);
|
|
1320
|
+
const element = createCollectionElement({
|
|
1321
|
+
$id: $id.value,
|
|
1322
|
+
path,
|
|
1323
|
+
customMessages,
|
|
1324
|
+
rules: unwrapped ?? {},
|
|
1325
|
+
stateValue: currentValue,
|
|
1326
|
+
index,
|
|
1327
|
+
options,
|
|
1328
|
+
storage,
|
|
1329
|
+
externalErrors: $externalErrors,
|
|
1330
|
+
schemaErrors: $schemaErrors,
|
|
1331
|
+
initialState: toRef(initialState.value ?? [], index),
|
|
1332
|
+
shortcuts,
|
|
1333
|
+
fieldName,
|
|
1334
|
+
schemaMode
|
|
1335
|
+
});
|
|
1336
|
+
if (element) return element;
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
}).filter((each) => !!each);
|
|
1340
|
+
previousStatus.filter(($each) => !state.value?.find((f) => $each.$id === f.$id)).forEach((_, index) => {
|
|
1341
|
+
storage.deleteArrayStatus($id.value, index.toString());
|
|
1342
|
+
});
|
|
1343
|
+
} else $eachStatus.value = [];
|
|
1344
|
+
}
|
|
1345
|
+
function define$watchState() {
|
|
1346
|
+
$unwatchState = watch(state, () => {
|
|
1347
|
+
if (state.value != null && !Object.hasOwn(state.value, "$id")) createStatus();
|
|
1348
|
+
else updateStatus();
|
|
1349
|
+
}, {
|
|
1350
|
+
deep: isVueSuperiorOrEqualTo3dotFive ? 1 : true,
|
|
1351
|
+
flush: "pre"
|
|
1352
|
+
});
|
|
1353
|
+
$unwatchDirty = watch(state, () => {
|
|
1354
|
+
if (scopeState.$autoDirty.value && !scopeState.$silent.value) $touch(false, true);
|
|
1355
|
+
}, { flush: "pre" });
|
|
1356
|
+
}
|
|
1357
|
+
function $watch() {
|
|
1358
|
+
define$watchState();
|
|
1359
|
+
scope = effectScope();
|
|
1360
|
+
scopeState = scope.run(() => {
|
|
1361
|
+
const $silentValue = computed({
|
|
1362
|
+
get: () => state.value,
|
|
1363
|
+
set(value) {
|
|
1364
|
+
$unwatchState?.();
|
|
1365
|
+
$unwatchDirty?.();
|
|
1366
|
+
state.value = value;
|
|
1367
|
+
define$watchState();
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
const $dirty = computed(() => {
|
|
1371
|
+
return $selfStatus.value.$dirty && (!$eachStatus.value.length || $eachStatus.value.every((statusOrField) => {
|
|
1372
|
+
return statusOrField.$dirty;
|
|
1373
|
+
}));
|
|
1374
|
+
});
|
|
1375
|
+
const $anyDirty = computed(() => {
|
|
1376
|
+
return $selfStatus.value.$anyDirty || $eachStatus.value.some((statusOrField) => {
|
|
1377
|
+
return statusOrField.$anyDirty;
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
const $invalid = computed(() => {
|
|
1381
|
+
return $selfStatus.value.$invalid || $eachStatus.value.some((statusOrField) => {
|
|
1382
|
+
return statusOrField.$invalid;
|
|
1383
|
+
});
|
|
1384
|
+
});
|
|
1385
|
+
const $correct = computed(() => {
|
|
1386
|
+
return (isEmpty($selfStatus.value.$rules) ? true : $selfStatus.value.$correct) && (!$eachStatus.value.length || $eachStatus.value.every((statusOrField) => {
|
|
1387
|
+
return statusOrField.$correct || statusOrField.$anyDirty && !statusOrField.$invalid;
|
|
1388
|
+
}));
|
|
1389
|
+
});
|
|
1390
|
+
const $error = computed(() => {
|
|
1391
|
+
return $selfStatus.value.$error || $eachStatus.value.some((statusOrField) => {
|
|
1392
|
+
return statusOrField.$error;
|
|
1393
|
+
});
|
|
1394
|
+
});
|
|
1395
|
+
const $ready = computed(() => {
|
|
1396
|
+
return !($invalid.value || $pending.value);
|
|
1397
|
+
});
|
|
1398
|
+
const $pending = computed(() => {
|
|
1399
|
+
return $selfStatus.value.$pending || $eachStatus.value.some((statusOrField) => {
|
|
1400
|
+
return statusOrField.$pending;
|
|
1401
|
+
});
|
|
1402
|
+
});
|
|
1403
|
+
const $edited = computed(() => {
|
|
1404
|
+
return !!$eachStatus.value.length && $eachStatus.value.every((statusOrField) => {
|
|
1405
|
+
return statusOrField.$edited;
|
|
1406
|
+
});
|
|
1407
|
+
});
|
|
1408
|
+
const $anyEdited = computed(() => {
|
|
1409
|
+
return $selfStatus.value.$anyEdited || $eachStatus.value.some((statusOrField) => {
|
|
1410
|
+
return statusOrField.$anyEdited;
|
|
1411
|
+
});
|
|
1412
|
+
});
|
|
1413
|
+
const $errors = computed(() => {
|
|
1414
|
+
return {
|
|
1415
|
+
$self: $selfStatus.value.$errors,
|
|
1416
|
+
$each: $eachStatus.value.map(($each) => $each.$errors)
|
|
1417
|
+
};
|
|
1418
|
+
});
|
|
1419
|
+
const $silentErrors = computed(() => {
|
|
1420
|
+
return {
|
|
1421
|
+
$self: $selfStatus.value.$silentErrors,
|
|
1422
|
+
$each: $eachStatus.value.map(($each) => $each.$silentErrors)
|
|
1423
|
+
};
|
|
1424
|
+
});
|
|
1425
|
+
const $rewardEarly = computed(() => {
|
|
1426
|
+
if ($localOptions.value.$rewardEarly != null) return $localOptions.value.$rewardEarly;
|
|
1427
|
+
else if (unref(options.rewardEarly) != null) return unref(options.rewardEarly);
|
|
1428
|
+
return false;
|
|
1429
|
+
});
|
|
1430
|
+
const $silent = computed(() => {
|
|
1431
|
+
if ($rewardEarly.value) return true;
|
|
1432
|
+
else if ($localOptions.value.$silent != null) return $localOptions.value.$silent;
|
|
1433
|
+
else if (unref(options.silent) != null) return unref(options.silent);
|
|
1434
|
+
else return false;
|
|
1435
|
+
});
|
|
1436
|
+
const $autoDirty = computed(() => {
|
|
1437
|
+
if ($localOptions.value.$autoDirty != null) return $localOptions.value.$autoDirty;
|
|
1438
|
+
else if (unref(options.autoDirty) != null) return unref(options.autoDirty);
|
|
1439
|
+
return true;
|
|
1440
|
+
});
|
|
1441
|
+
const $name = computed(() => fieldName);
|
|
1442
|
+
function processShortcuts() {
|
|
1443
|
+
if (shortcuts?.collections) Object.entries(shortcuts?.collections).forEach(([key, value]) => {
|
|
1444
|
+
const scope$1 = effectScope();
|
|
1445
|
+
$shortcuts$1[key] = scope$1.run(() => {
|
|
1446
|
+
const result = ref();
|
|
1447
|
+
watchEffect(() => {
|
|
1448
|
+
result.value = value(reactive({
|
|
1449
|
+
$dirty,
|
|
1450
|
+
$error,
|
|
1451
|
+
$silentValue,
|
|
1452
|
+
$pending,
|
|
1453
|
+
$invalid,
|
|
1454
|
+
$correct,
|
|
1455
|
+
$errors,
|
|
1456
|
+
$ready,
|
|
1457
|
+
$silentErrors,
|
|
1458
|
+
$anyDirty,
|
|
1459
|
+
$name,
|
|
1460
|
+
$each: $eachStatus,
|
|
1461
|
+
$self: $selfStatus,
|
|
1462
|
+
$value: state,
|
|
1463
|
+
$edited,
|
|
1464
|
+
$anyEdited
|
|
1465
|
+
}));
|
|
1466
|
+
});
|
|
1467
|
+
return result;
|
|
1468
|
+
});
|
|
1469
|
+
collectionScopes.push(scope$1);
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
const $shortcuts$1 = {};
|
|
1473
|
+
processShortcuts();
|
|
1474
|
+
return {
|
|
1475
|
+
$dirty,
|
|
1476
|
+
$anyDirty,
|
|
1477
|
+
$invalid,
|
|
1478
|
+
$correct,
|
|
1479
|
+
$error,
|
|
1480
|
+
$pending,
|
|
1481
|
+
$errors,
|
|
1482
|
+
$silentErrors,
|
|
1483
|
+
$ready,
|
|
1484
|
+
$name,
|
|
1485
|
+
$shortcuts: $shortcuts$1,
|
|
1486
|
+
$silentValue,
|
|
1487
|
+
$edited,
|
|
1488
|
+
$anyEdited,
|
|
1489
|
+
$rewardEarly,
|
|
1490
|
+
$silent,
|
|
1491
|
+
$autoDirty
|
|
1492
|
+
};
|
|
1493
|
+
});
|
|
1494
|
+
if (immediateScopeState.isPrimitiveArray.value && rulesDef.value.$each) console.warn(`${path} is a Array of primitives. Tracking can be lost when reassigning the Array. We advise to use an Array of objects instead`);
|
|
1495
|
+
}
|
|
1496
|
+
function $unwatch() {
|
|
1497
|
+
$unwatchState?.();
|
|
1498
|
+
if ($selfStatus.value) $selfStatus.value.$unwatch();
|
|
1499
|
+
if ($eachStatus.value) $eachStatus.value.forEach((element) => {
|
|
1500
|
+
if ("$dirty" in element) element.$unwatch();
|
|
1501
|
+
});
|
|
1502
|
+
scope.stop();
|
|
1503
|
+
scope = effectScope();
|
|
1504
|
+
immediateScope.stop();
|
|
1505
|
+
immediateScope = effectScope(true);
|
|
1506
|
+
collectionScopes.forEach((s) => s.stop());
|
|
1507
|
+
collectionScopes = [];
|
|
1508
|
+
}
|
|
1509
|
+
function $touch(runCommit = true, withConditions = false) {
|
|
1510
|
+
$selfStatus.value.$touch(runCommit, withConditions);
|
|
1511
|
+
$eachStatus.value.forEach(($each) => {
|
|
1512
|
+
$each.$touch(runCommit, withConditions);
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
function $reset(options$1, fromParent) {
|
|
1516
|
+
$unwatch();
|
|
1517
|
+
if (!fromParent) if (options$1?.toInitialState) state.value = cloneDeep(initialState.value);
|
|
1518
|
+
else if (options$1?.toState) {
|
|
1519
|
+
let newInitialState;
|
|
1520
|
+
if (typeof options$1?.toState === "function") newInitialState = options$1?.toState();
|
|
1521
|
+
else newInitialState = options$1?.toState;
|
|
1522
|
+
initialState.value = cloneDeep(newInitialState);
|
|
1523
|
+
state.value = cloneDeep(newInitialState);
|
|
1524
|
+
} else initialState.value = cloneDeep(state.value);
|
|
1525
|
+
if (options$1?.clearExternalErrors) $clearExternalErrors();
|
|
1526
|
+
$selfStatus.value.$reset(options$1, fromParent);
|
|
1527
|
+
$eachStatus.value.forEach(($each) => {
|
|
1528
|
+
$each.$reset(options$1, true);
|
|
1529
|
+
});
|
|
1530
|
+
if (!fromParent) createStatus();
|
|
1531
|
+
}
|
|
1532
|
+
async function $validate() {
|
|
1533
|
+
const data = state.value;
|
|
1534
|
+
try {
|
|
1535
|
+
const results = await Promise.allSettled([$selfStatus.value.$validate(), ...$eachStatus.value.map((status) => {
|
|
1536
|
+
return status.$validate();
|
|
1537
|
+
})]);
|
|
1538
|
+
const validationResults = results.every((value) => {
|
|
1539
|
+
if (value.status === "fulfilled") return value.value.valid === true;
|
|
1540
|
+
else return false;
|
|
1541
|
+
});
|
|
1542
|
+
return {
|
|
1543
|
+
valid: validationResults,
|
|
1544
|
+
data
|
|
1545
|
+
};
|
|
1546
|
+
} catch (e) {
|
|
1547
|
+
return {
|
|
1548
|
+
valid: false,
|
|
1549
|
+
data
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
function $clearExternalErrors() {
|
|
1554
|
+
$selfStatus.value.$clearExternalErrors();
|
|
1555
|
+
$eachStatus.value.forEach(($each) => {
|
|
1556
|
+
$each.$clearExternalErrors();
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
function $extractDirtyFields(filterNullishValues = true) {
|
|
1560
|
+
let dirtyFields = $eachStatus.value.map(($each) => {
|
|
1561
|
+
if (isNestedRulesStatus($each)) return $each.$extractDirtyFields(filterNullishValues);
|
|
1562
|
+
});
|
|
1563
|
+
if (filterNullishValues) {
|
|
1564
|
+
if (dirtyFields.every((value) => {
|
|
1565
|
+
return isEmpty(value);
|
|
1566
|
+
})) dirtyFields = [];
|
|
1567
|
+
}
|
|
1568
|
+
return dirtyFields;
|
|
1569
|
+
}
|
|
1570
|
+
const { $shortcuts,...restScopeState } = scopeState;
|
|
1571
|
+
return reactive({
|
|
1572
|
+
$self: $selfStatus,
|
|
1573
|
+
...restScopeState,
|
|
1574
|
+
...$shortcuts,
|
|
1575
|
+
$each: $eachStatus,
|
|
1576
|
+
$value: state,
|
|
1577
|
+
$validate,
|
|
1578
|
+
$unwatch,
|
|
1579
|
+
$watch,
|
|
1580
|
+
$touch,
|
|
1581
|
+
$reset,
|
|
1582
|
+
$extractDirtyFields,
|
|
1583
|
+
$clearExternalErrors
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
//#endregion
|
|
1588
|
+
//#region src/core/useRegle/root/createReactiveNestedStatus.ts
|
|
1589
|
+
function createReactiveNestedStatus({ rulesDef, state, path = "", rootRules, externalErrors, schemaErrors, rootSchemaErrors, validationGroups, initialState, fieldName,...commonArgs }) {
|
|
1590
|
+
let scope = effectScope();
|
|
1591
|
+
let scopeState;
|
|
1592
|
+
let nestedScopes = [];
|
|
1593
|
+
let $unwatchRules = null;
|
|
1594
|
+
let $unwatchSchemaErrors = null;
|
|
1595
|
+
let $unwatchExternalErrors = null;
|
|
1596
|
+
let $unwatchState = null;
|
|
1597
|
+
let $unwatchGroups = null;
|
|
1598
|
+
async function createReactiveFieldsStatus(watchSources = true) {
|
|
1599
|
+
const mapOfRulesDef = Object.entries(rulesDef.value);
|
|
1600
|
+
const scopedRulesStatus = Object.fromEntries(mapOfRulesDef.filter(([_, rule]) => !!rule).map(([statePropKey, statePropRules]) => {
|
|
1601
|
+
if (statePropRules) {
|
|
1602
|
+
const stateRef = toRef(state.value ?? {}, statePropKey);
|
|
1603
|
+
const statePropRulesRef = toRef(() => statePropRules);
|
|
1604
|
+
const $externalErrors = toRef(externalErrors?.value ?? {}, statePropKey);
|
|
1605
|
+
const $schemaErrors = computed(() => schemaErrors?.value?.[statePropKey]);
|
|
1606
|
+
const initialStateRef = toRef(initialState?.value ?? {}, statePropKey);
|
|
1607
|
+
return [statePropKey, createReactiveChildrenStatus({
|
|
1608
|
+
state: stateRef,
|
|
1609
|
+
rulesDef: statePropRulesRef,
|
|
1610
|
+
path: path ? `${path}.${statePropKey}` : statePropKey,
|
|
1611
|
+
externalErrors: $externalErrors,
|
|
1612
|
+
schemaErrors: $schemaErrors,
|
|
1613
|
+
initialState: initialStateRef,
|
|
1614
|
+
fieldName: statePropKey,
|
|
1615
|
+
...commonArgs
|
|
1616
|
+
})];
|
|
1617
|
+
}
|
|
1618
|
+
return [];
|
|
1619
|
+
}));
|
|
1620
|
+
const externalRulesStatus = Object.fromEntries(Object.entries(unref(externalErrors) ?? {}).filter(([key, errors]) => !(key in rulesDef.value) && !!errors).map(([key]) => {
|
|
1621
|
+
const stateRef = toRef(state.value ?? {}, key);
|
|
1622
|
+
const $externalErrors = toRef(externalErrors?.value ?? {}, key);
|
|
1623
|
+
const $schemaErrors = computed(() => schemaErrors?.value?.[key]);
|
|
1624
|
+
const initialStateRef = toRef(initialState?.value ?? {}, key);
|
|
1625
|
+
return [key, createReactiveChildrenStatus({
|
|
1626
|
+
state: stateRef,
|
|
1627
|
+
rulesDef: computed(() => ({})),
|
|
1628
|
+
path: path ? `${path}.${key}` : key,
|
|
1629
|
+
externalErrors: $externalErrors,
|
|
1630
|
+
schemaErrors: $schemaErrors,
|
|
1631
|
+
initialState: initialStateRef,
|
|
1632
|
+
fieldName: key,
|
|
1633
|
+
...commonArgs
|
|
1634
|
+
})];
|
|
1635
|
+
}));
|
|
1636
|
+
const schemasRulesStatus = Object.fromEntries(Object.entries(unref(schemaErrors) ?? {}).map(([key]) => {
|
|
1637
|
+
const stateRef = toRef(state.value ?? {}, key);
|
|
1638
|
+
const $externalErrors = toRef(externalErrors?.value ?? {}, key);
|
|
1639
|
+
const $schemaErrors = computed(() => schemaErrors?.value?.[key]);
|
|
1640
|
+
const initialStateRef = toRef(initialState?.value ?? {}, key);
|
|
1641
|
+
return [key, createReactiveChildrenStatus({
|
|
1642
|
+
state: stateRef,
|
|
1643
|
+
rulesDef: computed(() => ({})),
|
|
1644
|
+
path: path ? `${path}.${key}` : key,
|
|
1645
|
+
externalErrors: $externalErrors,
|
|
1646
|
+
schemaErrors: $schemaErrors,
|
|
1647
|
+
initialState: initialStateRef,
|
|
1648
|
+
fieldName: key,
|
|
1649
|
+
...commonArgs
|
|
1650
|
+
})];
|
|
1651
|
+
}));
|
|
1652
|
+
const statesWithNoRules = Object.fromEntries(Object.entries(state.value ?? {}).filter(([key]) => !(key in rulesDef.value) && !(key in (externalRulesStatus ?? {})) && !(key in (schemasRulesStatus ?? {}))).map(([key]) => {
|
|
1653
|
+
const stateRef = toRef(state.value ?? {}, key);
|
|
1654
|
+
const $externalErrors = toRef(externalErrors?.value ?? {}, key);
|
|
1655
|
+
const $schemaErrors = computed(() => schemaErrors?.value?.[key]);
|
|
1656
|
+
const initialStateRef = toRef(initialState?.value ?? {}, key);
|
|
1657
|
+
return [key, createReactiveChildrenStatus({
|
|
1658
|
+
state: stateRef,
|
|
1659
|
+
rulesDef: computed(() => ({})),
|
|
1660
|
+
path: path ? `${path}.${key}` : key,
|
|
1661
|
+
externalErrors: $externalErrors,
|
|
1662
|
+
schemaErrors: $schemaErrors,
|
|
1663
|
+
initialState: initialStateRef,
|
|
1664
|
+
fieldName: key,
|
|
1665
|
+
...commonArgs
|
|
1666
|
+
})];
|
|
1667
|
+
}));
|
|
1668
|
+
$fields.value = {
|
|
1669
|
+
...scopedRulesStatus,
|
|
1670
|
+
...externalRulesStatus,
|
|
1671
|
+
...schemasRulesStatus,
|
|
1672
|
+
...statesWithNoRules
|
|
1673
|
+
};
|
|
1674
|
+
if (watchSources) $watch();
|
|
1675
|
+
}
|
|
1676
|
+
const $fields = commonArgs.storage.getFieldsEntry(path);
|
|
1677
|
+
createReactiveFieldsStatus();
|
|
1678
|
+
function define$WatchExternalErrors() {
|
|
1679
|
+
if (externalErrors) $unwatchExternalErrors = watch(externalErrors, () => {
|
|
1680
|
+
$unwatch();
|
|
1681
|
+
createReactiveFieldsStatus();
|
|
1682
|
+
}, { deep: true });
|
|
1683
|
+
}
|
|
1684
|
+
function define$watchState() {
|
|
1685
|
+
$unwatchState = watch(state, () => {
|
|
1686
|
+
$unwatch();
|
|
1687
|
+
createReactiveFieldsStatus();
|
|
1688
|
+
if (scopeState.$autoDirty.value && !scopeState.$silent.value) $touch(false, true);
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
function $watch() {
|
|
1692
|
+
if (rootRules) {
|
|
1693
|
+
$unwatchRules?.();
|
|
1694
|
+
$unwatchRules = watch(rootRules, () => {
|
|
1695
|
+
$unwatch();
|
|
1696
|
+
createReactiveFieldsStatus();
|
|
1697
|
+
}, {
|
|
1698
|
+
deep: true,
|
|
1699
|
+
flush: "pre"
|
|
1700
|
+
});
|
|
1701
|
+
define$WatchExternalErrors();
|
|
1702
|
+
}
|
|
1703
|
+
if (rootSchemaErrors) {
|
|
1704
|
+
$unwatchSchemaErrors?.();
|
|
1705
|
+
$unwatchSchemaErrors = watch(rootSchemaErrors, () => {
|
|
1706
|
+
$unwatch();
|
|
1707
|
+
createReactiveFieldsStatus();
|
|
1708
|
+
}, {
|
|
1709
|
+
deep: true,
|
|
1710
|
+
flush: "post"
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
define$watchState();
|
|
1714
|
+
scopeState = scope.run(() => {
|
|
1715
|
+
const $value = computed({
|
|
1716
|
+
get: () => state.value,
|
|
1717
|
+
set(value) {
|
|
1718
|
+
$unwatch();
|
|
1719
|
+
state.value = value;
|
|
1720
|
+
createReactiveFieldsStatus();
|
|
1721
|
+
if (scopeState.$autoDirty.value && !scopeState.$silent.value) $touch(false, true);
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
const $silentValue = computed({
|
|
1725
|
+
get: () => state.value,
|
|
1726
|
+
set(value) {
|
|
1727
|
+
$unwatch();
|
|
1728
|
+
state.value = value;
|
|
1729
|
+
createReactiveFieldsStatus();
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
const $dirty = computed(() => {
|
|
1733
|
+
return !!Object.entries($fields.value).length && Object.entries($fields.value).every(([_, statusOrField]) => {
|
|
1734
|
+
return statusOrField?.$dirty;
|
|
1735
|
+
});
|
|
1736
|
+
});
|
|
1737
|
+
const $anyDirty = computed(() => {
|
|
1738
|
+
return Object.entries($fields.value).some(([_, statusOrField]) => {
|
|
1739
|
+
return statusOrField?.$anyDirty;
|
|
1740
|
+
});
|
|
1741
|
+
});
|
|
1742
|
+
const $invalid = computed(() => {
|
|
1743
|
+
return !!Object.entries($fields.value).length && Object.entries($fields.value).some(([_, statusOrField]) => {
|
|
1744
|
+
return statusOrField?.$invalid;
|
|
1745
|
+
});
|
|
1746
|
+
});
|
|
1747
|
+
const $correct = computed(() => {
|
|
1748
|
+
const fields = Object.entries($fields.value).filter(([_, statusOrField]) => {
|
|
1749
|
+
if (isFieldStatus(statusOrField)) return !statusOrField.$inactive;
|
|
1750
|
+
return true;
|
|
1751
|
+
});
|
|
1752
|
+
if (fields.length) return fields.every(([_, statusOrField]) => {
|
|
1753
|
+
if (commonArgs.schemaMode) return statusOrField.$correct;
|
|
1754
|
+
else if (isFieldStatus(statusOrField)) {
|
|
1755
|
+
if ("required" in statusOrField.$rules && statusOrField.$rules.required.$active) return statusOrField?.$correct;
|
|
1756
|
+
return !statusOrField.$invalid && !statusOrField.$pending;
|
|
1757
|
+
}
|
|
1758
|
+
return statusOrField?.$correct;
|
|
1759
|
+
});
|
|
1760
|
+
return false;
|
|
1761
|
+
});
|
|
1762
|
+
const $error = computed(() => {
|
|
1763
|
+
return !!Object.entries($fields.value).length && Object.entries($fields.value).some(([_, statusOrField]) => {
|
|
1764
|
+
return statusOrField?.$error;
|
|
1765
|
+
});
|
|
1766
|
+
});
|
|
1767
|
+
const $rewardEarly = computed(() => {
|
|
1768
|
+
if (unref(commonArgs.options.rewardEarly) != null) return unref(commonArgs.options.rewardEarly);
|
|
1769
|
+
return false;
|
|
1770
|
+
});
|
|
1771
|
+
const $silent = computed(() => {
|
|
1772
|
+
if (unref(commonArgs.options.silent) != null) return unref(commonArgs.options.silent);
|
|
1773
|
+
else if ($rewardEarly.value) return true;
|
|
1774
|
+
return false;
|
|
1775
|
+
});
|
|
1776
|
+
const $autoDirty = computed(() => {
|
|
1777
|
+
if (unref(commonArgs.options.autoDirty) != null) return unref(commonArgs.options.autoDirty);
|
|
1778
|
+
return true;
|
|
1779
|
+
});
|
|
1780
|
+
const $ready = computed(() => {
|
|
1781
|
+
if ($silent.value) return !($invalid.value || $pending.value);
|
|
1782
|
+
return $anyDirty.value && !($invalid.value || $pending.value);
|
|
1783
|
+
});
|
|
1784
|
+
const $localPending$1 = ref(false);
|
|
1785
|
+
const $pending = computed(() => {
|
|
1786
|
+
return $localPending$1.value || Object.entries($fields.value).some(([key, statusOrField]) => {
|
|
1787
|
+
return statusOrField?.$pending;
|
|
1788
|
+
});
|
|
1789
|
+
});
|
|
1790
|
+
const $errors = computed(() => {
|
|
1791
|
+
return Object.fromEntries(Object.entries($fields.value).map(([key, statusOrField]) => {
|
|
1792
|
+
return [key, statusOrField?.$errors];
|
|
1793
|
+
}));
|
|
1794
|
+
});
|
|
1795
|
+
const $silentErrors = computed(() => {
|
|
1796
|
+
return Object.fromEntries(Object.entries($fields.value).map(([key, statusOrField]) => {
|
|
1797
|
+
return [key, statusOrField?.$silentErrors];
|
|
1798
|
+
}));
|
|
1799
|
+
});
|
|
1800
|
+
const $edited = computed(() => {
|
|
1801
|
+
return !!Object.entries($fields.value).length && Object.entries($fields.value).every(([_, statusOrField]) => {
|
|
1802
|
+
return statusOrField?.$edited;
|
|
1803
|
+
});
|
|
1804
|
+
});
|
|
1805
|
+
const $anyEdited = computed(() => {
|
|
1806
|
+
return Object.entries($fields.value).some(([_, statusOrField]) => {
|
|
1807
|
+
return statusOrField?.$anyEdited;
|
|
1808
|
+
});
|
|
1809
|
+
});
|
|
1810
|
+
const $name = computed(() => fieldName);
|
|
1811
|
+
function processShortcuts() {
|
|
1812
|
+
if (commonArgs.shortcuts?.nested) Object.entries(commonArgs.shortcuts.nested).forEach(([key, value]) => {
|
|
1813
|
+
const scope$1 = effectScope();
|
|
1814
|
+
$shortcuts$1[key] = scope$1.run(() => {
|
|
1815
|
+
const result = ref();
|
|
1816
|
+
watchEffect(() => {
|
|
1817
|
+
result.value = value(reactive({
|
|
1818
|
+
$dirty,
|
|
1819
|
+
$value: state,
|
|
1820
|
+
$silentValue,
|
|
1821
|
+
$error,
|
|
1822
|
+
$pending,
|
|
1823
|
+
$invalid,
|
|
1824
|
+
$correct,
|
|
1825
|
+
$ready,
|
|
1826
|
+
$anyDirty,
|
|
1827
|
+
$name,
|
|
1828
|
+
$silentErrors,
|
|
1829
|
+
$errors,
|
|
1830
|
+
$fields,
|
|
1831
|
+
$edited,
|
|
1832
|
+
$anyEdited
|
|
1833
|
+
}));
|
|
1834
|
+
});
|
|
1835
|
+
return result;
|
|
1836
|
+
});
|
|
1837
|
+
nestedScopes.push(scope$1);
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
const $groups = computed({
|
|
1841
|
+
get: () => {
|
|
1842
|
+
if (validationGroups) return Object.fromEntries(Object.entries(validationGroups?.($fields.value) ?? {}).map(([key, entries]) => {
|
|
1843
|
+
if (entries.length) return [key, {
|
|
1844
|
+
...Object.fromEntries([
|
|
1845
|
+
"$invalid",
|
|
1846
|
+
"$error",
|
|
1847
|
+
"$pending",
|
|
1848
|
+
"$dirty",
|
|
1849
|
+
"$correct"
|
|
1850
|
+
].map((property) => [property, mergeBooleanGroupProperties(entries, property)])),
|
|
1851
|
+
...Object.fromEntries(["$errors", "$silentErrors"].map((property) => [property, mergeArrayGroupProperties(entries, property)]))
|
|
1852
|
+
}];
|
|
1853
|
+
return [];
|
|
1854
|
+
}));
|
|
1855
|
+
return {};
|
|
1856
|
+
},
|
|
1857
|
+
set() {}
|
|
1858
|
+
});
|
|
1859
|
+
const $shortcuts$1 = {};
|
|
1860
|
+
processShortcuts();
|
|
1861
|
+
return {
|
|
1862
|
+
$dirty,
|
|
1863
|
+
$anyDirty,
|
|
1864
|
+
$invalid,
|
|
1865
|
+
$correct,
|
|
1866
|
+
$error,
|
|
1867
|
+
$pending,
|
|
1868
|
+
$errors,
|
|
1869
|
+
$silentErrors,
|
|
1870
|
+
$ready,
|
|
1871
|
+
$name,
|
|
1872
|
+
$shortcuts: $shortcuts$1,
|
|
1873
|
+
$groups,
|
|
1874
|
+
$silentValue,
|
|
1875
|
+
$edited,
|
|
1876
|
+
$anyEdited,
|
|
1877
|
+
$localPending: $localPending$1,
|
|
1878
|
+
$autoDirty,
|
|
1879
|
+
$silent,
|
|
1880
|
+
$value
|
|
1881
|
+
};
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
function $unwatch() {
|
|
1885
|
+
$unwatchRules?.();
|
|
1886
|
+
$unwatchExternalErrors?.();
|
|
1887
|
+
$unwatchState?.();
|
|
1888
|
+
$unwatchGroups?.();
|
|
1889
|
+
$unwatchSchemaErrors?.();
|
|
1890
|
+
nestedScopes = [];
|
|
1891
|
+
scopeState = {};
|
|
1892
|
+
if ($fields.value) Object.entries($fields.value).forEach(([_, field]) => {
|
|
1893
|
+
field.$unwatch();
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
function $clearExternalErrors() {
|
|
1897
|
+
Object.entries($fields.value).forEach(([_, field]) => {
|
|
1898
|
+
field.$clearExternalErrors();
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
function $reset(options, fromParent) {
|
|
1902
|
+
$unwatchExternalErrors?.();
|
|
1903
|
+
$unwatch();
|
|
1904
|
+
if (!fromParent) if (options?.toInitialState) state.value = cloneDeep({ ...initialState.value ?? {} });
|
|
1905
|
+
else if (options?.toState) {
|
|
1906
|
+
let newInitialState;
|
|
1907
|
+
if (typeof options?.toState === "function") newInitialState = options?.toState();
|
|
1908
|
+
else newInitialState = options?.toState;
|
|
1909
|
+
initialState.value = cloneDeep(newInitialState);
|
|
1910
|
+
state.value = cloneDeep(newInitialState);
|
|
1911
|
+
} else initialState.value = cloneDeep(state.value);
|
|
1912
|
+
Object.values($fields.value).forEach((statusOrField) => {
|
|
1913
|
+
statusOrField.$reset(options, true);
|
|
1914
|
+
});
|
|
1915
|
+
if (options?.clearExternalErrors) $clearExternalErrors();
|
|
1916
|
+
define$WatchExternalErrors();
|
|
1917
|
+
if (!fromParent) createReactiveFieldsStatus();
|
|
1918
|
+
}
|
|
1919
|
+
function $touch(runCommit = true, withConditions = false) {
|
|
1920
|
+
Object.values($fields.value).forEach((statusOrField) => {
|
|
1921
|
+
statusOrField.$touch(runCommit, withConditions);
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
function filterNullishFields(fields) {
|
|
1925
|
+
return fields.filter(([key, value]) => {
|
|
1926
|
+
if (isObject(value)) return !(value && typeof value === "object" && "_null" in value) && !isEmpty(value);
|
|
1927
|
+
else if (Array.isArray(value)) return value.length;
|
|
1928
|
+
else return true;
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
function $extractDirtyFields(filterNullishValues = true) {
|
|
1932
|
+
let dirtyFields = Object.entries($fields.value).map(([key, field]) => {
|
|
1933
|
+
return [key, field.$extractDirtyFields(filterNullishValues)];
|
|
1934
|
+
});
|
|
1935
|
+
if (filterNullishValues) dirtyFields = filterNullishFields(dirtyFields);
|
|
1936
|
+
return Object.fromEntries(dirtyFields);
|
|
1937
|
+
}
|
|
1938
|
+
async function $validate() {
|
|
1939
|
+
try {
|
|
1940
|
+
if (commonArgs.schemaMode) if (commonArgs.onValidate) {
|
|
1941
|
+
$touch(false);
|
|
1942
|
+
scopeState.$localPending.value = true;
|
|
1943
|
+
return commonArgs.onValidate();
|
|
1944
|
+
} else return {
|
|
1945
|
+
valid: false,
|
|
1946
|
+
data: state.value
|
|
1947
|
+
};
|
|
1948
|
+
else {
|
|
1949
|
+
const data = state.value;
|
|
1950
|
+
const results = await Promise.allSettled(Object.values($fields.value).map((statusOrField) => {
|
|
1951
|
+
return statusOrField.$validate();
|
|
1952
|
+
}));
|
|
1953
|
+
const validationResults = results.every((value) => {
|
|
1954
|
+
if (value.status === "fulfilled") return value.value.valid === true;
|
|
1955
|
+
else return false;
|
|
1956
|
+
});
|
|
1957
|
+
return {
|
|
1958
|
+
valid: validationResults,
|
|
1959
|
+
data
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
} catch (e) {
|
|
1963
|
+
return {
|
|
1964
|
+
valid: false,
|
|
1965
|
+
data: state.value
|
|
1966
|
+
};
|
|
1967
|
+
} finally {
|
|
1968
|
+
scopeState.$localPending.value = false;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const { $shortcuts, $localPending,...restScopeState } = scopeState;
|
|
1972
|
+
return reactive({
|
|
1973
|
+
...restScopeState,
|
|
1974
|
+
...$shortcuts,
|
|
1975
|
+
$fields,
|
|
1976
|
+
$reset,
|
|
1977
|
+
$touch,
|
|
1978
|
+
$validate,
|
|
1979
|
+
$unwatch,
|
|
1980
|
+
$watch,
|
|
1981
|
+
$clearExternalErrors,
|
|
1982
|
+
$extractDirtyFields
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* Main resolver divider, will distribute the logic depending on the type of the current value (primitive, object, array)
|
|
1987
|
+
*/
|
|
1988
|
+
function createReactiveChildrenStatus({ rulesDef,...properties }) {
|
|
1989
|
+
if (isCollectionRulesDef(rulesDef, properties.state, properties.schemaMode)) return createReactiveCollectionStatus({
|
|
1990
|
+
rulesDef,
|
|
1991
|
+
...properties
|
|
1992
|
+
});
|
|
1993
|
+
else if (isNestedRulesDef(properties.state, rulesDef)) if (isRefObject(properties.state)) return createReactiveNestedStatus({
|
|
1994
|
+
rulesDef,
|
|
1995
|
+
...properties
|
|
1996
|
+
});
|
|
1997
|
+
else {
|
|
1998
|
+
const scope = effectScope();
|
|
1999
|
+
const scopeState = scope.run(() => {
|
|
2000
|
+
const fakeState = toRef(properties.state.value ? properties.state : ref({}));
|
|
2001
|
+
watch(() => properties.state.value, (value) => {
|
|
2002
|
+
fakeState.value = value;
|
|
2003
|
+
}, { deep: true });
|
|
2004
|
+
watch(fakeState, (value) => {
|
|
2005
|
+
properties.state.value = value;
|
|
2006
|
+
}, { deep: true });
|
|
2007
|
+
return { fakeState };
|
|
2008
|
+
});
|
|
2009
|
+
const { state,...restProperties } = properties;
|
|
2010
|
+
return createReactiveNestedStatus({
|
|
2011
|
+
rulesDef,
|
|
2012
|
+
...restProperties,
|
|
2013
|
+
state: scopeState.fakeState
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
else if (isValidatorRulesDef(rulesDef)) return createReactiveFieldStatus({
|
|
2017
|
+
rulesDef,
|
|
2018
|
+
...properties
|
|
2019
|
+
});
|
|
2020
|
+
return void 0;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
//#endregion
|
|
2024
|
+
//#region src/core/useRegle/root/useRootStorage.ts
|
|
2025
|
+
function useRootStorage({ initialState, options, scopeRules, state, customRules, shortcuts, schemaErrors, schemaMode = false, onValidate }) {
|
|
2026
|
+
const storage = useStorage();
|
|
2027
|
+
const regle = ref();
|
|
2028
|
+
if (isNestedRulesDef(state, scopeRules)) regle.value = createReactiveNestedStatus({
|
|
2029
|
+
rootRules: scopeRules,
|
|
2030
|
+
rulesDef: scopeRules,
|
|
2031
|
+
state,
|
|
2032
|
+
customMessages: customRules?.(),
|
|
2033
|
+
storage,
|
|
2034
|
+
options,
|
|
2035
|
+
externalErrors: options.externalErrors,
|
|
2036
|
+
validationGroups: options.validationGroups,
|
|
2037
|
+
initialState,
|
|
2038
|
+
shortcuts,
|
|
2039
|
+
fieldName: "root",
|
|
2040
|
+
path: "",
|
|
2041
|
+
schemaErrors,
|
|
2042
|
+
rootSchemaErrors: schemaErrors,
|
|
2043
|
+
schemaMode,
|
|
2044
|
+
onValidate
|
|
2045
|
+
});
|
|
2046
|
+
else if (isValidatorRulesDef(scopeRules)) regle.value = createReactiveFieldStatus({
|
|
2047
|
+
rulesDef: scopeRules,
|
|
2048
|
+
state,
|
|
2049
|
+
customMessages: customRules?.(),
|
|
2050
|
+
storage,
|
|
2051
|
+
options,
|
|
2052
|
+
externalErrors: options.externalErrors,
|
|
2053
|
+
initialState,
|
|
2054
|
+
shortcuts,
|
|
2055
|
+
fieldName: "root",
|
|
2056
|
+
path: "",
|
|
2057
|
+
schemaMode,
|
|
2058
|
+
schemaErrors,
|
|
2059
|
+
onValidate
|
|
2060
|
+
});
|
|
2061
|
+
if (getCurrentScope()) onScopeDispose(() => {
|
|
2062
|
+
regle.value?.$unwatch();
|
|
2063
|
+
});
|
|
2064
|
+
return reactive({ regle });
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
//#endregion
|
|
2068
|
+
//#region src/core/useRegle/useRegle.ts
|
|
2069
|
+
function createUseRegleComposable(customRules, options, shortcuts) {
|
|
2070
|
+
const globalOptions = {
|
|
2071
|
+
autoDirty: options?.autoDirty,
|
|
2072
|
+
lazy: options?.lazy,
|
|
2073
|
+
rewardEarly: options?.rewardEarly,
|
|
2074
|
+
silent: options?.silent,
|
|
2075
|
+
clearExternalErrorsOnChange: options?.clearExternalErrorsOnChange
|
|
2076
|
+
};
|
|
2077
|
+
function useRegle$1(state, rulesFactory, options$1) {
|
|
2078
|
+
const definedRules = isRef(rulesFactory) ? rulesFactory : typeof rulesFactory === "function" ? void 0 : computed(() => rulesFactory);
|
|
2079
|
+
const resolvedOptions = {
|
|
2080
|
+
...globalOptions,
|
|
2081
|
+
...options$1
|
|
2082
|
+
};
|
|
2083
|
+
const processedState = isRef(state) ? state : ref(state);
|
|
2084
|
+
const watchableRulesGetters = shallowRef(definedRules ?? {});
|
|
2085
|
+
if (typeof rulesFactory === "function") watchEffect(() => {
|
|
2086
|
+
watchableRulesGetters.value = rulesFactory(processedState);
|
|
2087
|
+
triggerRef(watchableRulesGetters);
|
|
2088
|
+
});
|
|
2089
|
+
const initialState = ref(isObject(processedState.value) ? { ...cloneDeep(processedState.value) } : cloneDeep(processedState.value));
|
|
2090
|
+
const regle = useRootStorage({
|
|
2091
|
+
scopeRules: watchableRulesGetters,
|
|
2092
|
+
state: processedState,
|
|
2093
|
+
options: resolvedOptions,
|
|
2094
|
+
initialState,
|
|
2095
|
+
customRules,
|
|
2096
|
+
shortcuts
|
|
2097
|
+
});
|
|
2098
|
+
return { r$: regle.regle };
|
|
2099
|
+
}
|
|
2100
|
+
return useRegle$1;
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* useRegle serves as the foundation for validation logic.
|
|
2104
|
+
*
|
|
2105
|
+
* It accepts the following inputs:
|
|
2106
|
+
*
|
|
2107
|
+
* @param state - This can be a plain object, a ref, a reactive object, or a structure containing nested refs.
|
|
2108
|
+
* @param rules - These should align with the structure of your state.
|
|
2109
|
+
* @param modifiers - Customize regle behaviour
|
|
2110
|
+
*
|
|
2111
|
+
* ```ts
|
|
2112
|
+
* import { useRegle } from '@regle/core';
|
|
2113
|
+
import { required } from '@regle/rules';
|
|
2114
|
+
|
|
2115
|
+
const { r$ } = useRegle({ email: '' }, {
|
|
2116
|
+
email: { required }
|
|
2117
|
+
})
|
|
2118
|
+
* ```
|
|
2119
|
+
* Docs: {@link https://reglejs.dev/core-concepts/}
|
|
2120
|
+
*/
|
|
2121
|
+
const useRegle = createUseRegleComposable();
|
|
2122
|
+
|
|
2123
|
+
//#endregion
|
|
2124
|
+
//#region src/core/useRegle/inferRules.ts
|
|
2125
|
+
function createInferRuleHelper() {
|
|
2126
|
+
function inferRules$1(state, rulesFactory) {
|
|
2127
|
+
return rulesFactory;
|
|
2128
|
+
}
|
|
2129
|
+
return inferRules$1;
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Rule type helper to provide autocomplete and typecheck to your form rules or part of your form rules
|
|
2133
|
+
* It will just return the rules without any processing.
|
|
2134
|
+
*
|
|
2135
|
+
* @param state - The state reference
|
|
2136
|
+
* @param rules - Your rule tree
|
|
2137
|
+
*/
|
|
2138
|
+
const inferRules = createInferRuleHelper();
|
|
2139
|
+
|
|
2140
|
+
//#endregion
|
|
2141
|
+
//#region src/core/defineRegleConfig.ts
|
|
2142
|
+
/**
|
|
2143
|
+
* Define a global regle configuration, where you can:
|
|
2144
|
+
* - Customize built-in rules messages
|
|
2145
|
+
* - Add your custom rules
|
|
2146
|
+
* - Define global modifiers
|
|
2147
|
+
* - Define shortcuts
|
|
2148
|
+
*
|
|
2149
|
+
* It will return:
|
|
2150
|
+
*
|
|
2151
|
+
* - a `useRegle` composable that can typecheck your custom rules
|
|
2152
|
+
* - an `inferRules` helper that can typecheck your custom rules
|
|
2153
|
+
*/
|
|
2154
|
+
function defineRegleConfig({ rules, modifiers, shortcuts }) {
|
|
2155
|
+
const useRegle$1 = createUseRegleComposable(rules, modifiers, shortcuts);
|
|
2156
|
+
useRegle$1.__config = {
|
|
2157
|
+
rules,
|
|
2158
|
+
modifiers,
|
|
2159
|
+
shortcuts
|
|
2160
|
+
};
|
|
2161
|
+
const inferRules$1 = createInferRuleHelper();
|
|
2162
|
+
return {
|
|
2163
|
+
useRegle: useRegle$1,
|
|
2164
|
+
inferRules: inferRules$1
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* Extend an already created custom `useRegle` (as the first parameter)
|
|
2169
|
+
*
|
|
2170
|
+
* It will return:
|
|
2171
|
+
*
|
|
2172
|
+
* - a `useRegle` composable that can typecheck your custom rules
|
|
2173
|
+
* - an `inferRules` helper that can typecheck your custom rules
|
|
2174
|
+
*/
|
|
2175
|
+
function extendRegleConfig(regle, { rules, modifiers, shortcuts }) {
|
|
2176
|
+
const rootConfig = regle.__config ?? {};
|
|
2177
|
+
const newRules = () => ({
|
|
2178
|
+
...rootConfig.rules?.(),
|
|
2179
|
+
...rules?.()
|
|
2180
|
+
});
|
|
2181
|
+
const newModifiers = rootConfig.modifiers && modifiers ? merge(rootConfig.modifiers, modifiers) : modifiers;
|
|
2182
|
+
const newShortcuts = rootConfig.shortcuts && shortcuts ? merge(rootConfig.shortcuts, shortcuts) : shortcuts;
|
|
2183
|
+
const useRegle$1 = createUseRegleComposable(newRules, newModifiers, newShortcuts);
|
|
2184
|
+
useRegle$1.__config = {
|
|
2185
|
+
rules: newRules,
|
|
2186
|
+
modifiers: newModifiers,
|
|
2187
|
+
shortcuts: newShortcuts
|
|
2188
|
+
};
|
|
2189
|
+
const inferRules$1 = createInferRuleHelper();
|
|
2190
|
+
return {
|
|
2191
|
+
useRegle: useRegle$1,
|
|
2192
|
+
inferRules: inferRules$1
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
//#endregion
|
|
2197
|
+
//#region src/core/mergeRegles.ts
|
|
2198
|
+
function mergeRegles(regles, _scoped) {
|
|
2199
|
+
const scoped = _scoped == null ? false : _scoped;
|
|
2200
|
+
const $value = computed({
|
|
2201
|
+
get: () => {
|
|
2202
|
+
if (scoped) return Object.values(regles).map((r) => r.$value);
|
|
2203
|
+
else return Object.fromEntries(Object.entries(regles).map(([key, r]) => [key, r.$value]));
|
|
2204
|
+
},
|
|
2205
|
+
set: (value) => {
|
|
2206
|
+
if (!scoped) {
|
|
2207
|
+
if (typeof value === "object") Object.entries(value).forEach(([key, newValue]) => regles[key].$value = newValue);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
});
|
|
2211
|
+
const $silentValue = computed({
|
|
2212
|
+
get: () => Object.fromEntries(Object.entries(regles).map(([key, r]) => [key, r.$silentValue])),
|
|
2213
|
+
set: (value) => {
|
|
2214
|
+
if (typeof value === "object") Object.entries(value).forEach(([key, newValue]) => regles[key].$silentValue = newValue);
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
const $dirty = computed(() => {
|
|
2218
|
+
const entries = Object.entries(regles);
|
|
2219
|
+
return !!entries.length && entries.every(([_, regle]) => {
|
|
2220
|
+
return regle?.$dirty;
|
|
2221
|
+
});
|
|
2222
|
+
});
|
|
2223
|
+
const $anyDirty = computed(() => {
|
|
2224
|
+
return Object.entries(regles).some(([_, regle]) => {
|
|
2225
|
+
return regle?.$anyDirty;
|
|
2226
|
+
});
|
|
2227
|
+
});
|
|
2228
|
+
const $invalid = computed(() => {
|
|
2229
|
+
return Object.entries(regles).some(([_, regle]) => {
|
|
2230
|
+
return regle?.$invalid;
|
|
2231
|
+
});
|
|
2232
|
+
});
|
|
2233
|
+
const $correct = computed(() => {
|
|
2234
|
+
const entries = Object.entries(regles);
|
|
2235
|
+
return !!entries.length && entries.every(([_, regle]) => {
|
|
2236
|
+
return regle?.$correct || regle.$anyDirty && !regle.$invalid;
|
|
2237
|
+
});
|
|
2238
|
+
});
|
|
2239
|
+
const $error = computed(() => {
|
|
2240
|
+
return Object.entries(regles).some(([_, regle]) => {
|
|
2241
|
+
return regle?.$error;
|
|
2242
|
+
});
|
|
2243
|
+
});
|
|
2244
|
+
const $ready = computed(() => {
|
|
2245
|
+
const entries = Object.entries(regles);
|
|
2246
|
+
return !!entries.length && entries.every(([_, regle]) => {
|
|
2247
|
+
return regle?.$ready;
|
|
2248
|
+
});
|
|
2249
|
+
});
|
|
2250
|
+
const $pending = computed(() => {
|
|
2251
|
+
return Object.entries(regles).some(([_, regle]) => {
|
|
2252
|
+
return regle?.$pending;
|
|
2253
|
+
});
|
|
2254
|
+
});
|
|
2255
|
+
const $errors = computed(() => {
|
|
2256
|
+
if (scoped) return Object.entries(regles).map(([_, regle]) => {
|
|
2257
|
+
return regle.$errors;
|
|
2258
|
+
});
|
|
2259
|
+
else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
|
|
2260
|
+
return [key, regle.$errors];
|
|
2261
|
+
}));
|
|
2262
|
+
});
|
|
2263
|
+
const $silentErrors = computed(() => {
|
|
2264
|
+
if (scoped) return Object.entries(regles).map(([_, regle]) => {
|
|
2265
|
+
return regle.$silentErrors;
|
|
2266
|
+
});
|
|
2267
|
+
else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
|
|
2268
|
+
return [key, regle.$silentErrors];
|
|
2269
|
+
}));
|
|
2270
|
+
});
|
|
2271
|
+
const $edited = computed(() => {
|
|
2272
|
+
const entries = Object.entries(regles);
|
|
2273
|
+
return !!entries.length && entries.every(([_, regle]) => {
|
|
2274
|
+
return regle?.$edited;
|
|
2275
|
+
});
|
|
2276
|
+
});
|
|
2277
|
+
const $anyEdited = computed(() => {
|
|
2278
|
+
return Object.entries(regles).some(([_, regle]) => {
|
|
2279
|
+
return regle?.$anyEdited;
|
|
2280
|
+
});
|
|
2281
|
+
});
|
|
2282
|
+
const $instances = computed(() => {
|
|
2283
|
+
if (scoped) return Object.values(regles);
|
|
2284
|
+
else return regles;
|
|
2285
|
+
});
|
|
2286
|
+
function $reset(options) {
|
|
2287
|
+
Object.values(regles).forEach((regle) => {
|
|
2288
|
+
regle.$reset(options);
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
function $touch() {
|
|
2292
|
+
Object.values(regles).forEach((regle) => {
|
|
2293
|
+
regle.$touch();
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
function $extractDirtyFields(filterNullishValues = true) {
|
|
2297
|
+
return Object.values(regles).map((regle) => regle.$extractDirtyFields(filterNullishValues));
|
|
2298
|
+
}
|
|
2299
|
+
function $clearExternalErrors() {
|
|
2300
|
+
Object.values(regles).forEach((regle) => {
|
|
2301
|
+
regle.$clearExternalErrors();
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
async function $validate() {
|
|
2305
|
+
try {
|
|
2306
|
+
const data = $value.value;
|
|
2307
|
+
const results = await Promise.allSettled(Object.values(regles).map((regle) => {
|
|
2308
|
+
return regle.$validate();
|
|
2309
|
+
}));
|
|
2310
|
+
const validationResults = results.every((value) => {
|
|
2311
|
+
if (value.status === "fulfilled") return value.value.valid === true;
|
|
2312
|
+
else return false;
|
|
2313
|
+
});
|
|
2314
|
+
return {
|
|
2315
|
+
valid: validationResults,
|
|
2316
|
+
data
|
|
2317
|
+
};
|
|
2318
|
+
} catch (e) {
|
|
2319
|
+
return {
|
|
2320
|
+
valid: false,
|
|
2321
|
+
data: $value.value
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
return reactive({
|
|
2326
|
+
...!scoped && { $silentValue },
|
|
2327
|
+
$errors,
|
|
2328
|
+
$silentErrors,
|
|
2329
|
+
$instances,
|
|
2330
|
+
$value,
|
|
2331
|
+
$dirty,
|
|
2332
|
+
$anyDirty,
|
|
2333
|
+
$invalid,
|
|
2334
|
+
$correct,
|
|
2335
|
+
$error,
|
|
2336
|
+
$pending,
|
|
2337
|
+
$ready,
|
|
2338
|
+
$edited,
|
|
2339
|
+
$anyEdited,
|
|
2340
|
+
$reset,
|
|
2341
|
+
$touch,
|
|
2342
|
+
$validate,
|
|
2343
|
+
$extractDirtyFields,
|
|
2344
|
+
$clearExternalErrors
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
//#endregion
|
|
2349
|
+
//#region src/core/createScopedUseRegle/useCollectScope.ts
|
|
2350
|
+
function createUseCollectScope(instances, options) {
|
|
2351
|
+
function useCollectScope$1(namespace) {
|
|
2352
|
+
const computedNamespace = computed(() => toValue(namespace));
|
|
2353
|
+
setEmptyNamespace();
|
|
2354
|
+
const r$ = ref(collectRegles(instances.value));
|
|
2355
|
+
const regle = reactive({ r$ });
|
|
2356
|
+
function setEmptyNamespace() {
|
|
2357
|
+
if (computedNamespace.value && !instances.value[computedNamespace.value]) instances.value[computedNamespace.value] = {};
|
|
2358
|
+
}
|
|
2359
|
+
watch(computedNamespace, setEmptyNamespace);
|
|
2360
|
+
watch(instances, (newInstances) => {
|
|
2361
|
+
r$.value = collectRegles(newInstances);
|
|
2362
|
+
}, { deep: true });
|
|
2363
|
+
function collectRegles(r$Instances) {
|
|
2364
|
+
if (computedNamespace.value) {
|
|
2365
|
+
const namespaceInstances = r$Instances[computedNamespace.value] ?? {};
|
|
2366
|
+
return mergeRegles(namespaceInstances, !options.asRecord);
|
|
2367
|
+
} else return mergeRegles(r$Instances["~~global"] ?? {}, !options.asRecord);
|
|
2368
|
+
}
|
|
2369
|
+
return { r$: regle.r$ };
|
|
2370
|
+
}
|
|
2371
|
+
return { useCollectScope: useCollectScope$1 };
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
//#endregion
|
|
2375
|
+
//#region src/core/createScopedUseRegle/useScopedRegle.ts
|
|
2376
|
+
function createUseScopedRegleComposable(instances, customUseRegle) {
|
|
2377
|
+
const scopedUseRegle = customUseRegle ?? useRegle;
|
|
2378
|
+
const useScopedRegle$1 = (state, rulesFactory, options) => {
|
|
2379
|
+
const { namespace, scopeKey,...restOptions } = options ?? {};
|
|
2380
|
+
scopedUseRegle.__config ??= {};
|
|
2381
|
+
const computedNamespace = computed(() => toValue(namespace));
|
|
2382
|
+
const $id = ref(`${Object.keys(instances.value).length + 1}-${randomId()}`);
|
|
2383
|
+
const instanceName = computed(() => {
|
|
2384
|
+
return options?.scopeKey ?? `instance-${$id.value}`;
|
|
2385
|
+
});
|
|
2386
|
+
const { r$ } = scopedUseRegle(state, rulesFactory, restOptions);
|
|
2387
|
+
register();
|
|
2388
|
+
tryOnScopeDispose(dispose);
|
|
2389
|
+
watch(computedNamespace, (newName, oldName) => {
|
|
2390
|
+
dispose(oldName);
|
|
2391
|
+
register();
|
|
2392
|
+
});
|
|
2393
|
+
if (getCurrentInstance()) onMounted(() => {
|
|
2394
|
+
const currentInstance = getCurrentInstance();
|
|
2395
|
+
if (typeof window !== "undefined" && currentInstance?.proxy?.$el?.parentElement) {
|
|
2396
|
+
if (document.documentElement && !document.documentElement.contains(currentInstance?.proxy?.$el?.parentElement)) dispose();
|
|
2397
|
+
}
|
|
2398
|
+
});
|
|
2399
|
+
function dispose(oldName) {
|
|
2400
|
+
const nameToClean = oldName ?? computedNamespace.value;
|
|
2401
|
+
if (nameToClean) {
|
|
2402
|
+
if (instances.value[nameToClean]) delete instances.value[nameToClean][instanceName.value];
|
|
2403
|
+
} else if (instances.value["~~global"][instanceName.value]) delete instances.value["~~global"][instanceName.value];
|
|
2404
|
+
}
|
|
2405
|
+
function register() {
|
|
2406
|
+
if (computedNamespace.value) {
|
|
2407
|
+
if (!instances.value[computedNamespace.value]) instances.value[computedNamespace.value] = {};
|
|
2408
|
+
instances.value[computedNamespace.value][instanceName.value] = r$;
|
|
2409
|
+
} else {
|
|
2410
|
+
if (!instances.value["~~global"]) instances.value["~~global"] = {};
|
|
2411
|
+
instances.value["~~global"][instanceName.value] = r$;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
return {
|
|
2415
|
+
r$,
|
|
2416
|
+
dispose,
|
|
2417
|
+
register
|
|
2418
|
+
};
|
|
2419
|
+
};
|
|
2420
|
+
return { useScopedRegle: useScopedRegle$1 };
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
//#endregion
|
|
2424
|
+
//#region src/core/createScopedUseRegle/createScopedUseRegle.ts
|
|
2425
|
+
function createScopedUseRegle(options) {
|
|
2426
|
+
const useInstances = options?.customStore ? () => {
|
|
2427
|
+
if (options.customStore) {
|
|
2428
|
+
if (!options.customStore?.value["~~global"]) options.customStore.value["~~global"] = {};
|
|
2429
|
+
else if (options.customStore?.value) options.customStore.value = { "~~global": {} };
|
|
2430
|
+
}
|
|
2431
|
+
return options.customStore;
|
|
2432
|
+
} : createGlobalState(() => {
|
|
2433
|
+
const $inst = ref({ "~~global": {} });
|
|
2434
|
+
return $inst;
|
|
2435
|
+
});
|
|
2436
|
+
const instances = useInstances();
|
|
2437
|
+
const { useScopedRegle: useScopedRegle$1 } = createUseScopedRegleComposable(instances, options?.customUseRegle);
|
|
2438
|
+
const { useCollectScope: useCollectScope$1 } = createUseCollectScope(instances, { asRecord: options?.asRecord });
|
|
2439
|
+
return {
|
|
2440
|
+
useScopedRegle: useScopedRegle$1,
|
|
2441
|
+
useCollectScope: useCollectScope$1
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
const { useCollectScope, useScopedRegle } = createScopedUseRegle();
|
|
2445
|
+
|
|
2446
|
+
//#endregion
|
|
2447
|
+
//#region src/core/createVariant.ts
|
|
2448
|
+
/**
|
|
2449
|
+
* Declare variations of state that depends on one value
|
|
2450
|
+
*
|
|
2451
|
+
* Autocomplete may not work here because of https://github.com/microsoft/TypeScript/issues/49547
|
|
2452
|
+
*
|
|
2453
|
+
* ```ts
|
|
2454
|
+
* // ⚠️ Use getter syntax for your rules () => {} or a computed one
|
|
2455
|
+
* const {r$} = useRegle(state, () => {
|
|
2456
|
+
* const variant = createVariant(state, 'type', [
|
|
2457
|
+
* {type: { literal: literal('EMAIL')}, email: { required, email }},
|
|
2458
|
+
* {type: { literal: literal('GITHUB')}, username: { required }},
|
|
2459
|
+
* {type: { required }},
|
|
2460
|
+
* ]);
|
|
2461
|
+
*
|
|
2462
|
+
* return {
|
|
2463
|
+
* ...variant.value,
|
|
2464
|
+
* };
|
|
2465
|
+
* })
|
|
2466
|
+
* ```
|
|
2467
|
+
*/
|
|
2468
|
+
function createVariant(root, discriminantKey, variants) {
|
|
2469
|
+
const watchableRoot = computed(() => toValue(root)[discriminantKey]);
|
|
2470
|
+
const computedRules = computed(() => {
|
|
2471
|
+
const selectedVariant = variants.find((variant) => {
|
|
2472
|
+
if (variant[discriminantKey] && "literal" in variant[discriminantKey]) {
|
|
2473
|
+
const literalRule = variant[discriminantKey]["literal"];
|
|
2474
|
+
if (isRuleDef(literalRule)) return unref(literalRule._params?.[0]) === watchableRoot.value;
|
|
2475
|
+
}
|
|
2476
|
+
});
|
|
2477
|
+
if (selectedVariant) return selectedVariant;
|
|
2478
|
+
else {
|
|
2479
|
+
const anyDiscriminantRules = variants.find((variant) => isObject(variant[discriminantKey]) && !Object.keys(variant[discriminantKey]).some((key) => key === "literal"));
|
|
2480
|
+
if (anyDiscriminantRules) return anyDiscriminantRules;
|
|
2481
|
+
else return {};
|
|
2482
|
+
}
|
|
2483
|
+
});
|
|
2484
|
+
return computedRules;
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* Narrow a nested variant field to a discriminated value
|
|
2488
|
+
*
|
|
2489
|
+
* ```ts
|
|
2490
|
+
* if (narrowVariant(r$.$fields, 'type', 'EMAIL')) {
|
|
2491
|
+
* r$.$fields.email.$value = 'foo';
|
|
2492
|
+
* }
|
|
2493
|
+
* ```
|
|
2494
|
+
*/
|
|
2495
|
+
function narrowVariant(root, discriminantKey, discriminantValue) {
|
|
2496
|
+
return isObject(root[discriminantKey]) && "$value" in root[discriminantKey] && root[discriminantKey]?.$value === discriminantValue;
|
|
2497
|
+
}
|
|
2498
|
+
/**
|
|
2499
|
+
* Narrow a nested variant root to a reactive reference
|
|
2500
|
+
*
|
|
2501
|
+
* ```vue
|
|
2502
|
+
* <script setup lang="ts">
|
|
2503
|
+
* const variantR$ = variantToRef(r$, 'type', 'EMAIL');
|
|
2504
|
+
* </script>
|
|
2505
|
+
* ```
|
|
2506
|
+
*/
|
|
2507
|
+
function variantToRef(root, discriminantKey, discriminantValue) {
|
|
2508
|
+
const fromRoot = isRef(root) ? toRef(root.value, "$fields") : toRef(root, "$fields");
|
|
2509
|
+
const returnedRef = ref();
|
|
2510
|
+
watch(fromRoot, async () => {
|
|
2511
|
+
await nextTick();
|
|
2512
|
+
if (narrowVariant(fromRoot.value, discriminantKey, discriminantValue)) returnedRef.value = fromRoot.value;
|
|
2513
|
+
else returnedRef.value = void 0;
|
|
2514
|
+
}, {
|
|
2515
|
+
immediate: true,
|
|
2516
|
+
flush: "pre"
|
|
2517
|
+
});
|
|
2518
|
+
return returnedRef;
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
//#endregion
|
|
2522
|
+
//#region src/core/refineRules.ts
|
|
2523
|
+
/**
|
|
2524
|
+
* Helper method to wrap an raw rules object
|
|
2525
|
+
*
|
|
2526
|
+
* Similar to:
|
|
2527
|
+
*
|
|
2528
|
+
* ```ts
|
|
2529
|
+
* const rules = {...} satisfies RegleUnknownRulesTree
|
|
2530
|
+
* ```
|
|
2531
|
+
*/
|
|
2532
|
+
function defineRules(rules) {
|
|
2533
|
+
return rules;
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Refine a raw rules object to set rules that depends on the state values.
|
|
2537
|
+
*
|
|
2538
|
+
* @example
|
|
2539
|
+
*
|
|
2540
|
+
* ```ts
|
|
2541
|
+
* const rules = refineRules({
|
|
2542
|
+
* password: { required, type: type<string>() },
|
|
2543
|
+
* }, (state) => {
|
|
2544
|
+
* return {
|
|
2545
|
+
* confirmPassword: { required, sameAs: sameAs(() => state.value.password)}
|
|
2546
|
+
* }
|
|
2547
|
+
* })
|
|
2548
|
+
* ```
|
|
2549
|
+
*/
|
|
2550
|
+
function refineRules(rules, refinement) {
|
|
2551
|
+
return (state) => merge({ ...rules }, refinement(state));
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
//#endregion
|
|
2555
|
+
export { InternalRuleType, createRule, createScopedUseRegle, createVariant, defineRegleConfig, defineRules, extendRegleConfig, flatErrors, inferRules, mergeRegles, narrowVariant, refineRules, unwrapRuleParameters, useCollectScope, useRegle, useRootStorage, useScopedRegle, variantToRef };
|