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
@@ -20,7 +20,7 @@
20
20
  * installForwarding(ClubMember);
21
21
  * // Now el.command delegates to el.behaviors.commandBehavior.command
22
22
  */
23
- import { resolveValue } from '../resolveValues.js';
23
+ import { resolveValue } from '../resolve/resolveValues.js';
24
24
  import assignGingerly from '../assignGingerly.js';
25
25
  /**
26
26
  * Installs property forwarding on a class prototype based on `static propLinks`.
@@ -21,7 +21,7 @@
21
21
  * // Now el.command delegates to el.behaviors.commandBehavior.command
22
22
  */
23
23
 
24
- import { resolveValue} from '../resolveValues.js';
24
+ import { resolveValue} from '../resolve/resolveValues.js';
25
25
  import {ResolveValuesOptions, IAssignGingerlyOptions} from '../types/assign-gingerly/types.js';
26
26
  import assignGingerly, { } from '../assignGingerly.js';
27
27
 
package/README.md CHANGED
@@ -54,6 +54,8 @@ So in our view this package helps fill the void left by not supporting the "is"
54
54
 
55
55
  Anyway, let's start out detailing the more innocent features of this package / polyfill.
56
56
 
57
+ Because the same declarative input can come from sources that aren't fully trusted — HTML attributes, user JSON, remote config — the package also provides an **opt-in permission layer** to block or redirect dangerous property assignments. See [docs/assign-permissions.md](docs/assign-permissions.md) for how to construct and pass a `PermissionProcessor`.
58
+
57
59
  The three utility functions are:
58
60
 
59
61
  ## assignGingerly
package/assignFeatures.js CHANGED
@@ -84,10 +84,8 @@ function isAsyncResolution(spawnish) {
84
84
  }
85
85
  /**
86
86
  * Resolves all configured spawns for the target constructor and caches them.
87
- * Returns a Promise if any resolution is async, otherwise undefined.
88
87
  */
