@regle/core 1.11.0-beta.1 → 1.11.0-beta.3

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.
@@ -1,7 +1,6 @@
1
1
  import { computed, effectScope, getCurrentInstance, getCurrentScope, isRef, markRaw, nextTick, onMounted, onScopeDispose, reactive, ref, shallowRef, toRef, toValue, triggerRef, unref, version, watch, watchEffect } from "vue";
2
2
  import { setupDevtoolsPlugin } from "@vue/devtools-api";
3
3
 
4
- //#region ../shared/utils/isFile.ts
5
4
  /**
6
5
  * Server side friendly way of checking for a File
7
6
  */
@@ -9,8 +8,6 @@ function isFile(value) {
9
8
  return value?.constructor?.name == "File" || value?.constructor?.name == "FileList";
10
9
  }
11
10
 
12
- //#endregion
13
- //#region ../shared/utils/isEmpty.ts
14
11
  /**
15
12
  * This is the inverse of isFilled. It will check if the value is in any way empty (including arrays and objects)
16
13
  *
@@ -30,12 +27,8 @@ function isEmpty(value, considerEmptyArrayInvalid = true) {
30
27
  return !String(value).length;
31
28
  }
32
29
 
33
- //#endregion
34
- //#region ../shared/utils/symbol.ts
35
30
  const RegleRuleSymbol = Symbol("regle-rule");
36
31
 
37
- //#endregion
38
- //#region ../shared/utils/cloneDeep.ts
39
32
  function getRegExpFlags(regExp) {
40
33
  if (typeof regExp.source.flags == "string") return regExp.source.flags;
41
34
  else {
@@ -62,8 +55,6 @@ function cloneDeep(obj) {
62
55
  return result;
63
56
  }
64
57
 
65
- //#endregion
66
- //#region ../shared/utils/object.utils.ts
67
58
  function isObject(obj) {
68
59
  if (obj && (obj instanceof Date || obj.constructor.name == "File" || obj.constructor.name == "FileList")) return false;
69
60
  return typeof obj === "object" && obj !== null && !Array.isArray(obj);
@@ -146,8 +137,6 @@ function dotPathObjectToNested(obj) {
146
137
  return result;
147
138
  }
148
139
 
149
- //#endregion
150
- //#region ../shared/utils/toDate.ts
151
140
  /**
152
141
  * This utility will coerce any string, number or Date value into a Date using the Date constructor.
153
142
  */
@@ -160,8 +149,6 @@ function toDate(argument) {
160
149
  else return /* @__PURE__ */ new Date(NaN);
161
150
  }
162
151
 
