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/assignFromAsync.ts
CHANGED
|
@@ -19,11 +19,10 @@
|
|
|
19
19
|
* }, { from: source });
|
|
20
20
|
* // target is now { color: 'red', text: 'Hello' }
|
|
21
21
|
*/
|
|
22
|
-
import { resolveValues } from './resolveValues.js';
|
|
22
|
+
import { resolveValues } from './resolve/resolveValues.js';
|
|
23
23
|
import {IAssignGingerlyOptions} from './types/assign-gingerly/types.js';
|
|
24
24
|
import assignGingerly from './assignGingerly.js';
|
|
25
|
-
import type {
|
|
26
|
-
import type { AssignFromHandler, AssignFromHandlerConstructor } from './types/assign-gingerly/types.js';
|
|
25
|
+
import type { PermissionProcessor, AssignFromHandler, AssignFromHandlerConstructor } from './types/assign-gingerly/types.js';
|
|
27
26
|
import {
|
|
28
27
|
expandSubstitutions, categorizeKeys, handleSpreads, isHandlerCommand
|
|
29
28
|
} from './assignFrom.js';
|
|
@@ -56,7 +55,7 @@ export async function assignFromAsync(
|
|
|
56
55
|
target: any,
|
|
57
56
|
pattern: Record<string, any>,
|
|
58
57
|
options: AssignFromOptions,
|
|
59
|
-
|
|
58
|
+
permissionProcessor?: PermissionProcessor
|
|
60
59
|
): Promise<any> {
|
|
61
60
|
// First: expand looped substitution variables (${x}, ${y}, ${z})
|
|
62
61
|
const expandedPattern = expandSubstitutions(pattern, options);
|
|
@@ -71,19 +70,20 @@ export async function assignFromAsync(
|
|
|
71
70
|
aka: options.aka,
|
|
72
71
|
akaMethods: options.akaMethods,
|
|
73
72
|
protocols: options.protocols,
|
|
74
|
-
root: target
|
|
73
|
+
root: target,
|
|
74
|
+
permissionProcessor
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
// Recursively handle "..." spread keys at all nesting levels
|
|
78
78
|
handleSpreads(resolved);
|
|
79
79
|
|
|
80
|
-
assignGingerly(target, resolved, options,
|
|
80
|
+
assignGingerly(target, resolved, options, permissionProcessor);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Process #[x] normal keys — resolve element, then apply remaining path + value
|
|
84
84
|
if (idRefNormalKeys.length > 0 && (options.pin || options.at)) {
|
|
85
85
|
const ids = { ...options.pin, ...options.at };
|
|
86
|
-
const { resolveIdVariable, parseIdRef } = await import('./resolveIdRef.js');
|
|
86
|
+
const { resolveIdVariable, parseIdRef } = await import('./resolve/resolveIdRef.js');
|
|
87
87
|
const { withMethods, aka, akaMethods, protocols, from } = options;
|
|
88
88
|
for (const key of idRefNormalKeys) {
|
|
89
89
|
const parsed = parseIdRef(key);
|
|
@@ -97,21 +97,21 @@ export async function assignFromAsync(
|
|
|
97
97
|
// Resolve the RHS value
|
|
98
98
|
const resolvedValue = await resolveValues(
|
|
99
99
|
{ __v: value }, from,
|
|
100
|
-
{ withMethods, aka, akaMethods, protocols, root: el }
|
|
100
|
+
{ withMethods, aka, akaMethods, protocols, root: el, permissionProcessor }
|
|
101
101
|
);
|
|
102
102
|
// Apply remaining path on the resolved element
|
|
103
|
-
assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options,
|
|
103
|
+
assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
|
|
104
104
|
} else {
|
|
105
105
|
// No remaining path — resolve and assign directly to the element
|
|
106
106
|
const resolvedValue = await resolveValues(
|
|
107
107
|
typeof value === 'object' && value !== null ? value : { __v: value },
|
|
108
108
|
from,
|
|
109
|
-
{ withMethods, aka, akaMethods, protocols, root: el }
|
|
109
|
+
{ withMethods, aka, akaMethods, protocols, root: el, permissionProcessor }
|
|
110
110
|
);
|
|
111
111
|
if ('__v' in resolvedValue) {
|
|
112
112
|
// Single value — can't assign to element root without a path
|
|
113
113
|
} else {
|
|
114
|
-
assignGingerly(el, resolvedValue, options,
|
|
114
|
+
assignGingerly(el, resolvedValue, options, permissionProcessor);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -120,12 +120,12 @@ export async function assignFromAsync(
|
|
|
120
120
|
if (handlerKeys.length > 0) {
|
|
121
121
|
|
|
122
122
|
_processHandlerCommands ??= (await import('./processHandlerCommands.js')).processHandlerCommands;
|
|
123
|
-
await _processHandlerCommands(target, handlerKeys, expandedPattern, options,
|
|
123
|
+
await _processHandlerCommands(target, handlerKeys, expandedPattern, options, permissionProcessor);
|
|
124
124
|
}
|
|
125
125
|
// Process #[x] handler keys — resolve element, then pass to handler processing
|
|
126
126
|
if (idRefHandlerKeys.length > 0 && (options.pin || options.at)) {
|
|
127
127
|
const ids = { ...options.pin, ...options.at };
|
|
128
|
-
const { resolveIdVariable, parseIdRef } = await import('./resolveIdRef.js');
|
|
128
|
+
const { resolveIdVariable, parseIdRef } = await import('./resolve/resolveIdRef.js');
|
|
129
129
|
_processHandlerCommands ??= (await import('./processHandlerCommands.js')).processHandlerCommands;
|
|
130
130
|
|
|
131
131
|
for (const key of idRefHandlerKeys) {
|
|
@@ -145,7 +145,7 @@ export async function assignFromAsync(
|
|
|
145
145
|
[syntheticKey]: expandedPattern[key]
|
|
146
146
|
};
|
|
147
147
|
|
|
148
|
-
await _processHandlerCommands(el, [syntheticKey], syntheticPattern, options,
|
|
148
|
+
await _processHandlerCommands(el, [syntheticKey], syntheticPattern, options, permissionProcessor);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
@@ -167,7 +167,7 @@ export async function assignFromAsync(
|
|
|
167
167
|
// Process bulk enhancements — dynamically imported only when option is present
|
|
168
168
|
if (options.enhance && options.enhance.length > 0) {
|
|
169
169
|
const { enhanceAll } = await import('./enhanceAll.js');
|
|
170
|
-
await enhanceAll(target, options.enhance,
|
|
170
|
+
await enhanceAll(target, options.enhance, permissionProcessor);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
return target;
|
package/assignGingerly.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { normalizeAliasOptions } from './resolve/getValues.js';
|
|
2
|
+
const DEFAULT_ENHANCEMENT_KEY = Symbol('assign-gingerly.default-enhancement-setup');
|
|
3
3
|
/**
|
|
4
4
|
* GUID for global instance map storage to ensure uniqueness across package versions
|
|
5
5
|
*/
|
|
@@ -35,6 +35,7 @@ export class EnhancementRegisteredEvent extends Event {
|
|
|
35
35
|
*/
|
|
36
36
|
export class EnhancementRegistry extends EventTarget {
|
|
37
37
|
#items = new Set();
|
|
38
|
+
#pendingSetups = new Map();
|
|
38
39
|
push(items) {
|
|
39
40
|
if (Array.isArray(items)) {
|
|
40
41
|
items.forEach(item => this.#items.add(item));
|
|
@@ -44,11 +45,12 @@ export class EnhancementRegistry extends EventTarget {
|
|
|
44
45
|
}
|
|
45
46
|
// Dispatch event after adding items
|
|
46
47
|
this.dispatchEvent(new EnhancementRegisteredEvent(items));
|
|
47
|
-
// Process features if present (
|
|
48
|
+
// Process features if present (async, tracked so callers can await)
|
|
48
49
|
const itemsArr = Array.isArray(items) ? items : [items];
|
|
49
50
|
for (const item of itemsArr) {
|
|
50
51
|
if (item.features) {
|
|
51
|
-
this.#assignFeatures(item.spawn, item.features);
|
|
52
|
+
const promise = this.#assignFeatures(item.spawn, item.features);
|
|
53
|
+
this._trackSetup(item.enhKey ?? DEFAULT_ENHANCEMENT_KEY, promise);
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -57,9 +59,40 @@ export class EnhancementRegistry extends EventTarget {
|
|
|
57
59
|
const featuresRegistry = this._featuresRegistry
|
|
58
60
|
?? (typeof customElements !== 'undefined' ? customElements.featuresRegistry : undefined);
|
|
59
61
|
if (featuresRegistry) {
|
|
60
|
-
assignFeatures(spawn, features, featuresRegistry);
|
|
62
|
+
await assignFeatures(spawn, features, featuresRegistry);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Wait for all pending setups for a given enhancement key to complete.
|
|
67
|
+
* @param enhKey - Enhancement key to wait for
|
|
68
|
+
*/
|
|
69
|
+
async whenDefined(enhKey) {
|
|
70
|
+
const pending = this.#pendingSetups.get(enhKey);
|
|
71
|
+
if (pending && pending.length > 0) {
|
|
72
|
+
await Promise.all(pending);
|
|
61
73
|
}
|
|
62
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Internal method to track a pending setup.
|
|
77
|
+
* @param name - Setup key
|
|
78
|
+
* @param promise - Promise representing the setup operation
|
|
79
|
+
*/
|
|
80
|
+
_trackSetup(name, promise) {
|
|
81
|
+
if (!this.#pendingSetups.has(name)) {
|
|
82
|
+
this.#pendingSetups.set(name, []);
|
|
83
|
+
}
|
|
84
|
+
this.#pendingSetups.get(name).push(promise);
|
|
85
|
+
// Clean up after completion
|
|
86
|
+
promise.finally(() => {
|
|
87
|
+
const pending = this.#pendingSetups.get(name);
|
|
88
|
+
if (pending) {
|
|
89
|
+
const index = pending.indexOf(promise);
|
|
90
|
+
if (index > -1) {
|
|
91
|
+
pending.splice(index, 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
63
96
|
getItems() {
|
|
64
97
|
return Array.from(this.#items);
|
|
65
98
|
}
|
|
@@ -106,9 +139,10 @@ export class ItemscopeRegistry extends EventTarget {
|
|
|
106
139
|
}
|
|
107
140
|
this.#configs.set(name, config);
|
|
108
141
|
this.dispatchEvent(new Event(name));
|
|
109
|
-
// Process features if present (
|
|
142
|
+
// Process features if present (async, tracked so callers can await)
|
|
110
143
|
if (config.features) {
|
|
111
|
-
this.#assignFeatures(config.manager, config.features);
|
|
144
|
+
const promise = this.#assignFeatures(config.manager, config.features);
|
|
145
|
+
this._trackSetup(name, promise);
|
|
112
146
|
}
|
|
113
147
|
}
|
|
114
148
|
async #assignFeatures(manager, features) {
|
|
@@ -116,7 +150,7 @@ export class ItemscopeRegistry extends EventTarget {
|
|
|
116
150
|
const featuresRegistry = this._featuresRegistry
|
|
117
151
|
?? (typeof customElements !== 'undefined' ? customElements.featuresRegistry : undefined);
|
|
118
152
|
if (featuresRegistry) {
|
|
119
|
-
assignFeatures(manager, features, featuresRegistry);
|
|
153
|
+
await assignFeatures(manager, features, featuresRegistry);
|
|
120
154
|
}
|
|
121
155
|
}
|
|
122
156
|
/**
|
|
@@ -369,13 +403,21 @@ export function isClassInstance(value) {
|
|
|
369
403
|
// Plain objects have Object.prototype or null as prototype
|
|
370
404
|
return proto !== Object.prototype && proto !== null;
|
|
371
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Check whether a method name is listed in withMethods and is not restricted
|
|
408
|
+
* by the permission processor. Restricted methods are treated as non-method
|
|
409
|
+
* property names so they fall through to normal access.
|
|
410
|
+
*/
|
|
411
|
+
export function isAllowedMethod(methodName, withMethods, permissionProcessor) {
|
|
412
|
+
return withMethods.has(methodName) && !permissionProcessor?.checkRestrictedMethod(methodName);
|
|
413
|
+
}
|
|
372
414
|
/**
|
|
373
415
|
* Helper function to evaluate a nested path with method calls
|
|
374
416
|
* Handles chained method calls where path segments can be methods
|
|
375
417
|
*
|
|
376
418
|
* Exported for use by eachTime.ts
|
|
377
419
|
*/
|
|
378
|
-
export function evaluatePathWithMethods(target, pathParts, value, withMethods) {
|
|
420
|
+
export function evaluatePathWithMethods(target, pathParts, value, withMethods, permissionProcessor) {
|
|
379
421
|
let current = target;
|
|
380
422
|
let i = 0;
|
|
381
423
|
// Process all segments except the last one
|
|
@@ -384,10 +426,10 @@ export function evaluatePathWithMethods(target, pathParts, value, withMethods) {
|
|
|
384
426
|
const nextPart = pathParts[i + 1];
|
|
385
427
|
// A trailing | marks a zero-argument method call: 'deref|' calls deref()
|
|
386
428
|
// without consuming the next segment. Only applies to names in withMethods.
|
|
387
|
-
const isZeroArgMethod = part.endsWith('|') &&
|
|
429
|
+
const isZeroArgMethod = part.endsWith('|') && isAllowedMethod(part.slice(0, -1), withMethods, permissionProcessor);
|
|
388
430
|
const nextIsMethod = withMethods.has(nextPart)
|
|
389
431
|
|| (nextPart.endsWith('|') && withMethods.has(nextPart.slice(0, -1)));
|
|
390
|
-
if (
|
|
432
|
+
if (isAllowedMethod(part, withMethods, permissionProcessor) || isZeroArgMethod) {
|
|
391
433
|
const methodName = isZeroArgMethod ? part.slice(0, -1) : part;
|
|
392
434
|
const method = current[methodName];
|
|
393
435
|
if (typeof method === 'function') {
|
|
@@ -421,12 +463,12 @@ export function evaluatePathWithMethods(target, pathParts, value, withMethods) {
|
|
|
421
463
|
// Strip a trailing | from the last segment only when it names a listed method;
|
|
422
464
|
// otherwise it is a literal property name (e.g. an exotic key ending in |).
|
|
423
465
|
const rawLastKey = pathParts[pathParts.length - 1];
|
|
424
|
-
const isZeroArg = rawLastKey.endsWith('|') &&
|
|
466
|
+
const isZeroArg = rawLastKey.endsWith('|') && isAllowedMethod(rawLastKey.slice(0, -1), withMethods, permissionProcessor);
|
|
425
467
|
const lastKey = isZeroArg ? rawLastKey.slice(0, -1) : rawLastKey;
|
|
426
468
|
return {
|
|
427
469
|
target: current,
|
|
428
470
|
lastKey,
|
|
429
|
-
isMethod:
|
|
471
|
+
isMethod: isAllowedMethod(lastKey, withMethods, permissionProcessor),
|
|
430
472
|
isZeroArg
|
|
431
473
|
};
|
|
432
474
|
}
|
|
@@ -474,7 +516,7 @@ function isReactiveForEachSymbol(segment, aliasMap) {
|
|
|
474
516
|
/**
|
|
475
517
|
* Apply a path to each item in an iterable
|
|
476
518
|
*/
|
|
477
|
-
function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, options,
|
|
519
|
+
function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, options, permissionProcessor) {
|
|
478
520
|
// Convert to array for iteration
|
|
479
521
|
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
480
522
|
// Apply the remaining path to each item
|
|
@@ -494,8 +536,8 @@ function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, opti
|
|
|
494
536
|
let current = item;
|
|
495
537
|
for (const part of pathToForEach) {
|
|
496
538
|
// A trailing | marks a zero-argument method call (only for names in withMethods)
|
|
497
|
-
const isZeroArgMethod = part.endsWith('|') &&
|
|
498
|
-
if (
|
|
539
|
+
const isZeroArgMethod = part.endsWith('|') && isAllowedMethod(part.slice(0, -1), withMethods, permissionProcessor);
|
|
540
|
+
if (isAllowedMethod(part, withMethods, permissionProcessor) || isZeroArgMethod) {
|
|
499
541
|
const methodName = isZeroArgMethod ? part.slice(0, -1) : part;
|
|
500
542
|
const method = current[methodName];
|
|
501
543
|
if (typeof method === 'function') {
|
|
@@ -530,12 +572,12 @@ function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, opti
|
|
|
530
572
|
}
|
|
531
573
|
// Recursively apply to the nested iterable
|
|
532
574
|
if (isIterable(current)) {
|
|
533
|
-
applyToEach(current, pathAfterForEach, value, withMethods, aliasMap, options,
|
|
575
|
+
applyToEach(current, pathAfterForEach, value, withMethods, aliasMap, options, permissionProcessor);
|
|
534
576
|
}
|
|
535
577
|
}
|
|
536
578
|
else {
|
|
537
579
|
// No nested @each, evaluate the remaining path normally
|
|
538
|
-
const result = evaluatePathWithMethods(item, remainingPath, value, withMethods);
|
|
580
|
+
const result = evaluatePathWithMethods(item, remainingPath, value, withMethods, permissionProcessor);
|
|
539
581
|
if (result.isMethod) {
|
|
540
582
|
// Last segment is a method - call it
|
|
541
583
|
const method = result.target[result.lastKey];
|
|
@@ -556,7 +598,7 @@ function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, opti
|
|
|
556
598
|
// Normal assignment
|
|
557
599
|
const lastKey = result.lastKey;
|
|
558
600
|
const parent = result.target;
|
|
559
|
-
if (redirectRestrictedProp(
|
|
601
|
+
if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
|
|
560
602
|
// skip
|
|
561
603
|
}
|
|
562
604
|
else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
@@ -565,7 +607,7 @@ function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, opti
|
|
|
565
607
|
if (typeof currentValue !== 'object' || currentValue === null) {
|
|
566
608
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
567
609
|
}
|
|
568
|
-
assignGingerly(currentValue, value, options,
|
|
610
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
569
611
|
}
|
|
570
612
|
else {
|
|
571
613
|
parent[lastKey] = value;
|
|
@@ -602,13 +644,11 @@ function applyAliases(key, aliasMap) {
|
|
|
602
644
|
/**
|
|
603
645
|
* Main assignGingerly function
|
|
604
646
|
*/
|
|
605
|
-
export function assignGingerly(target, source, options,
|
|
647
|
+
export function assignGingerly(target, source, options, permissionProcessor) {
|
|
606
648
|
if (!target || typeof target !== 'object') {
|
|
607
649
|
return target;
|
|
608
650
|
}
|
|
609
651
|
const { aliasMap, withMethods: withMethodsSet } = normalizeAliasOptions(options);
|
|
610
|
-
// Normalize restrictedPropSettings once per top-level call
|
|
611
|
-
const restrictedPropSet = buildRestrictedPropSet(permissions);
|
|
612
652
|
// Convert withAsyncMethods array to Set for O(1) lookup
|
|
613
653
|
const withAsyncMethodsSet = options?.withAsyncMethods
|
|
614
654
|
? options.withAsyncMethods instanceof Set
|
|
@@ -689,7 +729,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
689
729
|
let lhsParent;
|
|
690
730
|
let lhsKey;
|
|
691
731
|
if (withMethodsSet) {
|
|
692
|
-
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet);
|
|
732
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet, permissionProcessor);
|
|
693
733
|
lhsParent = result.target;
|
|
694
734
|
lhsKey = result.lastKey;
|
|
695
735
|
lhsValue = lhsParent[lhsKey];
|
|
@@ -700,7 +740,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
700
740
|
lhsValue = lhsParent[lhsKey];
|
|
701
741
|
}
|
|
702
742
|
//TODO: this logic seems to occur twice at least. Maybe make it a method?
|
|
703
|
-
if (checkRestrictedProp(
|
|
743
|
+
if (permissionProcessor?.checkRestrictedProp(lhsKey)) {
|
|
704
744
|
continue;
|
|
705
745
|
}
|
|
706
746
|
// Event handler: Element LHS + object RHS with 'on' property
|
|
@@ -710,7 +750,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
710
750
|
const capturedTarget = target;
|
|
711
751
|
const capturedOptions = options;
|
|
712
752
|
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
713
|
-
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {},
|
|
753
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {}, permissionProcessor);
|
|
714
754
|
});
|
|
715
755
|
continue;
|
|
716
756
|
}
|
|
@@ -723,7 +763,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
723
763
|
}
|
|
724
764
|
else {
|
|
725
765
|
// Plain key - direct operation on target
|
|
726
|
-
if (checkRestrictedProp(
|
|
766
|
+
if (permissionProcessor?.checkRestrictedProp(path)) {
|
|
727
767
|
continue;
|
|
728
768
|
}
|
|
729
769
|
// Event handler: Element LHS + object RHS with 'on' property
|
|
@@ -733,7 +773,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
733
773
|
const capturedTarget = target;
|
|
734
774
|
const capturedOptions = options;
|
|
735
775
|
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
736
|
-
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {},
|
|
776
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {}, permissionProcessor);
|
|
737
777
|
});
|
|
738
778
|
continue;
|
|
739
779
|
}
|
|
@@ -798,7 +838,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
798
838
|
}
|
|
799
839
|
}
|
|
800
840
|
// Apply negation to LHS — check restriction first
|
|
801
|
-
if (!checkRestrictedProp(
|
|
841
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
802
842
|
lhsParent[lhsLastKey] = !valueToNegate;
|
|
803
843
|
}
|
|
804
844
|
}
|
|
@@ -855,14 +895,14 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
855
895
|
if (path) {
|
|
856
896
|
const pathParts = isNestedPath(path) ? parsePath(path) : [path];
|
|
857
897
|
const lastKey = pathParts[pathParts.length - 1];
|
|
858
|
-
if (checkRestrictedProp(
|
|
898
|
+
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
859
899
|
continue;
|
|
860
900
|
}
|
|
861
901
|
// Navigate to the target sub-object
|
|
862
902
|
let mergeTarget;
|
|
863
903
|
if (isNestedPath(path)) {
|
|
864
904
|
if (withMethodsSet) {
|
|
865
|
-
const result = evaluatePathWithMethods(target, parsePath(path), value, withMethodsSet);
|
|
905
|
+
const result = evaluatePathWithMethods(target, parsePath(path), value, withMethodsSet, permissionProcessor);
|
|
866
906
|
mergeTarget = result.target[result.lastKey];
|
|
867
907
|
}
|
|
868
908
|
else {
|
|
@@ -884,7 +924,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
884
924
|
}
|
|
885
925
|
// Recursively merge if target is a valid object
|
|
886
926
|
if (mergeTarget && typeof mergeTarget === 'object') {
|
|
887
|
-
assignGingerly(mergeTarget, value, options,
|
|
927
|
+
assignGingerly(mergeTarget, value, options, permissionProcessor);
|
|
888
928
|
}
|
|
889
929
|
}
|
|
890
930
|
continue;
|
|
@@ -901,7 +941,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
901
941
|
(async () => {
|
|
902
942
|
try {
|
|
903
943
|
const { handleEachTime } = await import('./eachTime.js');
|
|
904
|
-
await handleEachTime(target, pathParts, forEachIndex, value, withMethodsSet, aliasMap, options,
|
|
944
|
+
await handleEachTime(target, pathParts, forEachIndex, value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
905
945
|
}
|
|
906
946
|
catch (error) {
|
|
907
947
|
console.error('Error in @eachTime:', error);
|
|
@@ -916,7 +956,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
916
956
|
let current = target;
|
|
917
957
|
if (pathToForEach.length > 0) {
|
|
918
958
|
if (withMethodsSet) {
|
|
919
|
-
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet);
|
|
959
|
+
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
920
960
|
// The result.target is the current position after evaluating the path
|
|
921
961
|
// This is already the iterable we want
|
|
922
962
|
current = result.target;
|
|
@@ -929,7 +969,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
929
969
|
}
|
|
930
970
|
// Apply to each item in the iterable
|
|
931
971
|
if (isIterable(current)) {
|
|
932
|
-
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options,
|
|
972
|
+
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options, permissionProcessor);
|
|
933
973
|
}
|
|
934
974
|
// If not iterable, let JavaScript throw error naturally when trying to iterate
|
|
935
975
|
continue;
|
|
@@ -943,11 +983,10 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
943
983
|
const capturedValue = value;
|
|
944
984
|
const capturedWithMethodsSet = withMethodsSet || new Set();
|
|
945
985
|
const capturedOptions = options;
|
|
946
|
-
const
|
|
947
|
-
const capturedRestrictedPropSet = restrictedPropSet;
|
|
986
|
+
const capturedPermissionProcessor = permissionProcessor;
|
|
948
987
|
(async () => {
|
|
949
988
|
const { evaluatePathWithAsyncMethods } = await import('./evaluatePathWithAsyncMethods.js');
|
|
950
|
-
const result = await evaluatePathWithAsyncMethods(capturedTarget, capturedPathParts, capturedValue, capturedWithMethodsSet, withAsyncMethodsSet);
|
|
989
|
+
const result = await evaluatePathWithAsyncMethods(capturedTarget, capturedPathParts, capturedValue, capturedWithMethodsSet, withAsyncMethodsSet, capturedPermissionProcessor);
|
|
951
990
|
if (result.isMethod || result.isAsyncMethod) {
|
|
952
991
|
// Last segment is a method — call it
|
|
953
992
|
const method = result.target[result.lastKey];
|
|
@@ -967,7 +1006,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
967
1006
|
// Not a method — assign the value
|
|
968
1007
|
const lastKey = result.lastKey;
|
|
969
1008
|
const parent = result.target;
|
|
970
|
-
if (redirectRestrictedProp(
|
|
1009
|
+
if (capturedPermissionProcessor?.redirectRestrictedProp(parent, lastKey, capturedValue)) {
|
|
971
1010
|
// skip
|
|
972
1011
|
}
|
|
973
1012
|
else if (typeof capturedValue === 'object' && capturedValue !== null && !Array.isArray(capturedValue)) {
|
|
@@ -976,7 +1015,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
976
1015
|
if (typeof currentValue !== 'object' || currentValue === null) {
|
|
977
1016
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
978
1017
|
}
|
|
979
|
-
assignGingerly(currentValue, capturedValue, capturedOptions,
|
|
1018
|
+
assignGingerly(currentValue, capturedValue, capturedOptions, capturedPermissionProcessor);
|
|
980
1019
|
}
|
|
981
1020
|
else {
|
|
982
1021
|
parent[lastKey] = capturedValue;
|
|
@@ -991,7 +1030,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
991
1030
|
}
|
|
992
1031
|
// Check if we need to handle methods
|
|
993
1032
|
if (withMethodsSet) {
|
|
994
|
-
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet);
|
|
1033
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet, permissionProcessor);
|
|
995
1034
|
if (result.isMethod) {
|
|
996
1035
|
// Last segment is a method - call it
|
|
997
1036
|
const method = result.target[result.lastKey];
|
|
@@ -1013,7 +1052,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
1013
1052
|
// Not a method - proceed with normal assignment using evaluated target
|
|
1014
1053
|
const lastKey = result.lastKey;
|
|
1015
1054
|
const parent = result.target;
|
|
1016
|
-
if (redirectRestrictedProp(
|
|
1055
|
+
if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
|
|
1017
1056
|
// skip
|
|
1018
1057
|
// Check for static assignTo protocol
|
|
1019
1058
|
}
|
|
@@ -1027,7 +1066,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
1027
1066
|
if (typeof currentValue !== 'object' || currentValue === null) {
|
|
1028
1067
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
1029
1068
|
}
|
|
1030
|
-
assignGingerly(currentValue, value, options,
|
|
1069
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
1031
1070
|
}
|
|
1032
1071
|
else {
|
|
1033
1072
|
// Property is writable - replace it
|
|
@@ -1042,7 +1081,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
1042
1081
|
// No withMethods - use original logic
|
|
1043
1082
|
const lastKey = pathParts[pathParts.length - 1];
|
|
1044
1083
|
const parent = ensureNestedPath(target, pathParts);
|
|
1045
|
-
if (redirectRestrictedProp(
|
|
1084
|
+
if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
|
|
1046
1085
|
// skip
|
|
1047
1086
|
// Check for static assignTo protocol
|
|
1048
1087
|
}
|
|
@@ -1058,7 +1097,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
1058
1097
|
throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
|
|
1059
1098
|
}
|
|
1060
1099
|
// Recursively apply assignGingerly to the readonly object
|
|
1061
|
-
assignGingerly(currentValue, value, options,
|
|
1100
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
1062
1101
|
}
|
|
1063
1102
|
else {
|
|
1064
1103
|
// Property is writable - replace it
|
|
@@ -1092,7 +1131,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
1092
1131
|
// Silently skip if not a function
|
|
1093
1132
|
continue;
|
|
1094
1133
|
}
|
|
1095
|
-
if (redirectRestrictedProp(
|
|
1134
|
+
if (permissionProcessor?.redirectRestrictedProp(target, key, value)) {
|
|
1096
1135
|
// skip
|
|
1097
1136
|
// Check for static assignTo protocol
|
|
1098
1137
|
}
|
|
@@ -1108,7 +1147,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
1108
1147
|
throw new Error(`Cannot merge object into readonly primitive property '${String(key)}'`);
|
|
1109
1148
|
}
|
|
1110
1149
|
// Recursively apply assignGingerly to the readonly object
|
|
1111
|
-
assignGingerly(currentValue, value, options,
|
|
1150
|
+
assignGingerly(currentValue, value, options, permissionProcessor);
|
|
1112
1151
|
}
|
|
1113
1152
|
else {
|
|
1114
1153
|
// Property is writable - replace it
|
|
@@ -1235,7 +1274,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
1235
1274
|
// Fire-and-forget bulk enhancements (async, non-blocking)
|
|
1236
1275
|
if (options?.enhance && options.enhance.length > 0 && typeof target === 'object' && target instanceof Element) {
|
|
1237
1276
|
import('./enhanceAll.js').then(({ enhanceAll }) => {
|
|
1238
|
-
enhanceAll(target, options.enhance,
|
|
1277
|
+
enhanceAll(target, options.enhance, permissionProcessor);
|
|
1239
1278
|
});
|
|
1240
1279
|
}
|
|
1241
1280
|
return target;
|