assign-gingerly 0.0.75 → 0.0.77
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/installForwarding.js +1 -1
- package/DX/installForwarding.ts +1 -1
- package/README.md +2 -0
- package/assignFeatures.js +10 -31
- package/assignFeatures.ts +15 -39
- package/assignFrom.js +18 -17
- package/assignFrom.ts +23 -23
- package/assignFromAsync.js +14 -13
- package/assignFromAsync.ts +15 -15
- package/assignGingerly.js +87 -48
- package/assignGingerly.ts +177 -131
- package/assignPermissions/PermissionProcessor.js +166 -0
- package/assignPermissions/PermissionProcessor.ts +210 -0
- package/assignPermissions/isAllowedImportPath.js +2 -6
- package/assignPermissions/isAllowedImportPath.ts +3 -5
- package/assignPermissions/isAllowedUrl.js +18 -0
- package/assignPermissions/isAllowedUrl.ts +15 -0
- package/assignTentatively.js +9 -11
- package/assignTentatively.ts +11 -13
- package/beVigilant.js +1 -1
- package/beVigilant.ts +1 -1
- package/buildCSSQuery.js +1 -1
- package/buildCSSQuery.ts +1 -1
- package/eachTime.js +5 -6
- package/eachTime.ts +12 -15
- package/enhanceAll.js +3 -3
- package/enhanceAll.ts +4 -4
- package/evaluatePathWithAsyncMethods.js +12 -8
- package/evaluatePathWithAsyncMethods.ts +129 -117
- package/handlers/addEventListener.js +11 -11
- package/handlers/addEventListener.ts +12 -13
- package/handlers/lazyLoad.ts +7 -7
- package/handlers/lazyLoadSwitch.ts +3 -3
- package/handlers/manageTemplateList.js +10 -10
- package/handlers/manageTemplateList.ts +12 -12
- package/handlers/rangeSelector.ts +3 -3
- package/index.js +2 -3
- package/index.ts +2 -3
- package/inferencer/types/assign-gingerly/types.d.ts +40 -9
- package/inferencer/types/el-maker/types.d.ts +33 -0
- package/inferencer/types/scratch-box/types.d.ts +3 -0
- package/inferencer/types/truth-sourcer/types.d.ts +4 -0
- package/inferencer/types/wc-info/SimpleWCInfo.d.ts +15 -0
- package/package.json +24 -23
- package/parseWithAttrs.js +1 -1
- package/parseWithAttrs.ts +1 -1
- package/processHandlerCommands.js +5 -7
- package/processHandlerCommands.ts +12 -15
- package/{getValues.js → resolve/getValues.js} +19 -8
- package/{getValues.ts → resolve/getValues.ts} +331 -314
- package/{resolveIdRef.js → resolve/resolveIdRef.js} +1 -1
- package/{resolveIdRef.ts → resolve/resolveIdRef.ts} +1 -1
- package/{resolveValues.ts → resolve/resolveValues.ts} +1 -1
- package/types/assign-gingerly/types.d.ts +40 -9
- package/assignPermissions/restrictedProps.js +0 -43
- package/assignPermissions/restrictedProps.ts +0 -53
- package/resolveAndAssignFeatures.js +0 -36
- package/resolveAndAssignFeatures.ts +0 -43
- /package/{resolveTemplate.js → resolve/resolveTemplate.js} +0 -0
- /package/{resolveTemplate.ts → resolve/resolveTemplate.ts} +0 -0
- /package/{resolveValues.js → resolve/resolveValues.js} +0 -0
package/assignGingerly.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import { EnhancementConfig } from "./types/assign-gingerly/types";
|
|
3
|
+
import { EnhancementConfig, EnhKey } from "./types/assign-gingerly/types";
|
|
4
4
|
import type { AssignFromOptions, FeatureConfigsMap, IAssignGingerlyOptions } from "./types/assign-gingerly/types";
|
|
5
|
-
import type {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
import type { PermissionProcessor } from './types/assign-gingerly/types.js';
|
|
6
|
+
import { normalizeAliasOptions } from './resolve/getValues.js';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_ENHANCEMENT_KEY = Symbol('assign-gingerly.default-enhancement-setup');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Constructor signature for ItemScope Manager classes
|
|
@@ -74,6 +74,7 @@ export class EnhancementRegisteredEvent extends Event {
|
|
|
74
74
|
*/
|
|
75
75
|
export class EnhancementRegistry extends EventTarget {
|
|
76
76
|
#items: Set<EnhancementConfig> = new Set();
|
|
77
|
+
#pendingSetups = new Map<EnhKey, Promise<void>[]>();
|
|
77
78
|
|
|
78
79
|
push(items: EnhancementConfig | EnhancementConfig[]): void {
|
|
79
80
|
if (Array.isArray(items)) {
|
|
@@ -85,11 +86,12 @@ export class EnhancementRegistry extends EventTarget {
|
|
|
85
86
|
// Dispatch event after adding items
|
|
86
87
|
this.dispatchEvent(new EnhancementRegisteredEvent(items));
|
|
87
88
|
|
|
88
|
-
// Process features if present (
|
|
89
|
+
// Process features if present (async, tracked so callers can await)
|
|
89
90
|
const itemsArr = Array.isArray(items) ? items : [items];
|
|
90
91
|
for (const item of itemsArr) {
|
|
91
92
|
if (item.features) {
|
|
92
|
-
this.#assignFeatures(item.spawn, item.features);
|
|
93
|
+
const promise = this.#assignFeatures(item.spawn, item.features);
|
|
94
|
+
this._trackSetup(item.enhKey ?? DEFAULT_ENHANCEMENT_KEY, promise);
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
}
|
|
@@ -99,8 +101,42 @@ export class EnhancementRegistry extends EventTarget {
|
|
|
99
101
|
const featuresRegistry = (this as any)._featuresRegistry
|
|
100
102
|
?? (typeof customElements !== 'undefined' ? (customElements as any).featuresRegistry : undefined);
|
|
101
103
|
if (featuresRegistry) {
|
|
102
|
-
assignFeatures(spawn, features, featuresRegistry);
|
|
104
|
+
await assignFeatures(spawn, features, featuresRegistry);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Wait for all pending setups for a given enhancement key to complete.
|
|
110
|
+
* @param enhKey - Enhancement key to wait for
|
|
111
|
+
*/
|
|
112
|
+
async whenDefined(enhKey: EnhKey): Promise<void> {
|
|
113
|
+
const pending = this.#pendingSetups.get(enhKey);
|
|
114
|
+
if (pending && pending.length > 0) {
|
|
115
|
+
await Promise.all(pending);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Internal method to track a pending setup.
|
|
121
|
+
* @param name - Setup key
|
|
122
|
+
* @param promise - Promise representing the setup operation
|
|
123
|
+
*/
|
|
124
|
+
_trackSetup(name: EnhKey, promise: Promise<void>): void {
|
|
125
|
+
if (!this.#pendingSetups.has(name)) {
|
|
126
|
+
this.#pendingSetups.set(name, []);
|
|
103
127
|
}
|
|
128
|
+
this.#pendingSetups.get(name)!.push(promise);
|
|
129
|
+
|
|
130
|
+
// Clean up after completion
|
|
131
|
+
promise.finally(() => {
|
|
132
|
+
const pending = this.#pendingSetups.get(name);
|
|
133
|
+
if (pending) {
|
|
134
|
+
const index = pending.indexOf(promise);
|
|
135
|
+
if (index > -1) {
|
|
136
|
+
pending.splice(index, 1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
104
140
|
}
|
|
105
141
|
|
|
106
142
|
getItems(): EnhancementConfig[] {
|
|
@@ -153,9 +189,10 @@ export class ItemscopeRegistry extends EventTarget {
|
|
|
153
189
|
this.#configs.set(name, config);
|
|
154
190
|
this.dispatchEvent(new Event(name));
|
|
155
191
|
|
|
156
|
-
// Process features if present (
|
|
192
|
+
// Process features if present (async, tracked so callers can await)
|
|
157
193
|
if ((config as any).features) {
|
|
158
|
-
this.#assignFeatures(config.manager, (config as any).features);
|
|
194
|
+
const promise = this.#assignFeatures(config.manager, (config as any).features);
|
|
195
|
+
this._trackSetup(name, promise);
|
|
159
196
|
}
|
|
160
197
|
}
|
|
161
198
|
|
|
@@ -164,7 +201,7 @@ export class ItemscopeRegistry extends EventTarget {
|
|
|
164
201
|
const featuresRegistry = (this as any)._featuresRegistry
|
|
165
202
|
?? (typeof customElements !== 'undefined' ? (customElements as any).featuresRegistry : undefined);
|
|
166
203
|
if (featuresRegistry) {
|
|
167
|
-
assignFeatures(manager, features, featuresRegistry);
|
|
204
|
+
await assignFeatures(manager, features, featuresRegistry);
|
|
168
205
|
}
|
|
169
206
|
}
|
|
170
207
|
|
|
@@ -250,30 +287,30 @@ function isIncCommand(key: string): boolean {
|
|
|
250
287
|
/**
|
|
251
288
|
* Helper function to parse an += command and extract the path
|
|
252
289
|
*/
|
|
253
|
-
function parseIncCommand(key: string): string | null {
|
|
254
|
-
if (!isIncCommand(key)) {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
return key.substring(0, key.length - 3); // Remove ' +=' suffix
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Apply the scalar and array semantics of the += command.
|
|
262
|
-
*/
|
|
263
|
-
function addValue(lhs: any, rhs: any): any {
|
|
264
|
-
if (Array.isArray(lhs)) {
|
|
265
|
-
return Array.isArray(rhs) ? [...lhs, ...rhs] : [...lhs, rhs];
|
|
266
|
-
}
|
|
267
|
-
if (typeof lhs === 'number' && typeof rhs === 'string') {
|
|
268
|
-
const parsed = Number(rhs);
|
|
269
|
-
return Number.isNaN(parsed) ? lhs + rhs : lhs + parsed;
|
|
270
|
-
}
|
|
271
|
-
if (typeof lhs === 'string' && typeof rhs === 'number') {
|
|
272
|
-
const parsed = Number(lhs);
|
|
273
|
-
return Number.isNaN(parsed) ? lhs + rhs : (parsed + rhs).toString();
|
|
274
|
-
}
|
|
275
|
-
return lhs + rhs;
|
|
276
|
-
}
|
|
290
|
+
function parseIncCommand(key: string): string | null {
|
|
291
|
+
if (!isIncCommand(key)) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
return key.substring(0, key.length - 3); // Remove ' +=' suffix
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Apply the scalar and array semantics of the += command.
|
|
299
|
+
*/
|
|
300
|
+
function addValue(lhs: any, rhs: any): any {
|
|
301
|
+
if (Array.isArray(lhs)) {
|
|
302
|
+
return Array.isArray(rhs) ? [...lhs, ...rhs] : [...lhs, rhs];
|
|
303
|
+
}
|
|
304
|
+
if (typeof lhs === 'number' && typeof rhs === 'string') {
|
|
305
|
+
const parsed = Number(rhs);
|
|
306
|
+
return Number.isNaN(parsed) ? lhs + rhs : lhs + parsed;
|
|
307
|
+
}
|
|
308
|
+
if (typeof lhs === 'string' && typeof rhs === 'number') {
|
|
309
|
+
const parsed = Number(lhs);
|
|
310
|
+
return Number.isNaN(parsed) ? lhs + rhs : (parsed + rhs).toString();
|
|
311
|
+
}
|
|
312
|
+
return lhs + rhs;
|
|
313
|
+
}
|
|
277
314
|
|
|
278
315
|
/**
|
|
279
316
|
* Helper function to check if a key represents a =! command
|
|
@@ -444,6 +481,19 @@ export function isClassInstance(value: any): boolean {
|
|
|
444
481
|
return proto !== Object.prototype && proto !== null;
|
|
445
482
|
}
|
|
446
483
|
|
|
484
|
+
/**
|
|
485
|
+
* Check whether a method name is listed in withMethods and is not restricted
|
|
486
|
+
* by the permission processor. Restricted methods are treated as non-method
|
|
487
|
+
* property names so they fall through to normal access.
|
|
488
|
+
*/
|
|
489
|
+
export function isAllowedMethod(
|
|
490
|
+
methodName: string,
|
|
491
|
+
withMethods: Set<string>,
|
|
492
|
+
permissionProcessor?: PermissionProcessor
|
|
493
|
+
): boolean {
|
|
494
|
+
return withMethods.has(methodName) && !permissionProcessor?.checkRestrictedMethod(methodName);
|
|
495
|
+
}
|
|
496
|
+
|
|
447
497
|
/**
|
|
448
498
|
* Helper function to evaluate a nested path with method calls
|
|
449
499
|
* Handles chained method calls where path segments can be methods
|
|
@@ -454,7 +504,8 @@ export function evaluatePathWithMethods(
|
|
|
454
504
|
target: any,
|
|
455
505
|
pathParts: string[],
|
|
456
506
|
value: any,
|
|
457
|
-
withMethods: Set<string
|
|
507
|
+
withMethods: Set<string>,
|
|
508
|
+
permissionProcessor?: PermissionProcessor
|
|
458
509
|
): { target: any; lastKey: string; isMethod: boolean; isZeroArg: boolean } {
|
|
459
510
|
let current = target;
|
|
460
511
|
let i = 0;
|
|
@@ -466,11 +517,11 @@ export function evaluatePathWithMethods(
|
|
|
466
517
|
|
|
467
518
|
// A trailing | marks a zero-argument method call: 'deref|' calls deref()
|
|
468
519
|
// without consuming the next segment. Only applies to names in withMethods.
|
|
469
|
-
const isZeroArgMethod = part.endsWith('|') &&
|
|
520
|
+
const isZeroArgMethod = part.endsWith('|') && isAllowedMethod(part.slice(0, -1), withMethods, permissionProcessor);
|
|
470
521
|
const nextIsMethod = withMethods.has(nextPart)
|
|
471
522
|
|| (nextPart.endsWith('|') && withMethods.has(nextPart.slice(0, -1)));
|
|
472
523
|
|
|
473
|
-
if (
|
|
524
|
+
if (isAllowedMethod(part, withMethods, permissionProcessor) || isZeroArgMethod) {
|
|
474
525
|
const methodName = isZeroArgMethod ? part.slice(0, -1) : part;
|
|
475
526
|
const method = current[methodName];
|
|
476
527
|
if (typeof method === 'function') {
|
|
@@ -503,12 +554,12 @@ export function evaluatePathWithMethods(
|
|
|
503
554
|
// Strip a trailing | from the last segment only when it names a listed method;
|
|
504
555
|
// otherwise it is a literal property name (e.g. an exotic key ending in |).
|
|
505
556
|
const rawLastKey = pathParts[pathParts.length - 1];
|
|
506
|
-
const isZeroArg = rawLastKey.endsWith('|') &&
|
|
557
|
+
const isZeroArg = rawLastKey.endsWith('|') && isAllowedMethod(rawLastKey.slice(0, -1), withMethods, permissionProcessor);
|
|
507
558
|
const lastKey = isZeroArg ? rawLastKey.slice(0, -1) : rawLastKey;
|
|
508
559
|
return {
|
|
509
560
|
target: current,
|
|
510
561
|
lastKey,
|
|
511
|
-
isMethod:
|
|
562
|
+
isMethod: isAllowedMethod(lastKey, withMethods, permissionProcessor),
|
|
512
563
|
isZeroArg
|
|
513
564
|
};
|
|
514
565
|
}
|
|
@@ -568,8 +619,7 @@ function applyToEach(
|
|
|
568
619
|
withMethods: Set<string>,
|
|
569
620
|
aliasMap: Map<string, string>,
|
|
570
621
|
options?: IAssignGingerlyOptions,
|
|
571
|
-
|
|
572
|
-
restrictedPropSet?: RestrictedPropSettingsMap
|
|
622
|
+
permissionProcessor?: PermissionProcessor
|
|
573
623
|
): void {
|
|
574
624
|
// Convert to array for iteration
|
|
575
625
|
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
@@ -594,8 +644,8 @@ function applyToEach(
|
|
|
594
644
|
let current = item;
|
|
595
645
|
for (const part of pathToForEach) {
|
|
596
646
|
// A trailing | marks a zero-argument method call (only for names in withMethods)
|
|
597
|
-
const isZeroArgMethod = part.endsWith('|') &&
|
|
598
|
-
if (
|
|
647
|
+
const isZeroArgMethod = part.endsWith('|') && isAllowedMethod(part.slice(0, -1), withMethods, permissionProcessor);
|
|
648
|
+
if (isAllowedMethod(part, withMethods, permissionProcessor) || isZeroArgMethod) {
|
|
599
649
|
const methodName = isZeroArgMethod ? part.slice(0, -1) : part;
|
|
600
650
|
const method = current[methodName];
|
|
601
651
|
if (typeof method === 'function') {
|
|
@@ -627,11 +677,11 @@ function applyToEach(
|
|
|
627
677
|
|
|
628
678
|
// Recursively apply to the nested iterable
|
|
629
679
|
if (isIterable(current)) {
|
|
630
|
-
applyToEach(current, pathAfterForEach, value, withMethods, aliasMap, options,
|
|
680
|
+
applyToEach(current, pathAfterForEach, value, withMethods, aliasMap, options, permissionProcessor);
|
|
631
681
|
}
|
|
632
682
|
} else {
|
|
633
683
|
// No nested @each, evaluate the remaining path normally
|
|
634
|
-
const result = evaluatePathWithMethods(item, remainingPath, value, withMethods);
|
|
684
|
+
const result = evaluatePathWithMethods(item, remainingPath, value, withMethods, permissionProcessor);
|
|
635
685
|
|
|
636
686
|
if (result.isMethod) {
|
|
637
687
|
// Last segment is a method - call it
|
|
@@ -651,7 +701,7 @@ function applyToEach(
|
|
|
651
701
|
const lastKey = result.lastKey;
|
|
652
702
|
const parent = result.target;
|
|
653
703
|
|
|
654
|
-
if (redirectRestrictedProp(
|
|
704
|
+
if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
|
|
655
705
|
// skip
|
|
656
706
|
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
657
707
|
if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
|
|
@@ -659,7 +709,7 @@ function applyToEach(
|
|
|
659
709
|
if (typeof currentValue !== 'object' || currentValue === null) {
|
|
660
710
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
661
711
|
}
|
|
662
|
-
assignGingerly(currentValue, value, options,
|
|
712
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
663
713
|
} else {
|
|
664
714
|
parent[lastKey] = value;
|
|
665
715
|
}
|
|
@@ -702,7 +752,7 @@ export function assignGingerly(
|
|
|
702
752
|
target: any,
|
|
703
753
|
source: Record<string | symbol, any>,
|
|
704
754
|
options?: IAssignGingerlyOptions,
|
|
705
|
-
|
|
755
|
+
permissionProcessor?: PermissionProcessor
|
|
706
756
|
): any {
|
|
707
757
|
if (!target || typeof target !== 'object') {
|
|
708
758
|
return target;
|
|
@@ -710,9 +760,6 @@ export function assignGingerly(
|
|
|
710
760
|
|
|
711
761
|
const { aliasMap, withMethods: withMethodsSet } = normalizeAliasOptions(options);
|
|
712
762
|
|
|
713
|
-
// Normalize restrictedPropSettings once per top-level call
|
|
714
|
-
const restrictedPropSet = buildRestrictedPropSet(permissions);
|
|
715
|
-
|
|
716
763
|
// Convert withAsyncMethods array to Set for O(1) lookup
|
|
717
764
|
const withAsyncMethodsSet = options?.withAsyncMethods
|
|
718
765
|
? options.withAsyncMethods instanceof Set
|
|
@@ -800,7 +847,7 @@ export function assignGingerly(
|
|
|
800
847
|
let lhsParent: any;
|
|
801
848
|
let lhsKey: string;
|
|
802
849
|
if (withMethodsSet) {
|
|
803
|
-
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet);
|
|
850
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet, permissionProcessor);
|
|
804
851
|
lhsParent = result.target;
|
|
805
852
|
lhsKey = result.lastKey;
|
|
806
853
|
lhsValue = lhsParent[lhsKey];
|
|
@@ -811,49 +858,49 @@ export function assignGingerly(
|
|
|
811
858
|
}
|
|
812
859
|
|
|
813
860
|
//TODO: this logic seems to occur twice at least. Maybe make it a method?
|
|
814
|
-
if (checkRestrictedProp(
|
|
815
|
-
continue;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// Event handler: Element LHS + object RHS with 'on' property
|
|
861
|
+
if (permissionProcessor?.checkRestrictedProp(lhsKey)) {
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Event handler: Element LHS + object RHS with 'on' property
|
|
819
866
|
if (lhsValue instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
820
867
|
const capturedLhs = lhsValue;
|
|
821
868
|
const capturedValue = value;
|
|
822
869
|
const capturedTarget = target;
|
|
823
|
-
const capturedOptions = options as AssignFromOptions | undefined;
|
|
824
|
-
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
825
|
-
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {},
|
|
870
|
+
const capturedOptions = options as AssignFromOptions | undefined;
|
|
871
|
+
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
872
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {}, permissionProcessor);
|
|
826
873
|
});
|
|
827
874
|
continue;
|
|
828
875
|
}
|
|
829
876
|
|
|
830
|
-
if (!(lhsKey in lhsParent)) {
|
|
831
|
-
lhsParent[lhsKey] = value;
|
|
832
|
-
} else {
|
|
833
|
-
lhsParent[lhsKey] = addValue(lhsValue, value);
|
|
877
|
+
if (!(lhsKey in lhsParent)) {
|
|
878
|
+
lhsParent[lhsKey] = value;
|
|
879
|
+
} else {
|
|
880
|
+
lhsParent[lhsKey] = addValue(lhsValue, value);
|
|
834
881
|
}
|
|
835
882
|
} else {
|
|
836
|
-
// Plain key - direct operation on target
|
|
837
|
-
if (checkRestrictedProp(
|
|
838
|
-
continue;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// Event handler: Element LHS + object RHS with 'on' property
|
|
883
|
+
// Plain key - direct operation on target
|
|
884
|
+
if (permissionProcessor?.checkRestrictedProp(path)) {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Event handler: Element LHS + object RHS with 'on' property
|
|
842
889
|
if (target[path] instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
843
890
|
const capturedLhs = target[path];
|
|
844
891
|
const capturedValue = value;
|
|
845
892
|
const capturedTarget = target;
|
|
846
|
-
const capturedOptions = options as AssignFromOptions | undefined;
|
|
847
|
-
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
848
|
-
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {},
|
|
893
|
+
const capturedOptions = options as AssignFromOptions | undefined;
|
|
894
|
+
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
895
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {}, permissionProcessor);
|
|
849
896
|
});
|
|
850
897
|
continue;
|
|
851
898
|
}
|
|
852
899
|
|
|
853
|
-
if (!(path in target)) {
|
|
854
|
-
target[path] = value;
|
|
855
|
-
} else {
|
|
856
|
-
target[path] = addValue(target[path], value);
|
|
900
|
+
if (!(path in target)) {
|
|
901
|
+
target[path] = value;
|
|
902
|
+
} else {
|
|
903
|
+
target[path] = addValue(target[path], value);
|
|
857
904
|
}
|
|
858
905
|
}
|
|
859
906
|
}
|
|
@@ -909,7 +956,7 @@ export function assignGingerly(
|
|
|
909
956
|
}
|
|
910
957
|
|
|
911
958
|
// Apply negation to LHS — check restriction first
|
|
912
|
-
if (!checkRestrictedProp(
|
|
959
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
913
960
|
lhsParent[lhsLastKey] = !valueToNegate;
|
|
914
961
|
}
|
|
915
962
|
}
|
|
@@ -961,19 +1008,19 @@ export function assignGingerly(
|
|
|
961
1008
|
}
|
|
962
1009
|
|
|
963
1010
|
// Handle Y= merge commands (recursive assignGingerly into sub-object)
|
|
964
|
-
if (isMergeCommand(key)) {
|
|
965
|
-
const path = parseMergeCommand(key);
|
|
966
|
-
if (path) {
|
|
967
|
-
const pathParts = isNestedPath(path) ? parsePath(path) : [path];
|
|
968
|
-
const lastKey = pathParts[pathParts.length - 1];
|
|
969
|
-
if (checkRestrictedProp(
|
|
970
|
-
continue;
|
|
971
|
-
}
|
|
972
|
-
// Navigate to the target sub-object
|
|
973
|
-
let mergeTarget: any;
|
|
1011
|
+
if (isMergeCommand(key)) {
|
|
1012
|
+
const path = parseMergeCommand(key);
|
|
1013
|
+
if (path) {
|
|
1014
|
+
const pathParts = isNestedPath(path) ? parsePath(path) : [path];
|
|
1015
|
+
const lastKey = pathParts[pathParts.length - 1];
|
|
1016
|
+
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
// Navigate to the target sub-object
|
|
1020
|
+
let mergeTarget: any;
|
|
974
1021
|
if (isNestedPath(path)) {
|
|
975
1022
|
if (withMethodsSet) {
|
|
976
|
-
const result = evaluatePathWithMethods(target, parsePath(path), value, withMethodsSet);
|
|
1023
|
+
const result = evaluatePathWithMethods(target, parsePath(path), value, withMethodsSet, permissionProcessor);
|
|
977
1024
|
mergeTarget = result.target[result.lastKey];
|
|
978
1025
|
} else {
|
|
979
1026
|
const pathParts = parsePath(path);
|
|
@@ -993,7 +1040,7 @@ export function assignGingerly(
|
|
|
993
1040
|
|
|
994
1041
|
// Recursively merge if target is a valid object
|
|
995
1042
|
if (mergeTarget && typeof mergeTarget === 'object') {
|
|
996
|
-
assignGingerly(mergeTarget, value, options,
|
|
1043
|
+
assignGingerly(mergeTarget, value, options, permissionProcessor);
|
|
997
1044
|
}
|
|
998
1045
|
}
|
|
999
1046
|
continue;
|
|
@@ -1021,11 +1068,10 @@ export function assignGingerly(
|
|
|
1021
1068
|
pathParts,
|
|
1022
1069
|
forEachIndex,
|
|
1023
1070
|
value,
|
|
1024
|
-
withMethodsSet,
|
|
1025
|
-
aliasMap,
|
|
1026
|
-
options,
|
|
1027
|
-
|
|
1028
|
-
restrictedPropSet
|
|
1071
|
+
withMethodsSet,
|
|
1072
|
+
aliasMap,
|
|
1073
|
+
options,
|
|
1074
|
+
permissionProcessor
|
|
1029
1075
|
);
|
|
1030
1076
|
} catch (error) {
|
|
1031
1077
|
console.error('Error in @eachTime:', error);
|
|
@@ -1042,7 +1088,7 @@ export function assignGingerly(
|
|
|
1042
1088
|
let current = target;
|
|
1043
1089
|
if (pathToForEach.length > 0) {
|
|
1044
1090
|
if (withMethodsSet) {
|
|
1045
|
-
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet);
|
|
1091
|
+
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
1046
1092
|
// The result.target is the current position after evaluating the path
|
|
1047
1093
|
// This is already the iterable we want
|
|
1048
1094
|
current = result.target;
|
|
@@ -1055,7 +1101,7 @@ export function assignGingerly(
|
|
|
1055
1101
|
|
|
1056
1102
|
// Apply to each item in the iterable
|
|
1057
1103
|
if (isIterable(current)) {
|
|
1058
|
-
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options,
|
|
1104
|
+
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options, permissionProcessor);
|
|
1059
1105
|
}
|
|
1060
1106
|
// If not iterable, let JavaScript throw error naturally when trying to iterate
|
|
1061
1107
|
|
|
@@ -1069,15 +1115,15 @@ export function assignGingerly(
|
|
|
1069
1115
|
const capturedTarget = target;
|
|
1070
1116
|
const capturedPathParts = pathParts;
|
|
1071
1117
|
const capturedValue = value;
|
|
1072
|
-
const capturedWithMethodsSet = withMethodsSet || new Set<string>();
|
|
1073
|
-
const capturedOptions = options;
|
|
1074
|
-
const
|
|
1075
|
-
const capturedRestrictedPropSet = restrictedPropSet;
|
|
1118
|
+
const capturedWithMethodsSet = withMethodsSet || new Set<string>();
|
|
1119
|
+
const capturedOptions = options;
|
|
1120
|
+
const capturedPermissionProcessor = permissionProcessor;
|
|
1076
1121
|
(async () => {
|
|
1077
1122
|
const { evaluatePathWithAsyncMethods } = await import('./evaluatePathWithAsyncMethods.js');
|
|
1078
1123
|
const result = await evaluatePathWithAsyncMethods(
|
|
1079
1124
|
capturedTarget, capturedPathParts, capturedValue,
|
|
1080
|
-
capturedWithMethodsSet, withAsyncMethodsSet
|
|
1125
|
+
capturedWithMethodsSet, withAsyncMethodsSet,
|
|
1126
|
+
capturedPermissionProcessor
|
|
1081
1127
|
);
|
|
1082
1128
|
|
|
1083
1129
|
if (result.isMethod || result.isAsyncMethod) {
|
|
@@ -1096,16 +1142,16 @@ export function assignGingerly(
|
|
|
1096
1142
|
} else {
|
|
1097
1143
|
// Not a method — assign the value
|
|
1098
1144
|
const lastKey = result.lastKey;
|
|
1099
|
-
const parent = result.target;
|
|
1100
|
-
if (redirectRestrictedProp(
|
|
1101
|
-
// skip
|
|
1102
|
-
} else if (typeof capturedValue === 'object' && capturedValue !== null && !Array.isArray(capturedValue)) {
|
|
1145
|
+
const parent = result.target;
|
|
1146
|
+
if (capturedPermissionProcessor?.redirectRestrictedProp(parent, lastKey, capturedValue)) {
|
|
1147
|
+
// skip
|
|
1148
|
+
} else if (typeof capturedValue === 'object' && capturedValue !== null && !Array.isArray(capturedValue)) {
|
|
1103
1149
|
if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
|
|
1104
1150
|
const currentValue = parent[lastKey];
|
|
1105
1151
|
if (typeof currentValue !== 'object' || currentValue === null) {
|
|
1106
1152
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
1107
1153
|
}
|
|
1108
|
-
assignGingerly(currentValue, capturedValue, capturedOptions,
|
|
1154
|
+
assignGingerly(currentValue, capturedValue, capturedOptions, capturedPermissionProcessor);
|
|
1109
1155
|
} else {
|
|
1110
1156
|
parent[lastKey] = capturedValue;
|
|
1111
1157
|
}
|
|
@@ -1119,7 +1165,7 @@ export function assignGingerly(
|
|
|
1119
1165
|
|
|
1120
1166
|
// Check if we need to handle methods
|
|
1121
1167
|
if (withMethodsSet) {
|
|
1122
|
-
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet);
|
|
1168
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet, permissionProcessor);
|
|
1123
1169
|
|
|
1124
1170
|
if (result.isMethod) {
|
|
1125
1171
|
// Last segment is a method - call it
|
|
@@ -1142,19 +1188,19 @@ export function assignGingerly(
|
|
|
1142
1188
|
const lastKey = result.lastKey;
|
|
1143
1189
|
const parent = result.target;
|
|
1144
1190
|
|
|
1145
|
-
if (redirectRestrictedProp(
|
|
1146
|
-
// skip
|
|
1147
|
-
// Check for static assignTo protocol
|
|
1148
|
-
} else if (lastKey in parent && tryAssignTo(parent[lastKey], value, parent, lastKey)) {
|
|
1149
|
-
continue;
|
|
1150
|
-
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1191
|
+
if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
|
|
1192
|
+
// skip
|
|
1193
|
+
// Check for static assignTo protocol
|
|
1194
|
+
} else if (lastKey in parent && tryAssignTo(parent[lastKey], value, parent, lastKey)) {
|
|
1195
|
+
continue;
|
|
1196
|
+
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1151
1197
|
// Check if property exists and is readonly
|
|
1152
1198
|
if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
|
|
1153
1199
|
const currentValue = parent[lastKey];
|
|
1154
1200
|
if (typeof currentValue !== 'object' || currentValue === null) {
|
|
1155
1201
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
1156
1202
|
}
|
|
1157
|
-
assignGingerly(currentValue, value, options,
|
|
1203
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
1158
1204
|
} else {
|
|
1159
1205
|
// Property is writable - replace it
|
|
1160
1206
|
parent[lastKey] = value;
|
|
@@ -1167,12 +1213,12 @@ export function assignGingerly(
|
|
|
1167
1213
|
const lastKey = pathParts[pathParts.length - 1];
|
|
1168
1214
|
const parent = ensureNestedPath(target, pathParts);
|
|
1169
1215
|
|
|
1170
|
-
if (redirectRestrictedProp(
|
|
1171
|
-
// skip
|
|
1172
|
-
// Check for static assignTo protocol
|
|
1173
|
-
} else if (lastKey in parent && tryAssignTo(parent[lastKey], value, parent, lastKey)) {
|
|
1174
|
-
continue;
|
|
1175
|
-
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1216
|
+
if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
|
|
1217
|
+
// skip
|
|
1218
|
+
// Check for static assignTo protocol
|
|
1219
|
+
} else if (lastKey in parent && tryAssignTo(parent[lastKey], value, parent, lastKey)) {
|
|
1220
|
+
continue;
|
|
1221
|
+
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1176
1222
|
// Check if property exists and is readonly
|
|
1177
1223
|
if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
|
|
1178
1224
|
// Property is readonly - check if current value is an object
|
|
@@ -1181,7 +1227,7 @@ export function assignGingerly(
|
|
|
1181
1227
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
1182
1228
|
}
|
|
1183
1229
|
// Recursively apply assignGingerly to the readonly object
|
|
1184
|
-
assignGingerly(currentValue, value, options,
|
|
1230
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
1185
1231
|
} else {
|
|
1186
1232
|
// Property is writable - replace it
|
|
1187
1233
|
parent[lastKey] = value;
|
|
@@ -1212,12 +1258,12 @@ export function assignGingerly(
|
|
|
1212
1258
|
continue;
|
|
1213
1259
|
}
|
|
1214
1260
|
|
|
1215
|
-
if (redirectRestrictedProp(
|
|
1216
|
-
// skip
|
|
1217
|
-
// Check for static assignTo protocol
|
|
1218
|
-
} else if (key in target && tryAssignTo(target[key], value, target, key)) {
|
|
1219
|
-
continue;
|
|
1220
|
-
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1261
|
+
if (permissionProcessor?.redirectRestrictedProp(target, key, value)) {
|
|
1262
|
+
// skip
|
|
1263
|
+
// Check for static assignTo protocol
|
|
1264
|
+
} else if (key in target && tryAssignTo(target[key], value, target, key)) {
|
|
1265
|
+
continue;
|
|
1266
|
+
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1221
1267
|
// Check if property exists and is readonly
|
|
1222
1268
|
if (key in target && isReadonlyProperty(target, key)) {
|
|
1223
1269
|
// Property is readonly - check if current value is an object
|
|
@@ -1226,7 +1272,7 @@ export function assignGingerly(
|
|
|
1226
1272
|
throw new Error(`Cannot merge object into readonly primitive property '${String(key)}'`);
|
|
1227
1273
|
}
|
|
1228
1274
|
// Recursively apply assignGingerly to the readonly object
|
|
1229
|
-
assignGingerly(currentValue, value, options,
|
|
1275
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
1230
1276
|
} else {
|
|
1231
1277
|
// Property is writable - replace it
|
|
1232
1278
|
target[key] = value;
|
|
@@ -1370,7 +1416,7 @@ export function assignGingerly(
|
|
|
1370
1416
|
// Fire-and-forget bulk enhancements (async, non-blocking)
|
|
1371
1417
|
if (options?.enhance && options.enhance.length > 0 && typeof target === 'object' && target instanceof Element) {
|
|
1372
1418
|
import('./enhanceAll.js').then(({ enhanceAll }) => {
|
|
1373
|
-
enhanceAll(target, options!.enhance!,
|
|
1419
|
+
enhanceAll(target, options!.enhance!, permissionProcessor);
|
|
1374
1420
|
});
|
|
1375
1421
|
}
|
|
1376
1422
|
|