163
- //#endregion
164
- //#region ../shared/utils/debounce.ts
165
152
  function debounce(func, wait, { immediate = false, trackDebounceRef } = {}) {
166
153
  let timeout;
167
154
  const debouncedFn = (...args) => {
@@ -202,8 +189,6 @@ function debounce(func, wait, { immediate = false, trackDebounceRef } = {}) {
202
189
  return debouncedFn;
203
190
  }
204
191
 
205
- //#endregion
206
- //#region ../shared/utils/isEqual.ts
207
192
  function isEqual(a, b, deep = false, firstDeep = true) {
208
193
  if (a === b) return true;
209
194
  if (a && b && typeof a == "object" && typeof b == "object") {
@@ -236,8 +221,6 @@ function isEqual(a, b, deep = false, firstDeep = true) {
236
221
  return a !== a && b !== b;
237
222
  }
238
223
 
239
- //#endregion
240
- //#region ../shared/utils/abortablePromise.ts
241
224
  var AbortError = class extends Error {
242
225
  constructor(message = "Promise was aborted") {
243
226
  super(message);
@@ -279,15 +262,11 @@ function abortablePromise(input) {
279
262
  };
280
263
  }
281
264
 
282
- //#endregion
283
- //#region src/types/rules/rule.internal.types.ts
284
265
  const InternalRuleType = {
285
266
  Inline: "__inline",
286
267
  Async: "__async"
287
268
  };
288
269
 
289
- //#endregion
290
- //#region src/types/utils/groups.ts
291
270
  function mergeBooleanGroupProperties(entries, property) {
292
271
  return entries.some((entry) => {
293
272
  if (!property) return false;
@@ -302,8 +281,6 @@ function mergeArrayGroupProperties(entries, property) {
302
281
  }, []);
303
282
  }
304
283
 
305
- //#endregion
306
- //#region src/core/createRule/unwrapRuleParameters.ts
307
284
  /**
308
285
  * Returns a clean list of parameters
309
286
  * Removing Ref and executing function to return the unwrapped value
@@ -335,8 +312,6 @@ function getFunctionParametersLength(func) {
335
312
  return (paramsMatch[0] || paramsMatch[1] || paramsMatch[2] || paramsMatch[3] || paramsMatch[4] || "").split(",").map((p) => p.trim()).filter((p) => p.length > 0).length;
336
313
  }
337
314
 
338
- //#endregion
339
- //#region src/core/createRule/defineRuleProcessors.ts
340
315
  function defineRuleProcessors(definition, ...params) {
341
316
  const { validator, type, async } = definition;
342
317
  const isAsync = async || type === InternalRuleType.Async || validator.constructor.name === "AsyncFunction";
@@ -393,8 +368,6 @@ function defineRuleProcessors(definition, ...params) {
393
368
  });
394
369
  }
395
370
 
396
- //#endregion
397
- //#region src/core/createRule/createRule.ts
398
371
  /**
399
372
  * Create a typed custom rule that can be used like default rules.
400
373
  * It can also be declared in the global options
@@ -458,8 +431,98 @@ function createRule(definition) {
458
431
  throw new Error("[createRule] validator must be a function");
459
432
  }
460
433
 
461
- //#endregion
462
- //#region src/core/useStorage/useStorage.ts
434
+ /**
435
+ * Checks if a Vue Ref is an object.
436
+ *
437
+ * @param obj - The Ref to check
438
+ * @returns True if the Ref is an object, false otherwise
439
+ */
440
+ function isRefObject(obj) {
441
+ return isObject(obj.value);
442
+ }
443
+ /**
444
+ * Unwraps a collection ($each) getter function or returns the getter directly if it's not a function.
445
+ *
446
+ * @template T - The type of the getter function or the getter value
447
+ * @param getter - The getter function or value to unwrap
448
+ * @param value - The value to pass to the getter
449
+ * @param index - The index to pass to the getter
450
+ * @returns An object containing the scope and the unwrapped value
451
+ */
452
+ function unwrapGetter(getter, value, index) {
453
+ const scope = effectScope();
454
+ let unwrapped;
455
+ if (getter instanceof Function) unwrapped = scope.run(() => getter(value, index ?? 0));
456
+ else unwrapped = getter;
457
+ return {
458
+ scope,
459
+ unwrapped
460
+ };
461
+ }
462
+
463
+ const VersionIs = {
464
+ LessThan: -1,
465
+ EqualTo: 0,
466
+ GreaterThan: 1
467
+ };
468
+ /**
469
+ * Compare two versions quickly.
470
+ * @param current Is this version greater, equal to, or less than the other?
471
+ * @param other The version to compare against the current version
472
+ * @return 1 if current is greater than other, 0 if they are equal or equivalent, and -1 if current is less than other
473
+ */
474
+ function versionCompare(current, other) {
475
+ const cp = String(current).split(".");
476
+ const op = String(other).split(".");
477
+ for (let depth = 0; depth < Math.min(cp.length, op.length); depth++) {
478
+ const cn = Number(cp[depth]);
479
+ const on = Number(op[depth]);
480
+ if (cn > on) return VersionIs.GreaterThan;
481
+ if (on > cn) return VersionIs.LessThan;
482
+ if (!isNaN(cn) && isNaN(on)) return VersionIs.GreaterThan;
483
+ if (isNaN(cn) && !isNaN(on)) return VersionIs.LessThan;
484
+ }
485
+ return VersionIs.EqualTo;
486
+ }
487
+ const isVueSuperiorOrEqualTo3dotFive = versionCompare(version, "3.5.0") === -1 ? false : true;
488
+
489
+ function uniqueIDNuxt() {
490
+ return Math.floor(Math.random() * Date.now()).toString();
491
+ }
492
+ /**
493
+ * Generates a random SSR compatible ID.
494
+ */
495
+ function randomId() {
496
+ if (typeof window === "undefined") return uniqueIDNuxt();
497
+ else return window.crypto.getRandomValues(new Uint32Array(1))[0].toString(10);
498
+ }
499
+
500
+ function tryOnScopeDispose(fn) {
501
+ if (getCurrentScope()) {
502
+ onScopeDispose(fn);
503
+ return true;
504
+ }
505
+ return false;
506
+ }
507
+ /**
508
+ * Creates a global state that is shared for scoped validation.
509
+ *
510
+ * @param stateFactory - The function that creates the state
511
+ * @returns The state factory function
512
+ */
513
+ function createGlobalState(stateFactory) {
514
+ let initialized = false;
515
+ let state;
516
+ const scope = effectScope(true);
517
+ return ((...args) => {
518
+ if (!initialized) {
519
+ state = scope.run(() => stateFactory(...args));
520
+ initialized = true;
521
+ }
522
+ return state;
523
+ });
524
+ }
525
+
463
526
  /**
464
527
  * Inspired by Vuelidate storage
465
528
  */
@@ -580,108 +643,6 @@ function useStorage() {
580
643
  };
581
644
  }
582
645
 
583
- //#endregion
584
- //#region src/utils/object.utils.ts
585
- /**
586
- * Checks if a Vue Ref is an object.
587
- *
588
- * @param obj - The Ref to check
589
- * @returns True if the Ref is an object, false otherwise
590
- */
591
- function isRefObject(obj) {
592
- return isObject(obj.value);
593
- }
594
- /**
595
- * Unwraps a collection ($each) getter function or returns the getter directly if it's not a function.
596
- *
597
- * @template T - The type of the getter function or the getter value
598
- * @param getter - The getter function or value to unwrap
599
- * @param value - The value to pass to the getter
600
- * @param index - The index to pass to the getter
601
- * @returns An object containing the scope and the unwrapped value
602
- */
603
- function unwrapGetter(getter, value, index) {
604
- const scope = effectScope();
605
- let unwrapped;
606
- if (getter instanceof Function) unwrapped = scope.run(() => getter(value, index ?? 0));
607
- else unwrapped = getter;
608
- return {
609
- scope,
610
- unwrapped
611
- };
612
- }
613
-
614
- //#endregion
615
- //#region src/utils/version-compare.ts
616
- const VersionIs = {
617
- LessThan: -1,
618
- EqualTo: 0,
619
- GreaterThan: 1
620
- };
621
- /**
622
- * Compare two versions quickly.
623
- * @param current Is this version greater, equal to, or less than the other?
624
- * @param other The version to compare against the current version
625
- * @return 1 if current is greater than other, 0 if they are equal or equivalent, and -1 if current is less than other
626
- */
627
- function versionCompare(current, other) {
628
- const cp = String(current).split(".");
629
- const op = String(other).split(".");
630
- for (let depth = 0; depth < Math.min(cp.length, op.length); depth++) {
631
- const cn = Number(cp[depth]);
632
- const on = Number(op[depth]);
633
- if (cn > on) return VersionIs.GreaterThan;
634
- if (on > cn) return VersionIs.LessThan;
635
- if (!isNaN(cn) && isNaN(on)) return VersionIs.GreaterThan;
636
- if (isNaN(cn) && !isNaN(on)) return VersionIs.LessThan;
637
- }
638
- return VersionIs.EqualTo;
639
- }
640
- const isVueSuperiorOrEqualTo3dotFive = versionCompare(version, "3.5.0") === -1 ? false : true;
641
-
642
- //#endregion
643
- //#region src/utils/randomId.ts
644
- function uniqueIDNuxt() {
645
- return Math.floor(Math.random() * Date.now()).toString();
646
- }
647
- /**
648
- * Generates a random SSR compatible ID.
649
- */
650
- function randomId() {
651
- if (typeof window === "undefined") return uniqueIDNuxt();
652
- else return window.crypto.getRandomValues(new Uint32Array(1))[0].toString(10);
653
- }
654
-
655
- //#endregion
656
- //#region src/utils/state.utils.ts
657
- function tryOnScopeDispose(fn) {
658
- if (getCurrentScope()) {
659
- onScopeDispose(fn);
660
- return true;
661
- }
662
- return false;
663
- }
664
- /**
665
- * Creates a global state that is shared for scoped validation.
666
- *
667
- * @param stateFactory - The function that creates the state
668
- * @returns The state factory function
669
- */
670
- function createGlobalState(stateFactory) {
671
- let initialized = false;
672
- let state;
673
- const scope = effectScope(true);
674
- return ((...args) => {
675
- if (!initialized) {
676
- state = scope.run(() => stateFactory(...args));
677
- initialized = true;
678
- }
679
- return state;
680
- });
681
- }
682
-
683
- //#endregion
684
- //#region src/core/useRegle/guards/ruleDef.guards.ts
685
646
  function isNestedRulesDef(state, rules) {
686
647
  return isRefObject(state) || isObject(rules.value) && !isEmpty(rules.value) && !Object.entries(rules.value).some(([_, rule]) => isRuleDef(rule) || typeof rule === "function");
687
648
  }
@@ -702,8 +663,6 @@ function isFormRuleDefinition(rule) {
702
663
  return true;
703
664
  }
704
665
 
705
- //#endregion
706
- //#region src/core/useRegle/guards/rule.status.guards.ts
707
666
  function isNestedRulesStatus(rule) {
708
667
  return isObject(rule) && "$fields" in rule;
709
668
  }
@@ -714,8 +673,6 @@ function isFieldStatus(rule) {
714
673
  return !!rule && "$rules" in rule;
715
674
  }
716
675
 
717
- //#endregion
718
- //#region src/core/useRegle/useErrors.ts
719
676
  function extractRulesIssues({ field, silent = false }) {
720
677
  const ruleIssues = Object.entries(field.$rules ?? {}).map(([key, rule]) => {
721
678
  let message = "";
@@ -794,8 +751,6 @@ function iterateErrors(errors, includePath = false, _path) {
794
751
  } else return Object.entries(errors).map(([key, value]) => iterateErrors(value, includePath, path?.concat(key))).flat();
795
752
  }
796
753
 
797
- //#endregion
798
- //#region src/core/useRegle/root/createReactiveRuleStatus.ts
799
754
  function createReactiveRuleStatus({ customMessages, rule, ruleKey, state, path, cachePath, storage, modifiers }) {
800
755
  let scope = effectScope();
801
756
  let scopeState = {};
@@ -829,21 +784,31 @@ function createReactiveRuleStatus({ customMessages, rule, ruleKey, state, path,
829
784
  ...$metadata.value
830
785
  }));
831
786
  const $active = computed(() => {
832
- if (isFormRuleDefinition(rule)) if (typeof rule.value.active === "function") return rule.value.active($defaultMetadata.value);
833
- else return !!rule.value.active;
834
- else return true;
787
+ try {
788
+ if (isFormRuleDefinition(rule)) if (typeof rule.value.active === "function") return rule.value.active($defaultMetadata.value);
789
+ else return !!rule.value.active;
790
+ else return true;
791
+ } catch (e) {
792
+ console.error(`Error in "active" function for "${path}.${ruleKey}" rule`, { cause: e });
793
+ return true;
794
+ }
835
795
  });
836
796
  function computeRuleProcessor(key) {
837
- let result = "";
838
- const customProcessor = customMessages ? customMessages[ruleKey]?.[key] : void 0;
839
- if (customProcessor) if (typeof customProcessor === "function") result = customProcessor($defaultMetadata.value);
840
- else result = customProcessor;
841
- if (isFormRuleDefinition(rule)) {
842
- const patchedKey = `_${key}_patched`;
843
- if (!(customProcessor && !rule.value[patchedKey])) if (typeof rule.value[key] === "function") result = rule.value[key]($defaultMetadata.value);
844
- else result = rule.value[key] ?? "";
797
+ try {
798
+ let result = "";
799
+ const customProcessor = customMessages ? customMessages[ruleKey]?.[key] : void 0;
800
+ if (customProcessor) if (typeof customProcessor === "function") result = customProcessor($defaultMetadata.value);
801
+ else result = customProcessor;
802
+ if (isFormRuleDefinition(rule)) {
803
+ const patchedKey = `_${key}_patched`;
804
+ if (!(customProcessor && !rule.value[patchedKey])) if (typeof rule.value[key] === "function") result = rule.value[key]($defaultMetadata.value);
805
+ else result = rule.value[key] ?? "";
806
+ }
807
+ return result;
808
+ } catch (e) {
809
+ console.error(`Error in "${key}" function for "${path}.${ruleKey}" rule`, { cause: e });
810
+ return "";
845
811
  }
846
- return result;
847
812
  }
848
813
  const $message = computed(() => {
849
814
  let message = computeRuleProcessor("message");
@@ -906,7 +871,7 @@ function createReactiveRuleStatus({ customMessages, rule, ruleKey, state, path,
906
871
  if (state.value !== cachedValue) return true;
907
872
  if (typeof validatorResult === "boolean") ruleResult = validatorResult;
908
873
  else {
909
- const { $valid: $valid$1,...rest } = validatorResult;
874
+ const { $valid: $valid$1, ...rest } = validatorResult;
910
875
  ruleResult = $valid$1;
911
876
  $metadata.value = rest;
912
877
  }
@@ -929,7 +894,7 @@ function createReactiveRuleStatus({ customMessages, rule, ruleKey, state, path,
929
894
  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");
930
895
  else if (resultOrPromise != null) if (typeof resultOrPromise === "boolean") ruleResult = resultOrPromise;
931
896
  else {
932
- const { $valid: $valid$1,...rest } = resultOrPromise;
897
+ const { $valid: $valid$1, ...rest } = resultOrPromise;
933
898
  ruleResult = $valid$1;
934
899
  $metadata.value = rest;
935
900
  }
@@ -970,8 +935,6 @@ function createReactiveRuleStatus({ customMessages, rule, ruleKey, state, path,
970
935
  });
971
936
  }
972
937
 
973
- //#endregion
974
- //#region src/core/useRegle/root/standard-schemas.ts
975
938
  function createStandardSchema(validateFn) {
976
939
  return { "~standard": {
977
940
  version: 1,
@@ -987,8 +950,7 @@ function createStandardSchema(validateFn) {
987
950
  } };
988
951
  }
989
952
 
990
- //#endregion
991
- //#region src/core/useRegle/root/createReactiveFieldStatus.ts
953
+ const DEFAULT_DEBOUNCE_TIME = 200;
992
954
  function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cachePath, fieldName, storage, options, externalErrors, schemaErrors, schemaMode, onUnwatch, $isArray, initialState, originalState, shortcuts, onValidate }) {
993
955
  let scope = effectScope();
994
956
  let scopeState;
@@ -1039,7 +1001,7 @@ function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cach
1039
1001
  storage.addRuleDeclEntry(cachePath, declaredRules);
1040
1002
  }
1041
1003
  function define$commit() {
1042
- if (scopeState.$debounce.value || scopeState.$haveAnyAsyncRule.value) $commit = debounce($commitHandler, scopeState.$debounce.value ?? (scopeState.$haveAnyAsyncRule ? 200 : 0), { trackDebounceRef: $isDebouncing });
1004
+ if (scopeState.$debounce.value > 0) $commit = debounce($commitHandler, scopeState.$debounce.value, { trackDebounceRef: $isDebouncing });
1043
1005
  else $commit = $commitHandler;
1044
1006
  }
1045
1007
  function $unwatch() {
@@ -1062,7 +1024,9 @@ function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cach
1062
1024
  const triggerPunishment = ref(false);
1063
1025
  const $anyDirty = computed(() => $dirty.value);
1064
1026
  const $debounce$1 = computed(() => {
1065
- return $localOptions.value.$debounce;
1027
+ if ($localOptions.value.$debounce != null) return $localOptions.value.$debounce;
1028
+ if (scopeState.$haveAnyAsyncRule.value) return DEFAULT_DEBOUNCE_TIME;
1029
+ return 0;
1066
1030
  });
1067
1031
  const $deepCompare = computed(() => {
1068
1032
  if ($localOptions.value.$deepCompare != null) return $localOptions.value.$deepCompare;
@@ -1188,6 +1152,16 @@ function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cach
1188
1152
  const $haveAnyAsyncRule$1 = computed(() => {
1189
1153
  return Object.values($rules.value).some((rule) => rule.$haveAsync);
1190
1154
  });
1155
+ const $modifiers = computed(() => {
1156
+ return {
1157
+ $debounce: $debounce$1.value,
1158
+ $lazy: $lazy$1.value,
1159
+ $rewardEarly: $rewardEarly$1.value,
1160
+ $autoDirty: $autoDirty$1.value,
1161
+ $silent: $silent.value,
1162
+ $clearExternalErrorsOnChange: $clearExternalErrorsOnChange$1.value
1163
+ };
1164
+ });
1191
1165
  function processShortcuts() {
1192
1166
  if (shortcuts?.fields) Object.entries(shortcuts.fields).forEach(([key, value]) => {
1193
1167
  const scope$1 = effectScope();
@@ -1249,7 +1223,8 @@ function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cach
1249
1223
  $dirty,
1250
1224
  processShortcuts,
1251
1225
  $silentValue,
1252
- $inactive
1226
+ $inactive,
1227
+ $modifiers
1253
1228
  };
1254
1229
  });
1255
1230
  define$watchState();
@@ -1395,7 +1370,7 @@ function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cach
1395
1370
  if (externalErrors?.value?.length) externalErrors.value = [];
1396
1371
  }
1397
1372
  if (!scopeState.$lazy.value && !scopeState.$dirty.value && !scopeState.$silent.value) $commitHandler();
1398
- const { $shortcuts, $validating, $autoDirty, $rewardEarly, $clearExternalErrorsOnChange, $haveAnyAsyncRule, $debounce, $lazy,...restScope } = scopeState;
1373
+ const { $shortcuts, $validating, $autoDirty, $rewardEarly, $clearExternalErrorsOnChange, $haveAnyAsyncRule, $debounce, $lazy, ...restScope } = scopeState;
1399
1374
  return reactive({
1400
1375
  ...restScope,
1401
1376
  $externalErrors: externalErrors,
@@ -1414,12 +1389,11 @@ function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cach
1414
1389
  $extractDirtyFields,
1415
1390
  $clearExternalErrors,
1416
1391
  $abort,
1392
+ "~modifiers": scopeState.$modifiers,
1417
1393
  ...createStandardSchema($validate)
1418
1394
  });
1419
1395
  }
1420
1396
 
1421
- //#endregion
1422
- //#region src/core/useRegle/root/collections/createReactiveCollectionElement.ts
1423
1397
  function createCollectionElement({ $id, path, cachePath, index, options, storage, stateValue, customMessages, rules, externalErrors, schemaErrors, initialState, originalState, shortcuts, fieldName, schemaMode }) {
1424
1398
  const $fieldId = stateValue.value?.$id ?? rules.$key ?? randomId();
1425
1399
  let $cachePath = `${cachePath}.${String($fieldId)}`;
@@ -1458,8 +1432,6 @@ function createCollectionElement({ $id, path, cachePath, index, options, storage
1458
1432
  return $status;
1459
1433
  }
1460
1434
 
1461
- //#endregion
1462
- //#region src/core/useRegle/root/collections/createReactiveCollectionRoot.ts
1463
1435
  function createReactiveCollectionStatus({ state, rulesDef, customMessages, path, storage, options, externalErrors, schemaErrors, schemaMode, initialState, originalState, shortcuts, fieldName }) {
1464
1436
  let scope = effectScope();
1465
1437
  let scopeState;
@@ -1696,6 +1668,16 @@ function createReactiveCollectionStatus({ state, rulesDef, customMessages, path,
1696
1668
  return true;
1697
1669
  });
1698
1670
  const $name = computed(() => fieldName);
1671
+ const $modifiers = computed(() => {
1672
+ return {
1673
+ $deepCompare: $localOptions.value.$deepCompare ?? false,
1674
+ $lazy: $localOptions.value.$lazy ?? false,
1675
+ $rewardEarly: $rewardEarly.value,
1676
+ $autoDirty: $autoDirty.value,
1677
+ $silent: $silent.value,
1678
+ $clearExternalErrorsOnChange: $localOptions.value.$clearExternalErrorsOnChange ?? false
1679
+ };
1680
+ });
1699
1681
  function processShortcuts() {
1700
1682
  if (shortcuts?.collections) Object.entries(shortcuts?.collections).forEach(([key, value]) => {
1701
1683
  const scope$1 = effectScope();
@@ -1750,7 +1732,8 @@ function createReactiveCollectionStatus({ state, rulesDef, customMessages, path,
1750
1732
  $rewardEarly,
1751
1733
  $silent,
1752
1734
  $autoDirty,
1753
- $issues
1735
+ $issues,
1736
+ $modifiers
1754
1737
  };
1755
1738
  });
1756
1739
  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`);
@@ -1844,7 +1827,7 @@ function createReactiveCollectionStatus({ state, rulesDef, customMessages, path,
1844
1827
  }
1845
1828
  return dirtyFields;
1846
1829
  }
1847
- const { $shortcuts,...restScopeState } = scopeState;
1830
+ const { $shortcuts, ...restScopeState } = scopeState;
1848
1831
  return reactive({
1849
1832
  $self: $selfStatus,
1850
1833
  ...restScopeState,
@@ -1862,13 +1845,12 @@ function createReactiveCollectionStatus({ state, rulesDef, customMessages, path,
1862
1845
  $abort,
1863
1846
  $extractDirtyFields,
1864
1847
  $clearExternalErrors,
1848
+ "~modifiers": scopeState.$modifiers,
1865
1849
  ...createStandardSchema($validate)
1866
1850
  });
1867
1851
  }
1868
1852
 
1869
- //#endregion
1870
- //#region src/core/useRegle/root/createReactiveNestedStatus.ts
1871
- function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, rootRules, externalErrors, schemaErrors, rootSchemaErrors, validationGroups, initialState, originalState, fieldName,...commonArgs }) {
1853
+ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, rootRules, externalErrors, schemaErrors, rootSchemaErrors, validationGroups, initialState, originalState, fieldName, ...commonArgs }) {
1872
1854
  let scope = effectScope();
1873
1855
  let scopeState;
1874
1856
  let nestedScopes = [];
@@ -2110,6 +2092,16 @@ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, roo
2110
2092
  return false;
2111
2093
  });
2112
2094
  const $name = computed(() => fieldName);
2095
+ const $modifiers = computed(() => {
2096
+ return {
2097
+ autoDirty: $autoDirty.value,
2098
+ lazy: unref(commonArgs.options.lazy) ?? false,
2099
+ rewardEarly: $rewardEarly.value,
2100
+ silent: $silent.value,
2101
+ clearExternalErrorsOnChange: unref(commonArgs.options.clearExternalErrorsOnChange) ?? false,
2102
+ id: unref(commonArgs.options.id)
2103
+ };
2104
+ });
2113
2105
  function processShortcuts() {
2114
2106
  if (commonArgs.shortcuts?.nested) Object.entries(commonArgs.shortcuts.nested).forEach(([key, value]) => {
2115
2107
  const scope$1 = effectScope();
@@ -2135,7 +2127,8 @@ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, roo
2135
2127
  $fields,
2136
2128
  $edited,
2137
2129
  $anyEdited,
2138
- $issues
2130
+ $issues,
2131
+ "~modifiers": unref(commonArgs.options)
2139
2132
  }));
2140
2133
  });
2141
2134
  return result;
@@ -2184,7 +2177,8 @@ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, roo
2184
2177
  $localPending,
2185
2178
  $autoDirty,
2186
2179
  $silent,
2187
- $value
2180
+ $value,
2181
+ $modifiers
2188
2182
  };
2189
2183
  });
2190
2184
  }
@@ -2275,7 +2269,7 @@ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, roo
2275
2269
  scopeState.$localPending.value = false;
2276
2270
  }
2277
2271
  }
2278
- const { $shortcuts, $localPending: _$localPending,...restScopeState } = scopeState;
2272
+ const { $shortcuts, $localPending: _$localPending, ...restScopeState } = scopeState;
2279
2273
  const fullStatus = reactive({
2280
2274
  ...restScopeState,
2281
2275
  ...$shortcuts,
@@ -2291,6 +2285,7 @@ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, roo
2291
2285
  $clearExternalErrors,
2292
2286
  $extractDirtyFields,
2293
2287
  $abort,
2288
+ ...!!rootRules ? { "~modifiers": scopeState.$modifiers } : {},
2294
2289
  ...createStandardSchema($validate)
2295
2290
  });
2296
2291
  watchEffect(() => {
@@ -2302,7 +2297,7 @@ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, roo
2302
2297
  /**
2303
2298
  * Main resolver divider, will distribute the logic depending on the type of the current value (primitive, object, array)
2304
2299
  */
2305
- function createReactiveChildrenStatus({ rulesDef,...properties }) {
2300
+ function createReactiveChildrenStatus({ rulesDef, ...properties }) {
2306
2301
  if (isCollectionRulesDef(rulesDef, properties.state, properties.schemaMode)) return createReactiveCollectionStatus({
2307
2302
  rulesDef,
2308
2303
  ...properties
@@ -2322,7 +2317,7 @@ function createReactiveChildrenStatus({ rulesDef,...properties }) {
2322
2317
  }, { deep: true });
2323
2318
  return { fakeState };
2324
2319
  });
2325
- const { state: _state,...restProperties } = properties;
2320
+ const { state: _state, ...restProperties } = properties;
2326
2321
  return createReactiveNestedStatus({
2327
2322
  rulesDef,
2328
2323
  ...restProperties,
@@ -2335,1248 +2330,1245 @@ function createReactiveChildrenStatus({ rulesDef,...properties }) {
2335
2330
  });
2336
2331
  }
2337
2332
 
2338
- //#endregion
2339
- //#region src/core/useRegle/root/useRootStorage.ts
2340
- function useRootStorage({ initialState, originalState, options, scopeRules, state, customRules, shortcuts, schemaErrors, schemaMode = false, onValidate }) {
2341
- const storage = useStorage();
2342
- const regle = ref();
2343
- const computedExternalErrors = ref();
2344
- let $unwatchExternalErrors;
2345
- let $unwatchComputedExternalErrors;
2346
- function defineExternalErrorsWatchSource() {
2347
- $unwatchExternalErrors = watch(() => options.externalErrors?.value, () => {
2348
- $unwatchComputedExternalErrors?.();
2349
- if (options.externalErrors?.value && Object.keys(options.externalErrors.value).some((key) => key.includes("."))) computedExternalErrors.value = dotPathObjectToNested(options.externalErrors.value);
2350
- else computedExternalErrors.value = options.externalErrors?.value;
2351
- defineComputedExternalErrorsWatchSource();
2352
- }, {
2353
- immediate: true,
2354
- deep: true
2355
- });
2356
- }
2357
- function defineComputedExternalErrorsWatchSource() {
2358
- $unwatchComputedExternalErrors = watch(() => computedExternalErrors.value, () => {
2359
- $unwatchExternalErrors?.();
2360
- if (options.externalErrors?.value) options.externalErrors.value = computedExternalErrors.value;
2361
- defineExternalErrorsWatchSource();
2362
- }, { deep: true });
2333
+ const COLORS = {
2334
+ ERROR: {
2335
+ text: 16777215,
2336
+ bg: 15680580
2337
+ },
2338
+ INVALID: {
2339
+ text: 16777215,
2340
+ bg: 16747520
2341
+ },
2342
+ VALID: {
2343
+ text: 16777215,
2344
+ bg: 1096065
2345
+ },
2346
+ PENDING: {
2347
+ text: 16777215,
2348
+ bg: 16096779
2349
+ },
2350
+ DIRTY: {
2351
+ text: 2042167,
2352
+ bg: 16708551
2353
+ },
2354
+ COMPONENT: {
2355
+ text: 16777215,
2356
+ bg: 6514417
2357
+ },
2358
+ PRISTINE: {
2359
+ text: 1120295,
2360
+ bg: 16777215
2361
+ },
2362
+ INACTIVE: {
2363
+ text: 0,
2364
+ bg: 11581118
2363
2365
  }
2364
- defineExternalErrorsWatchSource();
2365
- if (isNestedRulesDef(state, scopeRules)) regle.value = createReactiveNestedStatus({
2366
- rootRules: scopeRules,
2367
- rulesDef: scopeRules,
2368
- state,
2369
- customMessages: customRules?.(),
2370
- storage,
2371
- options,
2372
- externalErrors: computedExternalErrors,
2373
- validationGroups: options.validationGroups,
2374
- initialState,
2375
- originalState,
2376
- shortcuts,
2377
- fieldName: "root",
2378
- path: "",
2379
- cachePath: "",
2380
- schemaErrors,
2381
- rootSchemaErrors: schemaErrors,
2382
- schemaMode,
2383
- onValidate
2384
- });
2385
- else if (isValidatorRulesDef(scopeRules)) regle.value = createReactiveFieldStatus({
2386
- rulesDef: scopeRules,
2387
- state,
2388
- customMessages: customRules?.(),
2389
- storage,
2390
- options,
2391
- externalErrors: computedExternalErrors,
2392
- initialState,
2393
- originalState,
2394
- shortcuts,
2395
- fieldName: "root",
2396
- path: "",
2397
- cachePath: "",
2398
- schemaMode,
2399
- schemaErrors,
2400
- onValidate
2401
- });
2402
- if (getCurrentScope()) onScopeDispose(() => {
2403
- regle.value?.$unwatch();
2404
- $unwatchComputedExternalErrors?.();
2405
- $unwatchExternalErrors?.();
2406
- });
2407
- return reactive({ regle });
2408
- }
2409
-
2410
- //#endregion
2411
- //#region src/core/useRegle/shared.rootRegle.ts
2412
- function createRootRegleLogic({ state, rulesFactory, options, globalOptions, customRules, shortcuts }) {
2413
- const definedRules = isRef(rulesFactory) ? rulesFactory : typeof rulesFactory === "function" ? void 0 : computed(() => rulesFactory);
2414
- const resolvedOptions = {
2415
- ...globalOptions,
2416
- ...options
2417
- };
2418
- let unwatchRules;
2419
- const watchableRulesGetters = shallowRef(definedRules ?? {});
2420
- if (typeof rulesFactory === "function") unwatchRules = watchEffect(() => {
2421
- watchableRulesGetters.value = rulesFactory(state);
2422
- triggerRef(watchableRulesGetters);
2423
- });
2424
- const initialState = ref(isObject(state.value) ? { ...cloneDeep(state.value) } : cloneDeep(state.value));
2425
- const originalState = isObject(state.value) ? { ...cloneDeep(state.value) } : cloneDeep(state.value);
2426
- tryOnScopeDispose(() => {
2427
- unwatchRules?.();
2428
- });
2429
- const regle = useRootStorage({
2430
- scopeRules: watchableRulesGetters,
2431
- state,
2432
- options: resolvedOptions,
2433
- initialState,
2434
- originalState,
2435
- customRules,
2436
- shortcuts
2437
- });
2438
- if (process.env.NODE_ENV === "development" && regle.regle) registerRegleInstance(regle.regle, { name: toValue(resolvedOptions.id) });
2439
- return regle;
2440
- }
2366
+ };
2367
+ const PRIORITY_KEYS = {
2368
+ ROOT: [
2369
+ "$invalid",
2370
+ "$dirty",
2371
+ "$error",
2372
+ "$pending",
2373
+ "$valid",
2374
+ "$ready"
2375
+ ],
2376
+ FIELD: [
2377
+ "$value",
2378
+ "$invalid",
2379
+ "$dirty",
2380
+ "$error",
2381
+ "$pending",
2382
+ "$errors"
2383
+ ]
2384
+ };
2385
+ const INSPECTOR_IDS = {
2386
+ INSPECTOR: "regle-inspector",
2387
+ COMPONENTS: "components",
2388
+ TIMELINE: "regle-timeline"
2389
+ };
2441
2390
 
2442
- //#endregion
2443
- //#region src/devtools/registry.ts
2444
- var RegleDevtoolsRegistry = class {
2445
- instances = reactive(/* @__PURE__ */ new Map());
2446
- watchers = /* @__PURE__ */ new Map();
2447
- idCounter = 0;
2448
- notifyCallbacks = /* @__PURE__ */ new Set();
2449
- register(r$, options) {
2450
- const id = `regle-${++this.idCounter}`;
2451
- const name = options?.name || `Regle #${this.idCounter}`;
2452
- this.instances.set(id, {
2391
+ function useRegleDevtoolsRegistry() {
2392
+ const devtoolsApi = shallowRef();
2393
+ const instances = shallowRef(/* @__PURE__ */ new Map());
2394
+ const watchers = shallowRef(/* @__PURE__ */ new Map());
2395
+ let idCounter = 0;
2396
+ function setApi(api) {
2397
+ devtoolsApi.value = api;
2398
+ }
2399
+ function register(r$, options) {
2400
+ const id = `${options?.uid?.toString() ?? "regle"}#${++idCounter}`;
2401
+ const name = options?.name || `Regle #${idCounter}`;
2402
+ instances.value.set(id, {
2453
2403
  id,
2454
2404
  name,
2455
2405
  r$,
2456
2406
  componentName: options?.componentName ? `<${options.componentName}>` : void 0
2457
2407
  });
2458
- this.notifyDevtools();
2408
+ const stopHandle = watch(() => r$, () => notifyDevtools(), {
2409
+ deep: true,
2410
+ flush: "post"
2411
+ });
2412
+ regleDevtoolsRegistry.addWatcher(id, stopHandle);
2413
+ notifyDevtools();
2459
2414
  return id;
2460
2415
  }
2461
- unregister(id) {
2462
- this.instances.delete(id);
2463
- const watcher = this.watchers.get(id);
2416
+ function notifyDevtools() {
2417
+ if (devtoolsApi.value) emitInspectorState(devtoolsApi.value);
2418
+ }
2419
+ function unregister(id) {
2420
+ instances.value.delete(id);
2421
+ const watcher = watchers.value.get(id);
2464
2422
  if (watcher) {
2465
2423
  watcher();
2466
- this.watchers.delete(id);
2424
+ watchers.value.delete(id);
2467
2425
  }
2468
- this.notifyDevtools();
2469
- }
2470
- getAll() {
2471
- return Array.from(this.instances.values());
2426
+ notifyDevtools();
2472
2427
  }
2473
- get(id) {
2474
- return this.instances.get(id);
2428
+ function getAll() {
2429
+ return Array.from(instances.value.values());
2475
2430
  }
2476
- clear() {
2477
- this.watchers.forEach((stop) => stop());
2478
- this.watchers.clear();
2479
- this.instances.clear();
2480
- this.notifyDevtools();
2431
+ function get(id) {
2432
+ return instances.value.get(id);
2481
2433
  }
2482
- onInstancesChange(callback) {
2483
- this.notifyCallbacks.add(callback);
2484
- return () => {
2485
- this.notifyCallbacks.delete(callback);
2486
- };
2487
- }
2488
- addWatcher(id, stopHandle) {
2489
- this.watchers.set(id, stopHandle);
2434
+ function clear() {
2435
+ watchers.value.forEach((stop) => stop());
2436
+ watchers.value.clear();
2437
+ instances.value.clear();
2438
+ notifyDevtools();
2490
2439
  }
2491
- notifyDevtools() {
2492
- this.notifyCallbacks.forEach((callback) => callback());
2440
+ function addWatcher(id, stopHandle) {
2441
+ watchers.value.set(id, stopHandle);
2493
2442
  }
2494
- };
2495
- const regleDevtoolsRegistry = new RegleDevtoolsRegistry();
2443
+ return {
2444
+ register,
2445
+ unregister,
2446
+ getAll,
2447
+ get,
2448
+ clear,
2449
+ addWatcher,
2450
+ setApi,
2451
+ notifyDevtools
2452
+ };
2453
+ }
2454
+ const regleDevtoolsRegistry = useRegleDevtoolsRegistry();
2455
+ /**
2456
+ * To be used by `useRegle` composable.
2457
+ */
2496
2458
  function registerRegleInstance(r$, options) {
2497
2459
  if (typeof window === "undefined") return;
2498
2460
  const instance = getCurrentInstance();
2499
2461
  const componentName = instance?.type?.name || instance?.type?.__name;
2500
2462
  const id = regleDevtoolsRegistry.register(r$, {
2501
2463
  name: options?.name,
2502
- componentName
2464
+ componentName,
2465
+ uid: instance?.uid
2503
2466
  });
2504
2467
  tryOnScopeDispose(() => {
2505
2468
  regleDevtoolsRegistry.unregister(id);
2506
2469
  });
2507
2470
  }
2508
- function watchRegleInstance(id, r$, onChange) {
2509
- const stopHandle = watch(() => r$, onChange, {
2510
- deep: true,
2511
- flush: "post"
2512
- });
2513
- regleDevtoolsRegistry.addWatcher(id, stopHandle);
2514
- return stopHandle;
2515
- }
2516
2471
 
2517
- //#endregion
2518
- //#region src/core/useRegle/useRegle.ts
2519
- function createUseRegleComposable(customRules, options, shortcuts) {
2520
- const globalOptions = {
2521
- autoDirty: options?.autoDirty,
2522
- lazy: options?.lazy,
2523
- rewardEarly: options?.rewardEarly,
2524
- silent: options?.silent,
2525
- clearExternalErrorsOnChange: options?.clearExternalErrorsOnChange
2526
- };
2527
- function useRegle$1(state, rulesFactory, options$1) {
2528
- return { r$: createRootRegleLogic({
2529
- state: isRef(state) ? state : ref(state),
2530
- rulesFactory,
2531
- options: options$1,
2532
- globalOptions,
2533
- customRules,
2534
- shortcuts
2535
- }).regle };
2536
- }
2537
- return useRegle$1;
2472
+ function isMethod(value) {
2473
+ return typeof value === "function";
2538
2474
  }
2539
- /**
2540
- * useRegle serves as the foundation for validation logic.
2541
- *
2542
- * It accepts the following inputs:
2543
- *
2544
- * @param state - This can be a plain object, a ref, a reactive object, or a structure containing nested refs.
2545
- * @param rules - These should align with the structure of your state.
2546
- * @param modifiers - Customize regle behaviour
2547
- *
2548
- * ```ts
2549
- * import { useRegle } from '@regle/core';
2550
- import { required } from '@regle/rules';
2551
-
2552
- const { r$ } = useRegle({ email: '' }, {
2553
- email: { required }
2554
- })
2555
- * ```
2556
- * Docs: {@link https://reglejs.dev/core-concepts/}
2557
- */
2558
- const useRegle = createUseRegleComposable();
2559
-
2560
- //#endregion
2561
- //#region src/core/useRegle/inferRules.ts
2562
- function createInferRuleHelper() {
2563
- function inferRules$1(state, rulesFactory) {
2564
- return rulesFactory;
2565
- }
2566
- return inferRules$1;
2475
+ function getPriorityProperties(obj, keys) {
2476
+ return keys.filter((key) => key in obj && !isMethod(obj[key])).map((key) => {
2477
+ let editable = false;
2478
+ if (key === "$value") editable = true;
2479
+ return {
2480
+ key,
2481
+ value: obj[key],
2482
+ editable
2483
+ };
2484
+ });
2567
2485
  }
2568
- /**
2569
- * Rule type helper to provide autocomplete and typecheck to your form rules or part of your form rules
2570
- * It will just return the rules without any processing.
2571
- *
2572
- * @param state - The state reference
2573
- * @param rules - Your rule tree
2574
- */
2575
- const inferRules = createInferRuleHelper();
2576
-
2577
- //#endregion
2578
- //#region src/core/useRegle/useRules.ts
2579
- function registerRegleWithDevtools(regle, options) {
2580
- try {
2581
- registerRegleInstance(regle, { name: options?.devtoolsName });
2582
- } catch (e) {}
2486
+ function getRemainingProperties(obj, excludeKeys) {
2487
+ return Object.entries(obj).filter(([key]) => !excludeKeys.includes(key) && key.startsWith("$") && !isMethod(obj[key])).map(([key, value]) => ({
2488
+ key,
2489
+ value,
2490
+ editable: false
2491
+ }));
2583
2492
  }
2584
- function createEmptyRuleState(rules) {
2585
- const result = {};
2586
- if (Object.entries(rules).some(([_, rule]) => isRuleDef(rule) || typeof rule === "function")) return null;
2587
- for (const key in rules) {
2588
- const item = rules[key];
2589
- if (!!item && isObject(item) && "$each" in item && item["$each"] && isObject(item["$each"])) result[key] = [createEmptyRuleState(item["$each"])];
2590
- else if (isObject(item) && !isEmpty(item) && !Object.entries(item).some(([_, rule]) => isRuleDef(rule) || typeof rule === "function")) result[key] = createEmptyRuleState(item);
2591
- else result[key] = null;
2592
- }
2593
- return result;
2493
+ function parseFieldNodeId(nodeId) {
2494
+ if (!nodeId.includes(":field:")) return null;
2495
+ const [instanceId, , fieldName] = nodeId.split(":");
2496
+ return {
2497
+ instanceId,
2498
+ fieldName
2499
+ };
2594
2500
  }
2595
- function createUseRulesComposable(customRules, options, shortcuts) {
2596
- const globalOptions = {
2597
- autoDirty: options?.autoDirty,
2598
- lazy: options?.lazy,
2599
- rewardEarly: options?.rewardEarly,
2600
- silent: options?.silent,
2601
- clearExternalErrorsOnChange: options?.clearExternalErrorsOnChange
2501
+ function createFieldNodeId(instanceId, fieldName) {
2502
+ return `${instanceId}:field:${fieldName}`;
2503
+ }
2504
+ function parseRuleNodeId(nodeId) {
2505
+ if (!nodeId.includes(":rule:")) return null;
2506
+ const parts = nodeId.split(":rule:");
2507
+ if (parts.length !== 2) return null;
2508
+ const fieldPart = parts[0];
2509
+ const ruleName = parts[1];
2510
+ const fieldInfo = parseFieldNodeId(fieldPart);
2511
+ if (!fieldInfo) return null;
2512
+ return {
2513
+ ...fieldInfo,
2514
+ ruleName
2602
2515
  };
2603
- function useRules$1(rulesFactory, options$1) {
2604
- const definedRules = isRef(rulesFactory) ? rulesFactory : typeof rulesFactory === "function" ? void 0 : computed(() => rulesFactory);
2605
- const regle = createRootRegleLogic({
2606
- state: ref(createEmptyRuleState(definedRules?.value)),
2607
- rulesFactory: definedRules,
2608
- options: options$1,
2609
- globalOptions,
2610
- customRules,
2611
- shortcuts
2612
- });
2613
- registerRegleWithDevtools(regle.regle, options$1);
2614
- return regle.regle;
2615
- }
2616
- return useRules$1;
2617
2516
  }
2618
- /**
2619
- * useRules is a clone of useRegle, without the need to provide a state.
2620
- *
2621
- * It accepts the following inputs:
2622
- *
2623
- * @param rules - Your rules object
2624
- * @param modifiers - Customize regle behaviour
2625
- *
2626
- * ```ts
2627
- * import { useRules } from '@regle/core';
2628
- import { required } from '@regle/rules';
2629
-
2630
- const { r$ } = useRules({
2631
- email: { required }
2632
- })
2633
- * ```
2634
- */
2635
- const useRules = createUseRulesComposable();
2517
+ function createRuleNodeId(instanceId, fieldName, ruleName) {
2518
+ return `${createFieldNodeId(instanceId, fieldName)}:rule:${ruleName}`;
2519
+ }
2636
2520
 
2637
- //#endregion
2638
- //#region src/core/defineRegleConfig.ts
2639
2521
  /**
2640
- * Define a global regle configuration, where you can:
2641
- * - Customize built-in rules messages
2642
- * - Add your custom rules
2643
- * - Define global modifiers
2644
- * - Define shortcuts
2645
- *
2646
- * It will return:
2647
- *
2648
- * - a `useRegle` composable that can typecheck your custom rules
2649
- * - an `inferRules` helper that can typecheck your custom rules
2522
+ * Build state for a field node
2650
2523
  */
2651
- function defineRegleConfig({ rules, modifiers, shortcuts }) {
2652
- const useRegle$1 = createUseRegleComposable(rules, modifiers, shortcuts);
2653
- const useRules$1 = createUseRulesComposable(rules, modifiers, shortcuts);
2654
- useRegle$1.__config = {
2655
- rules,
2656
- modifiers,
2657
- shortcuts
2658
- };
2659
- useRules$1.__config = {
2660
- rules,
2661
- modifiers,
2662
- shortcuts
2663
- };
2664
- return {
2665
- useRegle: useRegle$1,
2666
- inferRules: createInferRuleHelper(),
2667
- useRules: useRules$1
2668
- };
2524
+ function buildFieldState(fieldStatus) {
2525
+ const state = {};
2526
+ const priorityProperties = getPriorityProperties(fieldStatus, PRIORITY_KEYS.FIELD);
2527
+ if (priorityProperties.length > 0) state["State"] = priorityProperties;
2528
+ const remainingProperties = getRemainingProperties(fieldStatus, [
2529
+ ...PRIORITY_KEYS.FIELD,
2530
+ "$rules",
2531
+ "$fields"
2532
+ ]);
2533
+ if (remainingProperties.length > 0) state["Other Properties"] = remainingProperties;
2534
+ if (fieldStatus["~modifiers"]) state["Modifiers"] = Object.entries(fieldStatus["~modifiers"]).map(([key, value]) => ({
2535
+ key,
2536
+ value,
2537
+ editable: false
2538
+ }));
2539
+ return state;
2669
2540
  }
2670
2541
  /**
2671
- * Extend an already created custom `useRegle` (as the first parameter)
2672
- *
2673
- * It will return:
2674
- *
2675
- * - a `useRegle` composable that can typecheck your custom rules
2676
- * - an `inferRules` helper that can typecheck your custom rules
2542
+ * Build state for a rule node
2677
2543
  */
2678
- function extendRegleConfig(regle, { rules, modifiers, shortcuts }) {
2679
- const rootConfig = regle.__config ?? {};
2680
- const newRules = () => ({
2681
- ...rootConfig.rules?.(),
2682
- ...rules?.()
2683
- });
2684
- const newModifiers = rootConfig.modifiers && modifiers ? merge(rootConfig.modifiers, modifiers) : rootConfig.modifiers ?? modifiers;
2685
- const newShortcuts = rootConfig.shortcuts && shortcuts ? merge(rootConfig.shortcuts, shortcuts) : rootConfig.shortcuts ?? shortcuts;
2686
- const useRegle$1 = createUseRegleComposable(newRules, newModifiers, newShortcuts);
2687
- useRegle$1.__config = {
2688
- rules: newRules,
2689
- modifiers: newModifiers,
2690
- shortcuts: newShortcuts
2691
- };
2692
- return {
2693
- useRegle: useRegle$1,
2694
- inferRules: createInferRuleHelper()
2695
- };
2544
+ function buildRuleState(ruleStatus) {
2545
+ const state = {};
2546
+ const ruleKeys = [
2547
+ "$type",
2548
+ "$valid",
2549
+ "$active",
2550
+ "$pending",
2551
+ "$message",
2552
+ "$tooltip"
2553
+ ];
2554
+ const priorityProperties = getPriorityProperties(ruleStatus, ruleKeys);
2555
+ if (priorityProperties.length > 0) state["Rule State"] = priorityProperties;
2556
+ if (ruleStatus.$params && Array.isArray(ruleStatus.$params) && ruleStatus.$params.length > 0) state["Parameters"] = [{
2557
+ key: "$params",
2558
+ value: ruleStatus.$params,
2559
+ editable: false
2560
+ }];
2561
+ if (ruleStatus.$metadata !== void 0 && ruleStatus.$metadata !== true && ruleStatus.$metadata !== false) state["Metadata"] = [{
2562
+ key: "$metadata",
2563
+ value: ruleStatus.$metadata,
2564
+ objectType: "reactive",
2565
+ editable: false
2566
+ }];
2567
+ const remainingProperties = getRemainingProperties(ruleStatus, [
2568
+ ...ruleKeys,
2569
+ "$params",
2570
+ "$metadata",
2571
+ "$haveAsync",
2572
+ "$validating",
2573
+ "$fieldDirty",
2574
+ "$fieldInvalid",
2575
+ "$fieldPending",
2576
+ "$fieldCorrect",
2577
+ "$fieldError",
2578
+ "$maybePending",
2579
+ "$externalErrors"
2580
+ ]);
2581
+ if (remainingProperties.length > 0) state["Other Properties"] = remainingProperties;
2582
+ return state;
2583
+ }
2584
+ function buildRootState(r$) {
2585
+ const state = {};
2586
+ const priorityProperties = getPriorityProperties(r$, PRIORITY_KEYS.ROOT);
2587
+ if (priorityProperties.length > 0) state["State"] = priorityProperties;
2588
+ const remainingProperties = getRemainingProperties(r$, [...PRIORITY_KEYS.ROOT, "$fields"]);
2589
+ if (remainingProperties.length > 0) state["Other Properties"] = remainingProperties;
2590
+ if (r$["~modifiers"]) state["Modifiers"] = Object.entries(r$["~modifiers"]).map(([key, value]) => ({
2591
+ key,
2592
+ value,
2593
+ editable: false
2594
+ }));
2595
+ return state;
2596
+ }
2597
+ function resolveFieldByPath(fields, path) {
2598
+ if (!fields || !path) return null;
2599
+ const segments = path.match(/[^.\[\]]+/g);
2600
+ if (!segments) return null;
2601
+ let current = fields;
2602
+ for (const segment of segments) {
2603
+ if (!current) return null;
2604
+ const index = parseInt(segment);
2605
+ if (!isNaN(index)) if (isCollectionRulesStatus(current) && Array.isArray(current.$each)) current = current.$each[index];
2606
+ else return null;
2607
+ else if (isCollectionRulesStatus(current) && current.$self && current.$self) current = current.$self;
2608
+ else if (isNestedRulesStatus(current) && current.$fields && current.$fields[segment]) current = current.$fields[segment];
2609
+ else if (current[segment]) current = current[segment];
2610
+ else return null;
2611
+ }
2612
+ return current;
2613
+ }
2614
+ function buildInspectorState(nodeId, getInstance) {
2615
+ const ruleInfo = parseRuleNodeId(nodeId);
2616
+ if (ruleInfo) {
2617
+ const { instanceId, fieldName, ruleName } = ruleInfo;
2618
+ const instance$1 = getInstance(instanceId);
2619
+ if (!instance$1 || !instance$1.r$.$fields) return null;
2620
+ const fieldStatus = resolveFieldByPath(instance$1.r$.$fields, fieldName);
2621
+ if (!fieldStatus || !("$rules" in fieldStatus)) return null;
2622
+ const ruleStatus = fieldStatus.$rules[ruleName];
2623
+ if (!ruleStatus) return null;
2624
+ return buildRuleState(ruleStatus);
2625
+ }
2626
+ const fieldInfo = parseFieldNodeId(nodeId);
2627
+ if (fieldInfo) {
2628
+ const { instanceId, fieldName } = fieldInfo;
2629
+ const instance$1 = getInstance(instanceId);
2630
+ if (!instance$1 || !instance$1.r$.$fields) return null;
2631
+ const fieldStatus = resolveFieldByPath(instance$1.r$.$fields, fieldName);
2632
+ if (!fieldStatus) return null;
2633
+ return buildFieldState(fieldStatus);
2634
+ }
2635
+ const instance = getInstance(nodeId);
2636
+ if (!instance) return null;
2637
+ return buildRootState(instance.r$);
2696
2638
  }
2697
2639
 
2698
- //#endregion
2699
- //#region src/core/mergeRegles.ts
2700
- function mergeRegles(regles, _scoped) {
2701
- const scoped = _scoped == null ? false : _scoped;
2702
- const $value = computed({
2703
- get: () => {
2704
- if (scoped) return Object.values(regles).map((r) => r.$value);
2705
- else return Object.fromEntries(Object.entries(regles).map(([key, r]) => [key, r.$value]));
2706
- },
2707
- set: (value) => {
2708
- if (!scoped) {
2709
- if (typeof value === "object") Object.entries(value).forEach(([key, newValue]) => regles[key].$value = newValue);
2710
- }
2640
+ function handleValidateAction(nodeId, api) {
2641
+ if (nodeId.includes(":rule:")) return;
2642
+ const fieldInfo = parseFieldNodeId(nodeId);
2643
+ if (fieldInfo) {
2644
+ const { instanceId, fieldName } = fieldInfo;
2645
+ const instance = regleDevtoolsRegistry.get(instanceId);
2646
+ if (instance && instance.r$.$fields) {
2647
+ const fieldStatus = resolveFieldByPath(instance.r$.$fields, fieldName);
2648
+ if (fieldStatus && typeof fieldStatus.$validate === "function") fieldStatus.$validate();
2711
2649
  }
2712
- });
2713
- const $silentValue = computed({
2714
- get: () => Object.fromEntries(Object.entries(regles).map(([key, r]) => [key, r.$silentValue])),
2715
- set: (value) => {
2716
- if (typeof value === "object") Object.entries(value).forEach(([key, newValue]) => regles[key].$silentValue = newValue);
2650
+ } else {
2651
+ const instance = regleDevtoolsRegistry.get(nodeId);
2652
+ if (instance && typeof instance.r$.$validate === "function") instance.r$.$validate();
2653
+ }
2654
+ emitInspectorState(api);
2655
+ }
2656
+ async function handleResetAction(nodeId, api, resetState = false) {
2657
+ const fieldInfo = parseFieldNodeId(nodeId);
2658
+ if (fieldInfo) {
2659
+ const { instanceId, fieldName } = fieldInfo;
2660
+ const instance = regleDevtoolsRegistry.get(instanceId);
2661
+ if (instance && instance.r$.$fields) {
2662
+ const fieldStatus = resolveFieldByPath(instance.r$.$fields, fieldName);
2663
+ if (fieldStatus && typeof fieldStatus.$reset === "function") fieldStatus.$reset({ toOriginalState: resetState });
2717
2664
  }
2665
+ } else {
2666
+ const instance = regleDevtoolsRegistry.get(nodeId);
2667
+ if (instance && typeof instance.r$.$reset === "function") instance.r$.$reset({ toOriginalState: resetState });
2668
+ }
2669
+ }
2670
+ function handleEditInspectorState(payload) {
2671
+ const { nodeId, path, state } = payload;
2672
+ if (!path.includes("$value")) return;
2673
+ if (!parseFieldNodeId(nodeId)) return;
2674
+ const [instanceId, _, fieldPath] = nodeId.split(":");
2675
+ const instance = regleDevtoolsRegistry.get(instanceId);
2676
+ if (instance && instance.r$.$fields) {
2677
+ const fieldStatus = resolveFieldByPath(instance.r$.$fields, fieldPath);
2678
+ if (fieldStatus && "$value" in fieldStatus) fieldStatus.$value = state.value;
2679
+ }
2680
+ }
2681
+ async function emitInspectorState(api) {
2682
+ api.sendInspectorState(INSPECTOR_IDS.INSPECTOR);
2683
+ api.sendInspectorTree(INSPECTOR_IDS.INSPECTOR);
2684
+ }
2685
+
2686
+ function buildNodeTags(fieldOrR$, componentName) {
2687
+ const tags = [];
2688
+ if (fieldOrR$.$error) tags.push({
2689
+ label: "error",
2690
+ textColor: COLORS.ERROR.text,
2691
+ backgroundColor: COLORS.ERROR.bg
2718
2692
  });
2719
- const $dirty = computed(() => {
2720
- const entries = Object.entries(regles);
2721
- return !!entries.length && entries.every(([_, regle]) => {
2722
- return regle?.$dirty;
2723
- });
2724
- });
2725
- const $anyDirty = computed(() => {
2726
- return Object.entries(regles).some(([_, regle]) => {
2727
- return regle?.$anyDirty;
2728
- });
2693
+ else if (fieldOrR$.$correct) tags.push({
2694
+ label: "correct",
2695
+ textColor: COLORS.VALID.text,
2696
+ backgroundColor: COLORS.VALID.bg
2729
2697
  });
2730
- const $invalid = computed(() => {
2731
- return Object.entries(regles).some(([_, regle]) => {
2732
- return regle?.$invalid;
2733
- });
2698
+ if (fieldOrR$.$pending) tags.push({
2699
+ label: "pending",
2700
+ textColor: COLORS.PENDING.text,
2701
+ backgroundColor: COLORS.PENDING.bg
2734
2702
  });
2735
- const $correct = computed(() => {
2736
- const entries = Object.entries(regles);
2737
- return !!entries.length && entries.every(([_, regle]) => {
2738
- return regle?.$correct || regle.$anyDirty && !regle.$invalid;
2739
- });
2703
+ if (fieldOrR$.$dirty) tags.push({
2704
+ label: "dirty",
2705
+ textColor: COLORS.DIRTY.text,
2706
+ backgroundColor: COLORS.DIRTY.bg
2740
2707
  });
2741
- const $error = computed(() => {
2742
- return Object.entries(regles).some(([_, regle]) => {
2743
- return regle?.$error;
2744
- });
2708
+ else if ("$rules" in fieldOrR$) tags.push({
2709
+ label: "pristine",
2710
+ textColor: COLORS.PRISTINE.text,
2711
+ backgroundColor: COLORS.PRISTINE.bg
2745
2712
  });
2746
- const $ready = computed(() => {
2747
- const entries = Object.entries(regles);
2748
- return !!entries.length && entries.every(([_, regle]) => {
2749
- return regle?.$ready;
2750
- });
2713
+ if (componentName) tags.push({
2714
+ label: componentName,
2715
+ textColor: COLORS.COMPONENT.text,
2716
+ backgroundColor: COLORS.COMPONENT.bg
2751
2717
  });
2752
- const $pending = computed(() => {
2753
- return Object.entries(regles).some(([_, regle]) => {
2754
- return regle?.$pending;
2755
- });
2756
- });
2757
- const $issues = computed(() => {
2758
- if (scoped) return Object.entries(regles).map(([_, regle]) => {
2759
- return regle.$issues;
2760
- });
2761
- else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
2762
- return [key, regle.$issues];
2763
- }));
2764
- });
2765
- const $silentIssues = computed(() => {
2766
- if (scoped) return Object.entries(regles).map(([_, regle]) => {
2767
- return regle.$silentIssues;
2768
- });
2769
- else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
2770
- return [key, regle.$silentIssues];
2771
- }));
2718
+ return tags;
2719
+ }
2720
+ function buildRuleTags(rule) {
2721
+ const tags = [];
2722
+ if (!rule.$active) tags.push({
2723
+ label: "inactive",
2724
+ textColor: COLORS.INACTIVE.text,
2725
+ backgroundColor: COLORS.INACTIVE.bg
2772
2726
  });
2773
- const $errors = computed(() => {
2774
- if (scoped) return Object.entries(regles).map(([_, regle]) => {
2775
- return regle.$errors;
2776
- });
2777
- else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
2778
- return [key, regle.$errors];
2779
- }));
2727
+ else if (!rule.$valid) tags.push({
2728
+ label: "invalid",
2729
+ textColor: COLORS.INVALID.text,
2730
+ backgroundColor: COLORS.INVALID.bg
2780
2731
  });
2781
- const $silentErrors = computed(() => {
2782
- if (scoped) return Object.entries(regles).map(([_, regle]) => {
2783
- return regle.$silentErrors;
2784
- });
2785
- else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
2786
- return [key, regle.$silentErrors];
2787
- }));
2732
+ else if (rule.$valid) tags.push({
2733
+ label: "valid",
2734
+ textColor: COLORS.VALID.text,
2735
+ backgroundColor: COLORS.VALID.bg
2788
2736
  });
2789
- const $edited = computed(() => {
2790
- const entries = Object.entries(regles);
2791
- return !!entries.length && entries.every(([_, regle]) => {
2792
- return regle?.$edited;
2793
- });
2737
+ if (rule.$pending) tags.push({
2738
+ label: "pending",
2739
+ textColor: COLORS.PENDING.text,
2740
+ backgroundColor: COLORS.PENDING.bg
2794
2741
  });
2795
- const $anyEdited = computed(() => {
2796
- return Object.entries(regles).some(([_, regle]) => {
2797
- return regle?.$anyEdited;
2798
- });
2742
+ return tags;
2743
+ }
2744
+ function buildRuleNodes(fieldStatus, instanceId, fieldPath) {
2745
+ const children = [];
2746
+ if (!fieldStatus.$rules || typeof fieldStatus.$rules !== "object") return children;
2747
+ Object.entries(fieldStatus.$rules).forEach(([ruleName, ruleStatus]) => {
2748
+ if (ruleStatus && typeof ruleStatus === "object") {
2749
+ const ruleTags = buildRuleTags(ruleStatus);
2750
+ children.push({
2751
+ id: createRuleNodeId(instanceId, fieldPath, ruleName),
2752
+ label: `⚙️ ${ruleName}`,
2753
+ tags: ruleTags,
2754
+ children: []
2755
+ });
2756
+ }
2799
2757
  });
2800
- const $instances = computed(() => {
2801
- if (scoped) return Object.values(regles);
2802
- else return regles;
2758
+ return children;
2759
+ }
2760
+ function buildCollectionItemNodes(fieldStatus, instanceId, fieldPath) {
2761
+ const children = [];
2762
+ if (!fieldStatus.$each || !Array.isArray(fieldStatus.$each)) return children;
2763
+ fieldStatus.$each.forEach((item, index) => {
2764
+ if (item && typeof item === "object") {
2765
+ const itemTags = buildNodeTags(item);
2766
+ const itemPath = `${fieldPath}[${index}]`;
2767
+ let itemChildren = [];
2768
+ if (isNestedRulesStatus(item)) itemChildren = buildNestedFieldNodes(item.$fields, instanceId, itemPath);
2769
+ else if (isFieldStatus(item)) itemChildren = buildRuleNodes(item, instanceId, itemPath);
2770
+ children.push({
2771
+ id: createFieldNodeId(instanceId, itemPath),
2772
+ label: `[${index}]`,
2773
+ tags: itemTags,
2774
+ children: itemChildren
2775
+ });
2776
+ }
2803
2777
  });
2804
- function $reset(options) {
2805
- Object.values(regles).forEach((regle) => {
2806
- regle.$reset(options);
2778
+ if (fieldStatus.$self && typeof fieldStatus.$self === "object" && !isEmpty(fieldStatus.$self.$rules)) {
2779
+ const itemTags = buildNodeTags(fieldStatus.$self);
2780
+ const itemPath = `${fieldPath}[$self]`;
2781
+ children.unshift({
2782
+ id: createFieldNodeId(instanceId, itemPath),
2783
+ label: "$self",
2784
+ tags: itemTags,
2785
+ children: buildRuleNodes(fieldStatus.$self, instanceId, itemPath)
2807
2786
  });
2808
2787
  }
2809
- function $touch() {
2810
- Object.values(regles).forEach((regle) => {
2811
- regle.$touch();
2812
- });
2813
- }
2814
- function $extractDirtyFields(filterNullishValues = true) {
2815
- return Object.values(regles).map((regle) => regle.$extractDirtyFields(filterNullishValues));
2816
- }
2817
- function $clearExternalErrors() {
2818
- Object.values(regles).forEach((regle) => {
2819
- regle.$clearExternalErrors();
2820
- });
2821
- }
2822
- async function $validate(forceValues) {
2823
- try {
2824
- if (forceValues) $value.value = forceValues;
2825
- const data = $value.value;
2826
- return {
2827
- valid: (await Promise.allSettled(Object.values(regles).map((regle) => {
2828
- return regle.$validate();
2829
- }))).every((value) => {
2830
- if (value.status === "fulfilled") return value.value.valid === true;
2831
- else return false;
2832
- }),
2833
- data,
2834
- errors: $errors.value,
2835
- issues: $issues.value
2836
- };
2837
- } catch {
2838
- return {
2839
- valid: false,
2840
- data: $value.value,
2841
- errors: $errors.value,
2842
- issues: $issues.value
2843
- };
2788
+ return children;
2789
+ }
2790
+ function buildNestedFieldNodes(fields, instanceId, parentPath) {
2791
+ const children = [];
2792
+ Object.entries(fields).forEach(([fieldName, fieldStatus]) => {
2793
+ if (fieldStatus && typeof fieldStatus === "object") {
2794
+ const fieldPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
2795
+ const fieldTags = buildNodeTags(fieldStatus);
2796
+ let isCollection = false;
2797
+ let isEmptyCollection = false;
2798
+ let fieldChildren = [];
2799
+ if (isCollectionRulesStatus(fieldStatus)) {
2800
+ fieldChildren = buildCollectionItemNodes(fieldStatus, instanceId, fieldPath);
2801
+ isCollection = true;
2802
+ if (fieldChildren.length === 0) isEmptyCollection = true;
2803
+ } else if (isNestedRulesStatus(fieldStatus)) fieldChildren = buildNestedFieldNodes(fieldStatus.$fields, instanceId, fieldPath);
2804
+ else if (isFieldStatus(fieldStatus)) fieldChildren = buildRuleNodes(fieldStatus, instanceId, fieldPath);
2805
+ children.push({
2806
+ id: createFieldNodeId(instanceId, fieldPath),
2807
+ label: `${isCollection ? `${fieldName}[${fieldChildren.length}]` : fieldName}${isEmptyCollection ? " (empty)" : ""}`,
2808
+ tags: fieldTags,
2809
+ children: fieldChildren
2810
+ });
2844
2811
  }
2845
- }
2846
- return reactive({
2847
- ...!scoped && { $silentValue },
2848
- $errors,
2849
- $issues,
2850
- $silentIssues,
2851
- $silentErrors,
2852
- $instances,
2853
- $value,
2854
- $dirty,
2855
- $anyDirty,
2856
- $invalid,
2857
- $correct,
2858
- $error,
2859
- $pending,
2860
- $ready,
2861
- $edited,
2862
- $anyEdited,
2863
- $reset,
2864
- $touch,
2865
- $validate,
2866
- $extractDirtyFields,
2867
- $clearExternalErrors
2868
2812
  });
2813
+ return children;
2869
2814
  }
2870
-
2871
- //#endregion
2872
- //#region src/core/createScopedUseRegle/useCollectScope.ts
2873
- function createUseCollectScope(instances, options) {
2874
- function useCollectScope$1(namespace) {
2875
- const computedNamespace = computed(() => toValue(namespace));
2876
- setEmptyNamespace();
2877
- const r$ = ref(collectRegles(instances.value));
2878
- const regle = reactive({ r$ });
2879
- function setEmptyNamespace() {
2880
- if (computedNamespace.value && !instances.value[computedNamespace.value]) instances.value[computedNamespace.value] = {};
2881
- }
2882
- watch(computedNamespace, setEmptyNamespace);
2883
- watch(instances, (newInstances) => {
2884
- r$.value = collectRegles(newInstances);
2885
- }, { deep: true });
2886
- function collectRegles(r$Instances) {
2887
- if (computedNamespace.value) return mergeRegles(r$Instances[computedNamespace.value] ?? {}, !options.asRecord);
2888
- else return mergeRegles(r$Instances["~~global"] ?? {}, !options.asRecord);
2815
+ function buildRootChildrenNodes(r$, instanceId) {
2816
+ const children = [];
2817
+ if (isFieldStatus(r$)) return buildRuleNodes(r$, instanceId, "root");
2818
+ if (!r$.$fields || typeof r$.$fields !== "object") return children;
2819
+ Object.entries(r$.$fields).forEach(([fieldName, fieldStatus]) => {
2820
+ if (fieldStatus && typeof fieldStatus === "object") {
2821
+ const fieldTags = buildNodeTags(fieldStatus);
2822
+ let fieldChildren = [];
2823
+ let isCollection = false;
2824
+ if (isCollectionRulesStatus(fieldStatus)) {
2825
+ fieldChildren = buildCollectionItemNodes(fieldStatus, instanceId, fieldName);
2826
+ isCollection = true;
2827
+ } else if (isNestedRulesStatus(fieldStatus)) fieldChildren = buildNestedFieldNodes(fieldStatus.$fields, instanceId, fieldName);
2828
+ else if (isFieldStatus(fieldStatus)) fieldChildren = buildRuleNodes(fieldStatus, instanceId, fieldName);
2829
+ children.push({
2830
+ id: createFieldNodeId(instanceId, fieldName),
2831
+ label: `${isCollection ? `${fieldName}[${fieldChildren.length}]` : fieldName}`,
2832
+ tags: fieldTags,
2833
+ children: fieldChildren
2834
+ });
2889
2835
  }
2890
- return { r$: regle.r$ };
2891
- }
2892
- return { useCollectScope: useCollectScope$1 };
2836
+ });
2837
+ return children;
2893
2838
  }
2894
-
2895
- //#endregion
2896
- //#region src/core/createScopedUseRegle/useScopedRegle.ts
2897
- function createUseScopedRegleComposable(instances, customUseRegle) {
2898
- const scopedUseRegle = customUseRegle ?? useRegle;
2899
- const useScopedRegle$1 = ((state, rulesFactory, options) => {
2900
- const { namespace, scopeKey: _scopeKey,...restOptions } = options ?? {};
2901
- scopedUseRegle.__config ??= {};
2902
- const computedNamespace = computed(() => toValue(namespace));
2903
- const $id = ref(`${Object.keys(instances.value).length + 1}-${randomId()}`);
2904
- const instanceName = computed(() => {
2905
- return options?.scopeKey ?? `instance-${$id.value}`;
2906
- });
2907
- const { r$ } = scopedUseRegle(state, rulesFactory, restOptions);
2908
- register();
2909
- tryOnScopeDispose(dispose);
2910
- watch(computedNamespace, (newName, oldName) => {
2911
- dispose(oldName);
2912
- register();
2913
- });
2914
- if (getCurrentInstance()) onMounted(() => {
2915
- const currentInstance = getCurrentInstance();
2916
- if (typeof window !== "undefined" && currentInstance?.proxy?.$el?.parentElement) {
2917
- if (document.documentElement && !document.documentElement.contains(currentInstance?.proxy?.$el?.parentElement)) dispose();
2918
- }
2919
- });
2920
- function dispose(oldName) {
2921
- const nameToClean = oldName ?? computedNamespace.value;
2922
- if (nameToClean) {
2923
- if (instances.value[nameToClean]) delete instances.value[nameToClean][instanceName.value];
2924
- } else if (instances.value["~~global"][instanceName.value]) delete instances.value["~~global"][instanceName.value];
2925
- }
2926
- function register() {
2927
- if (computedNamespace.value) {
2928
- if (!instances.value[computedNamespace.value]) instances.value[computedNamespace.value] = {};
2929
- instances.value[computedNamespace.value][instanceName.value] = r$;
2930
- } else {
2931
- if (!instances.value["~~global"]) instances.value["~~global"] = {};
2932
- instances.value["~~global"][instanceName.value] = r$;
2933
- }
2934
- }
2839
+ function buildInspectorTree(instances, filter) {
2840
+ const nodes = instances.map((instance) => {
2841
+ const { r$, id, name, componentName } = instance;
2935
2842
  return {
2936
- r$,
2937
- dispose,
2938
- register
2843
+ id,
2844
+ label: name,
2845
+ tags: buildNodeTags(r$, componentName),
2846
+ children: buildRootChildrenNodes(r$, id)
2939
2847
  };
2940
2848
  });
2941
- return { useScopedRegle: useScopedRegle$1 };
2942
- }
2943
-
2944
- //#endregion
2945
- //#region src/core/createScopedUseRegle/createScopedUseRegle.ts
2946
- function createScopedUseRegle(options) {
2947
- const instances = (options?.customStore ? () => {
2948
- if (options.customStore) {
2949
- if (!options.customStore?.value["~~global"]) options.customStore.value["~~global"] = {};
2950
- else if (options.customStore?.value) options.customStore.value = { "~~global": {} };
2951
- }
2952
- return options.customStore;
2953
- } : createGlobalState(() => {
2954
- return ref({ "~~global": {} });
2955
- }))();
2956
- const { useScopedRegle: useScopedRegle$1 } = createUseScopedRegleComposable(instances, options?.customUseRegle);
2957
- const { useCollectScope: useCollectScope$1 } = createUseCollectScope(instances, { asRecord: options?.asRecord });
2958
- return {
2959
- useScopedRegle: useScopedRegle$1,
2960
- useCollectScope: useCollectScope$1
2961
- };
2849
+ if (!filter || filter.trim() === "") return nodes;
2850
+ return filterInspectorTree(nodes, filter);
2851
+ }
2852
+ function filterInspectorTree(nodes, filter) {
2853
+ const lowerFilter = filter.toLowerCase();
2854
+ const filtered = [];
2855
+ for (const node of nodes) {
2856
+ const labelMatches = node.label.toLowerCase().includes(lowerFilter);
2857
+ const tagMatches = node.tags?.some((tag) => tag.label.toLowerCase().includes(lowerFilter)) ?? false;
2858
+ const filteredChildren = node.children ? filterInspectorTree(node.children, filter) : [];
2859
+ if (labelMatches || tagMatches || filteredChildren.length > 0) filtered.push({
2860
+ ...node,
2861
+ children: filteredChildren.length > 0 ? filteredChildren : node.children
2862
+ });
2863
+ }
2864
+ return filtered;
2962
2865
  }
2963
- const { useCollectScope, useScopedRegle } = createScopedUseRegle();
2964
2866
 
2965
- //#endregion
2966
- //#region src/core/variants/createVariant.ts
2967
- /**
2968
- * Declare variations of state that depends on one value
2969
- *
2970
- * Autocomplete may not work here because of https://github.com/microsoft/TypeScript/issues/49547
2971
- *
2972
- * ```ts
2973
- * // ⚠️ Use getter syntax for your rules () => {} or a computed one
2974
- * const {r$} = useRegle(state, () => {
2975
- * const variant = createVariant(state, 'type', [
2976
- * {type: { literal: literal('EMAIL')}, email: { required, email }},
2977
- * {type: { literal: literal('GITHUB')}, username: { required }},
2978
- * {type: { required }},
2979
- * ]);
2980
- *
2981
- * return {
2982
- * ...variant.value,
2983
- * };
2984
- * })
2985
- * ```
2986
- */
2987
- function createVariant(root, discriminantKey, variants) {
2988
- const watchableRoot = computed(() => toValue(root)[discriminantKey]);
2989
- return computed(() => {
2990
- const selectedVariant = variants.find((variant) => {
2991
- if (variant[discriminantKey] && "literal" in variant[discriminantKey]) {
2992
- const literalRule = variant[discriminantKey]["literal"];
2993
- if (isRuleDef(literalRule)) return unref(literalRule._params?.[0]) === watchableRoot.value;
2867
+ function createDevtools(app) {
2868
+ setupDevtoolsPlugin({
2869
+ id: "regle-devtools",
2870
+ label: "Regle",
2871
+ logo: "https://reglejs.dev/logo_main.png",
2872
+ packageName: "@regle/core",
2873
+ homepage: "https://reglejs.dev",
2874
+ componentStateTypes: ["Regles"],
2875
+ app
2876
+ }, (api) => {
2877
+ regleDevtoolsRegistry.setApi(api);
2878
+ api.addInspector({
2879
+ id: INSPECTOR_IDS.INSPECTOR,
2880
+ label: "Regle",
2881
+ noSelectionText: "No instance selected",
2882
+ icon: "rule",
2883
+ treeFilterPlaceholder: "Filter",
2884
+ stateFilterPlaceholder: "Filter validation status",
2885
+ nodeActions: [
2886
+ {
2887
+ icon: "check_circle",
2888
+ tooltip: "Validate",
2889
+ action: (nodeId) => {
2890
+ handleValidateAction(nodeId, api);
2891
+ }
2892
+ },
2893
+ {
2894
+ icon: "refresh",
2895
+ tooltip: "Reset validation state",
2896
+ action: (nodeId) => {
2897
+ handleResetAction(nodeId, api);
2898
+ }
2899
+ },
2900
+ {
2901
+ icon: "restore",
2902
+ tooltip: "Restore to original state",
2903
+ action: (nodeId) => {
2904
+ handleResetAction(nodeId, api, true);
2905
+ }
2906
+ }
2907
+ ]
2908
+ });
2909
+ regleDevtoolsRegistry.notifyDevtools();
2910
+ let componentInstances = [];
2911
+ let selectedNodeId = null;
2912
+ api.on.getInspectorTree(async (payload) => {
2913
+ api.unhighlightElement();
2914
+ if (payload.inspectorId === INSPECTOR_IDS.INSPECTOR) {
2915
+ const nodes = buildInspectorTree(regleDevtoolsRegistry.getAll(), payload.filter);
2916
+ if (nodes.length > 0) payload.rootNodes = nodes;
2917
+ else payload.rootNodes = [{
2918
+ id: "empty-regles",
2919
+ label: "No Regles instances found",
2920
+ children: []
2921
+ }];
2922
+ componentInstances = await api.getComponentInstances(app);
2994
2923
  }
2995
2924
  });
2996
- if (selectedVariant) return selectedVariant;
2997
- else {
2998
- const anyDiscriminantRules = variants.find((variant) => isObject(variant[discriminantKey]) && !Object.keys(variant[discriminantKey]).some((key) => key === "literal"));
2999
- if (anyDiscriminantRules) return anyDiscriminantRules;
3000
- else return {};
3001
- }
2925
+ api.on.getInspectorState((payload) => {
2926
+ api.unhighlightElement();
2927
+ if (payload.inspectorId === INSPECTOR_IDS.INSPECTOR) {
2928
+ const state = buildInspectorState(payload.nodeId, (id) => regleDevtoolsRegistry.get(id));
2929
+ const instance = componentInstances.find((instance$1) => {
2930
+ const [componentName] = payload.nodeId.split("#");
2931
+ return instance$1.uid.toString() === componentName;
2932
+ });
2933
+ if (instance?.uid && selectedNodeId !== payload.nodeId) {
2934
+ selectedNodeId = payload.nodeId;
2935
+ if (!parseFieldNodeId(payload.nodeId)) api.highlightElement(instance);
2936
+ }
2937
+ if (state) payload.state = state;
2938
+ else payload.state = {};
2939
+ }
2940
+ });
2941
+ api.on.editInspectorState((payload) => {
2942
+ if (payload.inspectorId === INSPECTOR_IDS.INSPECTOR) handleEditInspectorState(payload);
2943
+ });
3002
2944
  });
3003
2945
  }
3004
- /**
3005
- * Narrow a nested variant field to a discriminated value
3006
- *
3007
- * ```ts
3008
- * if (narrowVariant(r$, 'type', 'EMAIL')) {
3009
- * r$.email.$value = 'foo';
3010
- * }
3011
- * ```
3012
- */
3013
- function narrowVariant(root, discriminantKey, discriminantValue) {
3014
- return isObject(root[discriminantKey]) && "$value" in root[discriminantKey] && root[discriminantKey]?.$value === discriminantValue;
2946
+
2947
+ function useRootStorage({ initialState, originalState, options, scopeRules, state, customRules, shortcuts, schemaErrors, schemaMode = false, onValidate }) {
2948
+ const storage = useStorage();
2949
+ const regle = ref();
2950
+ const computedExternalErrors = ref();
2951
+ let $unwatchExternalErrors;
2952
+ let $unwatchComputedExternalErrors;
2953
+ function defineExternalErrorsWatchSource() {
2954
+ $unwatchExternalErrors = watch(() => options.externalErrors?.value, () => {
2955
+ $unwatchComputedExternalErrors?.();
2956
+ if (options.externalErrors?.value && Object.keys(options.externalErrors.value).some((key) => key.includes("."))) computedExternalErrors.value = dotPathObjectToNested(options.externalErrors.value);
2957
+ else computedExternalErrors.value = options.externalErrors?.value;
2958
+ defineComputedExternalErrorsWatchSource();
2959
+ }, {
2960
+ immediate: true,
2961
+ deep: true
2962
+ });
2963
+ }
2964
+ function defineComputedExternalErrorsWatchSource() {
2965
+ $unwatchComputedExternalErrors = watch(() => computedExternalErrors.value, () => {
2966
+ $unwatchExternalErrors?.();
2967
+ if (options.externalErrors?.value) options.externalErrors.value = computedExternalErrors.value;
2968
+ defineExternalErrorsWatchSource();
2969
+ }, { deep: true });
2970
+ }
2971
+ defineExternalErrorsWatchSource();
2972
+ if (isNestedRulesDef(state, scopeRules)) regle.value = createReactiveNestedStatus({
2973
+ rootRules: scopeRules,
2974
+ rulesDef: scopeRules,
2975
+ state,
2976
+ customMessages: customRules?.(),
2977
+ storage,
2978
+ options,
2979
+ externalErrors: computedExternalErrors,
2980
+ validationGroups: options.validationGroups,
2981
+ initialState,
2982
+ originalState,
2983
+ shortcuts,
2984
+ fieldName: "root",
2985
+ path: "",
2986
+ cachePath: "",
2987
+ schemaErrors,
2988
+ rootSchemaErrors: schemaErrors,
2989
+ schemaMode,
2990
+ onValidate
2991
+ });
2992
+ else if (isValidatorRulesDef(scopeRules)) regle.value = createReactiveFieldStatus({
2993
+ rulesDef: scopeRules,
2994
+ state,
2995
+ customMessages: customRules?.(),
2996
+ storage,
2997
+ options,
2998
+ externalErrors: computedExternalErrors,
2999
+ initialState,
3000
+ originalState,
3001
+ shortcuts,
3002
+ fieldName: "root",
3003
+ path: "",
3004
+ cachePath: "",
3005
+ schemaMode,
3006
+ schemaErrors,
3007
+ onValidate
3008
+ });
3009
+ if (getCurrentScope()) onScopeDispose(() => {
3010
+ regle.value?.$unwatch();
3011
+ $unwatchComputedExternalErrors?.();
3012
+ $unwatchExternalErrors?.();
3013
+ });
3014
+ if (typeof window !== "undefined" && regle.value) registerRegleInstance(regle.value, { name: toValue(options.id) });
3015
+ return reactive({ regle });
3015
3016
  }
3016
- function variantToRef(root, discriminantKey, discriminantValue, _options) {
3017
- const fromRoot = isRef(root) ? toRef(root.value, "$fields") : toRef(root, "$fields");
3018
- const returnedRef = ref();
3019
- watch(fromRoot, async () => {
3020
- await nextTick();
3021
- if (narrowVariant(fromRoot.value, discriminantKey, discriminantValue)) returnedRef.value = toRef(root).value;
3022
- else returnedRef.value = void 0;
3023
- }, {
3024
- immediate: true,
3025
- flush: "pre"
3017
+
3018
+ function createRootRegleLogic({ state, rulesFactory, options, globalOptions, customRules, shortcuts }) {
3019
+ const definedRules = isRef(rulesFactory) ? rulesFactory : typeof rulesFactory === "function" ? void 0 : computed(() => rulesFactory);
3020
+ const resolvedOptions = {
3021
+ ...globalOptions,
3022
+ ...options
3023
+ };
3024
+ let unwatchRules;
3025
+ const watchableRulesGetters = shallowRef(definedRules ?? {});
3026
+ if (typeof rulesFactory === "function") unwatchRules = watchEffect(() => {
3027
+ watchableRulesGetters.value = rulesFactory(state);
3028
+ triggerRef(watchableRulesGetters);
3029
+ });
3030
+ const initialState = ref(isObject(state.value) ? { ...cloneDeep(state.value) } : cloneDeep(state.value));
3031
+ const originalState = isObject(state.value) ? { ...cloneDeep(state.value) } : cloneDeep(state.value);
3032
+ tryOnScopeDispose(() => {
3033
+ unwatchRules?.();
3034
+ });
3035
+ return useRootStorage({
3036
+ scopeRules: watchableRulesGetters,
3037
+ state,
3038
+ options: resolvedOptions,
3039
+ initialState,
3040
+ originalState,
3041
+ customRules,
3042
+ shortcuts
3026
3043
  });
3027
- return returnedRef;
3028
3044
  }
3029
3045
 
3030
- //#endregion
3031
- //#region src/core/refineRules.ts
3046
+ function createUseRegleComposable(customRules, options, shortcuts) {
3047
+ const globalOptions = {
3048
+ autoDirty: options?.autoDirty,
3049
+ lazy: options?.lazy,
3050
+ rewardEarly: options?.rewardEarly,
3051
+ silent: options?.silent,
3052
+ clearExternalErrorsOnChange: options?.clearExternalErrorsOnChange
3053
+ };
3054
+ function useRegle$1(state, rulesFactory, options$1) {
3055
+ return { r$: createRootRegleLogic({
3056
+ state: isRef(state) ? state : ref(state),
3057
+ rulesFactory,
3058
+ options: options$1,
3059
+ globalOptions,
3060
+ customRules,
3061
+ shortcuts
3062
+ }).regle };
3063
+ }
3064
+ return useRegle$1;
3065
+ }
3032
3066
  /**
3033
- * Helper method to wrap an raw rules object
3067
+ * useRegle serves as the foundation for validation logic.
3034
3068
  *
3035
- * Similar to:
3069
+ * It accepts the following inputs:
3036
3070
  *
3037
- * ```ts
3038
- * const rules = {...} satisfies RegleUnknownRulesTree
3071
+ * @param state - This can be a plain object, a ref, a reactive object, or a structure containing nested refs.
3072
+ * @param rules - These should align with the structure of your state.
3073
+ * @param modifiers - Customize regle behaviour
3074
+ *
3075
+ * ```ts
3076
+ * import { useRegle } from '@regle/core';
3077
+ import { required } from '@regle/rules';
3078
+
3079
+ const { r$ } = useRegle({ email: '' }, {
3080
+ email: { required }
3081
+ })
3039
3082
  * ```
3083
+ * Docs: {@link https://reglejs.dev/core-concepts/}
3040
3084
  */
3041
- function defineRules(rules) {
3042
- return rules;
3085
+ const useRegle = createUseRegleComposable();
3086
+
3087
+ function createInferRuleHelper() {
3088
+ function inferRules$1(state, rulesFactory) {
3089
+ return rulesFactory;
3090
+ }
3091
+ return inferRules$1;
3043
3092
  }
3044
3093
  /**
3045
- * Refine a raw rules object to set rules that depends on the state values.
3046
- *
3047
- * @example
3094
+ * Rule type helper to provide autocomplete and typecheck to your form rules or part of your form rules
3095
+ * It will just return the rules without any processing.
3048
3096
  *
3049
- * ```ts
3050
- * const rules = refineRules({
3051
- * password: { required, type: type<string>() },
3052
- * }, (state) => {
3053
- * return {
3054
- * confirmPassword: { required, sameAs: sameAs(() => state.value.password)}
3055
- * }
3056
- * })
3057
- * ```
3097
+ * @param state - The state reference
3098
+ * @param rules - Your rule tree
3058
3099
  */
3059
- function refineRules(rules, refinement) {
3060
- return (state) => merge({ ...rules }, refinement(state));
3061
- }
3100
+ const inferRules = createInferRuleHelper();
3062
3101
 
3063
- //#endregion
3064
- //#region src/devtools/constants.ts
3065
- const COLORS = {
3066
- ERROR: {
3067
- text: 16777215,
3068
- bg: 15680580
3069
- },
3070
- INVALID: {
3071
- text: 16777215,
3072
- bg: 16281969
3073
- },
3074
- VALID: {
3075
- text: 16777215,
3076
- bg: 1096065
3077
- },
3078
- PENDING: {
3079
- text: 16777215,
3080
- bg: 16096779
3081
- },
3082
- DIRTY: {
3083
- text: 2042167,
3084
- bg: 16708551
3085
- },
3086
- COMPONENT: {
3087
- text: 16777215,
3088
- bg: 6514417
3102
+ function createEmptyRuleState(rules) {
3103
+ const result = {};
3104
+ if (Object.entries(rules).some(([_, rule]) => isRuleDef(rule) || typeof rule === "function")) return null;
3105
+ for (const key in rules) {
3106
+ const item = rules[key];
3107
+ if (!!item && isObject(item) && "$each" in item && item["$each"] && isObject(item["$each"])) result[key] = [createEmptyRuleState(item["$each"])];
3108
+ else if (isObject(item) && !isEmpty(item) && !Object.entries(item).some(([_, rule]) => isRuleDef(rule) || typeof rule === "function")) result[key] = createEmptyRuleState(item);
3109
+ else result[key] = null;
3089
3110
  }
3090
- };
3091
- const PRIORITY_KEYS = {
3092
- ROOT: [
3093
- "$invalid",
3094
- "$dirty",
3095
- "$error",
3096
- "$pending",
3097
- "$valid",
3098
- "$ready"
3099
- ],
3100
- FIELD: [
3101
- "$value",
3102
- "$invalid",
3103
- "$dirty",
3104
- "$error",
3105
- "$pending",
3106
- "$errors"
3107
- ]
3108
- };
3109
- const INSPECTOR_IDS = {
3110
- INSPECTOR: "regle-inspector",
3111
- TIMELINE: "regle-timeline"
3112
- };
3113
-
3114
- //#endregion
3115
- //#region src/devtools/utils.ts
3116
- function isMethod(value) {
3117
- return typeof value === "function";
3118
- }
3119
- function getPriorityProperties(obj, keys) {
3120
- return keys.filter((key) => key in obj && !isMethod(obj[key])).map((key) => {
3121
- let editable = false;
3122
- if (key === "$value") editable = true;
3123
- return {
3124
- key,
3125
- value: obj[key],
3126
- editable
3127
- };
3128
- });
3129
- }
3130
- function getRemainingProperties(obj, excludeKeys) {
3131
- return Object.entries(obj).filter(([key]) => !excludeKeys.includes(key) && key.startsWith("$") && !isMethod(obj[key])).map(([key, value]) => ({
3132
- key,
3133
- value,
3134
- editable: false
3135
- }));
3136
- }
3137
- function parseFieldNodeId(nodeId) {
3138
- if (!nodeId.includes(":field:")) return null;
3139
- const [instanceId, , fieldName] = nodeId.split(":");
3140
- return {
3141
- instanceId,
3142
- fieldName
3143
- };
3144
- }
3145
- function createFieldNodeId(instanceId, fieldName) {
3146
- return `${instanceId}:field:${fieldName}`;
3111
+ return result;
3147
3112
  }
3148
- function parseRuleNodeId(nodeId) {
3149
- if (!nodeId.includes(":rule:")) return null;
3150
- const parts = nodeId.split(":rule:");
3151
- if (parts.length !== 2) return null;
3152
- const fieldPart = parts[0];
3153
- const ruleName = parts[1];
3154
- const fieldInfo = parseFieldNodeId(fieldPart);
3155
- if (!fieldInfo) return null;
3156
- return {
3157
- ...fieldInfo,
3158
- ruleName
3113
+ function createUseRulesComposable(customRules, options, shortcuts) {
3114
+ const globalOptions = {
3115
+ autoDirty: options?.autoDirty,
3116
+ lazy: options?.lazy,
3117
+ rewardEarly: options?.rewardEarly,
3118
+ silent: options?.silent,
3119
+ clearExternalErrorsOnChange: options?.clearExternalErrorsOnChange
3159
3120
  };
3121
+ function useRules$1(rulesFactory, options$1) {
3122
+ const definedRules = isRef(rulesFactory) ? rulesFactory : typeof rulesFactory === "function" ? void 0 : computed(() => rulesFactory);
3123
+ return createRootRegleLogic({
3124
+ state: ref(createEmptyRuleState(definedRules?.value)),
3125
+ rulesFactory: definedRules,
3126
+ options: options$1,
3127
+ globalOptions,
3128
+ customRules,
3129
+ shortcuts
3130
+ }).regle;
3131
+ }
3132
+ return useRules$1;
3160
3133
  }
3161
- function createRuleNodeId(instanceId, fieldName, ruleName) {
3162
- return `${createFieldNodeId(instanceId, fieldName)}:rule:${ruleName}`;
3163
- }
3134
+ /**
3135
+ * useRules is a clone of useRegle, without the need to provide a state.
3136
+ *
3137
+ * It accepts the following inputs:
3138
+ *
3139
+ * @param rules - Your rules object
3140
+ * @param modifiers - Customize regle behaviour
3141
+ *
3142
+ * ```ts
3143
+ * import { useRules } from '@regle/core';
3144
+ import { required } from '@regle/rules';
3145
+
3146
+ const { r$ } = useRules({
3147
+ email: { required }
3148
+ })
3149
+ * ```
3150
+ */
3151
+ const useRules = createUseRulesComposable();
3164
3152
 
3165
- //#endregion
3166
- //#region src/devtools/state-builder.ts
3167
3153
  /**
3168
- * Build state for a field node
3154
+ * Define a global regle configuration, where you can:
3155
+ * - Customize built-in rules messages
3156
+ * - Add your custom rules
3157
+ * - Define global modifiers
3158
+ * - Define shortcuts
3159
+ *
3160
+ * It will return:
3161
+ *
3162
+ * - a `useRegle` composable that can typecheck your custom rules
3163
+ * - an `inferRules` helper that can typecheck your custom rules
3169
3164
  */
3170
- function buildFieldState(fieldStatus) {
3171
- const state = {};
3172
- const priorityProperties = getPriorityProperties(fieldStatus, PRIORITY_KEYS.FIELD);
3173
- if (priorityProperties.length > 0) state["State"] = priorityProperties;
3174
- const remainingProperties = getRemainingProperties(fieldStatus, [
3175
- ...PRIORITY_KEYS.FIELD,
3176
- "$rules",
3177
- "$fields"
3178
- ]);
3179
- if (remainingProperties.length > 0) state["Other Properties"] = remainingProperties;
3180
- return state;
3165
+ function defineRegleConfig({ rules, modifiers, shortcuts }) {
3166
+ const useRegle$1 = createUseRegleComposable(rules, modifiers, shortcuts);
3167
+ const useRules$1 = createUseRulesComposable(rules, modifiers, shortcuts);
3168
+ useRegle$1.__config = {
3169
+ rules,
3170
+ modifiers,
3171
+ shortcuts
3172
+ };
3173
+ useRules$1.__config = {
3174
+ rules,
3175
+ modifiers,
3176
+ shortcuts
3177
+ };
3178
+ return {
3179
+ useRegle: useRegle$1,
3180
+ inferRules: createInferRuleHelper(),
3181
+ useRules: useRules$1
3182
+ };
3181
3183
  }
3182
3184
  /**
3183
- * Build state for a rule node
3185
+ * Extend an already created custom `useRegle` (as the first parameter)
3186
+ *
3187
+ * It will return:
3188
+ *
3189
+ * - a `useRegle` composable that can typecheck your custom rules
3190
+ * - an `inferRules` helper that can typecheck your custom rules
3184
3191
  */
3185
- function buildRuleState(ruleStatus) {
3186
- const state = {};
3187
- const ruleKeys = [
3188
- "$type",
3189
- "$valid",
3190
- "$active",
3191
- "$pending",
3192
- "$message",
3193
- "$tooltip"
3194
- ];
3195
- const priorityProperties = getPriorityProperties(ruleStatus, ruleKeys);
3196
- if (priorityProperties.length > 0) state["Rule State"] = priorityProperties;
3197
- if (ruleStatus.$params && Array.isArray(ruleStatus.$params) && ruleStatus.$params.length > 0) state["Parameters"] = [{
3198
- key: "$params",
3199
- value: ruleStatus.$params,
3200
- editable: false
3201
- }];
3202
- if (ruleStatus.$metadata !== void 0 && ruleStatus.$metadata !== true && ruleStatus.$metadata !== false) state["Metadata"] = [{
3203
- key: "$metadata",
3204
- value: ruleStatus.$metadata,
3205
- editable: false
3206
- }];
3207
- const remainingProperties = getRemainingProperties(ruleStatus, [
3208
- ...ruleKeys,
3209
- "$params",
3210
- "$metadata",
3211
- "$validator",
3212
- "$parse",
3213
- "$reset",
3214
- "$unwatch",
3215
- "$watch",
3216
- "$haveAsync",
3217
- "$validating",
3218
- "$fieldDirty",
3219
- "$fieldInvalid",
3220
- "$fieldPending",
3221
- "$fieldCorrect",
3222
- "$fieldError",
3223
- "$maybePending",
3224
- "$externalErrors"
3225
- ]);
3226
- if (remainingProperties.length > 0) state["Other Properties"] = remainingProperties;
3227
- return state;
3228
- }
3229
- function buildRootState(r$) {
3230
- const state = {};
3231
- const priorityProperties = getPriorityProperties(r$, PRIORITY_KEYS.ROOT);
3232
- if (priorityProperties.length > 0) state["State"] = priorityProperties;
3233
- const remainingProperties = getRemainingProperties(r$, [...PRIORITY_KEYS.ROOT, "$fields"]);
3234
- if (remainingProperties.length > 0) state["Other Properties"] = remainingProperties;
3235
- return state;
3236
- }
3237
- function resolveFieldByPath(fields, path) {
3238
- if (!fields || !path) return null;
3239
- const segments = path.match(/[^.\[\]]+/g);
3240
- if (!segments) return null;
3241
- let current = fields;
3242
- for (const segment of segments) {
3243
- if (!current) return null;
3244
- const index = parseInt(segment);
3245
- if (!isNaN(index)) if (isCollectionRulesStatus(current) && Array.isArray(current.$each)) current = current.$each[index];
3246
- else return null;
3247
- else if (isNestedRulesStatus(current) && current.$fields && current.$fields[segment]) current = current.$fields[segment];
3248
- else if (current[segment]) current = current[segment];
3249
- else return null;
3250
- }
3251
- return current;
3252
- }
3253
- function buildInspectorState(nodeId, getInstance) {
3254
- const ruleInfo = parseRuleNodeId(nodeId);
3255
- if (ruleInfo) {
3256
- const { instanceId, fieldName, ruleName } = ruleInfo;
3257
- const instance$1 = getInstance(instanceId);
3258
- if (!instance$1 || !instance$1.r$.$fields) return null;
3259
- const fieldStatus = resolveFieldByPath(instance$1.r$.$fields, fieldName);
3260
- if (!fieldStatus || !("$rules" in fieldStatus)) return null;
3261
- const ruleStatus = fieldStatus.$rules[ruleName];
3262
- if (!ruleStatus) return null;
3263
- return buildRuleState(ruleStatus);
3264
- }
3265
- const fieldInfo = parseFieldNodeId(nodeId);
3266
- if (fieldInfo) {
3267
- const { instanceId, fieldName } = fieldInfo;
3268
- const instance$1 = getInstance(instanceId);
3269
- if (!instance$1 || !instance$1.r$.$fields) return null;
3270
- const fieldStatus = resolveFieldByPath(instance$1.r$.$fields, fieldName);
3271
- if (!fieldStatus) return null;
3272
- return buildFieldState(fieldStatus);
3273
- }
3274
- const instance = getInstance(nodeId);
3275
- if (!instance) return null;
3276
- return buildRootState(instance.r$);
3277
- }
3278
-
3279
- //#endregion
3280
- //#region src/devtools/actions.ts
3281
- function handleValidateAction(nodeId, api) {
3282
- if (nodeId.includes(":rule:")) return;
3283
- const fieldInfo = parseFieldNodeId(nodeId);
3284
- if (fieldInfo) {
3285
- const { instanceId, fieldName } = fieldInfo;
3286
- const instance = regleDevtoolsRegistry.get(instanceId);
3287
- if (instance && instance.r$.$fields) {
3288
- const fieldStatus = resolveFieldByPath(instance.r$.$fields, fieldName);
3289
- if (fieldStatus && typeof fieldStatus.$validate === "function") fieldStatus.$validate();
3290
- }
3291
- } else {
3292
- const instance = regleDevtoolsRegistry.get(nodeId);
3293
- if (instance && typeof instance.r$.$validate === "function") instance.r$.$validate();
3294
- }
3295
- emitInspectorState(api);
3296
- }
3297
- function handleResetAction(nodeId, api, resetState = false) {
3298
- const fieldInfo = parseFieldNodeId(nodeId);
3299
- if (fieldInfo) {
3300
- const { instanceId, fieldName } = fieldInfo;
3301
- const instance = regleDevtoolsRegistry.get(instanceId);
3302
- if (instance && instance.r$.$fields) {
3303
- const fieldStatus = resolveFieldByPath(instance.r$.$fields, fieldName);
3304
- if (fieldStatus && typeof fieldStatus.$reset === "function") fieldStatus.$reset({ toInitialState: resetState });
3305
- }
3306
- } else {
3307
- const instance = regleDevtoolsRegistry.get(nodeId);
3308
- if (instance && typeof instance.r$.$reset === "function") instance.r$.$reset({ toInitialState: resetState });
3309
- }
3310
- emitInspectorState(api);
3311
- }
3312
- function handleEditInspectorState(payload, api) {
3313
- const { nodeId, path, state } = payload;
3314
- if (!path.includes("$value")) return;
3315
- if (!parseFieldNodeId(nodeId)) return;
3316
- const [instanceId, _, fieldPath] = nodeId.split(":");
3317
- const instance = regleDevtoolsRegistry.get(instanceId);
3318
- if (instance && instance.r$.$fields) {
3319
- const fieldStatus = resolveFieldByPath(instance.r$.$fields, fieldPath);
3320
- if (fieldStatus && "$value" in fieldStatus) fieldStatus.$value = state.value;
3321
- }
3322
- emitInspectorState(api);
3323
- }
3324
- function emitInspectorState(api) {
3325
- setTimeout(() => {
3326
- api.sendInspectorState(INSPECTOR_IDS.INSPECTOR);
3327
- api.sendInspectorTree(INSPECTOR_IDS.INSPECTOR);
3328
- }, 100);
3192
+ function extendRegleConfig(regle, { rules, modifiers, shortcuts }) {
3193
+ const rootConfig = regle.__config ?? {};
3194
+ const newRules = () => ({
3195
+ ...rootConfig.rules?.(),
3196
+ ...rules?.()
3197
+ });
3198
+ const newModifiers = rootConfig.modifiers && modifiers ? merge(rootConfig.modifiers, modifiers) : rootConfig.modifiers ?? modifiers;
3199
+ const newShortcuts = rootConfig.shortcuts && shortcuts ? merge(rootConfig.shortcuts, shortcuts) : rootConfig.shortcuts ?? shortcuts;
3200
+ const useRegle$1 = createUseRegleComposable(newRules, newModifiers, newShortcuts);
3201
+ useRegle$1.__config = {
3202
+ rules: newRules,
3203
+ modifiers: newModifiers,
3204
+ shortcuts: newShortcuts
3205
+ };
3206
+ return {
3207
+ useRegle: useRegle$1,
3208
+ inferRules: createInferRuleHelper()
3209
+ };
3329
3210
  }
3330
3211
 
3331
- //#endregion
3332
- //#region src/devtools/tree-builder.ts
3333
- function buildNodeTags(fieldOrR$, componentName) {
3334
- const tags = [];
3335
- if (fieldOrR$.$error) tags.push({
3336
- label: "error",
3337
- textColor: COLORS.ERROR.text,
3338
- backgroundColor: COLORS.ERROR.bg
3212
+ function mergeRegles(regles, _scoped) {
3213
+ const scoped = _scoped == null ? false : _scoped;
3214
+ const $value = computed({
3215
+ get: () => {
3216
+ if (scoped) return Object.values(regles).map((r) => r.$value);
3217
+ else return Object.fromEntries(Object.entries(regles).map(([key, r]) => [key, r.$value]));
3218
+ },
3219
+ set: (value) => {
3220
+ if (!scoped) {
3221
+ if (typeof value === "object") Object.entries(value).forEach(([key, newValue]) => regles[key].$value = newValue);
3222
+ }
3223
+ }
3339
3224
  });
3340
- else if (fieldOrR$.$correct) tags.push({
3341
- label: "correct",
3342
- textColor: COLORS.VALID.text,
3343
- backgroundColor: COLORS.VALID.bg
3225
+ const $silentValue = computed({
3226
+ get: () => Object.fromEntries(Object.entries(regles).map(([key, r]) => [key, r.$silentValue])),
3227
+ set: (value) => {
3228
+ if (typeof value === "object") Object.entries(value).forEach(([key, newValue]) => regles[key].$silentValue = newValue);
3229
+ }
3344
3230
  });
3345
- if (fieldOrR$.$pending) tags.push({
3346
- label: "pending",
3347
- textColor: COLORS.PENDING.text,
3348
- backgroundColor: COLORS.PENDING.bg
3231
+ const $dirty = computed(() => {
3232
+ const entries = Object.entries(regles);
3233
+ return !!entries.length && entries.every(([_, regle]) => {
3234
+ return regle?.$dirty;
3235
+ });
3349
3236
  });
3350
- if (fieldOrR$.$dirty) tags.push({
3351
- label: "dirty",
3352
- textColor: COLORS.DIRTY.text,
3353
- backgroundColor: COLORS.DIRTY.bg
3237
+ const $anyDirty = computed(() => {
3238
+ return Object.entries(regles).some(([_, regle]) => {
3239
+ return regle?.$anyDirty;
3240
+ });
3354
3241
  });
3355
- if (componentName) tags.push({
3356
- label: componentName,
3357
- textColor: COLORS.COMPONENT.text,
3358
- backgroundColor: COLORS.COMPONENT.bg
3242
+ const $invalid = computed(() => {
3243
+ return Object.entries(regles).some(([_, regle]) => {
3244
+ return regle?.$invalid;
3245
+ });
3359
3246
  });
3360
- return tags;
3361
- }
3362
- function buildRuleTags(rule) {
3363
- const tags = [];
3364
- if (!rule.$active) tags.push({
3365
- label: "inactive",
3366
- textColor: 10265519,
3367
- backgroundColor: 15987958
3247
+ const $correct = computed(() => {
3248
+ const entries = Object.entries(regles);
3249
+ return !!entries.length && entries.every(([_, regle]) => {
3250
+ return regle?.$correct || regle.$anyDirty && !regle.$invalid;
3251
+ });
3368
3252
  });
3369
- else if (!rule.$valid) tags.push({
3370
- label: "invalid",
3371
- textColor: COLORS.INVALID.text,
3372
- backgroundColor: COLORS.INVALID.bg
3253
+ const $error = computed(() => {
3254
+ return Object.entries(regles).some(([_, regle]) => {
3255
+ return regle?.$error;
3256
+ });
3373
3257
  });
3374
- else if (rule.$valid) tags.push({
3375
- label: "valid",
3376
- textColor: COLORS.VALID.text,
3377
- backgroundColor: COLORS.VALID.bg
3258
+ const $ready = computed(() => {
3259
+ const entries = Object.entries(regles);
3260
+ return !!entries.length && entries.every(([_, regle]) => {
3261
+ return regle?.$ready;
3262
+ });
3378
3263
  });
3379
- if (rule.$pending) tags.push({
3380
- label: "pending",
3381
- textColor: COLORS.PENDING.text,
3382
- backgroundColor: COLORS.PENDING.bg
3264
+ const $pending = computed(() => {
3265
+ return Object.entries(regles).some(([_, regle]) => {
3266
+ return regle?.$pending;
3267
+ });
3383
3268
  });
3384
- return tags;
3385
- }
3386
- function buildRuleNodes(fieldStatus, instanceId, fieldPath) {
3387
- const children = [];
3388
- if (!fieldStatus.$rules || typeof fieldStatus.$rules !== "object") return children;
3389
- Object.entries(fieldStatus.$rules).forEach(([ruleName, ruleStatus]) => {
3390
- if (ruleStatus && typeof ruleStatus === "object") {
3391
- const ruleTags = buildRuleTags(ruleStatus);
3392
- children.push({
3393
- id: createRuleNodeId(instanceId, fieldPath, ruleName),
3394
- label: ruleName,
3395
- tags: ruleTags,
3396
- children: []
3397
- });
3398
- }
3269
+ const $issues = computed(() => {
3270
+ if (scoped) return Object.entries(regles).map(([_, regle]) => {
3271
+ return regle.$issues;
3272
+ });
3273
+ else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
3274
+ return [key, regle.$issues];
3275
+ }));
3399
3276
  });
3400
- return children;
3401
- }
3402
- function buildCollectionItemNodes(fieldStatus, instanceId, fieldPath) {
3403
- const children = [];
3404
- if (!fieldStatus.$each || !Array.isArray(fieldStatus.$each)) return children;
3405
- fieldStatus.$each.forEach((item, index) => {
3406
- if (item && typeof item === "object") {
3407
- const itemTags = buildNodeTags(item);
3408
- const itemPath = `${fieldPath}[${index}]`;
3409
- let itemChildren = [];
3410
- if (isNestedRulesStatus(item)) itemChildren = buildNestedFieldNodes(item.$fields, instanceId, itemPath);
3411
- else if (isFieldStatus(item)) itemChildren = buildRuleNodes(item, instanceId, itemPath);
3412
- children.push({
3413
- id: createFieldNodeId(instanceId, itemPath),
3414
- label: `[${index}]`,
3415
- tags: itemTags,
3416
- children: itemChildren
3417
- });
3418
- }
3277
+ const $silentIssues = computed(() => {
3278
+ if (scoped) return Object.entries(regles).map(([_, regle]) => {
3279
+ return regle.$silentIssues;
3280
+ });
3281
+ else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
3282
+ return [key, regle.$silentIssues];
3283
+ }));
3419
3284
  });
3420
- return children;
3421
- }
3422
- function buildNestedFieldNodes(fields, instanceId, parentPath) {
3423
- const children = [];
3424
- Object.entries(fields).forEach(([fieldName, fieldStatus]) => {
3425
- if (fieldStatus && typeof fieldStatus === "object") {
3426
- const fieldPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
3427
- const fieldTags = buildNodeTags(fieldStatus);
3428
- let fieldChildren = [];
3429
- if (isCollectionRulesStatus(fieldStatus)) fieldChildren = buildCollectionItemNodes(fieldStatus, instanceId, fieldPath);
3430
- else if (isNestedRulesStatus(fieldStatus)) fieldChildren = buildNestedFieldNodes(fieldStatus.$fields, instanceId, fieldPath);
3431
- else if (isFieldStatus(fieldStatus)) fieldChildren = buildRuleNodes(fieldStatus, instanceId, fieldPath);
3432
- children.push({
3433
- id: createFieldNodeId(instanceId, fieldPath),
3434
- label: fieldName,
3435
- tags: fieldTags,
3436
- children: fieldChildren
3437
- });
3285
+ const $errors = computed(() => {
3286
+ if (scoped) return Object.entries(regles).map(([_, regle]) => {
3287
+ return regle.$errors;
3288
+ });
3289
+ else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
3290
+ return [key, regle.$errors];
3291
+ }));
3292
+ });
3293
+ const $silentErrors = computed(() => {
3294
+ if (scoped) return Object.entries(regles).map(([_, regle]) => {
3295
+ return regle.$silentErrors;
3296
+ });
3297
+ else return Object.fromEntries(Object.entries(regles).map(([key, regle]) => {
3298
+ return [key, regle.$silentErrors];
3299
+ }));
3300
+ });
3301
+ const $edited = computed(() => {
3302
+ const entries = Object.entries(regles);
3303
+ return !!entries.length && entries.every(([_, regle]) => {
3304
+ return regle?.$edited;
3305
+ });
3306
+ });
3307
+ const $anyEdited = computed(() => {
3308
+ return Object.entries(regles).some(([_, regle]) => {
3309
+ return regle?.$anyEdited;
3310
+ });
3311
+ });
3312
+ const $instances = computed(() => {
3313
+ if (scoped) return Object.values(regles);
3314
+ else return regles;
3315
+ });
3316
+ function $reset(options) {
3317
+ Object.values(regles).forEach((regle) => {
3318
+ regle.$reset(options);
3319
+ });
3320
+ }
3321
+ function $touch() {
3322
+ Object.values(regles).forEach((regle) => {
3323
+ regle.$touch();
3324
+ });
3325
+ }
3326
+ function $extractDirtyFields(filterNullishValues = true) {
3327
+ return Object.values(regles).map((regle) => regle.$extractDirtyFields(filterNullishValues));
3328
+ }
3329
+ function $clearExternalErrors() {
3330
+ Object.values(regles).forEach((regle) => {
3331
+ regle.$clearExternalErrors();
3332
+ });
3333
+ }
3334
+ async function $validate(forceValues) {
3335
+ try {
3336
+ if (forceValues) $value.value = forceValues;
3337
+ const data = $value.value;
3338
+ return {
3339
+ valid: (await Promise.allSettled(Object.values(regles).map((regle) => {
3340
+ return regle.$validate();
3341
+ }))).every((value) => {
3342
+ if (value.status === "fulfilled") return value.value.valid === true;
3343
+ else return false;
3344
+ }),
3345
+ data,
3346
+ errors: $errors.value,
3347
+ issues: $issues.value
3348
+ };
3349
+ } catch {
3350
+ return {
3351
+ valid: false,
3352
+ data: $value.value,
3353
+ errors: $errors.value,
3354
+ issues: $issues.value
3355
+ };
3438
3356
  }
3357
+ }
3358
+ return reactive({
3359
+ ...!scoped && { $silentValue },
3360
+ $errors,
3361
+ $issues,
3362
+ $silentIssues,
3363
+ $silentErrors,
3364
+ $instances,
3365
+ $value,
3366
+ $dirty,
3367
+ $anyDirty,
3368
+ $invalid,
3369
+ $correct,
3370
+ $error,
3371
+ $pending,
3372
+ $ready,
3373
+ $edited,
3374
+ $anyEdited,
3375
+ $reset,
3376
+ $touch,
3377
+ $validate,
3378
+ $extractDirtyFields,
3379
+ $clearExternalErrors
3439
3380
  });
3440
- return children;
3441
3381
  }
3442
- function buildRootChildrenNodes(r$, instanceId) {
3443
- const children = [];
3444
- if (isFieldStatus(r$)) return buildRuleNodes(r$, instanceId, "root");
3445
- if (!r$.$fields || typeof r$.$fields !== "object") return children;
3446
- Object.entries(r$.$fields).forEach(([fieldName, fieldStatus]) => {
3447
- if (fieldStatus && typeof fieldStatus === "object") {
3448
- const fieldTags = buildNodeTags(fieldStatus);
3449
- let fieldChildren = [];
3450
- if (isCollectionRulesStatus(fieldStatus)) fieldChildren = buildCollectionItemNodes(fieldStatus, instanceId, fieldName);
3451
- else if (isNestedRulesStatus(fieldStatus)) fieldChildren = buildNestedFieldNodes(fieldStatus.$fields, instanceId, fieldName);
3452
- else if (isFieldStatus(fieldStatus)) fieldChildren = buildRuleNodes(fieldStatus, instanceId, fieldName);
3453
- children.push({
3454
- id: createFieldNodeId(instanceId, fieldName),
3455
- label: fieldName,
3456
- tags: fieldTags,
3457
- children: fieldChildren
3458
- });
3382
+
3383
+ function createUseCollectScope(instances, options) {
3384
+ function useCollectScope$1(namespace) {
3385
+ const computedNamespace = computed(() => toValue(namespace));
3386
+ setEmptyNamespace();
3387
+ const r$ = ref(collectRegles(instances.value));
3388
+ const regle = reactive({ r$ });
3389
+ function setEmptyNamespace() {
3390
+ if (computedNamespace.value && !instances.value[computedNamespace.value]) instances.value[computedNamespace.value] = {};
3459
3391
  }
3460
- });
3461
- return children;
3392
+ watch(computedNamespace, setEmptyNamespace);
3393
+ watch(instances, (newInstances) => {
3394
+ r$.value = collectRegles(newInstances);
3395
+ }, { deep: true });
3396
+ function collectRegles(r$Instances) {
3397
+ if (computedNamespace.value) return mergeRegles(r$Instances[computedNamespace.value] ?? {}, !options.asRecord);
3398
+ else return mergeRegles(r$Instances["~~global"] ?? {}, !options.asRecord);
3399
+ }
3400
+ return { r$: regle.r$ };
3401
+ }
3402
+ return { useCollectScope: useCollectScope$1 };
3462
3403
  }
3463
- function buildInspectorTree(instances, filter) {
3464
- const nodes = instances.map((instance) => {
3465
- const { r$, id, name, componentName } = instance;
3404
+
3405
+ function createUseScopedRegleComposable(instances, customUseRegle) {
3406
+ const scopedUseRegle = customUseRegle ?? useRegle;
3407
+ const useScopedRegle$1 = ((state, rulesFactory, options) => {
3408
+ const { namespace, scopeKey: _scopeKey, ...restOptions } = options ?? {};
3409
+ scopedUseRegle.__config ??= {};
3410
+ const computedNamespace = computed(() => toValue(namespace));
3411
+ const $id = ref(`${Object.keys(instances.value).length + 1}-${randomId()}`);
3412
+ const instanceName = computed(() => {
3413
+ return options?.scopeKey ?? `instance-${$id.value}`;
3414
+ });
3415
+ const { r$ } = scopedUseRegle(state, rulesFactory, restOptions);
3416
+ register();
3417
+ tryOnScopeDispose(dispose);
3418
+ watch(computedNamespace, (newName, oldName) => {
3419
+ dispose(oldName);
3420
+ register();
3421
+ });
3422
+ if (getCurrentInstance()) onMounted(() => {
3423
+ const currentInstance = getCurrentInstance();
3424
+ if (typeof window !== "undefined" && currentInstance?.proxy?.$el?.parentElement) {
3425
+ if (document.documentElement && !document.documentElement.contains(currentInstance?.proxy?.$el?.parentElement)) dispose();
3426
+ }
3427
+ });
3428
+ function dispose(oldName) {
3429
+ const nameToClean = oldName ?? computedNamespace.value;
3430
+ if (nameToClean) {
3431
+ if (instances.value[nameToClean]) delete instances.value[nameToClean][instanceName.value];
3432
+ } else if (instances.value["~~global"][instanceName.value]) delete instances.value["~~global"][instanceName.value];
3433
+ }
3434
+ function register() {
3435
+ if (computedNamespace.value) {
3436
+ if (!instances.value[computedNamespace.value]) instances.value[computedNamespace.value] = {};
3437
+ instances.value[computedNamespace.value][instanceName.value] = r$;
3438
+ } else {
3439
+ if (!instances.value["~~global"]) instances.value["~~global"] = {};
3440
+ instances.value["~~global"][instanceName.value] = r$;
3441
+ }
3442
+ }
3466
3443
  return {
3467
- id,
3468
- label: name,
3469
- tags: buildNodeTags(r$, componentName),
3470
- children: buildRootChildrenNodes(r$, id)
3444
+ r$,
3445
+ dispose,
3446
+ register
3471
3447
  };
3472
3448
  });
3473
- if (!filter || filter.trim() === "") return nodes;
3474
- return filterInspectorTree(nodes, filter);
3449
+ return { useScopedRegle: useScopedRegle$1 };
3475
3450
  }
3476
- function filterInspectorTree(nodes, filter) {
3477
- const lowerFilter = filter.toLowerCase();
3478
- const filtered = [];
3479
- for (const node of nodes) {
3480
- const labelMatches = node.label.toLowerCase().includes(lowerFilter);
3481
- const tagMatches = node.tags?.some((tag) => tag.label.toLowerCase().includes(lowerFilter)) ?? false;
3482
- const filteredChildren = node.children ? filterInspectorTree(node.children, filter) : [];
3483
- if (labelMatches || tagMatches || filteredChildren.length > 0) filtered.push({
3484
- ...node,
3485
- children: filteredChildren.length > 0 ? filteredChildren : node.children
3486
- });
3487
- }
3488
- return filtered;
3451
+
3452
+ function createScopedUseRegle(options) {
3453
+ const instances = (options?.customStore ? () => {
3454
+ if (options.customStore) {
3455
+ if (!options.customStore?.value["~~global"]) options.customStore.value["~~global"] = {};
3456
+ else if (options.customStore?.value) options.customStore.value = { "~~global": {} };
3457
+ }
3458
+ return options.customStore;
3459
+ } : createGlobalState(() => {
3460
+ return ref({ "~~global": {} });
3461
+ }))();
3462
+ const { useScopedRegle: useScopedRegle$1 } = createUseScopedRegleComposable(instances, options?.customUseRegle);
3463
+ const { useCollectScope: useCollectScope$1 } = createUseCollectScope(instances, { asRecord: options?.asRecord });
3464
+ return {
3465
+ useScopedRegle: useScopedRegle$1,
3466
+ useCollectScope: useCollectScope$1
3467
+ };
3489
3468
  }
3469
+ const { useCollectScope, useScopedRegle } = createScopedUseRegle();
3490
3470
 
3491
- //#endregion
3492
- //#region src/devtools/devtools.ts
3493
- function createDevtools(app) {
3494
- setupDevtoolsPlugin({
3495
- id: "regle-devtools",
3496
- label: "Regle",
3497
- logo: "https://reglejs.dev/logo_main.png",
3498
- packageName: "@regle/core",
3499
- homepage: "https://reglejs.dev",
3500
- componentStateTypes: [],
3501
- app
3502
- }, (api) => {
3503
- api.addInspector({
3504
- id: INSPECTOR_IDS.INSPECTOR,
3505
- label: "Regle",
3506
- noSelectionText: "No instance selected",
3507
- icon: "rule",
3508
- treeFilterPlaceholder: "Filter",
3509
- stateFilterPlaceholder: "Filter validation status",
3510
- nodeActions: [
3511
- {
3512
- icon: "check_circle",
3513
- tooltip: "Validate (with `$validate`)",
3514
- action: (nodeId) => {
3515
- handleValidateAction(nodeId, api);
3516
- }
3517
- },
3518
- {
3519
- icon: "refresh",
3520
- tooltip: "Reset validation state (with `$reset`)",
3521
- action: (nodeId) => {
3522
- handleResetAction(nodeId, api);
3523
- }
3524
- },
3525
- {
3526
- icon: "restore",
3527
- tooltip: "Restore to initial state (with `$reset`)",
3528
- action: (nodeId) => {
3529
- handleResetAction(nodeId, api, true);
3530
- }
3531
- }
3532
- ]
3533
- });
3534
- setupInstanceWatchers(api);
3535
- api.on.getInspectorTree((payload) => {
3536
- if (payload.inspectorId === INSPECTOR_IDS.INSPECTOR) payload.rootNodes = buildInspectorTree(regleDevtoolsRegistry.getAll(), payload.filter);
3537
- });
3538
- api.on.getInspectorState((payload) => {
3539
- if (payload.inspectorId === INSPECTOR_IDS.INSPECTOR) {
3540
- const state = buildInspectorState(payload.nodeId, (id) => regleDevtoolsRegistry.get(id));
3541
- if (state) payload.state = state;
3471
+ /**
3472
+ * Declare variations of state that depends on one value
3473
+ *
3474
+ * Autocomplete may not work here because of https://github.com/microsoft/TypeScript/issues/49547
3475
+ *
3476
+ * ```ts
3477
+ * // ⚠️ Use getter syntax for your rules () => {} or a computed one
3478
+ * const {r$} = useRegle(state, () => {
3479
+ * const variant = createVariant(state, 'type', [
3480
+ * {type: { literal: literal('EMAIL')}, email: { required, email }},
3481
+ * {type: { literal: literal('GITHUB')}, username: { required }},
3482
+ * {type: { required }},
3483
+ * ]);
3484
+ *
3485
+ * return {
3486
+ * ...variant.value,
3487
+ * };
3488
+ * })
3489
+ * ```
3490
+ */
3491
+ function createVariant(root, discriminantKey, variants) {
3492
+ const watchableRoot = computed(() => toValue(root)[discriminantKey]);
3493
+ return computed(() => {
3494
+ const selectedVariant = variants.find((variant) => {
3495
+ if (variant[discriminantKey] && "literal" in variant[discriminantKey]) {
3496
+ const literalRule = variant[discriminantKey]["literal"];
3497
+ if (isRuleDef(literalRule)) return unref(literalRule._params?.[0]) === watchableRoot.value;
3542
3498
  }
3543
3499
  });
3544
- api.on.editInspectorState((payload) => {
3545
- if (payload.inspectorId === INSPECTOR_IDS.INSPECTOR) handleEditInspectorState(payload, api);
3546
- });
3500
+ if (selectedVariant) return selectedVariant;
3501
+ else {
3502
+ const anyDiscriminantRules = variants.find((variant) => isObject(variant[discriminantKey]) && !Object.keys(variant[discriminantKey]).some((key) => key === "literal"));
3503
+ if (anyDiscriminantRules) return anyDiscriminantRules;
3504
+ else return {};
3505
+ }
3547
3506
  });
3548
3507
  }
3549
- function setupInstanceWatchers(api) {
3550
- const watchedInstances = /* @__PURE__ */ new Set();
3551
- const setupWatchers = () => {
3552
- regleDevtoolsRegistry.getAll().forEach((instance) => {
3553
- const { r$, id } = instance;
3554
- if (watchedInstances.has(id)) return;
3555
- const stopHandle = watchRegleInstance(id, r$, () => {
3556
- api.sendInspectorState(INSPECTOR_IDS.INSPECTOR);
3557
- api.sendInspectorTree(INSPECTOR_IDS.INSPECTOR);
3558
- });
3559
- regleDevtoolsRegistry.addWatcher(id, stopHandle);
3560
- watchedInstances.add(id);
3561
- });
3562
- };
3563
- setupWatchers();
3564
- regleDevtoolsRegistry.onInstancesChange(() => {
3565
- const currentIds = new Set(regleDevtoolsRegistry.getAll().map((i) => i.id));
3566
- for (const id of watchedInstances) if (!currentIds.has(id)) watchedInstances.delete(id);
3567
- api.sendInspectorTree(INSPECTOR_IDS.INSPECTOR);
3568
- setupWatchers();
3508
+ /**
3509
+ * Narrow a nested variant field to a discriminated value
3510
+ *
3511
+ * ```ts
3512
+ * if (narrowVariant(r$, 'type', 'EMAIL')) {
3513
+ * r$.email.$value = 'foo';
3514
+ * }
3515
+ * ```
3516
+ */
3517
+ function narrowVariant(root, discriminantKey, discriminantValue) {
3518
+ return isObject(root[discriminantKey]) && "$value" in root[discriminantKey] && root[discriminantKey]?.$value === discriminantValue;
3519
+ }
3520
+ function variantToRef(root, discriminantKey, discriminantValue, _options) {
3521
+ const fromRoot = isRef(root) ? toRef(root.value, "$fields") : toRef(root, "$fields");
3522
+ const returnedRef = ref();
3523
+ watch(fromRoot, async () => {
3524
+ await nextTick();
3525
+ if (narrowVariant(fromRoot.value, discriminantKey, discriminantValue)) returnedRef.value = toRef(root).value;
3526
+ else returnedRef.value = void 0;
3527
+ }, {
3528
+ immediate: true,
3529
+ flush: "pre"
3569
3530
  });
3531
+ return returnedRef;
3532
+ }
3533
+
3534
+ /**
3535
+ * Helper method to wrap an raw rules object
3536
+ *
3537
+ * Similar to:
3538
+ *
3539
+ * ```ts
3540
+ * const rules = {...} satisfies RegleUnknownRulesTree
3541
+ * ```
3542
+ */
3543
+ function defineRules(rules) {
3544
+ return rules;
3545
+ }
3546
+ /**
3547
+ * Refine a raw rules object to set rules that depends on the state values.
3548
+ *
3549
+ * @example
3550
+ *
3551
+ * ```ts
3552
+ * const rules = refineRules({
3553
+ * password: { required, type: type<string>() },
3554
+ * }, (state) => {
3555
+ * return {
3556
+ * confirmPassword: { required, sameAs: sameAs(() => state.value.password)}
3557
+ * }
3558
+ * })
3559
+ * ```
3560
+ */
3561
+ function refineRules(rules, refinement) {
3562
+ return (state) => merge({ ...rules }, refinement(state));
3570
3563
  }
3571
3564
 
3572
- //#endregion
3573
- //#region src/devtools/plugin.ts
3574
- const __USE_DEVTOOLS__ = process.env.NODE_ENV === "development";
3565
+ var version$1 = "1.11.0-beta.3";
3566
+
3575
3567
  const regleSymbol = Symbol("regle");
3568
+
3576
3569
  const RegleVuePlugin = { install(app) {
3577
- app.provide(regleSymbol, true);
3578
- if (typeof window !== "undefined" && __USE_DEVTOOLS__) createDevtools(app);
3570
+ app.provide(regleSymbol, version$1);
3571
+ if (typeof window !== "undefined" && true) createDevtools(app);
3579
3572
  } };
3580
3573
 
3581
- //#endregion
3582
- export { InternalRuleType, RegleVuePlugin, createRule, createScopedUseRegle, createVariant, defineRegleConfig, defineRules, extendRegleConfig, flatErrors, inferRules, mergeRegles, narrowVariant, refineRules, registerRegleInstance, unwrapRuleParameters, useCollectScope, useRegle, useRootStorage, useRules, useScopedRegle, variantToRef };
3574
+ export { InternalRuleType, RegleVuePlugin, createRule, createScopedUseRegle, createVariant, defineRegleConfig, defineRules, extendRegleConfig, flatErrors, inferRules, mergeRegles, narrowVariant, refineRules, unwrapRuleParameters, useCollectScope, useRegle, useRootStorage, useRules, useScopedRegle, variantToRef };