assign-gingerly 0.0.74 → 0.0.76

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 (62) hide show
  1. package/DX/installForwarding.js +1 -1
  2. package/DX/installForwarding.ts +1 -1
  3. package/README.md +2 -0
  4. package/assignFrom.js +18 -17
  5. package/assignFrom.ts +23 -23
  6. package/assignFromAsync.js +14 -13
  7. package/assignFromAsync.ts +15 -15
  8. package/assignGingerly.js +46 -42
  9. package/assignGingerly.ts +131 -124
  10. package/assignPermissions/PermissionProcessor.js +166 -0
  11. package/assignPermissions/PermissionProcessor.ts +210 -0
  12. package/assignPermissions/isAllowedImportPath.js +2 -6
  13. package/assignPermissions/isAllowedImportPath.ts +3 -5
  14. package/assignPermissions/isAllowedUrl.js +18 -0
  15. package/assignPermissions/isAllowedUrl.ts +15 -0
  16. package/assignTentatively.js +9 -11
  17. package/assignTentatively.ts +11 -13
  18. package/beVigilant.js +1 -1
  19. package/beVigilant.ts +1 -1
  20. package/buildCSSQuery.js +1 -1
  21. package/buildCSSQuery.ts +1 -1
  22. package/eachTime.js +5 -6
  23. package/eachTime.ts +12 -15
  24. package/enhanceAll.js +3 -3
  25. package/enhanceAll.ts +4 -4
  26. package/evaluatePathWithAsyncMethods.js +12 -8
  27. package/evaluatePathWithAsyncMethods.ts +129 -117
  28. package/handlers/addEventListener.js +11 -11
  29. package/handlers/addEventListener.ts +12 -13
  30. package/handlers/lazyLoad.ts +7 -7
  31. package/handlers/lazyLoadSwitch.ts +3 -3
  32. package/handlers/manageTemplateList.js +10 -10
  33. package/handlers/manageTemplateList.ts +12 -12
  34. package/handlers/rangeSelector.ts +3 -3
  35. package/index.js +2 -3
  36. package/index.ts +2 -3
  37. package/inferencer/types/assign-gingerly/types.d.ts +37 -23
  38. package/inferencer/types/el-maker/types.d.ts +33 -0
  39. package/inferencer/types/scratch-box/types.d.ts +3 -0
  40. package/inferencer/types/truth-sourcer/types.d.ts +4 -0
  41. package/package.json +25 -23
  42. package/parseWithAttrs.js +1 -1
  43. package/parseWithAttrs.ts +1 -1
  44. package/processHandlerCommands.js +5 -7
  45. package/processHandlerCommands.ts +12 -15
  46. package/{getValues.js → resolve/getValues.js} +19 -8
  47. package/{getValues.ts → resolve/getValues.ts} +331 -314
  48. package/{resolveIdRef.js → resolve/resolveIdRef.js} +1 -1
  49. package/{resolveIdRef.ts → resolve/resolveIdRef.ts} +1 -1
  50. package/{resolveValues.ts → resolve/resolveValues.ts} +1 -1
  51. package/types/assign-gingerly/types.d.ts +37 -8
  52. package/utils/findClassPrototypeInPath.js +57 -0
  53. package/utils/findClassPrototypeInPath.ts +62 -0
  54. package/utils/isAsyncSpawn.js +20 -0
  55. package/utils/isAsyncSpawn.ts +17 -0
  56. package/assignPermissions/restrictedProps.js +0 -43
  57. package/assignPermissions/restrictedProps.ts +0 -53
  58. package/resolveAndAssignFeatures.js +0 -36
  59. package/resolveAndAssignFeatures.ts +0 -43
  60. /package/{resolveTemplate.js → resolve/resolveTemplate.js} +0 -0
  61. /package/{resolveTemplate.ts → resolve/resolveTemplate.ts} +0 -0
  62. /package/{resolveValues.js → resolve/resolveValues.js} +0 -0
