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.
Files changed (61) hide show
  1. package/DX/installForwarding.js +1 -1
  2. package/DX/installForwarding.ts +1 -1
  3. package/README.md +2 -0
  4. package/assignFeatures.js +10 -31
  5. package/assignFeatures.ts +15 -39
  6. package/assignFrom.js +18 -17
  7. package/assignFrom.ts +23 -23
  8. package/assignFromAsync.js +14 -13
  9. package/assignFromAsync.ts +15 -15
  10. package/assignGingerly.js +87 -48
  11. package/assignGingerly.ts +177 -131
  12. package/assignPermissions/PermissionProcessor.js +166 -0
  13. package/assignPermissions/PermissionProcessor.ts +210 -0
  14. package/assignPermissions/isAllowedImportPath.js +2 -6
  15. package/assignPermissions/isAllowedImportPath.ts +3 -5
  16. package/assignPermissions/isAllowedUrl.js +18 -0
  17. package/assignPermissions/isAllowedUrl.ts +15 -0
  18. package/assignTentatively.js +9 -11
  19. package/assignTentatively.ts +11 -13
  20. package/beVigilant.js +1 -1
  21. package/beVigilant.ts +1 -1
  22. package/buildCSSQuery.js +1 -1
  23. package/buildCSSQuery.ts +1 -1
  24. package/eachTime.js +5 -6
  25. package/eachTime.ts +12 -15
  26. package/enhanceAll.js +3 -3
  27. package/enhanceAll.ts +4 -4
  28. package/evaluatePathWithAsyncMethods.js +12 -8
  29. package/evaluatePathWithAsyncMethods.ts +129 -117
  30. package/handlers/addEventListener.js +11 -11
  31. package/handlers/addEventListener.ts +12 -13
  32. package/handlers/lazyLoad.ts +7 -7
  33. package/handlers/lazyLoadSwitch.ts +3 -3
  34. package/handlers/manageTemplateList.js +10 -10
  35. package/handlers/manageTemplateList.ts +12 -12
  36. package/handlers/rangeSelector.ts +3 -3
  37. package/index.js +2 -3
  38. package/index.ts +2 -3
  39. package/inferencer/types/assign-gingerly/types.d.ts +40 -9
  40. package/inferencer/types/el-maker/types.d.ts +33 -0
  41. package/inferencer/types/scratch-box/types.d.ts +3 -0
  42. package/inferencer/types/truth-sourcer/types.d.ts +4 -0
  43. package/inferencer/types/wc-info/SimpleWCInfo.d.ts +15 -0
  44. package/package.json +24 -23
  45. package/parseWithAttrs.js +1 -1
  46. package/parseWithAttrs.ts +1 -1
  47. package/processHandlerCommands.js +5 -7
  48. package/processHandlerCommands.ts +12 -15
  49. package/{getValues.js → resolve/getValues.js} +19 -8
  50. package/{getValues.ts → resolve/getValues.ts} +331 -314
  51. package/{resolveIdRef.js → resolve/resolveIdRef.js} +1 -1
  52. package/{resolveIdRef.ts → resolve/resolveIdRef.ts} +1 -1
  53. package/{resolveValues.ts → resolve/resolveValues.ts} +1 -1
  54. package/types/assign-gingerly/types.d.ts +40 -9
  55. package/assignPermissions/restrictedProps.js +0 -43
  56. package/assignPermissions/restrictedProps.ts +0 -53
  57. package/resolveAndAssignFeatures.js +0 -36
  58. package/resolveAndAssignFeatures.ts +0 -43
  59. /package/{resolveTemplate.js → resolve/resolveTemplate.js} +0 -0
  60. /package/{resolveTemplate.ts → resolve/resolveTemplate.ts} +0 -0
  61. /package/{resolveValues.js → resolve/resolveValues.js} +0 -0
@@ -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 { AssignPermissions } from './types/assign-gingerly/types.js';
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
- permissions?: AssignPermissions
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, permissions);
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, permissions);
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, permissions);
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, permissions);
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, permissions);
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, permissions);
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 { buildRestrictedPropSet, checkRestrictedProp, redirectRestrictedProp } from './assignPermissions/restrictedProps.js';
2
- import { normalizeAliasOptions } from './getValues.js';
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 (fire-and-forget, uses shared featuresRegistry)
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 (fire-and-forget, uses shared featuresRegistry)
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('|') && withMethods.has(part.slice(0, -1));
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 (withMethods.has(part) || isZeroArgMethod) {
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('|') && withMethods.has(rawLastKey.slice(0, -1));
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: withMethods.has(lastKey),
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, permissions, restrictedPropSet) {
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('|') && withMethods.has(part.slice(0, -1));
498
- if (withMethods.has(part) || isZeroArgMethod) {
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, permissions, restrictedPropSet);
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(restrictedPropSet, parent, lastKey, value)) {
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, permissions);
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, permissions) {
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(restrictedPropSet, lhsKey)) {
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 ?? {}, permissions);
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(restrictedPropSet, path)) {
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 ?? {}, permissions);
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(restrictedPropSet, lhsLastKey)) {
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(restrictedPropSet, lastKey)) {
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, permissions);
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, permissions, restrictedPropSet);
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, permissions, restrictedPropSet);
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 capturedPermissions = permissions;
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(capturedRestrictedPropSet, parent, lastKey, capturedValue)) {
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, capturedPermissions);
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(restrictedPropSet, parent, lastKey, value)) {
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, permissions);
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(restrictedPropSet, parent, lastKey, value)) {
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, permissions);
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(restrictedPropSet, target, key, value)) {
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, permissions);
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, permissions);
1277
+ enhanceAll(target, options.enhance, permissionProcessor);
1239
1278
  });
1240
1279
  }
1241
1280
  return target;