89
- function resolveAndCacheSpawns(ctr, features, supportedFeatures) {
90
- let hasAsync = false;
88
+ async function resolveAndCacheSpawns(ctr, features, supportedFeatures) {
91
89
  const asyncResolutions = [];
92
90
  for (const key of Object.keys(features)) {
93
91
  const classCache = getOrCreateClassCache(ctr);
@@ -104,7 +102,6 @@ function resolveAndCacheSpawns(ctr, features, supportedFeatures) {
104
102
  continue;
105
103
  }
106
104
  if (isAsyncResolution(spawnish)) {
107
- hasAsync = true;
108
105
  asyncResolutions.push((async () => {
109
106
  const resolved = await resolveSpawn(spawnish);
110
107
  classCache.set(key, resolved);
@@ -114,10 +111,9 @@ function resolveAndCacheSpawns(ctr, features, supportedFeatures) {
114
111
  classCache.set(key, spawnish);
115
112
  }
116
113
  }
117
- if (hasAsync) {
118
- return Promise.all(asyncResolutions).then(() => undefined);
114
+ if (asyncResolutions.length > 0) {
115
+ await Promise.all(asyncResolutions);
119
116
  }
120
- return undefined;
121
117
  }
122
118
  /**
123
119
  * Installs a getter/setter pair on the constructor's prototype for the given feature key.
@@ -288,7 +284,7 @@ function installCallbackForwarding(ctr, key, callbacks) {
288
284
  featureKeys.add(key);
289
285
  }
290
286
  }
291
- function installOneFeature(ctr, key, features, supportedFeatures, featuresRegistry) {
287
+ async function installOneFeature(ctr, key, features, supportedFeatures, featuresRegistry) {
292
288
  // 1. Confirm the key is opted-in via supportedFeatures
293
289
  if (!(key in supportedFeatures)) {
294
290
  throw new Error(`assignFeatures: "${key}" is not declared in ${ctr.name || 'constructor'}.supportedFeatures`);
@@ -322,27 +318,13 @@ function installOneFeature(ctr, key, features, supportedFeatures, featuresRegist
322
318
  if (resolvedSpawn && !isAsyncSpawn(resolvedSpawn) &&
323
319
  Object.hasOwn(resolvedSpawn, 'onAssigned') &&
324
320
  typeof resolvedSpawn.onAssigned === 'function') {
325
- const result = resolvedSpawn.onAssigned(ctr, featureConfig, key);
326
- if (result && typeof result.then === 'function') {
327
- return result;
328
- }
321
+ await resolvedSpawn.onAssigned(ctr, featureConfig, key);
329
322
  }
330
- return undefined;
331
323
  }
332
- function installAllFeatures(ctr, features, supportedFeatures, featuresRegistry) {
333
- let asyncResult;
324
+ async function installAllFeatures(ctr, features, supportedFeatures, featuresRegistry) {
334
325
  for (const key of Object.keys(features)) {
335
- const result = installOneFeature(ctr, key, features, supportedFeatures, featuresRegistry);
336
- if (result) {
337
- if (!asyncResult) {
338
- asyncResult = result.then(() => undefined);
339
- }
340
- else {
341
- asyncResult = asyncResult.then(() => result.then(() => undefined));
342
- }
343
- }
326
+ await installOneFeature(ctr, key, features, supportedFeatures, featuresRegistry);
344
327
  }
345
- return asyncResult;
346
328
  }
347
329
  /**
348
330
  * Core assignFeatures implementation.
@@ -357,7 +339,7 @@ function installAllFeatures(ctr, features, supportedFeatures, featuresRegistry)
357
339
  * @param features - Map of feature keys to their injection configs
358
340
  * @param featuresRegistry - The registry to store injections in
359
341
  */
360
- export function assignFeatures(ctr, features, featuresRegistry) {
342
+ export async function assignFeatures(ctr, features, featuresRegistry) {
361
343
  // Validate that the constructor has static supportedFeatures
362
344
  const supportedFeatures = ctr.supportedFeatures;
363
345
  if (!supportedFeatures) {
@@ -366,11 +348,8 @@ export function assignFeatures(ctr, features, featuresRegistry) {
366
348
  // Resolve all configured spawns before installing getters. This is the single
367
349
  // source of truth for spawn resolution; other callers (defineWithFeatures, etc.)
368
350
  // delegate to assignFeatures.
369
- const spawnResolution = resolveAndCacheSpawns(ctr, features, supportedFeatures);
370
- if (spawnResolution) {
371
- return spawnResolution.then(() => installAllFeatures(ctr, features, supportedFeatures, featuresRegistry));
372
- }
373
- return installAllFeatures(ctr, features, supportedFeatures, featuresRegistry);
351
+ await resolveAndCacheSpawns(ctr, features, supportedFeatures);
352
+ await installAllFeatures(ctr, features, supportedFeatures, featuresRegistry);
374
353
  }
375
354
  /**
376
355
  * Captures own-properties that shadow feature getters and stores them as initVals.
package/assignFeatures.ts CHANGED
@@ -96,14 +96,12 @@ function isAsyncResolution(spawnish: any): boolean {
96
96
 
97
97
  /**
98
98
  * Resolves all configured spawns for the target constructor and caches them.
99
- * Returns a Promise if any resolution is async, otherwise undefined.
100
99
  */
101
- function resolveAndCacheSpawns(
100
+ async function resolveAndCacheSpawns(
102
101
  ctr: Function,
103
102
  features: FeatureConfigsMap,
104
103
  supportedFeatures: SupportedFeaturesMap
105
- ): Promise<void> | undefined {
106
- let hasAsync = false;
104
+ ): Promise<void> {
107
105
  const asyncResolutions: Promise<void>[] = [];
108
106
 
109
107
  for (const key of Object.keys(features)) {
@@ -123,7 +121,6 @@ function resolveAndCacheSpawns(
123
121
  }
124
122
 
125
123
  if (isAsyncResolution(spawnish)) {
126
- hasAsync = true;
127
124
  asyncResolutions.push((async () => {
128
125
  const resolved = await resolveSpawn(spawnish);
129
126
  classCache.set(key, resolved);
@@ -133,10 +130,9 @@ function resolveAndCacheSpawns(
133
130
  }
134
131
  }
135
132
 
136
- if (hasAsync) {
137
- return Promise.all(asyncResolutions).then(() => undefined);
133
+ if (asyncResolutions.length > 0) {
134
+ await Promise.all(asyncResolutions);
138
135
  }
139
- return undefined;
140
136
  }
141
137
 
142
138
  /**
@@ -349,13 +345,13 @@ function installCallbackForwarding(
349
345
  }
350
346
  }
351
347
 
352
- function installOneFeature(
348
+ async function installOneFeature(
353
349
  ctr: Function,
354
350
  key: string,
355
351
  features: FeatureConfigsMap,
356
352
  supportedFeatures: SupportedFeaturesMap,
357
353
  featuresRegistry: FeaturesRegistry
358
- ): Promise<void> | undefined {
354
+ ): Promise<void> {
359
355
  // 1. Confirm the key is opted-in via supportedFeatures
360
356
  if (!(key in supportedFeatures)) {
361
357
  throw new Error(
@@ -401,32 +397,19 @@ function installOneFeature(
401
397
  if (resolvedSpawn && !isAsyncSpawn(resolvedSpawn) &&
402
398
  Object.hasOwn(resolvedSpawn, 'onAssigned') &&
403
399
  typeof (resolvedSpawn as any).onAssigned === 'function') {
404
- const result = (resolvedSpawn as any).onAssigned(ctr, featureConfig, key);
405
- if (result && typeof result.then === 'function') {
406
- return result;
407
- }
400
+ await (resolvedSpawn as any).onAssigned(ctr, featureConfig, key);
408
401
  }
409
- return undefined;
410
402
  }
411
403
 
412
- function installAllFeatures(
404
+ async function installAllFeatures(
413
405
  ctr: Function,
414
406
  features: FeatureConfigsMap,
415
407
  supportedFeatures: SupportedFeaturesMap,
416
408
  featuresRegistry: FeaturesRegistry
417
- ): Promise<void> | undefined {
418
- let asyncResult: Promise<void> | undefined;
409
+ ): Promise<void> {
419
410
  for (const key of Object.keys(features)) {
420
- const result = installOneFeature(ctr, key, features, supportedFeatures, featuresRegistry);
421
- if (result) {
422
- if (!asyncResult) {
423
- asyncResult = result.then(() => undefined);
424
- } else {
425
- asyncResult = asyncResult.then(() => result.then(() => undefined));
426
- }
427
- }
411
+ await installOneFeature(ctr, key, features, supportedFeatures, featuresRegistry);
428
412
  }
429
- return asyncResult;
430
413
  }
431
414
 
432
415
  /**
@@ -442,11 +425,11 @@ function installAllFeatures(
442
425
  * @param features - Map of feature keys to their injection configs
443
426
  * @param featuresRegistry - The registry to store injections in
444
427
  */
445
- export function assignFeatures(
428
+ export async function assignFeatures(
446
429
  ctr: Function,
447
430
  features: FeatureConfigsMap,
448
431
  featuresRegistry: FeaturesRegistry
449
- ): Promise<void> | undefined {
432
+ ): Promise<void> {
450
433
  // Validate that the constructor has static supportedFeatures
451
434
  const supportedFeatures: SupportedFeaturesMap | undefined = (ctr as any).supportedFeatures;
452
435
 
@@ -459,15 +442,8 @@ export function assignFeatures(
459
442
  // Resolve all configured spawns before installing getters. This is the single
460
443
  // source of truth for spawn resolution; other callers (defineWithFeatures, etc.)
461
444
  // delegate to assignFeatures.
462
- const spawnResolution = resolveAndCacheSpawns(ctr, features, supportedFeatures);
463
-
464
- if (spawnResolution) {
465
- return spawnResolution.then(() =>
466
- installAllFeatures(ctr, features, supportedFeatures, featuresRegistry)
467
- );
468
- }
469
-
470
- return installAllFeatures(ctr, features, supportedFeatures, featuresRegistry);
445
+ await resolveAndCacheSpawns(ctr, features, supportedFeatures);
446
+ await installAllFeatures(ctr, features, supportedFeatures, featuresRegistry);
471
447
  }
472
448
 
473
449
  /**
@@ -682,7 +658,7 @@ export class PropertyBag {
682
658
  declare global {
683
659
  interface CustomElementRegistry {
684
660
  featuresRegistry: FeaturesRegistry;
685
- assignFeatures(ctr: Function, features: FeatureConfigsMap): Promise<void> | undefined;
661
+ assignFeatures(ctr: Function, features: FeatureConfigsMap): Promise<void>;
686
662
  }
687
663
  }
688
664
 
package/assignFrom.js CHANGED
@@ -8,9 +8,9 @@
8
8
  *
9
9
  * Handler commands (` =>`) are fire-and-forget (kicked off asynchronously, not awaited).
10
10
  */
11
- import { getValues, getValue } from './getValues.js';
11
+ import { getValues, getValue } from './resolve/getValues.js';
12
12
  import assignGingerly from './assignGingerly.js';
13
- import { resolveIdVariable, parseIdRef } from './resolveIdRef.js';
13
+ import { resolveIdVariable, parseIdRef } from './resolve/resolveIdRef.js';
14
14
  import { processInferredAssignments } from './inferredAssignments.js';
15
15
  /**
16
16
  * Supported substitution variables and their option keys.
@@ -377,7 +377,7 @@ function getEffectiveIds(options) {
377
377
  /**
378
378
  * Process #[x] normal keys synchronously.
379
379
  */
380
- function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, options, permissions) {
380
+ function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, options, permissionProcessor) {
381
381
  const ids = getEffectiveIds(options);
382
382
  if (!ids)
383
383
  return;
@@ -391,13 +391,13 @@ function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, option
391
391
  continue;
392
392
  const value = expandedPattern[key];
393
393
  if (parsed.remainingPath) {
394
- const resolvedValue = getValues({ __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target });
395
- assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissions);
394
+ const resolvedValue = getValues({ __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor });
395
+ assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
396
396
  }
397
397
  else {
398
- const resolvedValue = getValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target });
398
+ const resolvedValue = getValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor });
399
399
  if (!('__v' in resolvedValue)) {
400
- assignGingerly(el, resolvedValue, options, permissions);
400
+ assignGingerly(el, resolvedValue, options, permissionProcessor);
401
401
  }
402
402
  }
403
403
  }
@@ -411,15 +411,15 @@ function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, option
411
411
  * @param target - Object to merge resolved values into
412
412
  * @param pattern - Object whose RHS values may contain `?.` path strings
413
413
  * @param options - Options including `from` (source object)
414
- * @param permissions - Optional security permissions
414
+ * @param permissionProcessor - Optional security permissionProcessor
415
415
  * @returns The target object after merging
416
416
  */
417
- export function assignFrom(target, pattern, options, permissions) {
417
+ export function assignFrom(target, pattern, options, permissionProcessor) {
418
418
  // Expand looped substitution variables
419
419
  const expandedPattern = expandSubstitutions(pattern, options);
420
420
  // Categorize keys
421
421
  const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys } = categorizeKeys(expandedPattern);
422
- const resolveOptions = { ...options, root: target };
422
+ const resolveOptions = { ...options, root: target, permissionProcessor };
423
423
  // Process ?= ternary keys (sync)
424
424
  if (ternaryKeys.length > 0) {
425
425
  const ternaryResolved = {};
@@ -436,7 +436,7 @@ export function assignFrom(target, pattern, options, permissions) {
436
436
  }
437
437
  }
438
438
  if (Object.keys(ternaryResolved).length > 0) {
439
- assignGingerly(target, ternaryResolved, options, permissions);
439
+ assignGingerly(target, ternaryResolved, options, permissionProcessor);
440
440
  }
441
441
  }
442
442
  // Process normal keys via getValues (sync) + assignGingerly
@@ -459,7 +459,8 @@ export function assignFrom(target, pattern, options, permissions) {
459
459
  aka: options.aka,
460
460
  akaMethods: options.akaMethods,
461
461
  protocols: options.protocols,
462
- root: target
462
+ root: target,
463
+ permissionProcessor
463
464
  });
464
465
  }
465
466
  else {
@@ -472,16 +473,16 @@ export function assignFrom(target, pattern, options, permissions) {
472
473
  }
473
474
  const resolved = getValues(normalPattern, options.from, resolveOptions);
474
475
  handleSpreads(resolved);
475
- assignGingerly(target, resolved, options, permissions);
476
+ assignGingerly(target, resolved, options, permissionProcessor);
476
477
  }
477
478
  // Process #[x] normal keys (sync)
478
479
  if (idRefNormalKeys.length > 0) {
479
- processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, options, permissions);
480
+ processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, options, permissionProcessor);
480
481
  }
481
482
  // Process handler commands — fire-and-forget (async)
482
483
  if (handlerKeys.length > 0) {
483
484
  import('./processHandlerCommands.js').then(({ processHandlerCommands }) => {
484
- processHandlerCommands(target, handlerKeys, expandedPattern, options, permissions);
485
+ processHandlerCommands(target, handlerKeys, expandedPattern, options, permissionProcessor);
485
486
  });
486
487
  }
487
488
  // Process #[x] handler keys — fire-and-forget (async)
@@ -497,7 +498,7 @@ export function assignFrom(target, pattern, options, permissions) {
497
498
  continue;
498
499
  const syntheticKey = parsed.remainingPath ? `${parsed.remainingPath} =>` : ' =>';
499
500
  const syntheticPattern = { [syntheticKey]: expandedPattern[key] };
500
- processHandlerCommands(el, [syntheticKey], syntheticPattern, options, permissions);
501
+ processHandlerCommands(el, [syntheticKey], syntheticPattern, options, permissionProcessor);
501
502
  }
502
503
  });
503
504
  }
@@ -517,7 +518,7 @@ export function assignFrom(target, pattern, options, permissions) {
517
518
  // Process bulk enhancements — fire-and-forget (async)
518
519
  if (options.enhance && options.enhance.length > 0) {
519
520
  import('./enhanceAll.js').then(({ enhanceAll }) => {
520
- enhanceAll(target, options.enhance, permissions);
521
+ enhanceAll(target, options.enhance, permissionProcessor);
521
522
  });
522
523
  }
523
524
  return target;
package/assignFrom.ts CHANGED
@@ -9,12 +9,11 @@
9
9
  * Handler commands (` =>`) are fire-and-forget (kicked off asynchronously, not awaited).
10
10
  */
11
11
 
12
- import { getValues, getValue } from './getValues.js';
12
+ import { getValues, getValue } from './resolve/getValues.js';
13
13
  import assignGingerly from './assignGingerly.js';
14
- import { resolveIdVariable, parseIdRef } from './resolveIdRef.js';
14
+ import { resolveIdVariable, parseIdRef } from './resolve/resolveIdRef.js';
15
15
  import { processInferredAssignments } from './inferredAssignments.js';
16
- import type { AssignPermissions } from './types/assign-gingerly/types.js';
17
- import type { AssignFromOptions, AssignFromHandler, AssignFromHandlerConstructor } from './types/assign-gingerly/types.js';
16
+ import type { PermissionProcessor, AssignFromOptions, AssignFromHandler, AssignFromHandlerConstructor } from './types/assign-gingerly/types.js';
18
17
 
19
18
  // Re-export types for consumers
20
19
  export type { AssignFromOptions, AssignFromHandler, AssignFromHandlerConstructor };
@@ -387,11 +386,11 @@ function getEffectiveIds(options: AssignFromOptions): Record<string, any> | unde
387
386
  * Process #[x] normal keys synchronously.
388
387
  */
389
388
  function processIdRefNormalKeys(
390
- idRefNormalKeys: string[],
391
- expandedPattern: Record<string, any>,
392
- target: any,
393
- options: AssignFromOptions,
394
- permissions?: AssignPermissions
389
+ idRefNormalKeys: string[],
390
+ expandedPattern: Record<string, any>,
391
+ target: any,
392
+ options: AssignFromOptions,
393
+ permissionProcessor?: PermissionProcessor
395
394
  ): void {
396
395
  const ids = getEffectiveIds(options);
397
396
  if (!ids) return;
@@ -408,17 +407,17 @@ function processIdRefNormalKeys(
408
407
  if (parsed.remainingPath) {
409
408
  const resolvedValue = getValues(
410
409
  { __v: value }, from,
411
- { withMethods, aka, akaMethods, protocols, root: target }
410
+ { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor }
412
411
  );
413
- assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissions);
412
+ assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
414
413
  } else {
415
414
  const resolvedValue = getValues(
416
415
  typeof value === 'object' && value !== null ? value : { __v: value },
417
416
  from,
418
- { withMethods, aka, akaMethods, protocols, root: target }
417
+ { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor }
419
418
  );
420
419
  if (!('__v' in resolvedValue)) {
421
- assignGingerly(el, resolvedValue, options, permissions);
420
+ assignGingerly(el, resolvedValue, options, permissionProcessor);
422
421
  }
423
422
  }
424
423
  }
@@ -433,14 +432,14 @@ function processIdRefNormalKeys(
433
432
  * @param target - Object to merge resolved values into
434
433
  * @param pattern - Object whose RHS values may contain `?.` path strings
435
434
  * @param options - Options including `from` (source object)
436
- * @param permissions - Optional security permissions
435
+ * @param permissionProcessor - Optional security permissionProcessor
437
436
  * @returns The target object after merging
438
437
  */
439
438
  export function assignFrom(
440
439
  target: any,
441
440
  pattern: Record<string, any>,
442
441
  options: AssignFromOptions,
443
- permissions?: AssignPermissions
442
+ permissionProcessor?: PermissionProcessor
444
443
  ): any {
445
444
  // Expand looped substitution variables
446
445
  const expandedPattern = expandSubstitutions(pattern, options);
@@ -448,7 +447,7 @@ export function assignFrom(
448
447
  // Categorize keys
449
448
  const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys } = categorizeKeys(expandedPattern);
450
449
 
451
- const resolveOptions = { ...options, root: target } as any;
450
+ const resolveOptions = { ...options, root: target, permissionProcessor } as any;
452
451
 
453
452
  // Process ?= ternary keys (sync)
454
453
  if (ternaryKeys.length > 0) {
@@ -464,7 +463,7 @@ export function assignFrom(
464
463
  }
465
464
  }
466
465
  if (Object.keys(ternaryResolved).length > 0) {
467
- assignGingerly(target, ternaryResolved, options, permissions);
466
+ assignGingerly(target, ternaryResolved, options, permissionProcessor);
468
467
  }
469
468
  }
470
469
 
@@ -488,7 +487,8 @@ export function assignFrom(
488
487
  aka: options.aka,
489
488
  akaMethods: options.akaMethods,
490
489
  protocols: options.protocols,
491
- root: target
490
+ root: target,
491
+ permissionProcessor
492
492
  });
493
493
  } else {
494
494
  normalPattern[key] = el.id; // bare #[x] → ID string
@@ -502,18 +502,18 @@ export function assignFrom(
502
502
  const resolved = getValues(normalPattern, options.from, resolveOptions);
503
503
 
504
504
  handleSpreads(resolved);
505
- assignGingerly(target, resolved, options, permissions);
505
+ assignGingerly(target, resolved, options, permissionProcessor);
506
506
  }
507
507
 
508
508
  // Process #[x] normal keys (sync)
509
509
  if (idRefNormalKeys.length > 0) {
510
- processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, options, permissions);
510
+ processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, options, permissionProcessor);
511
511
  }
512
512
 
513
513
  // Process handler commands — fire-and-forget (async)
514
514
  if (handlerKeys.length > 0) {
515
515
  import('./processHandlerCommands.js').then(({ processHandlerCommands }) => {
516
- processHandlerCommands(target, handlerKeys, expandedPattern, options, permissions);
516
+ processHandlerCommands(target, handlerKeys, expandedPattern, options, permissionProcessor);
517
517
  });
518
518
  }
519
519
 
@@ -528,7 +528,7 @@ export function assignFrom(
528
528
  if (!el) continue;
529
529
  const syntheticKey = parsed.remainingPath ? `${parsed.remainingPath} =>` : ' =>';
530
530
  const syntheticPattern = { [syntheticKey]: expandedPattern[key] };
531
- processHandlerCommands(el, [syntheticKey], syntheticPattern, options, permissions);
531
+ processHandlerCommands(el, [syntheticKey], syntheticPattern, options, permissionProcessor);
532
532
  }
533
533
  });
534
534
  }
