assign-gingerly 0.0.79 → 0.0.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DX/paths.js +67 -18
- package/DX/paths.ts +162 -92
- package/README.md +9 -8
- package/assignGingerly.js +143 -54
- package/assignGingerly.ts +172 -56
- package/handleIshProperty.js +1 -1
- package/handleIshProperty.ts +1 -1
- package/handlers/manageTemplateList.js +1 -1
- package/handlers/manageTemplateList.ts +1 -1
- package/index.js +1 -1
- package/index.ts +1 -1
- package/inferencer/types/NewCustomElement.md +20 -440
- package/inferencer/types/NewHTMLFirstCustomElement.md +431 -0
- package/inferencer/types/NewJSFirstCustomElement.md +278 -0
- package/inferencer/types/roundabout/types.d.ts +1 -0
- package/object-extension.js +1 -1
- package/object-extension.ts +1 -1
- package/package.json +7 -6
- /package/{waitForEvent.js → utils/waitForEvent.js} +0 -0
- /package/{waitForEvent.ts → utils/waitForEvent.ts} +0 -0
- /package/{waitForSettled.js → utils/waitForSettled.js} +0 -0
- /package/{waitForSettled.ts → utils/waitForSettled.ts} +0 -0
package/assignGingerly.js
CHANGED
|
@@ -420,6 +420,7 @@ export function isAllowedMethod(methodName, withMethods, permissionProcessor) {
|
|
|
420
420
|
export function evaluatePathWithMethods(target, pathParts, value, withMethods, permissionProcessor) {
|
|
421
421
|
let current = target;
|
|
422
422
|
let i = 0;
|
|
423
|
+
let lastSegmentConsumed = false;
|
|
423
424
|
// Process all segments except the last one
|
|
424
425
|
while (i < pathParts.length - 1) {
|
|
425
426
|
const part = pathParts[i];
|
|
@@ -442,6 +443,9 @@ export function evaluatePathWithMethods(target, pathParts, value, withMethods, p
|
|
|
442
443
|
// Only current is method - call with next part as string arg
|
|
443
444
|
current = method.call(current, nextPart, ...appendArgs);
|
|
444
445
|
i++; // Skip next part since we consumed it as argument
|
|
446
|
+
if (i === pathParts.length - 1) {
|
|
447
|
+
lastSegmentConsumed = true;
|
|
448
|
+
}
|
|
445
449
|
}
|
|
446
450
|
}
|
|
447
451
|
else {
|
|
@@ -470,7 +474,8 @@ export function evaluatePathWithMethods(target, pathParts, value, withMethods, p
|
|
|
470
474
|
target: current,
|
|
471
475
|
lastKey,
|
|
472
476
|
isMethod: isAllowedMethod(lastKey, withMethods, permissionProcessor),
|
|
473
|
-
isZeroArg
|
|
477
|
+
isZeroArg,
|
|
478
|
+
lastSegmentConsumed
|
|
474
479
|
};
|
|
475
480
|
}
|
|
476
481
|
/**
|
|
@@ -623,6 +628,95 @@ function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, opti
|
|
|
623
628
|
}
|
|
624
629
|
}
|
|
625
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* Resolve the RHS of a toggle (=!) command to the value that should be negated.
|
|
633
|
+
* Supports:
|
|
634
|
+
* - '?.path' nested path strings resolved against target (returns the resolved value, or true if missing)
|
|
635
|
+
* - plain key strings looked up on target (returns the value, or true if missing)
|
|
636
|
+
* - resolved literal values (e.g., booleans from assignFrom) returned as-is
|
|
637
|
+
*/
|
|
638
|
+
function resolveValueToNegate(rhsPath, target) {
|
|
639
|
+
if (typeof rhsPath === 'string' && isNestedPath(rhsPath)) {
|
|
640
|
+
const rhsPathParts = parsePath(rhsPath);
|
|
641
|
+
let current = target;
|
|
642
|
+
let exists = true;
|
|
643
|
+
for (const part of rhsPathParts) {
|
|
644
|
+
if (current && typeof current === 'object' && part in current) {
|
|
645
|
+
current = current[part];
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
exists = false;
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return exists ? current : true;
|
|
653
|
+
}
|
|
654
|
+
if (typeof rhsPath === 'string') {
|
|
655
|
+
return (rhsPath in target) ? target[rhsPath] : true;
|
|
656
|
+
}
|
|
657
|
+
// Non-string resolved value (e.g., boolean, number, null from assignFrom)
|
|
658
|
+
return rhsPath;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Navigate a path that leads to an iterable, returning the iterable value or undefined.
|
|
662
|
+
* Handles withMethods and the evaluatePathWithMethods semantics.
|
|
663
|
+
*/
|
|
664
|
+
function getIterableAtPath(target, pathParts, value, withMethods, permissionProcessor) {
|
|
665
|
+
let current = target;
|
|
666
|
+
if (pathParts.length > 0) {
|
|
667
|
+
if (withMethods && withMethods.size > 0) {
|
|
668
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethods, permissionProcessor);
|
|
669
|
+
// evaluatePathWithMethods returns the container object + last key by default.
|
|
670
|
+
// If the last segment was consumed as a method argument, or the last segment
|
|
671
|
+
// is a zero-arg method marked with |, result.target is already the value.
|
|
672
|
+
if (result.lastSegmentConsumed) {
|
|
673
|
+
current = result.target;
|
|
674
|
+
}
|
|
675
|
+
else if (result.isMethod) {
|
|
676
|
+
const method = result.target[result.lastKey];
|
|
677
|
+
if (typeof method === 'function') {
|
|
678
|
+
const appendArgs = permissionProcessor?.getMethodAppendArgs(result.lastKey) ?? [];
|
|
679
|
+
current = method.call(result.target, ...appendArgs);
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
current = method;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
current = result.target[result.lastKey];
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
for (const part of pathParts) {
|
|
691
|
+
current = current[part];
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return isIterable(current) ? current : undefined;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Apply an operator command (+=, =!, -=, Y=) to each item in an iterable.
|
|
699
|
+
* Detects @each in the path, navigates to the iterable, then builds a synthetic
|
|
700
|
+
* command key for the remaining path and delegates to assignGingerly per item.
|
|
701
|
+
* Nested @each is handled recursively through assignGingerly.
|
|
702
|
+
*/
|
|
703
|
+
function applyCommandToEach(target, pathParts, commandSuffix, value, withMethods, aliasMap, options, permissionProcessor) {
|
|
704
|
+
const forEachIndex = pathParts.findIndex(part => isForEachSymbol(part, aliasMap));
|
|
705
|
+
if (forEachIndex === -1)
|
|
706
|
+
return;
|
|
707
|
+
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
708
|
+
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
709
|
+
const iterable = getIterableAtPath(target, pathToForEach, value, withMethods, permissionProcessor);
|
|
710
|
+
if (!iterable)
|
|
711
|
+
return;
|
|
712
|
+
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
713
|
+
const syntheticKey = pathAfterForEach.length > 0
|
|
714
|
+
? `?.${pathAfterForEach.join('?.')}${commandSuffix}`
|
|
715
|
+
: commandSuffix;
|
|
716
|
+
for (const item of items) {
|
|
717
|
+
assignGingerly(item, { [syntheticKey]: value }, options, permissionProcessor);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
626
720
|
/**
|
|
627
721
|
* Apply alias substitutions to a key string.
|
|
628
722
|
* Replaces complete tokens between `?.` delimiters with their aliased values.
|
|
@@ -727,6 +821,11 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
727
821
|
if (path) {
|
|
728
822
|
if (isNestedPath(path)) {
|
|
729
823
|
const pathParts = parsePath(path);
|
|
824
|
+
// Check for @each in path
|
|
825
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
826
|
+
applyCommandToEach(target, pathParts, ' +=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
730
829
|
// Check for withMethods path evaluation
|
|
731
830
|
let lhsValue;
|
|
732
831
|
let lhsParent;
|
|
@@ -795,54 +894,47 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
795
894
|
const lhsPath = parseToggleCommand(key);
|
|
796
895
|
if (lhsPath) {
|
|
797
896
|
const rhsPath = value;
|
|
798
|
-
// Resolve LHS
|
|
799
|
-
let lhsParent;
|
|
800
|
-
let lhsLastKey;
|
|
801
897
|
if (isNestedPath(lhsPath)) {
|
|
802
898
|
const lhsPathParts = parsePath(lhsPath);
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
//
|
|
814
|
-
|
|
815
|
-
|
|
899
|
+
// Check for @each in the LHS path
|
|
900
|
+
if (lhsPathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
901
|
+
// Resolve non-self-referencing RHS paths against the original target
|
|
902
|
+
// before iterating, so each item negates the same root value.
|
|
903
|
+
const resolvedValue = (rhsPath === '.' || typeof rhsPath !== 'string')
|
|
904
|
+
? rhsPath
|
|
905
|
+
: resolveValueToNegate(rhsPath, target);
|
|
906
|
+
applyCommandToEach(target, lhsPathParts, ' =!', resolvedValue, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
// No @each in path - standard toggle
|
|
910
|
+
const lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
911
|
+
const lhsParent = ensureNestedPath(target, lhsPathParts);
|
|
912
|
+
// Determine what to negate
|
|
913
|
+
let valueToNegate;
|
|
914
|
+
if (rhsPath === '.') {
|
|
915
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
816
916
|
}
|
|
817
917
|
else {
|
|
818
|
-
valueToNegate =
|
|
918
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
919
|
+
}
|
|
920
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
921
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
819
922
|
}
|
|
820
923
|
}
|
|
821
924
|
else {
|
|
822
|
-
//
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (current && typeof current === 'object' && part in current) {
|
|
829
|
-
current = current[part];
|
|
830
|
-
}
|
|
831
|
-
else {
|
|
832
|
-
exists = false;
|
|
833
|
-
break;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
valueToNegate = exists ? current : true;
|
|
925
|
+
// Plain key LHS
|
|
926
|
+
const lhsLastKey = lhsPath;
|
|
927
|
+
const lhsParent = target;
|
|
928
|
+
let valueToNegate;
|
|
929
|
+
if (rhsPath === '.') {
|
|
930
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
837
931
|
}
|
|
838
932
|
else {
|
|
839
|
-
|
|
840
|
-
|
|
933
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
934
|
+
}
|
|
935
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
936
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
841
937
|
}
|
|
842
|
-
}
|
|
843
|
-
// Apply negation to LHS — check restriction first
|
|
844
|
-
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
845
|
-
lhsParent[lhsLastKey] = !valueToNegate;
|
|
846
938
|
}
|
|
847
939
|
}
|
|
848
940
|
continue;
|
|
@@ -856,6 +948,11 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
856
948
|
let canDelete = true;
|
|
857
949
|
if (isNestedPath(path)) {
|
|
858
950
|
const pathParts = parsePath(path);
|
|
951
|
+
// Check for @each in path
|
|
952
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
953
|
+
applyCommandToEach(target, pathParts, ' -=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
859
956
|
if (pathParts.length === 0) {
|
|
860
957
|
parent = target;
|
|
861
958
|
}
|
|
@@ -901,6 +998,11 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
901
998
|
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
902
999
|
continue;
|
|
903
1000
|
}
|
|
1001
|
+
// Check for @each in path
|
|
1002
|
+
if (isNestedPath(path) && pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1003
|
+
applyCommandToEach(target, pathParts, ' Y=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
904
1006
|
// Navigate to the target sub-object
|
|
905
1007
|
let mergeTarget;
|
|
906
1008
|
if (isNestedPath(path)) {
|
|
@@ -956,22 +1058,9 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
956
1058
|
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
957
1059
|
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
958
1060
|
// Navigate to the iterable
|
|
959
|
-
|
|
960
|
-
if (pathToForEach.length > 0) {
|
|
961
|
-
if (withMethodsSet) {
|
|
962
|
-
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
963
|
-
// The result.target is the current position after evaluating the path
|
|
964
|
-
// This is already the iterable we want
|
|
965
|
-
current = result.target;
|
|
966
|
-
}
|
|
967
|
-
else {
|
|
968
|
-
for (const part of pathToForEach) {
|
|
969
|
-
current = current[part];
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
1061
|
+
const current = getIterableAtPath(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
973
1062
|
// Apply to each item in the iterable
|
|
974
|
-
if (
|
|
1063
|
+
if (current) {
|
|
975
1064
|
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options, permissionProcessor);
|
|
976
1065
|
}
|
|
977
1066
|
// If not iterable, let JavaScript throw error naturally when trying to iterate
|
package/assignGingerly.ts
CHANGED
|
@@ -506,9 +506,10 @@ export function evaluatePathWithMethods(
|
|
|
506
506
|
value: any,
|
|
507
507
|
withMethods: Set<string>,
|
|
508
508
|
permissionProcessor?: PermissionProcessor
|
|
509
|
-
): { target: any; lastKey: string; isMethod: boolean; isZeroArg: boolean } {
|
|
509
|
+
): { target: any; lastKey: string; isMethod: boolean; isZeroArg: boolean; lastSegmentConsumed: boolean } {
|
|
510
510
|
let current = target;
|
|
511
511
|
let i = 0;
|
|
512
|
+
let lastSegmentConsumed = false;
|
|
512
513
|
|
|
513
514
|
// Process all segments except the last one
|
|
514
515
|
while (i < pathParts.length - 1) {
|
|
@@ -533,6 +534,9 @@ export function evaluatePathWithMethods(
|
|
|
533
534
|
// Only current is method - call with next part as string arg
|
|
534
535
|
current = method.call(current, nextPart, ...appendArgs);
|
|
535
536
|
i++; // Skip next part since we consumed it as argument
|
|
537
|
+
if (i === pathParts.length - 1) {
|
|
538
|
+
lastSegmentConsumed = true;
|
|
539
|
+
}
|
|
536
540
|
}
|
|
537
541
|
} else {
|
|
538
542
|
// Not a function - just access property (create if needed)
|
|
@@ -561,7 +565,8 @@ export function evaluatePathWithMethods(
|
|
|
561
565
|
target: current,
|
|
562
566
|
lastKey,
|
|
563
567
|
isMethod: isAllowedMethod(lastKey, withMethods, permissionProcessor),
|
|
564
|
-
isZeroArg
|
|
568
|
+
isZeroArg,
|
|
569
|
+
lastSegmentConsumed
|
|
565
570
|
};
|
|
566
571
|
}
|
|
567
572
|
|
|
@@ -724,6 +729,110 @@ function applyToEach(
|
|
|
724
729
|
}
|
|
725
730
|
}
|
|
726
731
|
|
|
732
|
+
/**
|
|
733
|
+
* Resolve the RHS of a toggle (=!) command to the value that should be negated.
|
|
734
|
+
* Supports:
|
|
735
|
+
* - '?.path' nested path strings resolved against target (returns the resolved value, or true if missing)
|
|
736
|
+
* - plain key strings looked up on target (returns the value, or true if missing)
|
|
737
|
+
* - resolved literal values (e.g., booleans from assignFrom) returned as-is
|
|
738
|
+
*/
|
|
739
|
+
function resolveValueToNegate(rhsPath: any, target: any): any {
|
|
740
|
+
if (typeof rhsPath === 'string' && isNestedPath(rhsPath)) {
|
|
741
|
+
const rhsPathParts = parsePath(rhsPath);
|
|
742
|
+
let current = target;
|
|
743
|
+
let exists = true;
|
|
744
|
+
for (const part of rhsPathParts) {
|
|
745
|
+
if (current && typeof current === 'object' && part in current) {
|
|
746
|
+
current = current[part];
|
|
747
|
+
} else {
|
|
748
|
+
exists = false;
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return exists ? current : true;
|
|
753
|
+
}
|
|
754
|
+
if (typeof rhsPath === 'string') {
|
|
755
|
+
return (rhsPath in target) ? target[rhsPath] : true;
|
|
756
|
+
}
|
|
757
|
+
// Non-string resolved value (e.g., boolean, number, null from assignFrom)
|
|
758
|
+
return rhsPath;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Navigate a path that leads to an iterable, returning the iterable value or undefined.
|
|
763
|
+
* Handles withMethods and the evaluatePathWithMethods semantics.
|
|
764
|
+
*/
|
|
765
|
+
function getIterableAtPath(
|
|
766
|
+
target: any,
|
|
767
|
+
pathParts: string[],
|
|
768
|
+
value: any,
|
|
769
|
+
withMethods: Set<string> | undefined,
|
|
770
|
+
permissionProcessor?: PermissionProcessor
|
|
771
|
+
): any {
|
|
772
|
+
let current = target;
|
|
773
|
+
if (pathParts.length > 0) {
|
|
774
|
+
if (withMethods && withMethods.size > 0) {
|
|
775
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethods, permissionProcessor);
|
|
776
|
+
// evaluatePathWithMethods returns the container object + last key by default.
|
|
777
|
+
// If the last segment was consumed as a method argument, or the last segment
|
|
778
|
+
// is a zero-arg method marked with |, result.target is already the value.
|
|
779
|
+
if (result.lastSegmentConsumed) {
|
|
780
|
+
current = result.target;
|
|
781
|
+
} else if (result.isMethod) {
|
|
782
|
+
const method = result.target[result.lastKey];
|
|
783
|
+
if (typeof method === 'function') {
|
|
784
|
+
const appendArgs = permissionProcessor?.getMethodAppendArgs(result.lastKey) ?? [];
|
|
785
|
+
current = method.call(result.target, ...appendArgs);
|
|
786
|
+
} else {
|
|
787
|
+
current = method;
|
|
788
|
+
}
|
|
789
|
+
} else {
|
|
790
|
+
current = result.target[result.lastKey];
|
|
791
|
+
}
|
|
792
|
+
} else {
|
|
793
|
+
for (const part of pathParts) {
|
|
794
|
+
current = current[part];
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return isIterable(current) ? current : undefined;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Apply an operator command (+=, =!, -=, Y=) to each item in an iterable.
|
|
803
|
+
* Detects @each in the path, navigates to the iterable, then builds a synthetic
|
|
804
|
+
* command key for the remaining path and delegates to assignGingerly per item.
|
|
805
|
+
* Nested @each is handled recursively through assignGingerly.
|
|
806
|
+
*/
|
|
807
|
+
function applyCommandToEach(
|
|
808
|
+
target: any,
|
|
809
|
+
pathParts: string[],
|
|
810
|
+
commandSuffix: string,
|
|
811
|
+
value: any,
|
|
812
|
+
withMethods: Set<string> | undefined,
|
|
813
|
+
aliasMap: Map<string, string>,
|
|
814
|
+
options?: IAssignGingerlyOptions,
|
|
815
|
+
permissionProcessor?: PermissionProcessor
|
|
816
|
+
): void {
|
|
817
|
+
const forEachIndex = pathParts.findIndex(part => isForEachSymbol(part, aliasMap));
|
|
818
|
+
if (forEachIndex === -1) return;
|
|
819
|
+
|
|
820
|
+
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
821
|
+
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
822
|
+
|
|
823
|
+
const iterable = getIterableAtPath(target, pathToForEach, value, withMethods, permissionProcessor);
|
|
824
|
+
if (!iterable) return;
|
|
825
|
+
|
|
826
|
+
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
827
|
+
const syntheticKey = pathAfterForEach.length > 0
|
|
828
|
+
? `?.${pathAfterForEach.join('?.')}${commandSuffix}`
|
|
829
|
+
: commandSuffix;
|
|
830
|
+
|
|
831
|
+
for (const item of items) {
|
|
832
|
+
assignGingerly(item, { [syntheticKey]: value }, options, permissionProcessor);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
727
836
|
/**
|
|
728
837
|
* Apply alias substitutions to a key string.
|
|
729
838
|
* Replaces complete tokens between `?.` delimiters with their aliased values.
|
|
@@ -845,6 +954,12 @@ export function assignGingerly(
|
|
|
845
954
|
if (isNestedPath(path)) {
|
|
846
955
|
const pathParts = parsePath(path);
|
|
847
956
|
|
|
957
|
+
// Check for @each in path
|
|
958
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
959
|
+
applyCommandToEach(target, pathParts, ' +=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
|
|
848
963
|
// Check for withMethods path evaluation
|
|
849
964
|
let lhsValue: any;
|
|
850
965
|
let lhsParent: any;
|
|
@@ -915,52 +1030,51 @@ export function assignGingerly(
|
|
|
915
1030
|
const lhsPath = parseToggleCommand(key);
|
|
916
1031
|
if (lhsPath) {
|
|
917
1032
|
const rhsPath = value;
|
|
918
|
-
|
|
919
|
-
// Resolve LHS
|
|
920
|
-
let lhsParent: any;
|
|
921
|
-
let lhsLastKey: string;
|
|
1033
|
+
|
|
922
1034
|
if (isNestedPath(lhsPath)) {
|
|
923
1035
|
const lhsPathParts = parsePath(lhsPath);
|
|
924
|
-
lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
925
|
-
lhsParent = ensureNestedPath(target, lhsPathParts);
|
|
926
|
-
} else {
|
|
927
|
-
lhsLastKey = lhsPath;
|
|
928
|
-
lhsParent = target;
|
|
929
|
-
}
|
|
930
1036
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1037
|
+
// Check for @each in the LHS path
|
|
1038
|
+
if (lhsPathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1039
|
+
// Resolve non-self-referencing RHS paths against the original target
|
|
1040
|
+
// before iterating, so each item negates the same root value.
|
|
1041
|
+
const resolvedValue = (rhsPath === '.' || typeof rhsPath !== 'string')
|
|
1042
|
+
? rhsPath
|
|
1043
|
+
: resolveValueToNegate(rhsPath, target);
|
|
1044
|
+
applyCommandToEach(target, lhsPathParts, ' =!', resolvedValue, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// No @each in path - standard toggle
|
|
1049
|
+
const lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
1050
|
+
const lhsParent = ensureNestedPath(target, lhsPathParts);
|
|
1051
|
+
|
|
1052
|
+
// Determine what to negate
|
|
1053
|
+
let valueToNegate: any;
|
|
1054
|
+
if (rhsPath === '.') {
|
|
1055
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
937
1056
|
} else {
|
|
938
|
-
valueToNegate =
|
|
1057
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
1061
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
939
1062
|
}
|
|
940
1063
|
} else {
|
|
941
|
-
//
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
current = current[part];
|
|
949
|
-
} else {
|
|
950
|
-
exists = false;
|
|
951
|
-
break;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
valueToNegate = exists ? current : true;
|
|
1064
|
+
// Plain key LHS
|
|
1065
|
+
const lhsLastKey = lhsPath;
|
|
1066
|
+
const lhsParent = target;
|
|
1067
|
+
|
|
1068
|
+
let valueToNegate: any;
|
|
1069
|
+
if (rhsPath === '.') {
|
|
1070
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
955
1071
|
} else {
|
|
956
|
-
|
|
957
|
-
|
|
1072
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
1076
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
958
1077
|
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// Apply negation to LHS — check restriction first
|
|
962
|
-
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
963
|
-
lhsParent[lhsLastKey] = !valueToNegate;
|
|
964
1078
|
}
|
|
965
1079
|
}
|
|
966
1080
|
continue;
|
|
@@ -976,6 +1090,13 @@ export function assignGingerly(
|
|
|
976
1090
|
|
|
977
1091
|
if (isNestedPath(path)) {
|
|
978
1092
|
const pathParts = parsePath(path);
|
|
1093
|
+
|
|
1094
|
+
// Check for @each in path
|
|
1095
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1096
|
+
applyCommandToEach(target, pathParts, ' -=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
979
1100
|
if (pathParts.length === 0) {
|
|
980
1101
|
parent = target;
|
|
981
1102
|
} else {
|
|
@@ -1019,6 +1140,13 @@ export function assignGingerly(
|
|
|
1019
1140
|
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
1020
1141
|
continue;
|
|
1021
1142
|
}
|
|
1143
|
+
|
|
1144
|
+
// Check for @each in path
|
|
1145
|
+
if (isNestedPath(path) && pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1146
|
+
applyCommandToEach(target, pathParts, ' Y=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1022
1150
|
// Navigate to the target sub-object
|
|
1023
1151
|
let mergeTarget: any;
|
|
1024
1152
|
if (isNestedPath(path)) {
|
|
@@ -1086,24 +1214,12 @@ export function assignGingerly(
|
|
|
1086
1214
|
// Static forEach (@each) - existing logic
|
|
1087
1215
|
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
1088
1216
|
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
1089
|
-
|
|
1217
|
+
|
|
1090
1218
|
// Navigate to the iterable
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if (withMethodsSet) {
|
|
1094
|
-
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
1095
|
-
// The result.target is the current position after evaluating the path
|
|
1096
|
-
// This is already the iterable we want
|
|
1097
|
-
current = result.target;
|
|
1098
|
-
} else {
|
|
1099
|
-
for (const part of pathToForEach) {
|
|
1100
|
-
current = current[part];
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1219
|
+
const current = getIterableAtPath(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
1220
|
+
|
|
1105
1221
|
// Apply to each item in the iterable
|
|
1106
|
-
if (
|
|
1222
|
+
if (current) {
|
|
1107
1223
|
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options, permissionProcessor);
|
|
1108
1224
|
}
|
|
1109
1225
|
// If not iterable, let JavaScript throw error naturally when trying to iterate
|
package/handleIshProperty.js
CHANGED
|
@@ -47,7 +47,7 @@ async function defineIshProperty(element, managerName, options, assignGingerlyFn
|
|
|
47
47
|
let config = registry.get(managerName);
|
|
48
48
|
// If not registered, wait for registration
|
|
49
49
|
if (!config) {
|
|
50
|
-
const { waitForEvent } = await import('./waitForEvent.js');
|
|
50
|
+
const { waitForEvent } = await import('./utils/waitForEvent.js');
|
|
51
51
|
await waitForEvent(registry, managerName);
|
|
52
52
|
config = registry.get(managerName);
|
|
53
53
|
if (!config) {
|
package/handleIshProperty.ts
CHANGED
|
@@ -66,7 +66,7 @@ async function defineIshProperty(
|
|
|
66
66
|
|
|
67
67
|
// If not registered, wait for registration
|
|
68
68
|
if (!config) {
|
|
69
|
-
const { waitForEvent } = await import('./waitForEvent.js');
|
|
69
|
+
const { waitForEvent } = await import('./utils/waitForEvent.js');
|
|
70
70
|
await waitForEvent(registry, managerName);
|
|
71
71
|
config = registry.get(managerName);
|
|
72
72
|
|
|
@@ -224,7 +224,7 @@ export class ManageTemplateListHandler {
|
|
|
224
224
|
if (fragment.childNodes.length > 0) {
|
|
225
225
|
const waitOpt = resolvedParams.waitForSettled;
|
|
226
226
|
if (waitOpt) {
|
|
227
|
-
const { waitForSettled } = await import('../waitForSettled.js');
|
|
227
|
+
const { waitForSettled } = await import('../utils/waitForSettled.js');
|
|
228
228
|
const idleMs = typeof waitOpt === 'object' ? waitOpt.idleMs : 100;
|
|
229
229
|
const timeout = typeof waitOpt === 'object' ? waitOpt.timeout : undefined;
|
|
230
230
|
try {
|
|
@@ -263,7 +263,7 @@ export class ManageTemplateListHandler implements AssignFromHandler {
|
|
|
263
263
|
if (fragment.childNodes.length > 0) {
|
|
264
264
|
const waitOpt = resolvedParams.waitForSettled;
|
|
265
265
|
if (waitOpt) {
|
|
266
|
-
const { waitForSettled } = await import('../waitForSettled.js');
|
|
266
|
+
const { waitForSettled } = await import('../utils/waitForSettled.js');
|
|
267
267
|
const idleMs = typeof waitOpt === 'object' ? waitOpt.idleMs : 100;
|
|
268
268
|
const timeout = typeof waitOpt === 'object' ? waitOpt.timeout : undefined;
|
|
269
269
|
try {
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { assignGingerly } from './assignGingerly.js';
|
|
2
2
|
export { assignTentatively } from './assignTentatively.js';
|
|
3
3
|
export { EnhancementRegistry, ItemscopeRegistry, EnhancementRegisteredEvent } from './assignGingerly.js';
|
|
4
|
-
export { waitForEvent } from './waitForEvent.js';
|
|
4
|
+
export { waitForEvent } from './utils/waitForEvent.js';
|
|
5
5
|
export { ParserRegistry, globalParserRegistry } from './parserRegistry.js';
|
|
6
6
|
export { parseWithAttrs } from './parseWithAttrs.js';
|
|
7
7
|
export { buildCSSQuery } from './buildCSSQuery.js';
|
package/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export {assignGingerly} from './assignGingerly.js';
|
|
2
2
|
export {assignTentatively} from './assignTentatively.js';
|
|
3
3
|
export {EnhancementRegistry, ItemscopeRegistry, EnhancementRegisteredEvent} from './assignGingerly.js';
|
|
4
|
-
export {waitForEvent} from './waitForEvent.js';
|
|
4
|
+
export {waitForEvent} from './utils/waitForEvent.js';
|
|
5
5
|
export {ParserRegistry, globalParserRegistry} from './parserRegistry.js';
|
|
6
6
|
export {parseWithAttrs} from './parseWithAttrs.js';
|
|
7
7
|
export {buildCSSQuery} from './buildCSSQuery.js';
|