assign-gingerly 0.0.10 → 0.0.12
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 +38 -5
- package/assignGingerly.js +14 -10
- package/assignGingerly.ts +18 -29
- package/object-extension.js +11 -9
- package/object-extension.ts +11 -9
- package/package.json +1 -1
- package/types.d.ts +3 -2
package/README.md
CHANGED
|
@@ -607,7 +607,7 @@ When you access `element.enh.set.enhKey.property`, the proxy:
|
|
|
607
607
|
|
|
608
608
|
1. **Checks the registry**: Looks for a registry item with `enhKey` matching the property name
|
|
609
609
|
2. **Spawns if needed**: If found and the enhancement doesn't exist or is the wrong type:
|
|
610
|
-
- Creates a `SpawnContext` with `{
|
|
610
|
+
- Creates a `SpawnContext` with `{ config: registryItem }`
|
|
611
611
|
- Calls the constructor with `(element, ctx, initVals)`
|
|
612
612
|
- If a non-matching object already exists at `element.enh[enhKey]`, it's passed as `initVals`
|
|
613
613
|
- Stores the spawned instance at `element.enh[enhKey]`
|
|
@@ -628,23 +628,51 @@ This approach is part of a proposal to WHATWG for standardizing element enhancem
|
|
|
628
628
|
Enhancement classes should follow this constructor signature:
|
|
629
629
|
|
|
630
630
|
```TypeScript
|
|
631
|
-
interface SpawnContext<T> {
|
|
632
|
-
|
|
631
|
+
interface SpawnContext<T, TMountContext = any> {
|
|
632
|
+
config: IBaseRegistryItem<T>;
|
|
633
|
+
mountCtx?: TMountContext; // Optional custom context passed by caller
|
|
633
634
|
}
|
|
634
635
|
|
|
635
636
|
class Enhancement {
|
|
636
637
|
constructor(
|
|
637
638
|
oElement?: Element, // The element being enhanced
|
|
638
|
-
ctx?: SpawnContext, // Context with registry item info
|
|
639
|
+
ctx?: SpawnContext, // Context with registry item info and optional mountCtx
|
|
639
640
|
initVals?: Partial<T> // Initial values if property existed
|
|
640
641
|
) {
|
|
641
642
|
// Your initialization logic
|
|
643
|
+
// Access custom context via ctx.mountCtx if provided
|
|
642
644
|
}
|
|
643
645
|
}
|
|
644
646
|
```
|
|
645
647
|
|
|
646
648
|
All parameters are optional for backward compatibility with existing code.
|
|
647
649
|
|
|
650
|
+
**Passing Custom Context:**
|
|
651
|
+
|
|
652
|
+
You can pass custom context when calling `enh.get()` or `enh.whenResolved()`:
|
|
653
|
+
|
|
654
|
+
```TypeScript
|
|
655
|
+
// Pass custom context to the spawned instance
|
|
656
|
+
const myContext = { userId: 123, permissions: ['read', 'write'] };
|
|
657
|
+
const instance = element.enh.get(registryItem, myContext);
|
|
658
|
+
|
|
659
|
+
// The constructor receives it via ctx.mountCtx
|
|
660
|
+
class MyEnhancement {
|
|
661
|
+
constructor(oElement, ctx, initVals) {
|
|
662
|
+
console.log(ctx.mountCtx.userId); // 123
|
|
663
|
+
console.log(ctx.mountCtx.permissions); // ['read', 'write']
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
This is useful for:
|
|
669
|
+
- Passing authentication/authorization context
|
|
670
|
+
- Providing configuration that varies per invocation
|
|
671
|
+
- Sharing state between caller and enhancement
|
|
672
|
+
- Dependency injection of services or utilities
|
|
673
|
+
|
|
674
|
+
**Note**: The `mountCtx` is only available when explicitly calling `enh.get()` or `enh.whenResolved()`. It's not available when accessing via the `enh.set` proxy (since that's a property getter with no way to pass parameters).
|
|
675
|
+
|
|
648
676
|
### Registry Item with enhKey
|
|
649
677
|
|
|
650
678
|
Registry items now support optional `enhKey`, `withAttrs`, `canSpawn`, and `lifecycleKeys` properties:
|
|
@@ -1031,6 +1059,11 @@ const customRegistryItem = {
|
|
|
1031
1059
|
// Wait for the enhancement to be fully initialized
|
|
1032
1060
|
const instance = await element.enh.whenResolved(registryItem);
|
|
1033
1061
|
console.log(instance.data); // Data is loaded and ready
|
|
1062
|
+
|
|
1063
|
+
// With custom context
|
|
1064
|
+
const authContext = { token: 'abc123', userId: 456 };
|
|
1065
|
+
const instanceWithContext = await element.enh.whenResolved(registryItem, authContext);
|
|
1066
|
+
// The constructor receives authContext via ctx.mountCtx
|
|
1034
1067
|
```
|
|
1035
1068
|
|
|
1036
1069
|
**How `enh.whenResolved()` works:**
|
|
@@ -1185,7 +1218,7 @@ static canSpawn(obj: any, ctx?: SpawnContext<T>): boolean
|
|
|
1185
1218
|
```
|
|
1186
1219
|
|
|
1187
1220
|
- `obj`: The target object being enhanced (element, plain object, etc.)
|
|
1188
|
-
- `ctx`: Optional spawn context containing `{
|
|
1221
|
+
- `ctx`: Optional spawn context containing `{ config: IBaseRegistryItem<T> }`
|
|
1189
1222
|
- Returns: `true` to allow spawning, `false` to block
|
|
1190
1223
|
|
|
1191
1224
|
### Use Cases
|
package/assignGingerly.js
CHANGED
|
@@ -315,7 +315,7 @@ export function assignGingerly(target, source, options) {
|
|
|
315
315
|
const SpawnClass = registryItem.spawn;
|
|
316
316
|
// Check canSpawn if it exists
|
|
317
317
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
318
|
-
const ctx = {
|
|
318
|
+
const ctx = { config: registryItem };
|
|
319
319
|
if (!SpawnClass.canSpawn(target, ctx)) {
|
|
320
320
|
// canSpawn returned false, skip spawning
|
|
321
321
|
continue;
|
|
@@ -323,7 +323,7 @@ export function assignGingerly(target, source, options) {
|
|
|
323
323
|
}
|
|
324
324
|
// If target is an Element and registryItem has enhKey, pass element to constructor
|
|
325
325
|
if (registryItem.enhKey && typeof Element !== 'undefined' && target instanceof Element) {
|
|
326
|
-
const ctx = {
|
|
326
|
+
const ctx = { config: registryItem };
|
|
327
327
|
const initVals = target.enh?.[registryItem.enhKey] &&
|
|
328
328
|
!(target.enh[registryItem.enhKey] instanceof SpawnClass)
|
|
329
329
|
? target.enh[registryItem.enhKey]
|
|
@@ -344,9 +344,11 @@ export function assignGingerly(target, source, options) {
|
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
// Find the mapped property name
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
347
|
+
if (registryItem.symlinks) {
|
|
348
|
+
const mappedKey = registryItem.symlinks[sym];
|
|
349
|
+
if (mappedKey && instance && typeof instance === 'object') {
|
|
350
|
+
instance[mappedKey] = value;
|
|
351
|
+
}
|
|
350
352
|
}
|
|
351
353
|
}
|
|
352
354
|
}
|
|
@@ -370,7 +372,7 @@ export function assignGingerly(target, source, options) {
|
|
|
370
372
|
const SpawnClass = registryItem.spawn;
|
|
371
373
|
// Check canSpawn if it exists
|
|
372
374
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
373
|
-
const ctx = {
|
|
375
|
+
const ctx = { config: registryItem };
|
|
374
376
|
if (!SpawnClass.canSpawn(target, ctx)) {
|
|
375
377
|
// canSpawn returned false, skip spawning
|
|
376
378
|
return true;
|
|
@@ -378,7 +380,7 @@ export function assignGingerly(target, source, options) {
|
|
|
378
380
|
}
|
|
379
381
|
// If target is an Element and registryItem has enhKey, pass element to constructor
|
|
380
382
|
if (registryItem.enhKey && typeof Element !== 'undefined' && target instanceof Element) {
|
|
381
|
-
const ctx = {
|
|
383
|
+
const ctx = { config: registryItem };
|
|
382
384
|
const initVals = target.enh?.[registryItem.enhKey] &&
|
|
383
385
|
!(target.enh[registryItem.enhKey] instanceof SpawnClass)
|
|
384
386
|
? target.enh[registryItem.enhKey]
|
|
@@ -397,9 +399,11 @@ export function assignGingerly(target, source, options) {
|
|
|
397
399
|
target.enh[registryItem.enhKey] = instance;
|
|
398
400
|
}
|
|
399
401
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
402
|
+
if (registryItem.symlinks) {
|
|
403
|
+
const mappedKey = registryItem.symlinks[prop];
|
|
404
|
+
if (mappedKey && instance && typeof instance === 'object') {
|
|
405
|
+
instance[mappedKey] = value;
|
|
406
|
+
}
|
|
403
407
|
}
|
|
404
408
|
}
|
|
405
409
|
}
|
package/assignGingerly.ts
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration for enhancing elements with class instances
|
|
3
|
-
*/
|
|
4
|
-
export interface EnhancementConfig<T = any> {
|
|
5
|
-
spawn: {
|
|
6
|
-
new (oElement?: Element, ctx?: SpawnContext<T>, initVals?: Partial<T>): T;
|
|
7
|
-
canSpawn?: (obj: any, ctx?: SpawnContext<T>) => boolean;
|
|
8
|
-
};
|
|
9
|
-
symlinks: { [key: string | symbol]: keyof T };
|
|
10
|
-
enhKey?: string;
|
|
11
|
-
lifecycleKeys?: {
|
|
12
|
-
dispose?: string;
|
|
13
|
-
resolved?: string;
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
1
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
2
|
+
|
|
3
|
+
import { EnhancementConfig } from "./types";
|
|
20
4
|
|
|
21
5
|
/**
|
|
22
6
|
* @deprecated Use EnhancementConfig instead
|
|
@@ -379,7 +363,7 @@ export function assignGingerly(
|
|
|
379
363
|
|
|
380
364
|
// Check canSpawn if it exists
|
|
381
365
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
382
|
-
const ctx = {
|
|
366
|
+
const ctx = { config: registryItem };
|
|
383
367
|
if (!SpawnClass.canSpawn(target, ctx)) {
|
|
384
368
|
// canSpawn returned false, skip spawning
|
|
385
369
|
continue;
|
|
@@ -388,7 +372,7 @@ export function assignGingerly(
|
|
|
388
372
|
|
|
389
373
|
// If target is an Element and registryItem has enhKey, pass element to constructor
|
|
390
374
|
if (registryItem.enhKey && typeof Element !== 'undefined' && target instanceof Element) {
|
|
391
|
-
const ctx = {
|
|
375
|
+
const ctx = { config: registryItem };
|
|
392
376
|
const initVals = (target as any).enh?.[registryItem.enhKey] &&
|
|
393
377
|
!((target as any).enh[registryItem.enhKey] instanceof SpawnClass)
|
|
394
378
|
? (target as any).enh[registryItem.enhKey]
|
|
@@ -411,10 +395,13 @@ export function assignGingerly(
|
|
|
411
395
|
}
|
|
412
396
|
|
|
413
397
|
// Find the mapped property name
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
(instance
|
|
398
|
+
if(registryItem.symlinks){
|
|
399
|
+
const mappedKey = registryItem.symlinks[sym];
|
|
400
|
+
if (mappedKey && instance && typeof instance === 'object') {
|
|
401
|
+
(instance as any)[mappedKey] = value;
|
|
402
|
+
}
|
|
417
403
|
}
|
|
404
|
+
|
|
418
405
|
}
|
|
419
406
|
}
|
|
420
407
|
}
|
|
@@ -442,7 +429,7 @@ export function assignGingerly(
|
|
|
442
429
|
|
|
443
430
|
// Check canSpawn if it exists
|
|
444
431
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
445
|
-
const ctx = {
|
|
432
|
+
const ctx = { config: registryItem };
|
|
446
433
|
if (!SpawnClass.canSpawn(target, ctx)) {
|
|
447
434
|
// canSpawn returned false, skip spawning
|
|
448
435
|
return true;
|
|
@@ -451,7 +438,7 @@ export function assignGingerly(
|
|
|
451
438
|
|
|
452
439
|
// If target is an Element and registryItem has enhKey, pass element to constructor
|
|
453
440
|
if (registryItem.enhKey && typeof Element !== 'undefined' && target instanceof Element) {
|
|
454
|
-
const ctx = {
|
|
441
|
+
const ctx = { config: registryItem };
|
|
455
442
|
const initVals = (target as any).enh?.[registryItem.enhKey] &&
|
|
456
443
|
!((target as any).enh[registryItem.enhKey] instanceof SpawnClass)
|
|
457
444
|
? (target as any).enh[registryItem.enhKey]
|
|
@@ -471,11 +458,13 @@ export function assignGingerly(
|
|
|
471
458
|
(target as any).enh[registryItem.enhKey] = instance;
|
|
472
459
|
}
|
|
473
460
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
461
|
+
if(registryItem.symlinks){
|
|
462
|
+
const mappedKey = registryItem.symlinks[prop];
|
|
463
|
+
if (mappedKey && instance && typeof instance === 'object') {
|
|
464
|
+
(instance as any)[mappedKey] = value;
|
|
465
|
+
}
|
|
478
466
|
}
|
|
467
|
+
|
|
479
468
|
}
|
|
480
469
|
}
|
|
481
470
|
return true;
|
package/object-extension.js
CHANGED
|
@@ -48,9 +48,10 @@ class ElementEnhancementContainer {
|
|
|
48
48
|
/**
|
|
49
49
|
* Get or spawn an instance for a registry item
|
|
50
50
|
* @param registryItem - The registry item to get/spawn instance for
|
|
51
|
+
* @param mountCtx - Optional context to pass to the spawned instance
|
|
51
52
|
* @returns The spawned instance
|
|
52
53
|
*/
|
|
53
|
-
get(registryItem) {
|
|
54
|
+
get(registryItem, mountCtx) {
|
|
54
55
|
const element = this.element;
|
|
55
56
|
// Get the registry from customElementRegistry
|
|
56
57
|
const registry = element.customElementRegistry?.enhancementRegistry;
|
|
@@ -75,7 +76,7 @@ class ElementEnhancementContainer {
|
|
|
75
76
|
const SpawnClass = registryItem.spawn;
|
|
76
77
|
// Check canSpawn if it exists
|
|
77
78
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
78
|
-
const ctx = {
|
|
79
|
+
const ctx = { config: registryItem, mountCtx };
|
|
79
80
|
if (!SpawnClass.canSpawn(element, ctx)) {
|
|
80
81
|
// canSpawn returned false, return undefined
|
|
81
82
|
return undefined;
|
|
@@ -83,7 +84,7 @@ class ElementEnhancementContainer {
|
|
|
83
84
|
}
|
|
84
85
|
// Check if there's an enhKey
|
|
85
86
|
if (registryItem.enhKey) {
|
|
86
|
-
const ctx = {
|
|
87
|
+
const ctx = { config: registryItem, mountCtx };
|
|
87
88
|
const self = this;
|
|
88
89
|
// Parse attributes if withAttrs is defined
|
|
89
90
|
let attrInitVals = undefined;
|
|
@@ -111,7 +112,7 @@ class ElementEnhancementContainer {
|
|
|
111
112
|
}
|
|
112
113
|
else {
|
|
113
114
|
// No enhKey, just spawn with element
|
|
114
|
-
const ctx = {
|
|
115
|
+
const ctx = { config: registryItem, mountCtx };
|
|
115
116
|
instance = new SpawnClass(element, ctx);
|
|
116
117
|
}
|
|
117
118
|
// Store in global instance map
|
|
@@ -152,16 +153,17 @@ class ElementEnhancementContainer {
|
|
|
152
153
|
/**
|
|
153
154
|
* Wait for an enhancement instance to be resolved
|
|
154
155
|
* @param registryItem - The registry item to wait for
|
|
156
|
+
* @param mountCtx - Optional context to pass to the spawned instance
|
|
155
157
|
* @returns Promise that resolves with the spawned instance
|
|
156
158
|
*/
|
|
157
|
-
async whenResolved(registryItem) {
|
|
159
|
+
async whenResolved(registryItem, mountCtx) {
|
|
158
160
|
const lifecycleKeys = normalizeLifecycleKeys(registryItem?.lifecycleKeys);
|
|
159
161
|
const resolvedKey = lifecycleKeys?.resolved;
|
|
160
162
|
if (resolvedKey === undefined) {
|
|
161
163
|
throw new Error('Must specify resolved key in lifecycleKeys');
|
|
162
164
|
}
|
|
163
|
-
// Get or spawn the instance
|
|
164
|
-
const spawnedInstance = this.get(registryItem);
|
|
165
|
+
// Get or spawn the instance (pass mountCtx through)
|
|
166
|
+
const spawnedInstance = this.get(registryItem, mountCtx);
|
|
165
167
|
// Check if already resolved
|
|
166
168
|
if (spawnedInstance[resolvedKey]) {
|
|
167
169
|
return spawnedInstance;
|
|
@@ -209,7 +211,7 @@ class ElementEnhancementContainer {
|
|
|
209
211
|
const SpawnClass = registryItem.spawn;
|
|
210
212
|
// Check canSpawn if it exists
|
|
211
213
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
212
|
-
const ctx = {
|
|
214
|
+
const ctx = { config: registryItem };
|
|
213
215
|
if (!SpawnClass.canSpawn(element, ctx)) {
|
|
214
216
|
// canSpawn returned false, return undefined
|
|
215
217
|
return undefined;
|
|
@@ -221,7 +223,7 @@ class ElementEnhancementContainer {
|
|
|
221
223
|
initVals = self[prop];
|
|
222
224
|
}
|
|
223
225
|
// Create spawn context
|
|
224
|
-
const ctx = {
|
|
226
|
+
const ctx = { config: registryItem };
|
|
225
227
|
// Spawn the instance
|
|
226
228
|
instance = new SpawnClass(element, ctx, initVals);
|
|
227
229
|
// Store in global instance map
|
package/object-extension.ts
CHANGED
|
@@ -114,9 +114,10 @@ class ElementEnhancementContainer {
|
|
|
114
114
|
/**
|
|
115
115
|
* Get or spawn an instance for a registry item
|
|
116
116
|
* @param registryItem - The registry item to get/spawn instance for
|
|
117
|
+
* @param mountCtx - Optional context to pass to the spawned instance
|
|
117
118
|
* @returns The spawned instance
|
|
118
119
|
*/
|
|
119
|
-
get(registryItem: any): any {
|
|
120
|
+
get(registryItem: any, mountCtx?: any): any {
|
|
120
121
|
const element = this.element;
|
|
121
122
|
|
|
122
123
|
// Get the registry from customElementRegistry
|
|
@@ -148,7 +149,7 @@ class ElementEnhancementContainer {
|
|
|
148
149
|
|
|
149
150
|
// Check canSpawn if it exists
|
|
150
151
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
151
|
-
const ctx = {
|
|
152
|
+
const ctx = { config: registryItem, mountCtx };
|
|
152
153
|
if (!SpawnClass.canSpawn(element, ctx)) {
|
|
153
154
|
// canSpawn returned false, return undefined
|
|
154
155
|
return undefined;
|
|
@@ -157,7 +158,7 @@ class ElementEnhancementContainer {
|
|
|
157
158
|
|
|
158
159
|
// Check if there's an enhKey
|
|
159
160
|
if (registryItem.enhKey) {
|
|
160
|
-
const ctx = {
|
|
161
|
+
const ctx = { config: registryItem, mountCtx };
|
|
161
162
|
const self = this as any;
|
|
162
163
|
|
|
163
164
|
// Parse attributes if withAttrs is defined
|
|
@@ -192,7 +193,7 @@ class ElementEnhancementContainer {
|
|
|
192
193
|
self[registryItem.enhKey] = instance;
|
|
193
194
|
} else {
|
|
194
195
|
// No enhKey, just spawn with element
|
|
195
|
-
const ctx = {
|
|
196
|
+
const ctx = { config: registryItem, mountCtx };
|
|
196
197
|
instance = new SpawnClass(element, ctx);
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -243,9 +244,10 @@ class ElementEnhancementContainer {
|
|
|
243
244
|
/**
|
|
244
245
|
* Wait for an enhancement instance to be resolved
|
|
245
246
|
* @param registryItem - The registry item to wait for
|
|
247
|
+
* @param mountCtx - Optional context to pass to the spawned instance
|
|
246
248
|
* @returns Promise that resolves with the spawned instance
|
|
247
249
|
*/
|
|
248
|
-
async whenResolved(registryItem: any): Promise<any> {
|
|
250
|
+
async whenResolved(registryItem: any, mountCtx?: any): Promise<any> {
|
|
249
251
|
const lifecycleKeys = normalizeLifecycleKeys(registryItem?.lifecycleKeys);
|
|
250
252
|
const resolvedKey = lifecycleKeys?.resolved;
|
|
251
253
|
|
|
@@ -253,8 +255,8 @@ class ElementEnhancementContainer {
|
|
|
253
255
|
throw new Error('Must specify resolved key in lifecycleKeys');
|
|
254
256
|
}
|
|
255
257
|
|
|
256
|
-
// Get or spawn the instance
|
|
257
|
-
const spawnedInstance = this.get(registryItem);
|
|
258
|
+
// Get or spawn the instance (pass mountCtx through)
|
|
259
|
+
const spawnedInstance = this.get(registryItem, mountCtx);
|
|
258
260
|
|
|
259
261
|
// Check if already resolved
|
|
260
262
|
if ((spawnedInstance as any)[resolvedKey]) {
|
|
@@ -315,7 +317,7 @@ class ElementEnhancementContainer {
|
|
|
315
317
|
|
|
316
318
|
// Check canSpawn if it exists
|
|
317
319
|
if (typeof SpawnClass.canSpawn === 'function') {
|
|
318
|
-
const ctx = {
|
|
320
|
+
const ctx = { config: registryItem };
|
|
319
321
|
if (!SpawnClass.canSpawn(element, ctx)) {
|
|
320
322
|
// canSpawn returned false, return undefined
|
|
321
323
|
return undefined;
|
|
@@ -330,7 +332,7 @@ class ElementEnhancementContainer {
|
|
|
330
332
|
}
|
|
331
333
|
|
|
332
334
|
// Create spawn context
|
|
333
|
-
const ctx = {
|
|
335
|
+
const ctx = { config: registryItem };
|
|
334
336
|
|
|
335
337
|
// Spawn the instance
|
|
336
338
|
instance = new SpawnClass(element, ctx, initVals);
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -121,8 +121,9 @@ export type AttrPatterns<T = any> = {
|
|
|
121
121
|
};
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
export interface SpawnContext<T = any> {
|
|
125
|
-
|
|
124
|
+
export interface SpawnContext<T = any, TMountContext = any> {
|
|
125
|
+
config: EnhancementConfig<T>;
|
|
126
|
+
mountCtx?: TMountContext;
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
/**
|