@@ -551,7 +551,7 @@ export function assignFrom(
551
551
  // Process bulk enhancements — fire-and-forget (async)
552
552
  if (options.enhance && options.enhance.length > 0) {
553
553
  import('./enhanceAll.js').then(({ enhanceAll }) => {
554
- enhanceAll(target, options.enhance!, permissions);
554
+ enhanceAll(target, options.enhance!, permissionProcessor);
555
555
  });
556
556
  }
557
557
 
@@ -19,12 +19,12 @@
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 assignGingerly from './assignGingerly.js';
24
24
  import { expandSubstitutions, categorizeKeys, handleSpreads } from './assignFrom.js';
25
25
  // Module cache for processHandlerCommands — avoids await on dynamic import after first call
26
26
  let _processHandlerCommands;
27
- export async function assignFromAsync(target, pattern, options, permissions) {
27
+ export async function assignFromAsync(target, pattern, options, permissionProcessor) {
28
28
  // First: expand looped substitution variables (${x}, ${y}, ${z})
29
29
  const expandedPattern = expandSubstitutions(pattern, options);
30
30
  // Categorize keys
@@ -36,16 +36,17 @@ export async function assignFromAsync(target, pattern, options, permissions) {
36
36
  aka: options.aka,
37
37
  akaMethods: options.akaMethods,
38
38
  protocols: options.protocols,
39
- root: target
39
+ root: target,
40
+ permissionProcessor
40
41
  });
41
42
  // Recursively handle "..." spread keys at all nesting levels
42
43
  handleSpreads(resolved);
43
- assignGingerly(target, resolved, options, permissions);
44
+ assignGingerly(target, resolved, options, permissionProcessor);
44
45
  }
45
46
  // Process #[x] normal keys — resolve element, then apply remaining path + value
46
47
  if (idRefNormalKeys.length > 0 && (options.pin || options.at)) {
47
48
  const ids = { ...options.pin, ...options.at };
48
- const { resolveIdVariable, parseIdRef } = await import('./resolveIdRef.js');
49
+ const { resolveIdVariable, parseIdRef } = await import('./resolve/resolveIdRef.js');
49
50
  const { withMethods, aka, akaMethods, protocols, from } = options;
50
51
  for (const key of idRefNormalKeys) {
51
52
  const parsed = parseIdRef(key);
@@ -57,18 +58,18 @@ export async function assignFromAsync(target, pattern, options, permissions) {
57
58
  const value = expandedPattern[key];
58
59
  if (parsed.remainingPath) {
59
60
  // Resolve the RHS value
60
- const resolvedValue = await resolveValues({ __v: value }, from, { withMethods, aka, akaMethods, protocols, root: el });
61
+ const resolvedValue = await resolveValues({ __v: value }, from, { withMethods, aka, akaMethods, protocols, root: el, permissionProcessor });
61
62
  // Apply remaining path on the resolved element
62
- assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissions);
63
+ assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
63
64
  }
64
65
  else {
65
66
  // No remaining path — resolve and assign directly to the element
66
- const resolvedValue = await resolveValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, protocols, root: el });
67
+ const resolvedValue = await resolveValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, protocols, root: el, permissionProcessor });
67
68
  if ('__v' in resolvedValue) {
68
69
  // Single value — can't assign to element root without a path
69
70
  }