@@ -0,0 +1,210 @@
1
+ import type { AssignPermissions, RestrictedPropSetting } from '../types/assign-gingerly/types.js';
2
+ import { isAllowedUrl } from './isAllowedUrl.js';
3
+
4
+ export interface RestrictedPropSettingsMap {
5
+ props: Map<string, RestrictedPropSetting | undefined>;
6
+ attrs: Map<string, RestrictedPropSetting | undefined>;
7
+ }
8
+
9
+ export class PermissionProcessor {
10
+ private readonly permissions: AssignPermissions | undefined;
11
+ private readonly props: Map<string, RestrictedPropSetting | undefined>;
12
+ private readonly attrs: Map<string, RestrictedPropSetting | undefined>;
13
+ private readonly methods: Set<string>;
14
+ private readonly warned = new Set<string>();
15
+ private readonly warnedMethods = new Set<string>();
16
+
17
+ constructor(permissions: AssignPermissions | undefined) {
18
+ this.permissions = permissions;
19
+ const { props, attrs } = buildMaps(permissions);
20
+ this.props = props;
21
+ this.attrs = attrs;
22
+ this.methods = buildMethodSet(permissions);
23
+ }
24
+
25
+ get crossDomainImports(): boolean {
26
+ return !!this.permissions?.crossDomainImports;
27
+ }
28
+
29
+ get hasProps(): boolean {
30
+ return this.props.size > 0;
31
+ }
32
+
33
+ get hasAttrs(): boolean {
34
+ return this.attrs.size > 0;
35
+ }
36
+
37
+ checkRestrictedProp(key: string): boolean {
38
+ if (!this.props.has(key)) return false;
39
+ this.warnRestricted(key);
40
+ return true;
41
+ }
42
+
43
+ checkRestrictedMethod(methodName: string): boolean {
44
+ if (!this.methods.has(methodName)) return false;
45
+ this.warnRestrictedMethod(methodName);
46
+ return true;
47
+ }
48
+
49
+ redirectRestrictedProp(target: any, key: string, value: any): boolean {
50
+ if (!this.props.has(key)) return false;
51
+ const setting = this.props.get(key);
52
+ const { allowCrossDomain, allowFromSameDomain } = setting || {};
53
+
54
+ if (allowCrossDomain) {
55
+ return this.applyAllowedSetting(setting, target, key, value);
56
+ }
57
+
58
+ if (allowFromSameDomain) {
59
+ if (isAllowedUrl(value)) {
60
+ return this.applyAllowedSetting(setting, target, key, value);
61
+ }
62
+ this.warnRestricted(key);
63
+ return true;
64
+ }
65
+
66
+ if (setting?.useMethod) {
67
+ const method = target?.[setting.useMethod];
68
+ if (typeof method === 'function') {
69
+ method.call(target, value);
70
+ return true;
71
+ }
72
+ }
73
+
74
+ this.warnRestricted(key);
75
+ return true;
76
+ }
77
+
78
+ checkRestrictedAttributeCall(
79
+ methodName: string,
80
+ args: any[]
81
+ ): { blocked: boolean; attrName?: string } {
82
+ if (methodName !== 'setAttribute' || args.length === 0 || this.attrs.size === 0) {
83
+ return { blocked: false };
84
+ }
85
+ const attrName = String(args[0]);
86
+ if (!this.attrs.has(attrName)) {
87
+ return { blocked: false };
88
+ }
89
+ const setting = this.attrs.get(attrName);
90
+ const value = args[1];
91
+ const { allowCrossDomain, allowFromSameDomain } = setting || {};
92
+
93
+ if (allowCrossDomain) {
94
+ return { blocked: false, attrName };
95
+ }
96
+
97
+ if (allowFromSameDomain) {
98
+ if (isAllowedUrl(value)) {
99
+ return { blocked: false, attrName };
100
+ }
101
+ this.warnRestricted(attrName);
102
+ return { blocked: true, attrName };
103
+ }
104
+
105
+ this.warnRestricted(attrName);
106
+ return { blocked: true, attrName };
107
+ }
108
+
109
+ private applyAllowedSetting(
110
+ setting: RestrictedPropSetting | undefined,
111
+ target: any,
112
+ key: string,
113
+ value: any
114
+ ): boolean {
115
+ if (setting?.useMethod) {
116
+ const method = target?.[setting.useMethod];
117
+ if (typeof method === 'function') {
118
+ method.call(target, value);
119
+ return true;
120
+ }
121
+ this.warnRestricted(key);
122
+ return true;
123
+ }
124
+ return false;
125
+ }
126
+
127
+ private warnRestricted(key: string): void {
128
+ if (!this.warned.has(key)) {
129
+ this.warned.add(key);
130
+ console.warn(`assignGingerly: property '${key}' is in restrictedPropSettings — assignment skipped.`);
131
+ }
132
+ }
133
+
134
+ private warnRestrictedMethod(methodName: string): void {
135
+ if (!this.warnedMethods.has(methodName)) {
136
+ this.warnedMethods.add(methodName);
137
+ console.warn(`assignGingerly: method '${methodName}' is in restrictedMethodSettings — method call skipped.`);
138
+ }
139
+ }
140
+ }
141
+
142
+ function buildMaps(permissions: AssignPermissions | undefined): RestrictedPropSettingsMap {
143
+ const settings = permissions?.restrictedPropSettings;
144
+ const props = new Map<string, RestrictedPropSetting | undefined>();
145
+ const attrs = new Map<string, RestrictedPropSetting | undefined>();
146
+ if (!settings || settings.length === 0) {
147
+ return { props, attrs };
148
+ }
149
+
150
+ for (const setting of settings) {
151
+ if (typeof setting === 'string') {
152
+ if (props.has(setting)) {
153
+ throw new Error(`assignGingerly: duplicate restrictedPropSettings entry for '${setting}'.`);
154
+ }
155
+ props.set(setting, undefined);
156
+ continue;
157
+ }
158
+
159
+ const propNames = normalizeStrings(setting.props);
160
+ if (propNames.length === 0) {
161
+ throw new Error('assignGingerly: restrictedPropSettings object entry must have a props value.');
162
+ }
163
+ for (const prop of propNames) {
164
+ if (props.has(prop)) {
165
+ throw new Error(`assignGingerly: duplicate restrictedPropSettings entry for '${prop}'.`);
166
+ }
167
+ props.set(prop, setting);
168
+ }
169
+
170
+ const attrNames = normalizeAttrNames(setting.attr, propNames);
171
+ for (const attr of attrNames) {
172
+ if (attrs.has(attr)) {
173
+ throw new Error(`assignGingerly: duplicate restrictedPropSettings attr entry for '${attr}'.`);
174
+ }
175
+ attrs.set(attr, setting);
176
+ }
177
+ }
178
+ return { props, attrs };
179
+ }
180
+
181
+ function normalizeStrings(value: string | string[] | undefined): string[] {
182
+ if (value === undefined) return [];
183
+ return Array.isArray(value) ? value : [value];
184
+ }
185
+
186
+ function normalizeAttrNames(
187
+ attr: boolean | string | string[] | undefined,
188
+ propNames: string[]
189
+ ): string[] {
190
+ if (attr === undefined || attr === false) return [];
191
+ if (attr === true) return propNames;
192
+ return normalizeStrings(attr);
193
+ }
194
+
195
+ function buildMethodSet(permissions: AssignPermissions | undefined): Set<string> {
196
+ const methodSettings = permissions?.restrictedMethodSettings;
197
+ const methods = new Set<string>();
198
+ if (!methodSettings || methodSettings.length === 0) {
199
+ return methods;
200
+ }
201
+
202
+ for (const setting of methodSettings) {
203
+ if (typeof setting === 'string') {
204
+ methods.add(setting);
205
+ }
206
+ // Phase II: object-form RestrictedMethodConfig entries are ignored for now.
207
+ }
208
+
209
+ return methods;
210
+ }
@@ -1,3 +1,4 @@
1
+ import { isAllowedUrl } from './isAllowedUrl.js';
1
2
  /**
2
3
  * Check whether an import specifier resolves to the current origin or is
3
4
  * covered by an import-map entry.
@@ -8,12 +9,7 @@ export function isAllowedImportPath(value) {
8
9
  if (!value || !(value.startsWith('./') || value.startsWith('../') || value.startsWith('/'))) {
9
10
  return isImportMapSpecifier(value);
10
11
  }
11
- try {
12
- return new URL(value, document.baseURI).origin === location.origin;
13
- }
14
- catch {
15
- return false;
16
- }
12
+ return isAllowedUrl(value);
17
13
  }
18
14
  function isImportMapSpecifier(value) {
19
15
  const importMaps = document.querySelectorAll('script[type="importmap"]');
@@ -1,3 +1,5 @@
1
+ import { isAllowedUrl } from './isAllowedUrl.js';
2
+
1
3
  /**
2
4
  * Check whether an import specifier resolves to the current origin or is
3
5
  * covered by an import-map entry.
@@ -8,11 +10,7 @@ export function isAllowedImportPath(value: string): boolean {
8
10
  return isImportMapSpecifier(value);
9
11
  }
10
12
 
11
- try {
12
- return new URL(value, document.baseURI).origin === location.origin;
13
- } catch {
14
- return false;
15
- }
13
+ return isAllowedUrl(value);
16
14
  }
17
15
 
18
16
  function isImportMapSpecifier(value: string): boolean {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Check whether a URL string resolves to the current origin.
3
+ *
4
+ * Returns false for non-strings, empty strings, malformed URLs, or
5
+ * any URL whose origin differs from `location.origin`.
6
+ */
7
+ export function isAllowedUrl(value, base = document.baseURI) {
8
+ if (typeof document === 'undefined' || typeof location === 'undefined')
9
+ return false;
10
+ if (typeof value !== 'string' || value === '')
11
+ return false;
12
+ try {
13
+ return new URL(value, base).origin === location.origin;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Check whether a URL string resolves to the current origin.
3
+ *
4
+ * Returns false for non-strings, empty strings, malformed URLs, or
5
+ * any URL whose origin differs from `location.origin`.
6
+ */
7
+ export function isAllowedUrl(value: string, base = document.baseURI): boolean {
8
+ if (typeof document === 'undefined' || typeof location === 'undefined') return false;
9
+ if (typeof value !== 'string' || value === '') return false;
10
+ try {
11
+ return new URL(value, base).origin === location.origin;
12
+ } catch {
13
+ return false;
14
+ }
15
+ }
@@ -1,4 +1,3 @@
1
- import { buildRestrictedPropSet, checkRestrictedProp } from './assignPermissions/restrictedProps.js';
2
1
  /**
3
2
  * Helper function to check if a string key represents an += command
4
3
  */
@@ -114,13 +113,12 @@ function getTopLevelKey(path) {
114
113
  /**
115
114
  * Main assignTentatively function with reversal support
116
115
  */
117
- export function assignTentatively(target, source, options, permissions) {
116
+ export function assignTentatively(target, source, options, permissionProcessor) {
118
117
  if (!target || typeof target !== 'object') {
119
118
  return target;
120
119
  }
121
120
  const reversal = options?.reversal || {};
122
121
  const trackedCreatedPaths = new Set();
123
- const restrictedPropSet = buildRestrictedPropSet(permissions);
124
122
  // Process all keys from source
125
123
  for (const key of Object.keys(source)) {
126
124
  const value = source[key];
@@ -132,7 +130,7 @@ export function assignTentatively(target, source, options, permissions) {
132
130
  const pathParts = parsePath(path);
133
131
  const topLevelKey = pathParts[0];
134
132
  const lastKey = pathParts[pathParts.length - 1];
135
- if (checkRestrictedProp(restrictedPropSet, lastKey)) {
133
+ if (permissionProcessor?.checkRestrictedProp(lastKey)) {
136
134
  continue;
137
135
  }
138
136
  // Track if we created a new top-level path (BEFORE calling ensureNestedPath)
@@ -155,7 +153,7 @@ export function assignTentatively(target, source, options, permissions) {
155
153
  }
156
154
  else {
157
155
  // Plain key - direct operation on target
158
- if (checkRestrictedProp(restrictedPropSet, path)) {
156
+ if (permissionProcessor?.checkRestrictedProp(path)) {
159
157
  continue;
160
158
  }
161
159
  if (path in target) {
@@ -183,7 +181,7 @@ export function assignTentatively(target, source, options, permissions) {
183
181
  lhsPathParts = parsePath(lhsPath);
184
182
  const topLevelKey = lhsPathParts[0];
185
183
  lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
186
- if (checkRestrictedProp(restrictedPropSet, lhsLastKey)) {
184
+ if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
187
185
  continue;
188
186
  }
189
187
  // Track if we created a new top-level path (BEFORE calling ensureNestedPath)
@@ -195,7 +193,7 @@ export function assignTentatively(target, source, options, permissions) {
195
193
  else {
196
194
  lhsPathParts = [lhsPath];
197
195
  lhsLastKey = lhsPath;
198
- if (checkRestrictedProp(restrictedPropSet, lhsLastKey)) {
196
+ if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
199
197
  continue;
200
198
  }
201
199
  lhsParent = target;
@@ -301,7 +299,7 @@ export function assignTentatively(target, source, options, permissions) {
301
299
  const pathParts = parsePath(key);
302
300
  const topLevelKey = pathParts[0];
303
301
  const lastKey = pathParts[pathParts.length - 1];
304
- if (checkRestrictedProp(restrictedPropSet, lastKey)) {
302
+ if (permissionProcessor?.checkRestrictedProp(lastKey)) {
305
303
  continue;
306
304
  }
307
305
  // Track if we created a new top-level path (BEFORE calling ensureNestedPath)
@@ -323,7 +321,7 @@ export function assignTentatively(target, source, options, permissions) {
323
321
  }
324
322
  // For nested objects, recursively apply with nested reversal tracking
325
323
  const nestedReversal = {};
326
- assignTentatively(parent[lastKey], value, { reversal: nestedReversal }, permissions);
324
+ assignTentatively(parent[lastKey], value, { reversal: nestedReversal }, permissionProcessor);
327
325
  // Merge nested reversals
328
326
  for (const revKey of Object.keys(nestedReversal)) {
329
327
  if (!(revKey in reversal)) {
@@ -344,7 +342,7 @@ export function assignTentatively(target, source, options, permissions) {
344
342
  }
345
343
  else {
346
344
  // Non-nested key
347
- if (checkRestrictedProp(restrictedPropSet, key)) {
345
+ if (permissionProcessor?.checkRestrictedProp(key)) {
348
346
  continue;
349
347
  }
350
348
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
@@ -359,7 +357,7 @@ export function assignTentatively(target, source, options, permissions) {
359
357
  target[key] = {};
360
358
  }
361
359
  const nestedReversal = {};
362
- assignTentatively(target[key], value, { reversal: nestedReversal }, permissions);
360
+ assignTentatively(target[key], value, { reversal: nestedReversal }, permissionProcessor);
363
361
  // Merge nested reversals
364
362
  for (const revKey of Object.keys(nestedReversal)) {
365
363
  if (!(revKey in reversal)) {
@@ -1,9 +1,7 @@
1
1
  /**
2
2
  * assignTentatively — reversible assignment with change tracking.
3
3
  */
4
- import type { IAssignTentativelyOptions } from './types/assign-gingerly/types.js';
5
- import type { AssignPermissions } from './types/assign-gingerly/types.js';
6
- import { buildRestrictedPropSet, checkRestrictedProp } from './assignPermissions/restrictedProps.js';
4
+ import type { IAssignTentativelyOptions, PermissionProcessor } from './types/assign-gingerly/types.js';
7
5
  export type { IAssignTentativelyOptions };
8
6
 
9
7
  /**
@@ -137,7 +135,7 @@ export function assignTentatively(
137
135
  target: any,
138
136
  source: Record<string | symbol, any>,
139
137
  options?: IAssignTentativelyOptions,
140
- permissions?: AssignPermissions
138
+ permissionProcessor?: PermissionProcessor
141
139
  ): any {
142
140
  if (!target || typeof target !== 'object') {
143
141
  return target;
@@ -145,7 +143,7 @@ export function assignTentatively(
145
143
 
146
144
  const reversal = options?.reversal || {};
147
145
  const trackedCreatedPaths = new Set<string>();
148
- const restrictedPropSet = buildRestrictedPropSet(permissions);
146
+
149
147
 
150
148
  // Process all keys from source
151
149
  for (const key of Object.keys(source)) {
@@ -160,7 +158,7 @@ export function assignTentatively(
160
158
  const topLevelKey = pathParts[0];
161
159
  const lastKey = pathParts[pathParts.length - 1];
162
160
 
163
- if (checkRestrictedProp(restrictedPropSet, lastKey)) {
161
+ if (permissionProcessor?.checkRestrictedProp(lastKey)) {
164
162
  continue;
165
163
  }
166
164
 
@@ -184,7 +182,7 @@ export function assignTentatively(
184
182
  }
185
183
  } else {
186
184
  // Plain key - direct operation on target
187
- if (checkRestrictedProp(restrictedPropSet, path)) {
185
+ if (permissionProcessor?.checkRestrictedProp(path)) {
188
186
  continue;
189
187
  }
190
188
  if (path in target) {
@@ -215,7 +213,7 @@ export function assignTentatively(
215
213
  const topLevelKey = lhsPathParts[0];
216
214
  lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
217
215
 
218
- if (checkRestrictedProp(restrictedPropSet, lhsLastKey)) {
216
+ if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
219
217
  continue;
220
218
  }
221
219
 
@@ -228,7 +226,7 @@ export function assignTentatively(
228
226
  } else {
229
227
  lhsPathParts = [lhsPath];
230
228
  lhsLastKey = lhsPath;
231
- if (checkRestrictedProp(restrictedPropSet, lhsLastKey)) {
229
+ if (permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
232
230
  continue;
233
231
  }
234
232
  lhsParent = target;
@@ -337,7 +335,7 @@ export function assignTentatively(
337
335
  const topLevelKey = pathParts[0];
338
336
  const lastKey = pathParts[pathParts.length - 1];
339
337
 
340
- if (checkRestrictedProp(restrictedPropSet, lastKey)) {
338
+ if (permissionProcessor?.checkRestrictedProp(lastKey)) {
341
339
  continue;
342
340
  }
343
341
 
@@ -362,7 +360,7 @@ export function assignTentatively(
362
360
  }
363
361
  // For nested objects, recursively apply with nested reversal tracking
364
362
  const nestedReversal: Record<string | symbol, any> = {};
365
- assignTentatively(parent[lastKey], value, { reversal: nestedReversal }, permissions);
363
+ assignTentatively(parent[lastKey], value, { reversal: nestedReversal }, permissionProcessor);
366
364
  // Merge nested reversals
367
365
  for (const revKey of Object.keys(nestedReversal)) {
368
366
  if (!(revKey in reversal)) {
@@ -381,7 +379,7 @@ export function assignTentatively(
381
379
  }
382
380
  } else {
383
381
  // Non-nested key
384
- if (checkRestrictedProp(restrictedPropSet, key)) {
382
+ if (permissionProcessor?.checkRestrictedProp(key)) {
385
383
  continue;
386
384
  }
387
385
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
@@ -396,7 +394,7 @@ export function assignTentatively(
396
394
  target[key] = {};
397
395
  }
398
396
  const nestedReversal: Record<string | symbol, any> = {};
399
- assignTentatively(target[key], value, { reversal: nestedReversal }, permissions);
397
+ assignTentatively(target[key], value, { reversal: nestedReversal }, permissionProcessor);
400
398
  // Merge nested reversals
401
399
  for (const revKey of Object.keys(nestedReversal)) {
402
400
  if (!(revKey in reversal)) {
package/beVigilant.js CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { Infer } from './inferencer/inferencer.js';
11
11
  import { withScopePerimeter } from './inferencer/withScopePerimeter.js';
12
- import { registerInCache } from './resolveIdRef.js';
12
+ import { registerInCache } from './resolve/resolveIdRef.js';
13
13
  /**
14
14
  * Set up a MutationObserver that watches for new [itemprop] elements
15
15
  * and applies inferred assignments from the live `from` object.
package/beVigilant.ts CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  import { Infer } from './inferencer/inferencer.js';
12
12
  import { withScopePerimeter } from './inferencer/withScopePerimeter.js';
13
- import { registerInCache } from './resolveIdRef.js';
13
+ import { registerInCache } from './resolve/resolveIdRef.js';
14
14
 
15
15
  /**
16
16
  * Set up a MutationObserver that watches for new [itemprop] elements
package/buildCSSQuery.js CHANGED
@@ -1,4 +1,4 @@
1
- import { resolveTemplate } from './resolveTemplate.js';
1
+ import { resolveTemplate } from './resolve/resolveTemplate.js';
2
2
  /**
3
3
  * Extracts attribute names from withAttrs configuration
4
4
  * Resolves template variables and excludes underscore-prefixed config keys
package/buildCSSQuery.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EnhancementConfig, AttrPatterns } from './types/assign-gingerly/types';
2
- import { resolveTemplate } from './resolveTemplate.js';
2
+ import { resolveTemplate } from './resolve/resolveTemplate.js';
3
3
 
4
4
  /**
5
5
  * Extracts attribute names from withAttrs configuration
package/eachTime.js CHANGED
@@ -3,7 +3,6 @@
3
3
  * This module is dynamically loaded only when @eachTime is encountered
4
4
  * Provides event-driven iteration over elements as they mount
5
5
  */
6
- import { redirectRestrictedProp } from './assignPermissions/restrictedProps.js';
7
6
  /**
8
7
  * Check if a value is an EventTarget
9
8
  */
@@ -21,7 +20,7 @@ function isEventTarget(value) {
21
20
  * @param aliasMap - Map of aliases for token substitution
22
21
  * @param options - Options including required AbortSignal
23
22
  */
24
- export async function handleEachTime(target, pathParts, forEachIndex, value, withMethods, aliasMap, options, permissions, restrictedPropSet) {
23
+ export async function handleEachTime(target, pathParts, forEachIndex, value, withMethods, aliasMap, options, permissionProcessor) {
25
24
  // Validate signal - required for cleanup
26
25
  if (!options?.signal) {
27
26
  throw new Error('@eachTime requires an AbortSignal in options.signal for cleanup');
@@ -35,7 +34,7 @@ export async function handleEachTime(target, pathParts, forEachIndex, value, wit
35
34
  // Import evaluatePathWithMethods for navigation
36
35
  const { evaluatePathWithMethods } = await import('./assignGingerly.js');
37
36
  if (withMethods && withMethods.size > 0) {
38
- const result = evaluatePathWithMethods(target, pathToEventSource, value, withMethods);
37
+ const result = evaluatePathWithMethods(target, pathToEventSource, value, withMethods, permissionProcessor);
39
38
  eventSource = result.target;
40
39
  }
41
40
  else {
@@ -61,7 +60,7 @@ export async function handleEachTime(target, pathParts, forEachIndex, value, wit
61
60
  // Import needed functions from assignGingerly
62
61
  const { evaluatePathWithMethods, assignGingerly, isReadonlyProperty } = await import('./assignGingerly.js');
63
62
  if (pathAfterForEach.length > 0) {
64
- const result = evaluatePathWithMethods(mountedElement, pathAfterForEach, value, withMethods || new Set());
63
+ const result = evaluatePathWithMethods(mountedElement, pathAfterForEach, value, withMethods || new Set(), permissionProcessor);
65
64
  if (result.isMethod) {
66
65
  // Last segment is a method - call it
67
66
  const method = result.target[result.lastKey];
@@ -82,7 +81,7 @@ export async function handleEachTime(target, pathParts, forEachIndex, value, wit
82
81
  // Normal assignment
83
82
  const lastKey = result.lastKey;
84
83
  const parent = result.target;
85
- if (redirectRestrictedProp(restrictedPropSet, parent, lastKey, value)) {
84
+ if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
86
85
  // skip
87
86
  }
88
87
  else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
@@ -93,7 +92,7 @@ export async function handleEachTime(target, pathParts, forEachIndex, value, wit
93
92
  throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
94
93
  }
95
94
  // Recursively apply assignGingerly
96
- assignGingerly(currentValue, value, options, permissions);
95
+ assignGingerly(currentValue, value, options, permissionProcessor);
97
96
  }
98
97
  else {
99
98
  // Property is writable - replace it
package/eachTime.ts CHANGED
@@ -4,10 +4,7 @@
4
4
  * Provides event-driven iteration over elements as they mount
5
5
  */
6
6
 
7
- import type { IAssignGingerlyOptions } from './types/assign-gingerly/types.js';
8
- import type { AssignPermissions } from './types/assign-gingerly/types.js';
9
- import { redirectRestrictedProp } from './assignPermissions/restrictedProps.js';
10
- import type { RestrictedPropSettingsMap } from './assignPermissions/restrictedProps.js';
7
+ import type { PermissionProcessor, IAssignGingerlyOptions } from './types/assign-gingerly/types.js';
11
8
 
12
9
  /**
13
10
  * Check if a value is an EventTarget
@@ -32,11 +29,10 @@ export async function handleEachTime(
32
29
  pathParts: string[],
33
30
  forEachIndex: number,
34
31
  value: any,
35
- withMethods: Set<string> | undefined,
36
- aliasMap: Map<string, string>,
37
- options?: IAssignGingerlyOptions,
38
- permissions?: AssignPermissions,
39
- restrictedPropSet?: RestrictedPropSettingsMap
32
+ withMethods: Set<string> | undefined,
33
+ aliasMap: Map<string, string>,
34
+ options?: IAssignGingerlyOptions,
35
+ permissionProcessor?: PermissionProcessor
40
36
  ): Promise<void> {
41
37
  // Validate signal - required for cleanup
42
38
  if (!options?.signal) {
@@ -54,7 +50,7 @@ export async function handleEachTime(
54
50
  const { evaluatePathWithMethods } = await import('./assignGingerly.js');
55
51
 
56
52
  if (withMethods && withMethods.size > 0) {
57
- const result = evaluatePathWithMethods(target, pathToEventSource, value, withMethods);
53
+ const result = evaluatePathWithMethods(target, pathToEventSource, value, withMethods, permissionProcessor);
58
54
  eventSource = result.target;
59
55
  } else {
60
56
  // Simple property navigation
@@ -90,7 +86,8 @@ export async function handleEachTime(
90
86
  mountedElement,
91
87
  pathAfterForEach,
92
88
  value,
93
- withMethods || new Set()
89
+ withMethods || new Set(),
90
+ permissionProcessor
94
91
  );
95
92
 
96
93
  if (result.isMethod) {
@@ -111,9 +108,9 @@ export async function handleEachTime(
111
108
  const lastKey = result.lastKey;
112
109
  const parent = result.target;
113
110
 
114
- if (redirectRestrictedProp(restrictedPropSet, parent, lastKey, value)) {
115
- // skip
116
- } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
111
+ if (permissionProcessor?.redirectRestrictedProp(parent, lastKey, value)) {
112
+ // skip
113
+ } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
117
114
  // Check if property exists and is readonly
118
115
  if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
119
116
  const currentValue = parent[lastKey];
@@ -123,7 +120,7 @@ export async function handleEachTime(
123
120
  );
124
121
  }
125
122
  // Recursively apply assignGingerly
126
- assignGingerly(currentValue, value, options, permissions);
123
+ assignGingerly(currentValue, value, options, permissionProcessor);
127
124
  } else {
128
125
  // Property is writable - replace it
129
126
  parent[lastKey] = value;
package/enhanceAll.js CHANGED
@@ -33,13 +33,13 @@ import { findClassPrototypeInPath } from './utils/findClassPrototypeInPath.js';
33
33
  * @param target - The DOM element to search within
34
34
  * @param configs - Array of enhancement configurations
35
35
  */
36
- export async function enhanceAll(target, configs, permissions) {
36
+ export async function enhanceAll(target, configs, permissionProcessor) {
37
37
  for (const config of configs) {
38
38
  // Validate EMC path unless cross-domain imports are explicitly permitted
39
- if (!permissions?.crossDomainImports && !isAllowedImportPath(config.emc)) {
39
+ if (!permissionProcessor?.crossDomainImports && !isAllowedImportPath(config.emc)) {
40
40
  throw new Error(`enhanceAll: EMC path "${config.emc}" is a cross-domain URL. ` +
41
41
  `Only same-origin paths or import-map-covered specifiers are allowed by default. ` +
42
- `Pass { crossDomainImports: true } in permissions to override.`);
42
+ `Pass { crossDomainImports: true } in permissionProcessor to override.`);
43
43
  }
44
44
  // 1. Import the EMC JSON
45
45
  const emcModule = await import(config.emc, { with: { type: 'json' } });