assign-gingerly 0.0.18 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -20
- package/assignGingerly.js +29 -16
- package/assignGingerly.ts +29 -17
- package/buildCSSQuery.js +2 -35
- package/buildCSSQuery.ts +2 -46
- package/global.d.ts +25 -0
- package/index.js +2 -1
- package/index.ts +2 -1
- package/object-extension.js +5 -14
- package/object-extension.ts +6 -16
- package/package.json +5 -1
- package/parseWithAttrs.js +2 -38
- package/parseWithAttrs.ts +2 -50
- package/resolveTemplate.js +49 -0
- package/resolveTemplate.ts +59 -0
- package/types/assign-gingerly/types.d.ts +3 -3
package/README.md
CHANGED
|
@@ -292,7 +292,7 @@ This guarantees that applying the reversal object restores the object to its exa
|
|
|
292
292
|
## Dependency injection based on a registry object and a Symbolic reference mapping
|
|
293
293
|
|
|
294
294
|
```Typescript
|
|
295
|
-
interface
|
|
295
|
+
interface IEnhancementRegistryItem<T = any, TObjToExtend = any> {
|
|
296
296
|
spawn: {new(objToExtend: TObjToExtend, ctx: SpawnContext, initVals: Partial<T>): T}
|
|
297
297
|
symlinks?: {[key: symbol]: keyof T}
|
|
298
298
|
// Optional: for element enhancement access
|
|
@@ -317,15 +317,15 @@ class YourEnhancement{
|
|
|
317
317
|
set madAboutFourteen(nv){}
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
class
|
|
321
|
-
push(
|
|
320
|
+
class EnhancementRegistry{
|
|
321
|
+
push(IEnhancementRegistryItem | IEnhancementRegistryItem[]){
|
|
322
322
|
...
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
//Here's where the dependency injection mapping takes place
|
|
327
|
-
const
|
|
328
|
-
|
|
327
|
+
const EnhancementRegistry = new EnhancementRegistry;
|
|
328
|
+
EnhancementRegistry.push([
|
|
329
329
|
{
|
|
330
330
|
symlinks: {
|
|
331
331
|
[isHappy]: 'isHappy'
|
|
@@ -347,7 +347,7 @@ const result = assignGingerly({}, {
|
|
|
347
347
|
'?.style?.height': '40px',
|
|
348
348
|
'?.enh?.mellowYellow?.madAboutFourteen': true
|
|
349
349
|
}, {
|
|
350
|
-
registry:
|
|
350
|
+
registry: EnhancementRegistry
|
|
351
351
|
});
|
|
352
352
|
//result.set[isMellow] = false;
|
|
353
353
|
```
|
|
@@ -401,7 +401,7 @@ const registryItem = {
|
|
|
401
401
|
enhKey: 'myEnh'
|
|
402
402
|
};
|
|
403
403
|
|
|
404
|
-
const registry = new
|
|
404
|
+
const registry = new EnhancementRegistry();
|
|
405
405
|
registry.push(registryItem);
|
|
406
406
|
|
|
407
407
|
const element = document.createElement('div');
|
|
@@ -438,7 +438,7 @@ const registryItem = {
|
|
|
438
438
|
}
|
|
439
439
|
};
|
|
440
440
|
|
|
441
|
-
const registry = new
|
|
441
|
+
const registry = new EnhancementRegistry();
|
|
442
442
|
registry.push(registryItem);
|
|
443
443
|
|
|
444
444
|
const target = {};
|
|
@@ -465,7 +465,7 @@ const result = assignGingerly({}, {
|
|
|
465
465
|
'?.style.height': '40px',
|
|
466
466
|
'?.enh?.mellowYellow?.madAboutFourteen': true
|
|
467
467
|
}, {
|
|
468
|
-
registry:
|
|
468
|
+
registry: EnhancementRegistry
|
|
469
469
|
});
|
|
470
470
|
```
|
|
471
471
|
</details>
|
|
@@ -551,7 +551,7 @@ myElement.assignGingerly({
|
|
|
551
551
|
<details>
|
|
552
552
|
<summary>Lazy Registry Creation</summary>
|
|
553
553
|
|
|
554
|
-
Each `CustomElementRegistry` instance gets its own `enhancementRegistry` property via a lazy getter. The `
|
|
554
|
+
Each `CustomElementRegistry` instance gets its own `enhancementRegistry` property via a lazy getter. The `EnhancementRegistry` instance is created on first access and cached for subsequent uses:
|
|
555
555
|
|
|
556
556
|
```TypeScript
|
|
557
557
|
const element1 = document.createElement('div');
|
|
@@ -572,7 +572,7 @@ console.log(registry1 === element1.customElementRegistry.enhancementRegistry); /
|
|
|
572
572
|
You can still provide an explicit `registry` option to override the automatic behavior:
|
|
573
573
|
|
|
574
574
|
```TypeScript
|
|
575
|
-
const customRegistry = new
|
|
575
|
+
const customRegistry = new EnhancementRegistry();
|
|
576
576
|
// ... configure customRegistry ...
|
|
577
577
|
|
|
578
578
|
myElement.assignGingerly({
|
|
@@ -593,7 +593,7 @@ The `enh.set` proxy allows you to assign properties to enhancements using a clea
|
|
|
593
593
|
|
|
594
594
|
```TypeScript
|
|
595
595
|
import 'assign-gingerly/object-extension.js';
|
|
596
|
-
//import {
|
|
596
|
+
//import { EnhancementRegistry } from 'assign-gingerly';
|
|
597
597
|
|
|
598
598
|
// Define an enhancement class
|
|
599
599
|
class MyEnhancement {
|
|
@@ -662,7 +662,7 @@ Element enhancement classes should follow this constructor signature:
|
|
|
662
662
|
|
|
663
663
|
```TypeScript
|
|
664
664
|
interface SpawnContext<T, TMountContext = any> {
|
|
665
|
-
config:
|
|
665
|
+
config: IEnhancementRegistryItem<T>;
|
|
666
666
|
mountCtx?: TMountContext; // Optional custom context passed by caller
|
|
667
667
|
}
|
|
668
668
|
|
|
@@ -716,7 +716,7 @@ This is useful for:
|
|
|
716
716
|
In addition to spawn and symlinks, registry items support optional properties `enhKey`, `withAttrs`, `canSpawn`, and `lifecycleKeys`:
|
|
717
717
|
|
|
718
718
|
```TypeScript
|
|
719
|
-
interface
|
|
719
|
+
interface IEnhancementRegistryItem<T, TObj = Element> {
|
|
720
720
|
spawn: {
|
|
721
721
|
new (obj?: TObj, ctx?: SpawnContext<T>, initVals?: Partial<T>): T;
|
|
722
722
|
canSpawn?: (obj: TObj, ctx?: SpawnContext<T>) => boolean; // Optional spawn guard
|
|
@@ -813,10 +813,10 @@ console.log(element.enh.plainData); // { prop1: 'value1', prop2: 'value2' }
|
|
|
813
813
|
<details>
|
|
814
814
|
<summary>Finding Registry Items by enhKey</summary>
|
|
815
815
|
|
|
816
|
-
The `
|
|
816
|
+
The `EnhancementRegistry` class includes a `findByEnhKey` method:
|
|
817
817
|
|
|
818
818
|
```TypeScript
|
|
819
|
-
const registry = new
|
|
819
|
+
const registry = new EnhancementRegistry();
|
|
820
820
|
registry.push({
|
|
821
821
|
spawn: MyEnhancement,
|
|
822
822
|
enhKey: 'myEnh'
|
|
@@ -1228,7 +1228,7 @@ class DivOnlyEnhancement {
|
|
|
1228
1228
|
}
|
|
1229
1229
|
}
|
|
1230
1230
|
|
|
1231
|
-
const registry = new
|
|
1231
|
+
const registry = new EnhancementRegistry();
|
|
1232
1232
|
registry.push({
|
|
1233
1233
|
spawn: DivOnlyEnhancement,
|
|
1234
1234
|
enhKey: 'divOnly'
|
|
@@ -1262,7 +1262,7 @@ static canSpawn(obj: any, ctx?: SpawnContext<T>): boolean
|
|
|
1262
1262
|
```
|
|
1263
1263
|
|
|
1264
1264
|
- `obj`: The target object being enhanced (element, plain object, etc.)
|
|
1265
|
-
- `ctx`: Optional spawn context containing `{ config:
|
|
1265
|
+
- `ctx`: Optional spawn context containing `{ config: IEnhancementRegistryItem<T> }`
|
|
1266
1266
|
- Returns: `true` to allow spawning, `false` to block
|
|
1267
1267
|
|
|
1268
1268
|
### Use Cases
|
|
@@ -1329,7 +1329,7 @@ class ValidatedEnhancement {
|
|
|
1329
1329
|
### Example with Dependency Injection
|
|
1330
1330
|
|
|
1331
1331
|
```TypeScript
|
|
1332
|
-
import assignGingerly, {
|
|
1332
|
+
import assignGingerly, { EnhancementRegistry } from 'assign-gingerly';
|
|
1333
1333
|
|
|
1334
1334
|
class ElementOnlyEnhancement {
|
|
1335
1335
|
value = null;
|
|
@@ -1339,7 +1339,7 @@ class ElementOnlyEnhancement {
|
|
|
1339
1339
|
}
|
|
1340
1340
|
}
|
|
1341
1341
|
|
|
1342
|
-
const registry = new
|
|
1342
|
+
const registry = new EnhancementRegistry();
|
|
1343
1343
|
const enhSymbol = Symbol.for('myEnhancement');
|
|
1344
1344
|
|
|
1345
1345
|
registry.push({
|
package/assignGingerly.js
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
// Polyfill for Map.prototype.getOrInsert and WeakMap.prototype.getOrInsert
|
|
2
|
+
if (typeof Map.prototype.getOrInsertComputed !== 'function') {
|
|
3
|
+
Map.prototype.getOrInsertComputed = function (key, insert) {
|
|
4
|
+
if (this.has(key))
|
|
5
|
+
return this.get(key);
|
|
6
|
+
const value = insert();
|
|
7
|
+
this.set(key, value);
|
|
8
|
+
return value;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
if (typeof WeakMap.prototype.getOrInsertComputed !== 'function') {
|
|
12
|
+
WeakMap.prototype.getOrInsertComputed = function (key, insert) {
|
|
13
|
+
if (this.has(key))
|
|
14
|
+
return this.get(key);
|
|
15
|
+
const value = insert();
|
|
16
|
+
this.set(key, value);
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
1
20
|
/**
|
|
2
21
|
* GUID for global instance map storage to ensure uniqueness across package versions
|
|
3
22
|
*/
|
|
@@ -16,21 +35,21 @@ export function getInstanceMap() {
|
|
|
16
35
|
/**
|
|
17
36
|
* Base registry class for managing enhancement configurations
|
|
18
37
|
*/
|
|
19
|
-
export class
|
|
20
|
-
items = [];
|
|
38
|
+
export class EnhancementRegistry {
|
|
39
|
+
#items = [];
|
|
21
40
|
push(items) {
|
|
22
41
|
if (Array.isArray(items)) {
|
|
23
|
-
this
|
|
42
|
+
this.#items.push(...items);
|
|
24
43
|
}
|
|
25
44
|
else {
|
|
26
|
-
this
|
|
45
|
+
this.#items.push(items);
|
|
27
46
|
}
|
|
28
47
|
}
|
|
29
48
|
getItems() {
|
|
30
|
-
return this
|
|
49
|
+
return this.#items;
|
|
31
50
|
}
|
|
32
51
|
findBySymbol(symbol) {
|
|
33
|
-
return this
|
|
52
|
+
return this.#items.find(item => {
|
|
34
53
|
const symlinks = item.symlinks;
|
|
35
54
|
if (!symlinks)
|
|
36
55
|
return false;
|
|
@@ -43,7 +62,7 @@ export class BaseRegistry {
|
|
|
43
62
|
});
|
|
44
63
|
}
|
|
45
64
|
findByEnhKey(enhKey) {
|
|
46
|
-
return this
|
|
65
|
+
return this.#items.find(item => item.enhKey === enhKey);
|
|
47
66
|
}
|
|
48
67
|
}
|
|
49
68
|
/**
|
|
@@ -142,7 +161,7 @@ export function assignGingerly(target, source, options) {
|
|
|
142
161
|
if (!target || typeof target !== 'object') {
|
|
143
162
|
return target;
|
|
144
163
|
}
|
|
145
|
-
const registry = options?.registry instanceof
|
|
164
|
+
const registry = options?.registry instanceof EnhancementRegistry
|
|
146
165
|
? options.registry
|
|
147
166
|
: options?.registry
|
|
148
167
|
? new options.registry()
|
|
@@ -305,10 +324,7 @@ export function assignGingerly(target, source, options) {
|
|
|
305
324
|
if (registryItem) {
|
|
306
325
|
const instanceMap = getInstanceMap();
|
|
307
326
|
// Get or initialize the instances map for this target
|
|
308
|
-
|
|
309
|
-
instanceMap.set(target, new Map());
|
|
310
|
-
}
|
|
311
|
-
const instances = instanceMap.get(target);
|
|
327
|
+
const instances = instanceMap.getOrInsertComputed(target, () => new Map());
|
|
312
328
|
// Check if instance already exists (keyed by registryItem)
|
|
313
329
|
let instance = instances.get(registryItem);
|
|
314
330
|
if (!instance) {
|
|
@@ -363,10 +379,7 @@ export function assignGingerly(target, source, options) {
|
|
|
363
379
|
const registryItem = registry.findBySymbol(prop);
|
|
364
380
|
if (registryItem) {
|
|
365
381
|
const instanceMap = getInstanceMap();
|
|
366
|
-
|
|
367
|
-
instanceMap.set(target, new Map());
|
|
368
|
-
}
|
|
369
|
-
const instances = instanceMap.get(target);
|
|
382
|
+
const instances = instanceMap.getOrInsertComputed(target, () => new Map());
|
|
370
383
|
let instance = instances.get(registryItem);
|
|
371
384
|
if (!instance) {
|
|
372
385
|
const SpawnClass = registryItem.spawn;
|
package/assignGingerly.ts
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
import { EnhancementConfig } from "./types/assign-gingerly/types";
|
|
4
4
|
|
|
5
|
+
// Polyfill for Map.prototype.getOrInsert and WeakMap.prototype.getOrInsert
|
|
6
|
+
if (typeof Map.prototype.getOrInsertComputed !== 'function') {
|
|
7
|
+
Map.prototype.getOrInsertComputed = function(key, insert) {
|
|
8
|
+
if (this.has(key)) return this.get(key);
|
|
9
|
+
const value = insert();
|
|
10
|
+
this.set(key, value);
|
|
11
|
+
return value;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (typeof WeakMap.prototype.getOrInsertComputed !== 'function') {
|
|
15
|
+
WeakMap.prototype.getOrInsertComputed = function(key, insert) {
|
|
16
|
+
if (this.has(key)) return this.get(key);
|
|
17
|
+
const value = insert();
|
|
18
|
+
this.set(key, value);
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
5
23
|
/**
|
|
6
24
|
* @deprecated Use EnhancementConfig instead
|
|
7
25
|
*/
|
|
@@ -11,7 +29,7 @@ export type IBaseRegistryItem<T = any> = EnhancementConfig<T>;
|
|
|
11
29
|
* Interface for the options passed to assignGingerly
|
|
12
30
|
*/
|
|
13
31
|
export interface IAssignGingerlyOptions {
|
|
14
|
-
registry?: typeof
|
|
32
|
+
registry?: typeof EnhancementRegistry | EnhancementRegistry;
|
|
15
33
|
}
|
|
16
34
|
|
|
17
35
|
/**
|
|
@@ -34,23 +52,23 @@ export function getInstanceMap(): WeakMap<object, Map<EnhancementConfig, any>> {
|
|
|
34
52
|
/**
|
|
35
53
|
* Base registry class for managing enhancement configurations
|
|
36
54
|
*/
|
|
37
|
-
export class
|
|
38
|
-
|
|
55
|
+
export class EnhancementRegistry {
|
|
56
|
+
#items: EnhancementConfig[] = [];
|
|
39
57
|
|
|
40
58
|
push(items: EnhancementConfig | EnhancementConfig[]): void {
|
|
41
59
|
if (Array.isArray(items)) {
|
|
42
|
-
this
|
|
60
|
+
this.#items.push(...items);
|
|
43
61
|
} else {
|
|
44
|
-
this
|
|
62
|
+
this.#items.push(items);
|
|
45
63
|
}
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
getItems(): EnhancementConfig[] {
|
|
49
|
-
return this
|
|
67
|
+
return this.#items;
|
|
50
68
|
}
|
|
51
69
|
|
|
52
70
|
findBySymbol(symbol: symbol | string): EnhancementConfig | undefined {
|
|
53
|
-
return this
|
|
71
|
+
return this.#items.find(item => {
|
|
54
72
|
const symlinks = item.symlinks;
|
|
55
73
|
if (!symlinks) return false;
|
|
56
74
|
return Object.keys(symlinks).some(key => {
|
|
@@ -63,7 +81,7 @@ export class BaseRegistry {
|
|
|
63
81
|
}
|
|
64
82
|
|
|
65
83
|
findByEnhKey(enhKey: string | symbol): EnhancementConfig | undefined {
|
|
66
|
-
return this
|
|
84
|
+
return this.#items.find(item => item.enhKey === enhKey);
|
|
67
85
|
}
|
|
68
86
|
}
|
|
69
87
|
|
|
@@ -179,7 +197,7 @@ export function assignGingerly(
|
|
|
179
197
|
return target;
|
|
180
198
|
}
|
|
181
199
|
|
|
182
|
-
const registry = options?.registry instanceof
|
|
200
|
+
const registry = options?.registry instanceof EnhancementRegistry
|
|
183
201
|
? options.registry
|
|
184
202
|
: options?.registry
|
|
185
203
|
? new options.registry()
|
|
@@ -350,10 +368,7 @@ export function assignGingerly(
|
|
|
350
368
|
if (registryItem) {
|
|
351
369
|
const instanceMap = getInstanceMap();
|
|
352
370
|
// Get or initialize the instances map for this target
|
|
353
|
-
|
|
354
|
-
instanceMap.set(target, new Map());
|
|
355
|
-
}
|
|
356
|
-
const instances = instanceMap.get(target)!;
|
|
371
|
+
const instances = instanceMap.getOrInsertComputed(target, () => new Map());
|
|
357
372
|
|
|
358
373
|
// Check if instance already exists (keyed by registryItem)
|
|
359
374
|
let instance = instances.get(registryItem);
|
|
@@ -418,10 +433,7 @@ export function assignGingerly(
|
|
|
418
433
|
const registryItem = registry.findBySymbol(prop);
|
|
419
434
|
if (registryItem) {
|
|
420
435
|
const instanceMap = getInstanceMap();
|
|
421
|
-
|
|
422
|
-
instanceMap.set(target, new Map());
|
|
423
|
-
}
|
|
424
|
-
const instances = instanceMap.get(target)!;
|
|
436
|
+
const instances = instanceMap.getOrInsertComputed(target, () => new Map());
|
|
425
437
|
let instance = instances.get(registryItem);
|
|
426
438
|
|
|
427
439
|
if (!instance) {
|
package/buildCSSQuery.js
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
* Resolves template variables in a string recursively
|
|
3
|
-
* @param template - Template string with ${var} placeholders
|
|
4
|
-
* @param patterns - The patterns object containing variable values
|
|
5
|
-
* @param resolvedCache - Cache of already resolved values
|
|
6
|
-
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
7
|
-
* @returns Resolved string
|
|
8
|
-
*/
|
|
9
|
-
function resolveTemplate(template, patterns, resolvedCache, visitedKeys = new Set()) {
|
|
10
|
-
return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
|
|
11
|
-
// Check if already resolved
|
|
12
|
-
if (resolvedCache.has(varName)) {
|
|
13
|
-
return resolvedCache.get(varName);
|
|
14
|
-
}
|
|
15
|
-
// Check for circular reference
|
|
16
|
-
if (visitedKeys.has(varName)) {
|
|
17
|
-
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
18
|
-
}
|
|
19
|
-
const value = patterns[varName];
|
|
20
|
-
if (value === undefined) {
|
|
21
|
-
throw new Error(`Undefined template variable: ${varName}`);
|
|
22
|
-
}
|
|
23
|
-
if (typeof value === 'string') {
|
|
24
|
-
// Recursively resolve
|
|
25
|
-
visitedKeys.add(varName);
|
|
26
|
-
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
27
|
-
visitedKeys.delete(varName);
|
|
28
|
-
resolvedCache.set(varName, resolved);
|
|
29
|
-
return resolved;
|
|
30
|
-
}
|
|
31
|
-
// Non-string value, return as-is
|
|
32
|
-
return String(value);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
1
|
+
import { resolveTemplate } from './resolveTemplate.js';
|
|
35
2
|
/**
|
|
36
3
|
* Extracts attribute names from withAttrs configuration
|
|
37
4
|
* Resolves template variables and excludes underscore-prefixed config keys
|
|
38
5
|
* @param withAttrs - The attribute patterns configuration
|
|
39
6
|
* @returns Array of resolved attribute names
|
|
40
7
|
*/
|
|
41
|
-
function extractAttributeNames(withAttrs) {
|
|
8
|
+
export function extractAttributeNames(withAttrs) {
|
|
42
9
|
const names = [];
|
|
43
10
|
const resolvedCache = new Map();
|
|
44
11
|
// Add base if present
|
package/buildCSSQuery.ts
CHANGED
|
@@ -1,49 +1,5 @@
|
|
|
1
1
|
import { EnhancementConfig, AttrPatterns } from './types/assign-gingerly/types';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Resolves template variables in a string recursively
|
|
5
|
-
* @param template - Template string with ${var} placeholders
|
|
6
|
-
* @param patterns - The patterns object containing variable values
|
|
7
|
-
* @param resolvedCache - Cache of already resolved values
|
|
8
|
-
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
9
|
-
* @returns Resolved string
|
|
10
|
-
*/
|
|
11
|
-
function resolveTemplate(
|
|
12
|
-
template: string,
|
|
13
|
-
patterns: Record<string, any>,
|
|
14
|
-
resolvedCache: Map<string, string>,
|
|
15
|
-
visitedKeys: Set<string> = new Set()
|
|
16
|
-
): string {
|
|
17
|
-
return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
|
|
18
|
-
// Check if already resolved
|
|
19
|
-
if (resolvedCache.has(varName)) {
|
|
20
|
-
return resolvedCache.get(varName)!;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Check for circular reference
|
|
24
|
-
if (visitedKeys.has(varName)) {
|
|
25
|
-
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const value = patterns[varName];
|
|
29
|
-
|
|
30
|
-
if (value === undefined) {
|
|
31
|
-
throw new Error(`Undefined template variable: ${varName}`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (typeof value === 'string') {
|
|
35
|
-
// Recursively resolve
|
|
36
|
-
visitedKeys.add(varName);
|
|
37
|
-
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
38
|
-
visitedKeys.delete(varName);
|
|
39
|
-
resolvedCache.set(varName, resolved);
|
|
40
|
-
return resolved;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Non-string value, return as-is
|
|
44
|
-
return String(value);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
2
|
+
import { resolveTemplate } from './resolveTemplate.js';
|
|
47
3
|
|
|
48
4
|
/**
|
|
49
5
|
* Extracts attribute names from withAttrs configuration
|
|
@@ -51,7 +7,7 @@ function resolveTemplate(
|
|
|
51
7
|
* @param withAttrs - The attribute patterns configuration
|
|
52
8
|
* @returns Array of resolved attribute names
|
|
53
9
|
*/
|
|
54
|
-
function extractAttributeNames(withAttrs: AttrPatterns<any>): string[] {
|
|
10
|
+
export function extractAttributeNames(withAttrs: AttrPatterns<any>): string[] {
|
|
55
11
|
const names: string[] = [];
|
|
56
12
|
const resolvedCache = new Map<string, string>();
|
|
57
13
|
|
package/global.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Type declarations for Map.prototype.getOrInsert and WeakMap.prototype.getOrInsert
|
|
2
|
+
// Feature is now supported in all modern browsers (Chrome 146+, Firefox 134+, Safari 18.2+)
|
|
3
|
+
// See: https://web-platform-dx.github.io/web-features-explorer/features/getorinsert/
|
|
4
|
+
|
|
5
|
+
interface Map<K, V> {
|
|
6
|
+
/**
|
|
7
|
+
* Returns the value associated with the key if it exists, otherwise inserts
|
|
8
|
+
* the value returned by the insert callback and returns it.
|
|
9
|
+
* @param key The key to look up
|
|
10
|
+
* @param insert A callback that returns the value to insert if the key doesn't exist
|
|
11
|
+
* @returns The existing or newly inserted value
|
|
12
|
+
*/
|
|
13
|
+
getOrInsertComputed(key: K, insert: () => V): V;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface WeakMap<K extends object, V> {
|
|
17
|
+
/**
|
|
18
|
+
* Returns the value associated with the key if it exists, otherwise inserts
|
|
19
|
+
* the value returned by the insert callback and returns it.
|
|
20
|
+
* @param key The key to look up
|
|
21
|
+
* @param insert A callback that returns the value to insert if the key doesn't exist
|
|
22
|
+
* @returns The existing or newly inserted value
|
|
23
|
+
*/
|
|
24
|
+
getOrInsertComputed(key: K, insert: () => V): V;
|
|
25
|
+
}
|
package/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { assignGingerly } from './assignGingerly.js';
|
|
2
2
|
export { assignTentatively } from './assignTentatively.js';
|
|
3
|
-
export {
|
|
3
|
+
export { EnhancementRegistry } from './assignGingerly.js';
|
|
4
4
|
export { waitForEvent } from './waitForEvent.js';
|
|
5
5
|
export { ParserRegistry, globalParserRegistry } from './parserRegistry.js';
|
|
6
6
|
export { parseWithAttrs } from './parseWithAttrs.js';
|
|
7
7
|
export { buildCSSQuery } from './buildCSSQuery.js';
|
|
8
|
+
export { resolveTemplate } from './resolveTemplate.js';
|
|
8
9
|
import './object-extension.js';
|
package/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export {assignGingerly} from './assignGingerly.js';
|
|
2
2
|
export {assignTentatively} from './assignTentatively.js';
|
|
3
|
-
export {
|
|
3
|
+
export {EnhancementRegistry} from './assignGingerly.js';
|
|
4
4
|
export {waitForEvent} from './waitForEvent.js';
|
|
5
5
|
export {ParserRegistry, globalParserRegistry} from './parserRegistry.js';
|
|
6
6
|
export {parseWithAttrs} from './parseWithAttrs.js';
|
|
7
7
|
export {buildCSSQuery} from './buildCSSQuery.js';
|
|
8
|
+
export {resolveTemplate} from './resolveTemplate.js';
|
|
8
9
|
import './object-extension.js';
|
package/object-extension.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import assignGingerly, {
|
|
1
|
+
import assignGingerly, { EnhancementRegistry, getInstanceMap } from './assignGingerly.js';
|
|
2
2
|
import { parseWithAttrs } from './parseWithAttrs.js';
|
|
3
3
|
/**
|
|
4
4
|
* Normalizes lifecycleKeys to always return an object with dispose and resolved keys
|
|
@@ -21,7 +21,7 @@ if (typeof CustomElementRegistry !== 'undefined') {
|
|
|
21
21
|
Object.defineProperty(CustomElementRegistry.prototype, 'enhancementRegistry', {
|
|
22
22
|
get: function () {
|
|
23
23
|
// Create a new BaseRegistry instance on first access and cache it
|
|
24
|
-
const registry = new
|
|
24
|
+
const registry = new EnhancementRegistry();
|
|
25
25
|
// Replace the getter with the actual value
|
|
26
26
|
Object.defineProperty(this, 'enhancementRegistry', {
|
|
27
27
|
value: registry,
|
|
@@ -66,10 +66,7 @@ class ElementEnhancementContainer {
|
|
|
66
66
|
}
|
|
67
67
|
// Get or create instance using the global instance map
|
|
68
68
|
const instanceMap = getInstanceMap();
|
|
69
|
-
|
|
70
|
-
instanceMap.set(element, new Map());
|
|
71
|
-
}
|
|
72
|
-
const instances = instanceMap.get(element);
|
|
69
|
+
const instances = instanceMap.getOrInsertComputed(element, () => new Map());
|
|
73
70
|
let instance = instances.get(registryItem);
|
|
74
71
|
if (!instance) {
|
|
75
72
|
// Need to spawn
|
|
@@ -201,10 +198,7 @@ class ElementEnhancementContainer {
|
|
|
201
198
|
const SpawnClass = registryItem.spawn;
|
|
202
199
|
// Check the global instance map first
|
|
203
200
|
const instanceMap = getInstanceMap();
|
|
204
|
-
|
|
205
|
-
instanceMap.set(element, new Map());
|
|
206
|
-
}
|
|
207
|
-
const instances = instanceMap.get(element);
|
|
201
|
+
const instances = instanceMap.getOrInsertComputed(element, () => new Map());
|
|
208
202
|
let instance = instances.get(registryItem);
|
|
209
203
|
if (!instance) {
|
|
210
204
|
// Need to spawn
|
|
@@ -259,10 +253,7 @@ if (typeof Element !== 'undefined') {
|
|
|
259
253
|
const enhContainerWeakMap = new WeakMap();
|
|
260
254
|
Object.defineProperty(Element.prototype, 'enh', {
|
|
261
255
|
get: function () {
|
|
262
|
-
|
|
263
|
-
enhContainerWeakMap.set(this, new ElementEnhancementContainer(this));
|
|
264
|
-
}
|
|
265
|
-
return enhContainerWeakMap.get(this);
|
|
256
|
+
return enhContainerWeakMap.getOrInsertComputed(this, () => new ElementEnhancementContainer(this));
|
|
266
257
|
},
|
|
267
258
|
enumerable: true,
|
|
268
259
|
configurable: true,
|
package/object-extension.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import assignGingerly, {
|
|
1
|
+
import assignGingerly, { EnhancementRegistry, IAssignGingerlyOptions, getInstanceMap, INSTANCE_MAP_GUID } from './assignGingerly.js';
|
|
2
2
|
import { parseWithAttrs } from './parseWithAttrs.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -21,7 +21,7 @@ function normalizeLifecycleKeys(lifecycleKeys: true | { dispose?: string | symbo
|
|
|
21
21
|
*/
|
|
22
22
|
declare global {
|
|
23
23
|
interface CustomElementRegistry {
|
|
24
|
-
enhancementRegistry: typeof
|
|
24
|
+
enhancementRegistry: typeof EnhancementRegistry | EnhancementRegistry;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
interface Element {
|
|
@@ -84,7 +84,7 @@ if (typeof CustomElementRegistry !== 'undefined') {
|
|
|
84
84
|
Object.defineProperty(CustomElementRegistry.prototype, 'enhancementRegistry', {
|
|
85
85
|
get: function () {
|
|
86
86
|
// Create a new BaseRegistry instance on first access and cache it
|
|
87
|
-
const registry = new
|
|
87
|
+
const registry = new EnhancementRegistry();
|
|
88
88
|
// Replace the getter with the actual value
|
|
89
89
|
Object.defineProperty(this, 'enhancementRegistry', {
|
|
90
90
|
value: registry,
|
|
@@ -136,10 +136,7 @@ class ElementEnhancementContainer {
|
|
|
136
136
|
|
|
137
137
|
// Get or create instance using the global instance map
|
|
138
138
|
const instanceMap = getInstanceMap();
|
|
139
|
-
|
|
140
|
-
instanceMap.set(element, new Map());
|
|
141
|
-
}
|
|
142
|
-
const instances = instanceMap.get(element)!;
|
|
139
|
+
const instances = instanceMap.getOrInsertComputed(element, () => new Map());
|
|
143
140
|
|
|
144
141
|
let instance = instances.get(registryItem);
|
|
145
142
|
|
|
@@ -304,10 +301,7 @@ class ElementEnhancementContainer {
|
|
|
304
301
|
|
|
305
302
|
// Check the global instance map first
|
|
306
303
|
const instanceMap = getInstanceMap();
|
|
307
|
-
|
|
308
|
-
instanceMap.set(element, new Map());
|
|
309
|
-
}
|
|
310
|
-
const instances = instanceMap.get(element)!;
|
|
304
|
+
const instances = instanceMap.getOrInsertComputed(element, () => new Map());
|
|
311
305
|
|
|
312
306
|
let instance = instances.get(registryItem);
|
|
313
307
|
|
|
@@ -376,11 +370,7 @@ if (typeof Element !== 'undefined') {
|
|
|
376
370
|
|
|
377
371
|
Object.defineProperty(Element.prototype, 'enh', {
|
|
378
372
|
get: function (this: Element) {
|
|
379
|
-
|
|
380
|
-
enhContainerWeakMap.set(this, new ElementEnhancementContainer(this));
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return enhContainerWeakMap.get(this);
|
|
373
|
+
return enhContainerWeakMap.getOrInsertComputed(this, () => new ElementEnhancementContainer(this));
|
|
384
374
|
},
|
|
385
375
|
enumerable: true,
|
|
386
376
|
configurable: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assign-gingerly",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "This package provides a utility function for carefully merging one object into another.",
|
|
5
5
|
"homepage": "https://github.com/bahrus/assign-gingerly#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
"./buildCSSQuery.js": {
|
|
48
48
|
"default": "./buildCSSQuery.js",
|
|
49
49
|
"types": "./buildCSSQuery.ts"
|
|
50
|
+
},
|
|
51
|
+
"./resolveTemplate.js": {
|
|
52
|
+
"default": "./resolveTemplate.js",
|
|
53
|
+
"types": "./resolveTemplate.ts"
|
|
50
54
|
}
|
|
51
55
|
},
|
|
52
56
|
"main": "index.js",
|
package/parseWithAttrs.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { globalParserRegistry } from './parserRegistry.js';
|
|
2
|
+
import { resolveTemplate } from './resolveTemplate.js';
|
|
2
3
|
// Module-level cache for parsed attribute values
|
|
3
4
|
// Structure: Map<configKey, Map<attrValue, parsedValue>>
|
|
4
5
|
const parseCache = new Map();
|
|
@@ -89,10 +90,7 @@ function parseWithCache(attrValue, config, parser) {
|
|
|
89
90
|
}
|
|
90
91
|
// Get or create cache for this config
|
|
91
92
|
const cacheKey = getCacheKey(config);
|
|
92
|
-
|
|
93
|
-
parseCache.set(cacheKey, new Map());
|
|
94
|
-
}
|
|
95
|
-
const valueCache = parseCache.get(cacheKey);
|
|
93
|
+
const valueCache = parseCache.getOrInsertComputed(cacheKey, () => new Map());
|
|
96
94
|
// Use special key for null values
|
|
97
95
|
const valueCacheKey = attrValue === null ? '__NULL__' : attrValue;
|
|
98
96
|
// Check if we have a cached value
|
|
@@ -160,40 +158,6 @@ function getAttributeValue(element, attrName, allowUnprefixed) {
|
|
|
160
158
|
return enhValue;
|
|
161
159
|
return element.getAttribute(attrName);
|
|
162
160
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Resolves template variables in a string recursively
|
|
165
|
-
* @param template - Template string with ${var} placeholders
|
|
166
|
-
* @param patterns - The patterns object containing variable values
|
|
167
|
-
* @param resolvedCache - Cache of already resolved values
|
|
168
|
-
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
169
|
-
* @returns Resolved string
|
|
170
|
-
*/
|
|
171
|
-
function resolveTemplate(template, patterns, resolvedCache, visitedKeys = new Set()) {
|
|
172
|
-
return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
|
|
173
|
-
// Check if already resolved
|
|
174
|
-
if (resolvedCache.has(varName)) {
|
|
175
|
-
return resolvedCache.get(varName);
|
|
176
|
-
}
|
|
177
|
-
// Check for circular reference
|
|
178
|
-
if (visitedKeys.has(varName)) {
|
|
179
|
-
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
180
|
-
}
|
|
181
|
-
const value = patterns[varName];
|
|
182
|
-
if (value === undefined) {
|
|
183
|
-
throw new Error(`Undefined template variable: ${varName}`);
|
|
184
|
-
}
|
|
185
|
-
if (typeof value === 'string') {
|
|
186
|
-
// Recursively resolve
|
|
187
|
-
visitedKeys.add(varName);
|
|
188
|
-
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
189
|
-
visitedKeys.delete(varName);
|
|
190
|
-
resolvedCache.set(varName, resolved);
|
|
191
|
-
return resolved;
|
|
192
|
-
}
|
|
193
|
-
// Non-string value, return as-is
|
|
194
|
-
return String(value);
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
161
|
/**
|
|
198
162
|
* Gets the default parser for a given instanceOf type
|
|
199
163
|
*/
|
package/parseWithAttrs.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AttrPatterns, AttrConfig } from './types/assign-gingerly/types';
|
|
2
2
|
import { globalParserRegistry } from './parserRegistry.js';
|
|
3
|
+
import { resolveTemplate } from './resolveTemplate.js';
|
|
3
4
|
|
|
4
5
|
// Module-level cache for parsed attribute values
|
|
5
6
|
// Structure: Map<configKey, Map<attrValue, parsedValue>>
|
|
@@ -107,11 +108,7 @@ function parseWithCache(
|
|
|
107
108
|
|
|
108
109
|
// Get or create cache for this config
|
|
109
110
|
const cacheKey = getCacheKey(config);
|
|
110
|
-
|
|
111
|
-
parseCache.set(cacheKey, new Map());
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const valueCache = parseCache.get(cacheKey)!;
|
|
111
|
+
const valueCache = parseCache.getOrInsertComputed(cacheKey, () => new Map());
|
|
115
112
|
|
|
116
113
|
// Use special key for null values
|
|
117
114
|
const valueCacheKey = attrValue === null ? '__NULL__' : attrValue;
|
|
@@ -193,51 +190,6 @@ function getAttributeValue(
|
|
|
193
190
|
return element.getAttribute(attrName);
|
|
194
191
|
}
|
|
195
192
|
|
|
196
|
-
/**
|
|
197
|
-
* Resolves template variables in a string recursively
|
|
198
|
-
* @param template - Template string with ${var} placeholders
|
|
199
|
-
* @param patterns - The patterns object containing variable values
|
|
200
|
-
* @param resolvedCache - Cache of already resolved values
|
|
201
|
-
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
202
|
-
* @returns Resolved string
|
|
203
|
-
*/
|
|
204
|
-
function resolveTemplate(
|
|
205
|
-
template: string,
|
|
206
|
-
patterns: Record<string, any>,
|
|
207
|
-
resolvedCache: Map<string, string>,
|
|
208
|
-
visitedKeys: Set<string> = new Set()
|
|
209
|
-
): string {
|
|
210
|
-
return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
|
|
211
|
-
// Check if already resolved
|
|
212
|
-
if (resolvedCache.has(varName)) {
|
|
213
|
-
return resolvedCache.get(varName)!;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Check for circular reference
|
|
217
|
-
if (visitedKeys.has(varName)) {
|
|
218
|
-
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const value = patterns[varName];
|
|
222
|
-
|
|
223
|
-
if (value === undefined) {
|
|
224
|
-
throw new Error(`Undefined template variable: ${varName}`);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (typeof value === 'string') {
|
|
228
|
-
// Recursively resolve
|
|
229
|
-
visitedKeys.add(varName);
|
|
230
|
-
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
231
|
-
visitedKeys.delete(varName);
|
|
232
|
-
resolvedCache.set(varName, resolved);
|
|
233
|
-
return resolved;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Non-string value, return as-is
|
|
237
|
-
return String(value);
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
193
|
/**
|
|
242
194
|
* Gets the default parser for a given instanceOf type
|
|
243
195
|
*/
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves template variables in a string recursively
|
|
3
|
+
* Supports ${varName} syntax for variable substitution
|
|
4
|
+
*
|
|
5
|
+
* @param template - Template string with ${var} placeholders
|
|
6
|
+
* @param patterns - The patterns object containing variable values
|
|
7
|
+
* @param resolvedCache - Cache of already resolved values
|
|
8
|
+
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
9
|
+
* @returns Resolved string
|
|
10
|
+
*
|
|
11
|
+
* @throws Error if circular reference is detected
|
|
12
|
+
* @throws Error if template variable is undefined
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const patterns = {
|
|
16
|
+
* base: 'my-component',
|
|
17
|
+
* theme: '${base}-theme'
|
|
18
|
+
* };
|
|
19
|
+
* const cache = new Map();
|
|
20
|
+
*
|
|
21
|
+
* resolveTemplate('${theme}', patterns, cache);
|
|
22
|
+
* // Returns: 'my-component-theme'
|
|
23
|
+
*/
|
|
24
|
+
export function resolveTemplate(template, patterns, resolvedCache, visitedKeys = new Set()) {
|
|
25
|
+
return template.replace(/\$\{(\w+)\}/g, (_match, varName) => {
|
|
26
|
+
// Check if already resolved
|
|
27
|
+
if (resolvedCache.has(varName)) {
|
|
28
|
+
return resolvedCache.get(varName);
|
|
29
|
+
}
|
|
30
|
+
// Check for circular reference
|
|
31
|
+
if (visitedKeys.has(varName)) {
|
|
32
|
+
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
33
|
+
}
|
|
34
|
+
const value = patterns[varName];
|
|
35
|
+
if (value === undefined) {
|
|
36
|
+
throw new Error(`Undefined template variable: ${varName}`);
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === 'string') {
|
|
39
|
+
// Recursively resolve
|
|
40
|
+
visitedKeys.add(varName);
|
|
41
|
+
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
42
|
+
visitedKeys.delete(varName);
|
|
43
|
+
resolvedCache.set(varName, resolved);
|
|
44
|
+
return resolved;
|
|
45
|
+
}
|
|
46
|
+
// Non-string value, return as-is
|
|
47
|
+
return String(value);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves template variables in a string recursively
|
|
3
|
+
* Supports ${varName} syntax for variable substitution
|
|
4
|
+
*
|
|
5
|
+
* @param template - Template string with ${var} placeholders
|
|
6
|
+
* @param patterns - The patterns object containing variable values
|
|
7
|
+
* @param resolvedCache - Cache of already resolved values
|
|
8
|
+
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
9
|
+
* @returns Resolved string
|
|
10
|
+
*
|
|
11
|
+
* @throws Error if circular reference is detected
|
|
12
|
+
* @throws Error if template variable is undefined
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const patterns = {
|
|
16
|
+
* base: 'my-component',
|
|
17
|
+
* theme: '${base}-theme'
|
|
18
|
+
* };
|
|
19
|
+
* const cache = new Map();
|
|
20
|
+
*
|
|
21
|
+
* resolveTemplate('${theme}', patterns, cache);
|
|
22
|
+
* // Returns: 'my-component-theme'
|
|
23
|
+
*/
|
|
24
|
+
export function resolveTemplate(
|
|
25
|
+
template: string,
|
|
26
|
+
patterns: Record<string, any>,
|
|
27
|
+
resolvedCache: Map<string, string>,
|
|
28
|
+
visitedKeys: Set<string> = new Set()
|
|
29
|
+
): string {
|
|
30
|
+
return template.replace(/\$\{(\w+)\}/g, (_match, varName) => {
|
|
31
|
+
// Check if already resolved
|
|
32
|
+
if (resolvedCache.has(varName)) {
|
|
33
|
+
return resolvedCache.get(varName)!;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check for circular reference
|
|
37
|
+
if (visitedKeys.has(varName)) {
|
|
38
|
+
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const value = patterns[varName];
|
|
42
|
+
|
|
43
|
+
if (value === undefined) {
|
|
44
|
+
throw new Error(`Undefined template variable: ${varName}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof value === 'string') {
|
|
48
|
+
// Recursively resolve
|
|
49
|
+
visitedKeys.add(varName);
|
|
50
|
+
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
51
|
+
visitedKeys.delete(varName);
|
|
52
|
+
resolvedCache.set(varName, resolved);
|
|
53
|
+
return resolved;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Non-string value, return as-is
|
|
57
|
+
return String(value);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -151,20 +151,20 @@ export interface SpawnContext<T = any, TMountContext = any> {
|
|
|
151
151
|
/**
|
|
152
152
|
* @deprecated Use EnhancementConfig instead
|
|
153
153
|
*/
|
|
154
|
-
export type
|
|
154
|
+
export type IEnhancementRegistryItem<T = any> = EnhancementConfig<T>;
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
157
|
* Interface for the options passed to assignGingerly
|
|
158
158
|
*/
|
|
159
159
|
export interface IAssignGingerlyOptions {
|
|
160
|
-
registry?: typeof
|
|
160
|
+
registry?: typeof EnhancementRegistry | EnhancementRegistry;
|
|
161
161
|
bypassChecks?: boolean;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
165
|
* Base registry class for managing enhancement configurations
|
|
166
166
|
*/
|
|
167
|
-
export declare class
|
|
167
|
+
export declare class EnhancementRegistry {
|
|
168
168
|
private items;
|
|
169
169
|
push(items: EnhancementConfig | EnhancementConfig[]): void;
|
|
170
170
|
getItems(): EnhancementConfig[];
|