70
71
  else {
71
- assignGingerly(el, resolvedValue, options, permissions);
72
+ assignGingerly(el, resolvedValue, options, permissionProcessor);
72
73
  }
73
74
  }
74
75
  }
@@ -76,12 +77,12 @@ export async function assignFromAsync(target, pattern, options, permissions) {
76
77
  // Process handler commands ( =>) — cached after first load to avoid await overhead
77
78
  if (handlerKeys.length > 0) {
78
79
  _processHandlerCommands ??= (await import('./processHandlerCommands.js')).processHandlerCommands;
79
- await _processHandlerCommands(target, handlerKeys, expandedPattern, options, permissions);
80
+ await _processHandlerCommands(target, handlerKeys, expandedPattern, options, permissionProcessor);
80
81
  }
81
82
  // Process #[x] handler keys — resolve element, then pass to handler processing
82
83
  if (idRefHandlerKeys.length > 0 && (options.pin || options.at)) {
83
84
  const ids = { ...options.pin, ...options.at };
84
- const { resolveIdVariable, parseIdRef } = await import('./resolveIdRef.js');
85
+ const { resolveIdVariable, parseIdRef } = await import('./resolve/resolveIdRef.js');
85
86
  _processHandlerCommands ??= (await import('./processHandlerCommands.js')).processHandlerCommands;
86
87
  for (const key of idRefHandlerKeys) {
87
88
  const parsed = parseIdRef(key);
@@ -98,7 +99,7 @@ export async function assignFromAsync(target, pattern, options, permissions) {
98
99
  const syntheticPattern = {
99
100
  [syntheticKey]: expandedPattern[key]
100
101
  };
101
- await _processHandlerCommands(el, [syntheticKey], syntheticPattern, options, permissions);
102
+ await _processHandlerCommands(el, [syntheticKey], syntheticPattern, options, permissionProcessor);
102
103
  }
103
104
  }
104
105
  // Process inferred assignments — dynamically imported only when option is present
@@ -117,7 +118,7 @@ export async function assignFromAsync(target, pattern, options, permissions) {
117
118
  // Process bulk enhancements — dynamically imported only when option is present
118
119
  if (options.enhance && options.enhance.length > 0) {
119
120
  const { enhanceAll } = await import('./enhanceAll.js');
120
- await enhanceAll(target, options.enhance, permissions);
121
+ await enhanceAll(target, options.enhance, permissionProcessor);
121
122
  }
122
123
  return target;
123
124
  }